Make secret file optional on install ()

* Make secret file optional on install

Fixes 

Signed-off-by: Nolan Brubaker <brubakern@vmware.com>
pull/1721/head
Nolan Brubaker 2019-08-01 18:57:36 -04:00 committed by KubeKween
parent 2a6929d453
commit 635dd27e1a
9 changed files with 92 additions and 63 deletions

View File

@ -0,0 +1 @@
Make --secret-file argument on `velero install` optional, add --no-secret flag for explicit confirmation

View File

@ -55,6 +55,7 @@ type InstallOptions struct {
ResticPodMemLimit string ResticPodMemLimit string
RestoreOnly bool RestoreOnly bool
SecretFile string SecretFile string
NoSecret bool
DryRun bool DryRun bool
BackupStorageConfig flag.Map BackupStorageConfig flag.Map
VolumeSnapshotConfig flag.Map VolumeSnapshotConfig flag.Map
@ -67,7 +68,8 @@ type InstallOptions struct {
func (o *InstallOptions) BindFlags(flags *pflag.FlagSet) { func (o *InstallOptions) BindFlags(flags *pflag.FlagSet) {
flags.StringVar(&o.ProviderName, "provider", o.ProviderName, "provider name for backup and volume storage") flags.StringVar(&o.ProviderName, "provider", o.ProviderName, "provider name for backup and volume storage")
flags.StringVar(&o.BucketName, "bucket", o.BucketName, "name of the object storage bucket where backups should be stored") flags.StringVar(&o.BucketName, "bucket", o.BucketName, "name of the object storage bucket where backups should be stored")
flags.StringVar(&o.SecretFile, "secret-file", o.SecretFile, "file containing credentials for backup and volume provider") flags.StringVar(&o.SecretFile, "secret-file", o.SecretFile, "file containing credentials for backup and volume provider. If not specified, --no-secret must be used for confirmation. Optional.")
flags.BoolVar(&o.NoSecret, "no-secret", o.NoSecret, "flag indicating if a secret should be created. Must be used as confirmation if --secret-file is not provided. Optional.")
flags.StringVar(&o.Image, "image", o.Image, "image to use for the Velero and restic server pods. Optional.") flags.StringVar(&o.Image, "image", o.Image, "image to use for the Velero and restic server pods. Optional.")
flags.StringVar(&o.Prefix, "prefix", o.Prefix, "prefix under which all Velero data should be stored within the bucket. Optional.") flags.StringVar(&o.Prefix, "prefix", o.Prefix, "prefix under which all Velero data should be stored within the bucket. Optional.")
flags.Var(&o.PodAnnotations, "pod-annotations", "annotations to add to the Velero and restic pods. Optional. Format is key1=value1,key2=value2") flags.Var(&o.PodAnnotations, "pod-annotations", "annotations to add to the Velero and restic pods. Optional. Format is key1=value1,key2=value2")
@ -112,14 +114,17 @@ func NewInstallOptions() *InstallOptions {
// AsVeleroOptions translates the values provided at the command line into values used to instantiate Kubernetes resources // AsVeleroOptions translates the values provided at the command line into values used to instantiate Kubernetes resources
func (o *InstallOptions) AsVeleroOptions() (*install.VeleroOptions, error) { func (o *InstallOptions) AsVeleroOptions() (*install.VeleroOptions, error) {
var secretData []byte
if o.SecretFile != "" && !o.NoSecret {
realPath, err := filepath.Abs(o.SecretFile) realPath, err := filepath.Abs(o.SecretFile)
if err != nil { if err != nil {
return nil, err return nil, err
} }
secretData, err := ioutil.ReadFile(realPath) secretData, err = ioutil.ReadFile(realPath)
if err != nil { if err != nil {
return nil, err return nil, err
} }
}
veleroPodResources, err := parseResourceRequests(o.VeleroPodCPURequest, o.VeleroPodMemRequest, o.VeleroPodCPULimit, o.VeleroPodMemLimit) veleroPodResources, err := parseResourceRequests(o.VeleroPodCPURequest, o.VeleroPodMemRequest, o.VeleroPodCPULimit, o.VeleroPodMemLimit)
if err != nil { if err != nil {
return nil, err return nil, err
@ -179,7 +184,7 @@ This is useful as a starting point for more customized installations.
# velero install --bucket gcp-backups --provider gcp --secret-file ./gcp-creds.json --wait # velero install --bucket gcp-backups --provider gcp --secret-file ./gcp-creds.json --wait
# velero install --bucket backups --provider aws --backup-location-config region=us-west-2 --secret-file ./an-empty-file --snapshot-location-config region=us-west-2 --pod-annotations iam.amazonaws.com/role=arn:aws:iam::<AWS_ACCOUNT_ID>:role/<VELERO_ROLE_NAME> # velero install --bucket backups --provider aws --backup-location-config region=us-west-2 --snapshot-location-config region=us-west-2 --no-secret --pod-annotations iam.amazonaws.com/role=arn:aws:iam::<AWS_ACCOUNT_ID>:role/<VELERO_ROLE_NAME>
# velero install --bucket gcp-backups --provider gcp --secret-file ./gcp-creds.json --velero-pod-cpu-request=1000m --velero-pod-cpu-limit=5000m --velero-pod-mem-request=512Mi --velero-pod-mem-limit=1024Mi # velero install --bucket gcp-backups --provider gcp --secret-file ./gcp-creds.json --velero-pod-cpu-request=1000m --velero-pod-cpu-limit=5000m --velero-pod-mem-request=512Mi --velero-pod-mem-limit=1024Mi
@ -238,6 +243,9 @@ func (o *InstallOptions) Run(c *cobra.Command, f client.Factory) error {
return errors.Wrap(err, errorMsg) return errors.Wrap(err, errorMsg)
} }
} }
if o.SecretFile == "" {
fmt.Printf("\nNo secret file was specified, no Secret created.\n\n")
}
fmt.Printf("Velero is installed! ⛵ Use 'kubectl logs deployment/velero -n %s' to view the status.\n", o.Namespace) fmt.Printf("Velero is installed! ⛵ Use 'kubectl logs deployment/velero -n %s' to view the status.\n", o.Namespace)
return nil return nil
} }
@ -268,8 +276,11 @@ func (o *InstallOptions) Validate(c *cobra.Command, args []string, f client.Fact
return errors.New("--provider is required") return errors.New("--provider is required")
} }
if o.SecretFile == "" { switch {
return errors.New("--secret-file is required") case o.SecretFile == "" && !o.NoSecret:
return errors.New("One of --secret-file or --no-secret is required")
case o.SecretFile != "" && o.NoSecret:
return errors.New("Cannot use both --secret-file and --no-secret")
} }
return nil return nil

View File

@ -129,18 +129,6 @@ func DaemonSet(namespace string, opts ...podTemplateOption) *appsv1.DaemonSet {
Name: "VELERO_SCRATCH_DIR", Name: "VELERO_SCRATCH_DIR",
Value: "/scratch", Value: "/scratch",
}, },
{
Name: "AZURE_CREDENTIALS_FILE",
Value: "/credentials/cloud",
},
{
Name: "GOOGLE_APPLICATION_CREDENTIALS",
Value: "/credentials/cloud",
},
{
Name: "AWS_SHARED_CREDENTIALS_FILE",
Value: "/credentials/cloud",
},
}, },
Resources: c.resources, Resources: c.resources,
}, },
@ -150,7 +138,7 @@ func DaemonSet(namespace string, opts ...podTemplateOption) *appsv1.DaemonSet {
}, },
} }
if !c.withoutCredentialsVolume { if c.withSecret {
daemonSet.Spec.Template.Spec.Volumes = append( daemonSet.Spec.Template.Spec.Volumes = append(
daemonSet.Spec.Template.Spec.Volumes, daemonSet.Spec.Template.Spec.Volumes,
corev1.Volume{ corev1.Volume{
@ -170,6 +158,21 @@ func DaemonSet(namespace string, opts ...podTemplateOption) *appsv1.DaemonSet {
MountPath: "/credentials", MountPath: "/credentials",
}, },
) )
daemonSet.Spec.Template.Spec.Containers[0].Env = append(daemonSet.Spec.Template.Spec.Containers[0].Env, []corev1.EnvVar{
{
Name: "GOOGLE_APPLICATION_CREDENTIALS",
Value: "/credentials/cloud",
},
{
Name: "AWS_SHARED_CREDENTIALS_FILE",
Value: "/credentials/cloud",
},
{
Name: "AZURE_CREDENTIALS_FILE",
Value: "/credentials/cloud",
},
}...)
} }
daemonSet.Spec.Template.Spec.Containers[0].Env = append(daemonSet.Spec.Template.Spec.Containers[0].Env, c.envVars...) daemonSet.Spec.Template.Spec.Containers[0].Env = append(daemonSet.Spec.Template.Spec.Containers[0].Env, c.envVars...)

View File

@ -29,10 +29,11 @@ func TestDaemonSet(t *testing.T) {
assert.Equal(t, "restic", ds.Spec.Template.Spec.Containers[0].Name) assert.Equal(t, "restic", ds.Spec.Template.Spec.Containers[0].Name)
assert.Equal(t, "velero", ds.ObjectMeta.Namespace) assert.Equal(t, "velero", ds.ObjectMeta.Namespace)
ds = DaemonSet("velero", WithoutCredentialsVolume())
assert.Equal(t, 2, len(ds.Spec.Template.Spec.Volumes))
ds = DaemonSet("velero", WithImage("gcr.io/heptio-images/velero:v0.11")) ds = DaemonSet("velero", WithImage("gcr.io/heptio-images/velero:v0.11"))
assert.Equal(t, "gcr.io/heptio-images/velero:v0.11", ds.Spec.Template.Spec.Containers[0].Image) assert.Equal(t, "gcr.io/heptio-images/velero:v0.11", ds.Spec.Template.Spec.Containers[0].Image)
assert.Equal(t, corev1.PullIfNotPresent, ds.Spec.Template.Spec.Containers[0].ImagePullPolicy) assert.Equal(t, corev1.PullIfNotPresent, ds.Spec.Template.Spec.Containers[0].ImagePullPolicy)
ds = DaemonSet("velero", WithSecret(true))
assert.Equal(t, 6, len(ds.Spec.Template.Spec.Containers[0].Env))
assert.Equal(t, 3, len(ds.Spec.Template.Spec.Volumes))
} }

