2018-02-28 01:35:35 +00:00
|
|
|
/*
|
2020-05-06 17:52:44 +00:00
|
|
|
Copyright 2018, 2019, 2020 the Velero contributors.
|
2018-02-28 01:35:35 +00:00
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2018-04-19 19:56:51 +00:00
|
|
|
package install
|
|
|
|
|
|
|
|
import (
|
2019-09-10 18:58:42 +00:00
|
|
|
"time"
|
|
|
|
|
2018-04-19 19:56:51 +00:00
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
|
|
rbacv1beta1 "k8s.io/api/rbac/v1beta1"
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
2019-04-15 21:10:11 +00:00
|
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
2019-03-13 17:23:00 +00:00
|
|
|
|
2019-09-30 21:26:56 +00:00
|
|
|
v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
|
|
|
"github.com/vmware-tanzu/velero/pkg/buildinfo"
|
|
|
|
"github.com/vmware-tanzu/velero/pkg/generated/crds"
|
2018-04-19 19:56:51 +00:00
|
|
|
)
|
|
|
|
|
2019-05-03 18:10:48 +00:00
|
|
|
// Use "latest" if the build process didn't supply a version
|
|
|
|
func imageVersion() string {
|
|
|
|
if buildinfo.Version == "" {
|
|
|
|
return "latest"
|
|
|
|
}
|
|
|
|
return buildinfo.Version
|
|
|
|
}
|
|
|
|
|
2019-04-15 21:10:11 +00:00
|
|
|
// DefaultImage is the default image to use for the Velero deployment and restic daemonset containers.
|
2019-07-29 19:13:06 +00:00
|
|
|
var (
|
2019-10-22 21:39:43 +00:00
|
|
|
DefaultImage = "velero/velero:" + imageVersion()
|
2019-07-29 19:13:06 +00:00
|
|
|
DefaultVeleroPodCPURequest = "500m"
|
|
|
|
DefaultVeleroPodMemRequest = "128Mi"
|
|
|
|
DefaultVeleroPodCPULimit = "1000m"
|
|
|
|
DefaultVeleroPodMemLimit = "256Mi"
|
2019-07-31 19:24:47 +00:00
|
|
|
DefaultResticPodCPURequest = "0"
|
|
|
|
DefaultResticPodMemRequest = "0"
|
|
|
|
DefaultResticPodCPULimit = "0"
|
|
|
|
DefaultResticPodMemLimit = "0"
|
2019-07-29 19:13:06 +00:00
|
|
|
)
|
2019-04-15 21:10:11 +00:00
|
|
|
|
2018-04-19 19:56:51 +00:00
|
|
|
func labels() map[string]string {
|
|
|
|
return map[string]string{
|
2019-01-25 03:33:07 +00:00
|
|
|
"component": "velero",
|
2018-04-19 19:56:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-03 22:22:34 +00:00
|
|
|
func podAnnotations(userAnnotations map[string]string) map[string]string {
|
|
|
|
// Use the default annotations as a starting point
|
|
|
|
base := map[string]string{
|
2018-06-06 21:35:06 +00:00
|
|
|
"prometheus.io/scrape": "true",
|
|
|
|
"prometheus.io/port": "8085",
|
|
|
|
"prometheus.io/path": "/metrics",
|
|
|
|
}
|
2019-07-03 22:22:34 +00:00
|
|
|
|
|
|
|
// Merge base annotations with user annotations to enforce CLI precedence
|
|
|
|
for k, v := range userAnnotations {
|
|
|
|
base[k] = v
|
|
|
|
}
|
|
|
|
|
|
|
|
return base
|
2018-06-06 21:35:06 +00:00
|
|
|
}
|
|
|
|
|
2018-06-14 02:19:42 +00:00
|
|
|
func containerPorts() []corev1.ContainerPort {
|
|
|
|
return []corev1.ContainerPort{
|
|
|
|
{
|
|
|
|
Name: "metrics",
|
|
|
|
ContainerPort: 8085,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-19 19:56:51 +00:00
|
|
|
func objectMeta(namespace, name string) metav1.ObjectMeta {
|
|
|
|
return metav1.ObjectMeta{
|
|
|
|
Name: name,
|
|
|
|
Namespace: namespace,
|
|
|
|
Labels: labels(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-03 20:45:18 +00:00
|
|
|
func ServiceAccount(namespace string, annotations map[string]string) *corev1.ServiceAccount {
|
|
|
|
objMeta := objectMeta(namespace, "velero")
|
|
|
|
objMeta.Annotations = annotations
|
2018-04-19 19:56:51 +00:00
|
|
|
return &corev1.ServiceAccount{
|
2019-10-03 20:45:18 +00:00
|
|
|
ObjectMeta: objMeta,
|
2019-03-13 17:23:00 +00:00
|
|
|
TypeMeta: metav1.TypeMeta{
|
|
|
|
Kind: "ServiceAccount",
|
|
|
|
APIVersion: corev1.SchemeGroupVersion.String(),
|
|
|
|
},
|
2018-04-19 19:56:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func ClusterRoleBinding(namespace string) *rbacv1beta1.ClusterRoleBinding {
|
2019-03-13 17:23:00 +00:00
|
|
|
crb := &rbacv1beta1.ClusterRoleBinding{
|
|
|
|
ObjectMeta: objectMeta("", "velero"),
|
|
|
|
TypeMeta: metav1.TypeMeta{
|
|
|
|
Kind: "ClusterRoleBinding",
|
|
|
|
APIVersion: rbacv1beta1.SchemeGroupVersion.String(),
|
2018-04-19 19:56:51 +00:00
|
|
|
},
|
|
|
|
Subjects: []rbacv1beta1.Subject{
|
|
|
|
{
|
|
|
|
Kind: "ServiceAccount",
|
|
|
|
Namespace: namespace,
|
2019-01-25 03:33:07 +00:00
|
|
|
Name: "velero",
|
2018-04-19 19:56:51 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
RoleRef: rbacv1beta1.RoleRef{
|
|
|
|
Kind: "ClusterRole",
|
|
|
|
Name: "cluster-admin",
|
|
|
|
APIGroup: "rbac.authorization.k8s.io",
|
|
|
|
},
|
|
|
|
}
|
2019-03-13 17:23:00 +00:00
|
|
|
|
|
|
|
return crb
|
2018-04-19 19:56:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func Namespace(namespace string) *corev1.Namespace {
|
|
|
|
return &corev1.Namespace{
|
2019-03-13 17:23:00 +00:00
|
|
|
ObjectMeta: objectMeta("", namespace),
|
|
|
|
TypeMeta: metav1.TypeMeta{
|
|
|
|
Kind: "Namespace",
|
|
|
|
APIVersion: corev1.SchemeGroupVersion.String(),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-31 20:48:16 +00:00
|
|
|
func BackupStorageLocation(namespace, provider, bucket, prefix string, config map[string]string, caCert []byte) *v1.BackupStorageLocation {
|
2019-03-13 17:23:00 +00:00
|
|
|
return &v1.BackupStorageLocation{
|
|
|
|
ObjectMeta: objectMeta(namespace, "default"),
|
|
|
|
TypeMeta: metav1.TypeMeta{
|
|
|
|
Kind: "BackupStorageLocation",
|
|
|
|
APIVersion: v1.SchemeGroupVersion.String(),
|
|
|
|
},
|
|
|
|
Spec: v1.BackupStorageLocationSpec{
|
|
|
|
Provider: provider,
|
|
|
|
StorageType: v1.StorageType{
|
|
|
|
ObjectStorage: &v1.ObjectStorageLocation{
|
|
|
|
Bucket: bucket,
|
|
|
|
Prefix: prefix,
|
2020-03-31 20:48:16 +00:00
|
|
|
CACert: caCert,
|
2019-03-13 17:23:00 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
Config: config,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func VolumeSnapshotLocation(namespace, provider string, config map[string]string) *v1.VolumeSnapshotLocation {
|
|
|
|
return &v1.VolumeSnapshotLocation{
|
|
|
|
ObjectMeta: objectMeta(namespace, "default"),
|
|
|
|
TypeMeta: metav1.TypeMeta{
|
|
|
|
Kind: "VolumeSnapshotLocation",
|
|
|
|
APIVersion: v1.SchemeGroupVersion.String(),
|
|
|
|
},
|
|
|
|
Spec: v1.VolumeSnapshotLocationSpec{
|
|
|
|
Provider: provider,
|
|
|
|
Config: config,
|
2018-04-19 19:56:51 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2019-04-15 21:10:11 +00:00
|
|
|
|
|
|
|
func Secret(namespace string, data []byte) *corev1.Secret {
|
|
|
|
return &corev1.Secret{
|
|
|
|
ObjectMeta: objectMeta(namespace, "cloud-credentials"),
|
|
|
|
TypeMeta: metav1.TypeMeta{
|
|
|
|
Kind: "Secret",
|
|
|
|
APIVersion: corev1.SchemeGroupVersion.String(),
|
|
|
|
},
|
|
|
|
Data: map[string][]byte{
|
|
|
|
"cloud": data,
|
|
|
|
},
|
|
|
|
Type: corev1.SecretTypeOpaque,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func appendUnstructured(list *unstructured.UnstructuredList, obj runtime.Object) error {
|
|
|
|
u, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&obj)
|
2019-10-03 20:45:18 +00:00
|
|
|
|
2019-04-15 21:10:11 +00:00
|
|
|
// Remove the status field so we're not sending blank data to the server.
|
|
|
|
// On CRDs, having an empty status is actually a validation error.
|
|
|
|
delete(u, "status")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
list.Items = append(list.Items, unstructured.Unstructured{Object: u})
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type VeleroOptions struct {
|
2019-09-10 18:58:42 +00:00
|
|
|
Namespace string
|
|
|
|
Image string
|
|
|
|
ProviderName string
|
|
|
|
Bucket string
|
|
|
|
Prefix string
|
|
|
|
PodAnnotations map[string]string
|
2019-10-03 20:45:18 +00:00
|
|
|
ServiceAccountAnnotations map[string]string
|
2019-09-10 18:58:42 +00:00
|
|
|
VeleroPodResources corev1.ResourceRequirements
|
|
|
|
ResticPodResources corev1.ResourceRequirements
|
|
|
|
SecretData []byte
|
|
|
|
RestoreOnly bool
|
|
|
|
UseRestic bool
|
|
|
|
UseVolumeSnapshots bool
|
|
|
|
BSLConfig map[string]string
|
|
|
|
VSLConfig map[string]string
|
|
|
|
DefaultResticMaintenanceFrequency time.Duration
|
2019-10-03 23:44:52 +00:00
|
|
|
Plugins []string
|
2019-10-18 14:41:59 +00:00
|
|
|
NoDefaultBackupLocation bool
|
2020-03-31 20:48:16 +00:00
|
|
|
CACertData []byte
|
2020-05-06 17:52:44 +00:00
|
|
|
Features []string
|
2019-04-15 21:10:11 +00:00
|
|
|
}
|
|
|
|
|
2019-11-04 22:36:04 +00:00
|
|
|
func AllCRDs() *unstructured.UnstructuredList {
|
2019-04-15 21:10:11 +00:00
|
|
|
resources := new(unstructured.UnstructuredList)
|
|
|
|
// Set the GVK so that the serialization framework outputs the list properly
|
|
|
|
resources.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "List"})
|
|
|
|
|
2019-09-24 22:37:28 +00:00
|
|
|
for _, crd := range crds.CRDs {
|
|
|
|
crd.SetLabels(labels())
|
2019-04-15 21:10:11 +00:00
|
|
|
appendUnstructured(resources, crd)
|
|
|
|
}
|
|
|
|
|
2019-11-04 22:36:04 +00:00
|
|
|
return resources
|
|
|
|
}
|
|
|
|
|
|
|
|
// AllResources returns a list of all resources necessary to install Velero, in the appropriate order, into a Kubernetes cluster.
|
|
|
|
// Items are unstructured, since there are different data types returned.
|
|
|
|
func AllResources(o *VeleroOptions) (*unstructured.UnstructuredList, error) {
|
|
|
|
resources := AllCRDs()
|
|
|
|
|
2019-04-15 21:10:11 +00:00
|
|
|
ns := Namespace(o.Namespace)
|
|
|
|
appendUnstructured(resources, ns)
|
|
|
|
|
|
|
|
crb := ClusterRoleBinding(o.Namespace)
|
|
|
|
appendUnstructured(resources, crb)
|
|
|
|
|
2019-10-03 20:45:18 +00:00
|
|
|
sa := ServiceAccount(o.Namespace, o.ServiceAccountAnnotations)
|
2019-04-15 21:10:11 +00:00
|
|
|
appendUnstructured(resources, sa)
|
|
|
|
|
2019-08-01 22:57:36 +00:00
|
|
|
if o.SecretData != nil {
|
|
|
|
sec := Secret(o.Namespace, o.SecretData)
|
|
|
|
appendUnstructured(resources, sec)
|
|
|
|
}
|
2019-04-15 21:10:11 +00:00
|
|
|
|
2019-10-18 14:41:59 +00:00
|
|
|
if !o.NoDefaultBackupLocation {
|
2020-03-31 20:48:16 +00:00
|
|
|
bsl := BackupStorageLocation(o.Namespace, o.ProviderName, o.Bucket, o.Prefix, o.BSLConfig, o.CACertData)
|
2019-10-18 14:41:59 +00:00
|
|
|
appendUnstructured(resources, bsl)
|
|
|
|
}
|
2019-04-15 21:10:11 +00:00
|
|
|
|
2019-05-08 18:15:28 +00:00
|
|
|
// A snapshot location may not be desirable for users relying on restic
|
|
|
|
if o.UseVolumeSnapshots {
|
|
|
|
vsl := VolumeSnapshotLocation(o.Namespace, o.ProviderName, o.VSLConfig)
|
|
|
|
appendUnstructured(resources, vsl)
|
|
|
|
}
|
2019-04-15 21:10:11 +00:00
|
|
|
|
2019-08-01 22:57:36 +00:00
|
|
|
secretPresent := o.SecretData != nil
|
|
|
|
|
2019-09-10 18:58:42 +00:00
|
|
|
deployOpts := []podTemplateOption{
|
2019-07-03 22:22:34 +00:00
|
|
|
WithAnnotations(o.PodAnnotations),
|
2019-04-15 21:10:11 +00:00
|
|
|
WithImage(o.Image),
|
2019-07-29 19:13:06 +00:00
|
|
|
WithResources(o.VeleroPodResources),
|
2019-08-01 22:57:36 +00:00
|
|
|
WithSecret(secretPresent),
|
2019-09-10 18:58:42 +00:00
|
|
|
WithDefaultResticMaintenanceFrequency(o.DefaultResticMaintenanceFrequency),
|
|
|
|
}
|
|
|
|
|
2020-05-06 17:52:44 +00:00
|
|
|
if len(o.Features) > 0 {
|
|
|
|
deployOpts = append(deployOpts, WithFeatures(o.Features))
|
|
|
|
}
|
|
|
|
|
2019-04-15 21:10:11 +00:00
|
|
|
if o.RestoreOnly {
|
2019-09-10 18:58:42 +00:00
|
|
|
deployOpts = append(deployOpts, WithRestoreOnly())
|
2019-04-15 21:10:11 +00:00
|
|
|
}
|
2019-09-10 18:58:42 +00:00
|
|
|
|
2019-10-03 23:44:52 +00:00
|
|
|
if len(o.Plugins) > 0 {
|
|
|
|
deployOpts = append(deployOpts, WithPlugins(o.Plugins))
|
|
|
|
}
|
|
|
|
|
2019-09-10 18:58:42 +00:00
|
|
|
deploy := Deployment(o.Namespace, deployOpts...)
|
|
|
|
|
2019-04-15 21:10:11 +00:00
|
|
|
appendUnstructured(resources, deploy)
|
|
|
|
|
|
|
|
if o.UseRestic {
|
2020-05-06 17:52:44 +00:00
|
|
|
dsOpts := []podTemplateOption{
|
2019-07-03 22:22:34 +00:00
|
|
|
WithAnnotations(o.PodAnnotations),
|
2019-04-15 21:10:11 +00:00
|
|
|
WithImage(o.Image),
|
2019-07-31 19:24:47 +00:00
|
|
|
WithResources(o.ResticPodResources),
|
2019-08-01 22:57:36 +00:00
|
|
|
WithSecret(secretPresent),
|
2020-05-06 17:52:44 +00:00
|
|
|
}
|
|
|
|
if len(o.Features) > 0 {
|
|
|
|
dsOpts = append(dsOpts, WithFeatures(o.Features))
|
|
|
|
}
|
|
|
|
ds := DaemonSet(o.Namespace, dsOpts...)
|
2019-04-15 21:10:11 +00:00
|
|
|
appendUnstructured(resources, ds)
|
|
|
|
}
|
|
|
|
|
|
|
|
return resources, nil
|
|
|
|
}
|