diff --git a/changelogs/unreleased/2503-ashish-amarnath b/changelogs/unreleased/2503-ashish-amarnath new file mode 100644 index 000000000..02c965c1c --- /dev/null +++ b/changelogs/unreleased/2503-ashish-amarnath @@ -0,0 +1 @@ +allow feature flags to be passed from install CLI diff --git a/pkg/cmd/cli/install/install.go b/pkg/cmd/cli/install/install.go index db5002cc0..ce8185348 100644 --- a/pkg/cmd/cli/install/install.go +++ b/pkg/cmd/cli/install/install.go @@ -1,5 +1,5 @@ /* -Copyright 2019 the Velero contributors. +Copyright 2019,2020 the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -69,6 +69,7 @@ type InstallOptions struct { NoDefaultBackupLocation bool CRDsOnly bool CACertFile string + Features string } // BindFlags adds command line values to the options struct. @@ -95,12 +96,13 @@ func (o *InstallOptions) BindFlags(flags *pflag.FlagSet) { flags.BoolVar(&o.UseVolumeSnapshots, "use-volume-snapshots", o.UseVolumeSnapshots, "whether or not to create snapshot location automatically. Set to false if you do not plan to create volume snapshots via a storage provider.") flags.BoolVar(&o.RestoreOnly, "restore-only", o.RestoreOnly, "run the server in restore-only mode. Optional.") flags.BoolVar(&o.DryRun, "dry-run", o.DryRun, "generate resources, but don't send them to the cluster. Use with -o. Optional.") - flags.BoolVar(&o.UseRestic, "use-restic", o.UseRestic, "create restic deployment. Optional.") + flags.BoolVar(&o.UseRestic, "use-restic", o.UseRestic, "create restic daemonset. Optional.") flags.BoolVar(&o.Wait, "wait", o.Wait, "wait for Velero deployment to be ready. Optional.") flags.DurationVar(&o.DefaultResticMaintenanceFrequency, "default-restic-prune-frequency", o.DefaultResticMaintenanceFrequency, "how often 'restic prune' is run for restic repositories by default. Optional.") flags.Var(&o.Plugins, "plugins", "Plugin container images to install into the Velero Deployment") flags.BoolVar(&o.CRDsOnly, "crds-only", o.CRDsOnly, "only generate CustomResourceDefinition resources. Useful for updating CRDs for an existing Velero install.") flags.StringVar(&o.CACertFile, "cacert", o.CACertFile, "file containing a certificate bundle to use when verifying TLS connections to the object store. Optional.") + flags.StringVar(&o.Features, "features", o.Features, "comma separated list of Velero feature flags to be set on the Velero deployment and the restic daemonset, if restic is enabled") } // NewInstallOptions instantiates a new, default InstallOptions struct. @@ -180,6 +182,7 @@ func (o *InstallOptions) AsVeleroOptions() (*install.VeleroOptions, error) { Plugins: o.Plugins, NoDefaultBackupLocation: o.NoDefaultBackupLocation, CACertData: caCertData, + Features: strings.Split(o.Features, ","), }, nil } diff --git a/pkg/install/daemonset.go b/pkg/install/daemonset.go index 145f75b68..2d8c8344d 100644 --- a/pkg/install/daemonset.go +++ b/pkg/install/daemonset.go @@ -1,5 +1,5 @@ /* -Copyright 2018, 2019 the Velero contributors. +Copyright 2018, 2019, 2020 the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ limitations under the License. package install import ( + "fmt" "strings" appsv1 "k8s.io/api/apps/v1" @@ -40,6 +41,14 @@ func DaemonSet(namespace string, opts ...podTemplateOption) *appsv1.DaemonSet { } + resticArgs := []string{ + "restic", + "server", + } + if len(c.features) > 0 { + resticArgs = append(resticArgs, fmt.Sprintf("--features=%s", strings.Join(c.features, ","))) + } + userID := int64(0) mountPropagationMode := corev1.MountPropagationHostToContainer @@ -92,10 +101,7 @@ func DaemonSet(namespace string, opts ...podTemplateOption) *appsv1.DaemonSet { Command: []string{ "/velero", }, - Args: []string{ - "restic", - "server", - }, + Args: resticArgs, VolumeMounts: []corev1.VolumeMount{ { diff --git a/pkg/install/daemonset_test.go b/pkg/install/daemonset_test.go index 75b44a674..db9976fe5 100644 --- a/pkg/install/daemonset_test.go +++ b/pkg/install/daemonset_test.go @@ -1,5 +1,5 @@ /* -Copyright 2019 the Velero contributors. +Copyright 2019, 2020 the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -36,4 +36,8 @@ func TestDaemonSet(t *testing.T) { ds = DaemonSet("velero", WithSecret(true)) assert.Equal(t, 7, len(ds.Spec.Template.Spec.Containers[0].Env)) assert.Equal(t, 3, len(ds.Spec.Template.Spec.Volumes)) + + ds = DaemonSet("velero", WithFeatures([]string{"foo,bar,baz"})) + assert.Len(t, ds.Spec.Template.Spec.Containers[0].Args, 3) + assert.Equal(t, "--features=foo,bar,baz", ds.Spec.Template.Spec.Containers[0].Args[2]) } diff --git a/pkg/install/deployment.go b/pkg/install/deployment.go index 94c48d2d0..32303cf57 100644 --- a/pkg/install/deployment.go +++ b/pkg/install/deployment.go @@ -1,5 +1,5 @@ /* -Copyright 2018, 2019 the Velero contributors. +Copyright 2018, 2019, 2020 the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -39,6 +39,7 @@ type podTemplateConfig struct { withSecret bool defaultResticMaintenanceFrequency time.Duration plugins []string + features []string } func WithImage(image string) podTemplateOption { @@ -100,6 +101,12 @@ func WithPlugins(plugins []string) podTemplateOption { } } +func WithFeatures(features []string) podTemplateOption { + return func(c *podTemplateConfig) { + c.features = features + } +} + func Deployment(namespace string, opts ...podTemplateOption) *appsv1.Deployment { // TODO: Add support for server args c := &podTemplateConfig{ @@ -117,6 +124,11 @@ func Deployment(namespace string, opts ...podTemplateOption) *appsv1.Deployment } + args := []string{"server"} + if len(c.features) > 0 { + args = append(args, fmt.Sprintf("--features=%s", strings.Join(c.features, ","))) + } + containerLabels := labels() containerLabels["deploy"] = "velero" @@ -145,9 +157,7 @@ func Deployment(namespace string, opts ...podTemplateOption) *appsv1.Deployment Command: []string{ "/velero", }, - Args: []string{ - "server", - }, + Args: args, VolumeMounts: []corev1.VolumeMount{ { Name: "plugins", diff --git a/pkg/install/deployment_test.go b/pkg/install/deployment_test.go index 13970a963..85f067488 100644 --- a/pkg/install/deployment_test.go +++ b/pkg/install/deployment_test.go @@ -1,5 +1,5 @@ /* -Copyright 2019 the Velero contributors. +Copyright 2019, 2020 the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -49,4 +49,8 @@ func TestDeployment(t *testing.T) { deploy = Deployment("velero", WithDefaultResticMaintenanceFrequency(24*time.Hour)) assert.Len(t, deploy.Spec.Template.Spec.Containers[0].Args, 2) assert.Equal(t, "--default-restic-prune-frequency=24h0m0s", deploy.Spec.Template.Spec.Containers[0].Args[1]) + + deploy = Deployment("velero", WithFeatures([]string{"EnableCSI", "foo", "bar", "baz"})) + assert.Len(t, deploy.Spec.Template.Spec.Containers[0].Args, 2) + assert.Equal(t, "--features=EnableCSI,foo,bar,baz", deploy.Spec.Template.Spec.Containers[0].Args[1]) } diff --git a/pkg/install/resources.go b/pkg/install/resources.go index 8c072737f..35d1bc4f2 100644 --- a/pkg/install/resources.go +++ b/pkg/install/resources.go @@ -1,5 +1,5 @@ /* -Copyright 2018, 2019 the Velero contributors. +Copyright 2018, 2019, 2020 the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -219,6 +219,7 @@ type VeleroOptions struct { Plugins []string NoDefaultBackupLocation bool CACertData []byte + Features []string } func AllCRDs() *unstructured.UnstructuredList { @@ -274,6 +275,10 @@ func AllResources(o *VeleroOptions) (*unstructured.UnstructuredList, error) { WithDefaultResticMaintenanceFrequency(o.DefaultResticMaintenanceFrequency), } + if len(o.Features) > 0 { + deployOpts = append(deployOpts, WithFeatures(o.Features)) + } + if o.RestoreOnly { deployOpts = append(deployOpts, WithRestoreOnly()) } @@ -287,12 +292,16 @@ func AllResources(o *VeleroOptions) (*unstructured.UnstructuredList, error) { appendUnstructured(resources, deploy) if o.UseRestic { - ds := DaemonSet(o.Namespace, + dsOpts := []podTemplateOption{ WithAnnotations(o.PodAnnotations), WithImage(o.Image), WithResources(o.ResticPodResources), WithSecret(secretPresent), - ) + } + if len(o.Features) > 0 { + dsOpts = append(dsOpts, WithFeatures(o.Features)) + } + ds := DaemonSet(o.Namespace, dsOpts...) appendUnstructured(resources, ds) }