View File

@ -28,11 +28,11 @@ type podTemplateOption func(*podTemplateConfig)
type podTemplateConfig struct { type podTemplateConfig struct {
image string image string
withoutCredentialsVolume bool
envVars []corev1.EnvVar envVars []corev1.EnvVar
restoreOnly bool restoreOnly bool
annotations map[string]string annotations map[string]string
resources corev1.ResourceRequirements resources corev1.ResourceRequirements
withSecret bool
} }
func WithImage(image string) podTemplateOption { func WithImage(image string) podTemplateOption {
@ -47,12 +47,6 @@ func WithAnnotations(annotations map[string]string) podTemplateOption {
} }
} }
func WithoutCredentialsVolume() podTemplateOption {
return func(c *podTemplateConfig) {
c.withoutCredentialsVolume = true
}
}
func WithEnvFromSecretKey(varName, secret, key string) podTemplateOption { func WithEnvFromSecretKey(varName, secret, key string) podTemplateOption {
return func(c *podTemplateConfig) { return func(c *podTemplateConfig) {
c.envVars = append(c.envVars, corev1.EnvVar{ c.envVars = append(c.envVars, corev1.EnvVar{
@ -69,6 +63,12 @@ func WithEnvFromSecretKey(varName, secret, key string) podTemplateOption {
} }
} }
func WithSecret(secretPresent bool) podTemplateOption {
return func(c *podTemplateConfig) {
c.withSecret = secretPresent
}
}
func WithRestoreOnly() podTemplateOption { func WithRestoreOnly() podTemplateOption {
return func(c *podTemplateConfig) { return func(c *podTemplateConfig) {
c.restoreOnly = true c.restoreOnly = true
@ -144,18 +144,6 @@ func Deployment(namespace string, opts ...podTemplateOption) *appsv1.Deployment
Name: "VELERO_SCRATCH_DIR", Name: "VELERO_SCRATCH_DIR",
Value: "/scratch", Value: "/scratch",
}, },
{
Name: "GOOGLE_APPLICATION_CREDENTIALS",
Value: "/credentials/cloud",
},
{
Name: "AWS_SHARED_CREDENTIALS_FILE",
Value: "/credentials/cloud",
},
{
Name: "AZURE_CREDENTIALS_FILE",
Value: "/credentials/cloud",
},
}, },
Resources: c.resources, Resources: c.resources,
}, },
@ -179,7 +167,7 @@ func Deployment(namespace string, opts ...podTemplateOption) *appsv1.Deployment
}, },
} }
if !c.withoutCredentialsVolume { if c.withSecret {
deployment.Spec.Template.Spec.Volumes = append( deployment.Spec.Template.Spec.Volumes = append(
deployment.Spec.Template.Spec.Volumes, deployment.Spec.Template.Spec.Volumes,
corev1.Volume{ corev1.Volume{
@ -199,6 +187,21 @@ func Deployment(namespace string, opts ...podTemplateOption) *appsv1.Deployment
MountPath: "/credentials", MountPath: "/credentials",
}, },
) )
deployment.Spec.Template.Spec.Containers[0].Env = append(deployment.Spec.Template.Spec.Containers[0].Env, []corev1.EnvVar{
{
Name: "GOOGLE_APPLICATION_CREDENTIALS",
Value: "/credentials/cloud",
},
{
Name: "AWS_SHARED_CREDENTIALS_FILE",
Value: "/credentials/cloud",
},
{
Name: "AZURE_CREDENTIALS_FILE",
Value: "/credentials/cloud",
},
}...)
} }
deployment.Spec.Template.Spec.Containers[0].Env = append(deployment.Spec.Template.Spec.Containers[0].Env, c.envVars...) deployment.Spec.Template.Spec.Containers[0].Env = append(deployment.Spec.Template.Spec.Containers[0].Env, c.envVars...)

