diff --git a/changelogs/unreleased/2096-dinesh b/changelogs/unreleased/2096-dinesh new file mode 100644 index 000000000..3e9b9c3d8 --- /dev/null +++ b/changelogs/unreleased/2096-dinesh @@ -0,0 +1 @@ +Enableing Velero to switch credentials (`AWS_PROFILE`) if multiple s3-compatible backupLocations are present \ No newline at end of file diff --git a/pkg/controller/pod_volume_backup_controller.go b/pkg/controller/pod_volume_backup_controller.go index ba67a7117..a9e211cf3 100644 --- a/pkg/controller/pod_volume_backup_controller.go +++ b/pkg/controller/pod_volume_backup_controller.go @@ -227,13 +227,19 @@ func (c *podVolumeBackupController) processBackup(req *velerov1api.PodVolumeBack req.Spec.Tags, ) - // if this is azure, set resticCmd.Env appropriately + // Running restic command might need additional provider specific environment variables. Based on the provider, we + // set resticCmd.Env appropriately (currently for Azure and S3 based backuplocations) var env []string if strings.HasPrefix(req.Spec.RepoIdentifier, "azure") { if env, err = restic.AzureCmdEnv(c.backupLocationLister, req.Namespace, req.Spec.BackupStorageLocation); err != nil { return c.fail(req, errors.Wrap(err, "error setting restic cmd env").Error(), log) } resticCmd.Env = env + } else if strings.HasPrefix(req.Spec.RepoIdentifier, "s3") { + if env, err = restic.S3CmdEnv(c.backupLocationLister, req.Namespace, req.Spec.BackupStorageLocation); err != nil { + return c.fail(req, errors.Wrap(err, "error setting restic cmd env").Error(), log) + } + resticCmd.Env = env } // If this is a PVC, look for the most recent completed pod volume backup for it and get diff --git a/pkg/controller/pod_volume_restore_controller.go b/pkg/controller/pod_volume_restore_controller.go index cd09940b4..6e72d5b08 100644 --- a/pkg/controller/pod_volume_restore_controller.go +++ b/pkg/controller/pod_volume_restore_controller.go @@ -328,13 +328,20 @@ func (c *podVolumeRestoreController) restorePodVolume(req *velerov1api.PodVolume volumePath, ) - // if this is azure, set resticCmd.Env appropriately + // Running restic command might need additional provider specific environment variables. Based on the provider, we + // set resticCmd.Env appropriately (currently for Azure and S3 based backuplocations) if strings.HasPrefix(req.Spec.RepoIdentifier, "azure") { env, err := restic.AzureCmdEnv(c.backupLocationLister, req.Namespace, req.Spec.BackupStorageLocation) if err != nil { return c.failRestore(req, errors.Wrap(err, "error setting restic cmd env").Error(), log) } resticCmd.Env = env + } else if strings.HasPrefix(req.Spec.RepoIdentifier, "s3") { + env, err := restic.S3CmdEnv(c.backupLocationLister, req.Namespace, req.Spec.BackupStorageLocation) + if err != nil { + return c.failRestore(req, errors.Wrap(err, "error setting restic cmd env").Error(), log) + } + resticCmd.Env = env } var stdout, stderr string diff --git a/pkg/restic/aws.go b/pkg/restic/aws.go new file mode 100644 index 000000000..78550a3c6 --- /dev/null +++ b/pkg/restic/aws.go @@ -0,0 +1,36 @@ +/* +Copyright 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. +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. +*/ + +package restic + +const ( + // AWS specific environment variable + awsProfileEnvVar = "AWS_PROFILE" + awsProfileKey = "profile" +) + +// getS3ResticEnvVars gets the environment variables that restic +// relies on (AWS_PROFILE) based on info in the provided object +// storage location config map. +func getS3ResticEnvVars(config map[string]string) (map[string]string, error) { + result := make(map[string]string) + + if profile, ok := config[awsProfileKey]; ok { + result[awsProfileEnvVar] = profile + } + + return result, nil +} diff --git a/pkg/restic/azure.go b/pkg/restic/azure.go index 0f1405b80..069ddc6ed 100644 --- a/pkg/restic/azure.go +++ b/pkg/restic/azure.go @@ -114,10 +114,10 @@ func mapLookup(data map[string]string) func(string) string { } } -// getResticEnvVars gets the environment variables that restic +// getAzureResticEnvVars gets the environment variables that restic // relies on (AZURE_ACCOUNT_NAME and AZURE_ACCOUNT_KEY) based // on info in the provided object storage location config map. -func getResticEnvVars(config map[string]string) (map[string]string, error) { +func getAzureResticEnvVars(config map[string]string) (map[string]string, error) { storageAccountKey, _, err := getStorageAccountKey(config) if err != nil { return nil, err diff --git a/pkg/restic/common.go b/pkg/restic/common.go index 54a9af769..1a78707f1 100644 --- a/pkg/restic/common.go +++ b/pkg/restic/common.go @@ -228,7 +228,7 @@ func AzureCmdEnv(backupLocationLister velerov1listers.BackupStorageLocationListe return nil, errors.Wrap(err, "error getting backup storage location") } - azureVars, err := getResticEnvVars(loc.Spec.Config) + azureVars, err := getAzureResticEnvVars(loc.Spec.Config) if err != nil { return nil, errors.Wrap(err, "error getting azure restic env vars") } @@ -240,3 +240,26 @@ func AzureCmdEnv(backupLocationLister velerov1listers.BackupStorageLocationListe return env, nil } + +// S3CmdEnv returns a list of environment variables (in the format var=val) that +// should be used when running a restic command for an S3 backend. This list is +// the current environment, plus the AWS-specific variables restic needs, namely +// a credential profile. +func S3CmdEnv(backupLocationLister velerov1listers.BackupStorageLocationLister, namespace, backupLocation string) ([]string, error) { + loc, err := backupLocationLister.BackupStorageLocations(namespace).Get(backupLocation) + if err != nil { + return nil, errors.Wrap(err, "error getting backup storage location") + } + + awsVars, err := getS3ResticEnvVars(loc.Spec.Config) + if err != nil { + return nil, errors.Wrap(err, "error getting aws restic env vars") + } + + env := os.Environ() + for k, v := range awsVars { + env = append(env, fmt.Sprintf("%s=%s", k, v)) + } + + return env, nil +} diff --git a/pkg/restic/repository_manager.go b/pkg/restic/repository_manager.go index a5bacab26..233d3e5e2 100644 --- a/pkg/restic/repository_manager.go +++ b/pkg/restic/repository_manager.go @@ -254,6 +254,16 @@ func (rm *repositoryManager) exec(cmd *Command, backupLocation string) error { return err } cmd.Env = env + } else if strings.HasPrefix(cmd.RepoIdentifier, "s3") { + if !cache.WaitForCacheSync(rm.ctx.Done(), rm.backupLocationInformerSynced) { + return errors.New("timed out waiting for cache to sync") + } + + env, err := S3CmdEnv(rm.backupLocationLister, rm.namespace, backupLocation) + if err != nil { + return err + } + cmd.Env = env } stdout, stderr, err := veleroexec.RunCommand(cmd.Cmd())