From f009fe9bd17e128be99ff6403aaaba4c5a6293fa Mon Sep 17 00:00:00 2001 From: Nolan Brubaker Date: Thu, 3 Oct 2019 19:44:52 -0400 Subject: [PATCH] Add --plugins flag to velero install (#1930) * Add plugins flag to install Signed-off-by: Nolan Brubaker --- changelogs/unreleased/1930-nrb | 1 + pkg/builder/container_builder.go | 33 +++++++++++++++++++ .../container_builder_test.go} | 4 +-- pkg/cmd/cli/install/install.go | 3 ++ pkg/cmd/cli/plugin/add.go | 33 ++----------------- pkg/install/deployment.go | 17 ++++++++++ pkg/install/resources.go | 6 +++- 7 files changed, 63 insertions(+), 34 deletions(-) create mode 100644 changelogs/unreleased/1930-nrb rename pkg/{cmd/cli/plugin/add_test.go => builder/container_builder_test.go} (96%) diff --git a/changelogs/unreleased/1930-nrb b/changelogs/unreleased/1930-nrb new file mode 100644 index 000000000..616d3c33a --- /dev/null +++ b/changelogs/unreleased/1930-nrb @@ -0,0 +1 @@ +Add a new required --plugins flag for velero install command. --plugins takes a list of container images to add as initcontainers. diff --git a/pkg/builder/container_builder.go b/pkg/builder/container_builder.go index 848347763..d58b3e03f 100644 --- a/pkg/builder/container_builder.go +++ b/pkg/builder/container_builder.go @@ -17,6 +17,8 @@ limitations under the License. package builder import ( + "strings" + corev1api "k8s.io/api/core/v1" ) @@ -35,6 +37,32 @@ func ForContainer(name, image string) *ContainerBuilder { } } +// ForPluginContainer is a helper builder specifically for plugin init containers +func ForPluginContainer(image string, pullPolicy corev1api.PullPolicy) *ContainerBuilder { + volumeMount := ForVolumeMount("plugins", "/target").Result() + return ForContainer(getName(image), image).PullPolicy(pullPolicy).VolumeMounts(volumeMount) +} + +// getName returns the 'name' component of a docker +// image (i.e. everything after the last '/' and before +// any subsequent ':') +func getName(image string) string { + slashIndex := strings.LastIndex(image, "/") + colonIndex := strings.LastIndex(image, ":") + + start := 0 + if slashIndex > 0 { + start = slashIndex + 1 + } + + end := len(image) + if colonIndex > slashIndex { + end = colonIndex + } + + return image[start:end] +} + // Result returns the built Container. func (b *ContainerBuilder) Result() *corev1api.Container { return b.object @@ -66,3 +94,8 @@ func (b *ContainerBuilder) Env(vars ...*corev1api.EnvVar) *ContainerBuilder { } return b } + +func (b *ContainerBuilder) PullPolicy(pullPolicy corev1api.PullPolicy) *ContainerBuilder { + b.object.ImagePullPolicy = pullPolicy + return b +} diff --git a/pkg/cmd/cli/plugin/add_test.go b/pkg/builder/container_builder_test.go similarity index 96% rename from pkg/cmd/cli/plugin/add_test.go rename to pkg/builder/container_builder_test.go index f87bad9e4..4a3d7a584 100644 --- a/pkg/cmd/cli/plugin/add_test.go +++ b/pkg/builder/container_builder_test.go @@ -1,5 +1,5 @@ /* -Copyright 2018 the Velero contributors. +Copyright 2018, 2019 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. @@ -13,7 +13,7 @@ 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. */ -package plugin +package builder import ( "testing" diff --git a/pkg/cmd/cli/install/install.go b/pkg/cmd/cli/install/install.go index 79b70fa2b..55d7a303e 100644 --- a/pkg/cmd/cli/install/install.go +++ b/pkg/cmd/cli/install/install.go @@ -64,6 +64,7 @@ type InstallOptions struct { Wait bool UseVolumeSnapshots bool DefaultResticMaintenanceFrequency time.Duration + Plugins flag.StringArray } // BindFlags adds command line values to the options struct. @@ -92,6 +93,7 @@ func (o *InstallOptions) BindFlags(flags *pflag.FlagSet) { flags.BoolVar(&o.UseRestic, "use-restic", o.UseRestic, "create restic deployment. 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. Optional.") } // NewInstallOptions instantiates a new, default InstallOptions struct. @@ -155,6 +157,7 @@ func (o *InstallOptions) AsVeleroOptions() (*install.VeleroOptions, error) { BSLConfig: o.BackupStorageConfig.Data(), VSLConfig: o.VolumeSnapshotConfig.Data(), DefaultResticMaintenanceFrequency: o.DefaultResticMaintenanceFrequency, + Plugins: o.Plugins, }, nil } diff --git a/pkg/cmd/cli/plugin/add.go b/pkg/cmd/cli/plugin/add.go index a8eea20ae..50e7f1f0b 100644 --- a/pkg/cmd/cli/plugin/add.go +++ b/pkg/cmd/cli/plugin/add.go @@ -28,6 +28,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + "github.com/vmware-tanzu/velero/pkg/builder" "github.com/vmware-tanzu/velero/pkg/client" "github.com/vmware-tanzu/velero/pkg/cmd" "github.com/vmware-tanzu/velero/pkg/cmd/util/flag" @@ -104,17 +105,7 @@ func NewAddCommand(f client.Factory) *cobra.Command { } // add the plugin as an init container - plugin := v1.Container{ - Name: getName(args[0]), - Image: args[0], - ImagePullPolicy: v1.PullPolicy(imagePullPolicyFlag.String()), - VolumeMounts: []v1.VolumeMount{ - { - Name: pluginsVolumeName, - MountPath: "/target", - }, - }, - } + plugin := *builder.ForPluginContainer(args[0], v1.PullPolicy(imagePullPolicyFlag.String())).Result() veleroDeploy.Spec.Template.Spec.InitContainers = append(veleroDeploy.Spec.Template.Spec.InitContainers, plugin) @@ -134,23 +125,3 @@ func NewAddCommand(f client.Factory) *cobra.Command { return c } - -// getName returns the 'name' component of a docker -// image (i.e. everything after the last '/' and before -// any subsequent ':') -func getName(image string) string { - slashIndex := strings.LastIndex(image, "/") - colonIndex := strings.LastIndex(image, ":") - - start := 0 - if slashIndex > 0 { - start = slashIndex + 1 - } - - end := len(image) - if colonIndex > slashIndex { - end = colonIndex - } - - return image[start:end] -} diff --git a/pkg/install/deployment.go b/pkg/install/deployment.go index 168653a1d..cc86e54dd 100644 --- a/pkg/install/deployment.go +++ b/pkg/install/deployment.go @@ -24,6 +24,8 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/vmware-tanzu/velero/pkg/builder" ) type podTemplateOption func(*podTemplateConfig) @@ -36,6 +38,7 @@ type podTemplateConfig struct { resources corev1.ResourceRequirements withSecret bool defaultResticMaintenanceFrequency time.Duration + plugins []string } func WithImage(image string) podTemplateOption { @@ -91,6 +94,12 @@ func WithDefaultResticMaintenanceFrequency(val time.Duration) podTemplateOption } } +func WithPlugins(plugins []string) podTemplateOption { + return func(c *podTemplateConfig) { + c.plugins = plugins + } +} + func Deployment(namespace string, opts ...podTemplateOption) *appsv1.Deployment { // TODO: Add support for server args c := &podTemplateConfig{ @@ -236,5 +245,13 @@ func Deployment(namespace string, opts ...podTemplateOption) *appsv1.Deployment deployment.Spec.Template.Spec.Containers[0].Args = append(deployment.Spec.Template.Spec.Containers[0].Args, fmt.Sprintf("--default-restic-prune-frequency=%v", c.defaultResticMaintenanceFrequency)) } + if len(c.plugins) > 0 { + for _, image := range c.plugins { + container := *builder.ForPluginContainer(image, pullPolicy).Result() + deployment.Spec.Template.Spec.InitContainers = append(deployment.Spec.Template.Spec.InitContainers, container) + } + + } + return deployment } diff --git a/pkg/install/resources.go b/pkg/install/resources.go index 05ccc00ac..a27e5ae1c 100644 --- a/pkg/install/resources.go +++ b/pkg/install/resources.go @@ -215,6 +215,7 @@ type VeleroOptions struct { BSLConfig map[string]string VSLConfig map[string]string DefaultResticMaintenanceFrequency time.Duration + Plugins []string } // AllResources returns a list of all resources necessary to install Velero, in the appropriate order, into a Kubernetes cluster. @@ -266,13 +267,16 @@ func AllResources(o *VeleroOptions) (*unstructured.UnstructuredList, error) { deployOpts = append(deployOpts, WithRestoreOnly()) } + if len(o.Plugins) > 0 { + deployOpts = append(deployOpts, WithPlugins(o.Plugins)) + } + deploy := Deployment(o.Namespace, deployOpts...) appendUnstructured(resources, deploy) if o.UseRestic { ds := DaemonSet(o.Namespace, - WithAnnotations(o.PodAnnotations), WithImage(o.Image), WithResources(o.ResticPodResources),