View File

@ -32,15 +32,16 @@ func TestDeployment(t *testing.T) {
assert.Equal(t, "--restore-only", deploy.Spec.Template.Spec.Containers[0].Args[1]) assert.Equal(t, "--restore-only", deploy.Spec.Template.Spec.Containers[0].Args[1])
deploy = Deployment("velero", WithEnvFromSecretKey("my-var", "my-secret", "my-key")) deploy = Deployment("velero", WithEnvFromSecretKey("my-var", "my-secret", "my-key"))
envSecret := deploy.Spec.Template.Spec.Containers[0].Env[4] envSecret := deploy.Spec.Template.Spec.Containers[0].Env[1]
assert.Equal(t, "my-var", envSecret.Name) assert.Equal(t, "my-var", envSecret.Name)
assert.Equal(t, "my-secret", envSecret.ValueFrom.SecretKeyRef.LocalObjectReference.Name) assert.Equal(t, "my-secret", envSecret.ValueFrom.SecretKeyRef.LocalObjectReference.Name)
assert.Equal(t, "my-key", envSecret.ValueFrom.SecretKeyRef.Key) assert.Equal(t, "my-key", envSecret.ValueFrom.SecretKeyRef.Key)
deploy = Deployment("velero", WithoutCredentialsVolume())
assert.Equal(t, 2, len(deploy.Spec.Template.Spec.Volumes))
deploy = Deployment("velero", WithImage("gcr.io/heptio-images/velero:v0.11")) deploy = Deployment("velero", WithImage("gcr.io/heptio-images/velero:v0.11"))
assert.Equal(t, "gcr.io/heptio-images/velero:v0.11", deploy.Spec.Template.Spec.Containers[0].Image) assert.Equal(t, "gcr.io/heptio-images/velero:v0.11", deploy.Spec.Template.Spec.Containers[0].Image)
assert.Equal(t, corev1.PullIfNotPresent, deploy.Spec.Template.Spec.Containers[0].ImagePullPolicy) assert.Equal(t, corev1.PullIfNotPresent, deploy.Spec.Template.Spec.Containers[0].ImagePullPolicy)
deploy = Deployment("velero", WithSecret(true))
assert.Equal(t, 4, len(deploy.Spec.Template.Spec.Containers[0].Env))
assert.Equal(t, 3, len(deploy.Spec.Template.Spec.Volumes))
} }

View File

@ -229,8 +229,10 @@ func AllResources(o *VeleroOptions) (*unstructured.UnstructuredList, error) {
sa := ServiceAccount(o.Namespace) sa := ServiceAccount(o.Namespace)
appendUnstructured(resources, sa) appendUnstructured(resources, sa)
if o.SecretData != nil {
sec := Secret(o.Namespace, o.SecretData) sec := Secret(o.Namespace, o.SecretData)
appendUnstructured(resources, sec) appendUnstructured(resources, sec)
}
bsl := BackupStorageLocation(o.Namespace, o.ProviderName, o.Bucket, o.Prefix, o.BSLConfig) bsl := BackupStorageLocation(o.Namespace, o.ProviderName, o.Bucket, o.Prefix, o.BSLConfig)
appendUnstructured(resources, bsl) appendUnstructured(resources, bsl)
@ -241,15 +243,19 @@ func AllResources(o *VeleroOptions) (*unstructured.UnstructuredList, error) {
appendUnstructured(resources, vsl) appendUnstructured(resources, vsl)
} }
secretPresent := o.SecretData != nil
deploy := Deployment(o.Namespace, deploy := Deployment(o.Namespace,
WithAnnotations(o.PodAnnotations), WithAnnotations(o.PodAnnotations),
WithImage(o.Image), WithImage(o.Image),
WithResources(o.VeleroPodResources), WithResources(o.VeleroPodResources),
WithSecret(secretPresent),
) )
if o.RestoreOnly { if o.RestoreOnly {
deploy = Deployment(o.Namespace, deploy = Deployment(o.Namespace,
WithAnnotations(o.PodAnnotations), WithAnnotations(o.PodAnnotations),
WithImage(o.Image), WithImage(o.Image),
WithSecret(secretPresent),
WithRestoreOnly(), WithRestoreOnly(),
) )
} }
@ -257,9 +263,11 @@ func AllResources(o *VeleroOptions) (*unstructured.UnstructuredList, error) {
if o.UseRestic { if o.UseRestic {
ds := DaemonSet(o.Namespace, ds := DaemonSet(o.Namespace,
WithAnnotations(o.PodAnnotations), WithAnnotations(o.PodAnnotations),
WithImage(o.Image), WithImage(o.Image),
WithResources(o.ResticPodResources), WithResources(o.ResticPodResources),
WithSecret(secretPresent),
) )
appendUnstructured(resources, ds) appendUnstructured(resources, ds)
} }

View File

@ -292,13 +292,11 @@ velero install \
--pod-annotations iam.amazonaws.com/role=arn:aws:iam::<AWS_ACCOUNT_ID>:role/<VELERO_ROLE_NAME> \ --pod-annotations iam.amazonaws.com/role=arn:aws:iam::<AWS_ACCOUNT_ID>:role/<VELERO_ROLE_NAME> \
--provider aws \ --provider aws \
--bucket $BUCKET \ --bucket $BUCKET \
--secret-file ./credentials-velero \
--backup-location-config region=$REGION \ --backup-location-config region=$REGION \
--snapshot-location-config region=$REGION --snapshot-location-config region=$REGION \
--no-secret
``` ```
Note that the `--secret-file` argument is required, but it can be an empty file.
[0]: namespace.md [0]: namespace.md
[5]: https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html [5]: https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html
[6]: api-types/volumesnapshotlocation.md#aws [6]: api-types/volumesnapshotlocation.md#aws

View File

@ -14,7 +14,8 @@ The Velero client includes an `install` command to specify the settings for each
velero install \ velero install \
--provider <YOUR_PROVIDER> \ --provider <YOUR_PROVIDER> \
--bucket <YOUR_BUCKET> \ --bucket <YOUR_BUCKET> \
--secret-file <PATH_TO_FILE> \ [--secret-file <PATH_TO_FILE>] \
[--no-secret] \
[--backup-location-config] \ [--backup-location-config] \
[--snapshot-location-config] \ [--snapshot-location-config] \
[--namespace] \ [--namespace] \
@ -23,6 +24,8 @@ velero install \
[--pod-annotations] \ [--pod-annotations] \
``` ```
When using node-based IAM policies, `--secret-file` is not required, but `--no-secret` is required for confirmation.
For provider-specific instructions, see: For provider-specific instructions, see:
* [Run Velero on AWS][0] * [Run Velero on AWS][0]