diff --git a/docs/restic.md b/docs/restic.md index 53afb6540..4f2447191 100644 --- a/docs/restic.md +++ b/docs/restic.md @@ -23,15 +23,9 @@ cross-volume-type data migrations. Stay tuned as this evolves! ### Prerequisites -- A working install of Ark version 0.9.0 or later. See [Set up Ark][2] +- A working install of Ark version 0.10.0 or later. See [Set up Ark][2] - A local clone of [the latest release tag of the Ark repository][3] -#### Additional steps if upgrading from version 0.9 alpha - -- Manually delete all of the repositories/data from your existing restic bucket -- Delete all Ark backups from your cluster using `ark backup delete` -- Delete all secrets named `ark-restic-credentials` across all namespaces in your cluster - ### Instructions 1. Download an updated Ark client from the [latest release][3], and move it to a location in your PATH. @@ -49,20 +43,6 @@ cross-volume-type data migrations. Stay tuned as this evolves! - GCP: `kubectl apply -f examples/gcp/20-restic-daemonset.yaml` - Minio: `kubectl apply -f examples/minio/30-restic-daemonset.yaml` -1. Create a new bucket for restic to store its data in, and give the `heptio-ark` IAM user access to it, similarly to -the main Ark bucket you've already set up. Note that this must be a different bucket than the main Ark bucket. -We plan to remove this limitation in a future release. - -1. Uncomment `resticLocation` in your Ark config and set the value appropriately, then apply: - - - AWS: `kubectl apply -f examples/aws/00-ark-config.yaml` - - Azure: `kubectl apply -f examples/azure/10-ark-config.yaml` - - GCP: `kubectl apply -f examples/gcp/00-ark-config.yaml` - - Minio: `kubectl apply -f examples/minio/10-ark-config.yaml` - - Note that `resticLocation` may either be just a bucket name, e.g. `my-restic-bucket`, or a bucket name plus a prefix under - which you'd like the restic data to be stored, e.g. `my-restic-bucket/ark-repos`. - You're now ready to use Ark with restic. ## Back up @@ -139,8 +119,6 @@ You're now ready to use Ark with restic. ## Limitations -- You cannot use the main Ark bucket for storing restic backups. We plan to address this issue -in a future release. - `hostPath` volumes are not supported. [Local persistent volumes][4] are supported. - Those of you familiar with [restic][1] may know that it encrypts all of its data. We've decided to use a static, common encryption key for all restic repositories created by Ark. **This means that anyone who has access to your diff --git a/docs/storage-layout-reorg-v0.10.md b/docs/storage-layout-reorg-v0.10.md index 4ac522317..d4eb47fde 100644 --- a/docs/storage-layout-reorg-v0.10.md +++ b/docs/storage-layout-reorg-v0.10.md @@ -26,6 +26,25 @@ Prior to v0.10, Ark stored data in an object storage bucket using the following ... ``` +Ark also stored restic data, if applicable, in a separate object storage bucket, structured as: + +``` +/[/] + namespace-1/ + data/ + index/ + keys/ + snapshots/ + config + namespace-2/ + data/ + index/ + keys/ + snapshots/ + config + ... +``` + As of v0.10, we've reorganized this layout to provide a cleaner and more extensible directory structure. The new layout looks like: ``` @@ -48,6 +67,20 @@ As of v0.10, we've reorganized this layout to provide a cleaner and more extensi restore-of-backup-2-logs.gz restore-of-backup-2-results.gz ... + restic/ + namespace-1/ + data/ + index/ + keys/ + snapshots/ + config + namespace-2/ + data/ + index/ + keys/ + snapshots/ + config + ... ... ``` @@ -104,7 +137,22 @@ rclone copy ${RCLONE_REMOTE_NAME}:${ARK_TEMP_MIGRATION_BUCKET} ${RCLONE_REMOTE_N # contains an exact copy of the temporary bucket's contents: rclone check ${RCLONE_REMOTE_NAME}:${ARK_BUCKET}/backups ${RCLONE_REMOTE_NAME}:${ARK_TEMP_MIGRATION_BUCKET} -# 9. Once you've confirmed that Ark v0.10 works with your revised Ark +# 9. OPTIONAL: If you have restic data to migrate: + +# a. Copy the contents of your Ark restic location into your +# Ark bucket, under the 'restic/' directory/prefix: + ARK_RESTIC_LOCATION= + rclone copy ${RCLONE_REMOTE_NAME}:${ARK_RESTIC_LOCATION} ${RCLONE_REMOTE_NAME}:${ARK_BUCKET}/restic + +# b. Check that the 'restic/' directory in your Ark bucket now +# contains an exact copy of your restic location: + rclone check ${RCLONE_REMOTE_NAME}:${ARK_BUCKET}/restic ${RCLONE_REMOTE_NAME}:${ARK_RESTIC_LOCATION} + +# c. Delete your ResticRepository custom resources to allow Ark +# to find them in the new location: + kubectl -n heptio-ark delete resticrepositories --all + +# 10. Once you've confirmed that Ark v0.10 works with your revised Ark # bucket, you can delete the temporary migration bucket. ``` diff --git a/examples/aws/05-ark-backupstoragelocation.yaml b/examples/aws/05-ark-backupstoragelocation.yaml index 04789c478..da3abe6df 100644 --- a/examples/aws/05-ark-backupstoragelocation.yaml +++ b/examples/aws/05-ark-backupstoragelocation.yaml @@ -24,9 +24,4 @@ spec: bucket: config: region: - # Uncomment the below line to enable restic integration. - # The format for resticLocation is [/], - # e.g. "my-restic-bucket" or "my-restic-bucket/repos". - # This MUST be a different bucket than the main Ark bucket - # specified just above. - # restic-location: + diff --git a/examples/azure/05-ark-backupstoragelocation.yaml b/examples/azure/05-ark-backupstoragelocation.yaml index 9a94d2a2e..6e06c495a 100644 --- a/examples/azure/05-ark-backupstoragelocation.yaml +++ b/examples/azure/05-ark-backupstoragelocation.yaml @@ -25,9 +25,3 @@ spec: config: resourceGroup: storageAccount: - # Uncomment the below line to enable restic integration. - # The format for resticLocation is [/], - # e.g. "my-restic-bucket" or "my-restic-bucket/repos". - # This MUST be a different bucket than the main Ark bucket - # specified just above. - # restic-location: \ No newline at end of file diff --git a/examples/gcp/05-ark-backupstoragelocation.yaml b/examples/gcp/05-ark-backupstoragelocation.yaml index 346aff838..34c83b98c 100644 --- a/examples/gcp/05-ark-backupstoragelocation.yaml +++ b/examples/gcp/05-ark-backupstoragelocation.yaml @@ -21,10 +21,4 @@ metadata: spec: provider: gcp objectStorage: - bucket: - # Uncomment the below line to enable restic integration. - # The format for resticLocation is [/], - # e.g. "my-restic-bucket" or "my-restic-bucket/repos". - # This MUST be a different bucket than the main Ark bucket - # specified just above. - # restic-location: \ No newline at end of file + bucket: diff --git a/examples/ibm/05-ark-backupstoragelocation.yaml b/examples/ibm/05-ark-backupstoragelocation.yaml index 2e65b45ad..21f89df2b 100644 --- a/examples/ibm/05-ark-backupstoragelocation.yaml +++ b/examples/ibm/05-ark-backupstoragelocation.yaml @@ -26,9 +26,3 @@ spec: s3ForcePathStyle: "true" s3Url: region: - # Uncomment the below line to enable restic integration. - # The format for resticLocation is [/], - # e.g. "my-restic-bucket" or "my-restic-bucket/repos". - # This MUST be a different bucket than the main Ark bucket - # specified just above. - # restic-location: diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 28cf9b8a7..ad2edf838 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -289,10 +289,8 @@ func (s *server) run() error { s.blockStore = blockStore } - if backupStorageLocation.Spec.Config[restic.ResticLocationConfigKey] != "" { - if err := s.initRestic(backupStorageLocation); err != nil { - return err - } + if err := s.initRestic(backupStorageLocation); err != nil { + return err } if err := s.runControllers(config, backupStorageLocation); err != nil { @@ -749,22 +747,20 @@ func (s *server) runControllers(config *api.Config, defaultBackupLocation *api.B wg.Done() }() - if s.resticManager != nil { - resticRepoController := controller.NewResticRepositoryController( - s.logger, - s.sharedInformerFactory.Ark().V1().ResticRepositories(), - s.arkClient.ArkV1(), - defaultBackupLocation, - s.resticManager, - ) - wg.Add(1) - go func() { - // TODO only having a single worker may be an issue since maintenance - // can take a long time. - resticRepoController.Run(ctx, 1) - wg.Done() - }() - } + resticRepoController := controller.NewResticRepositoryController( + s.logger, + s.sharedInformerFactory.Ark().V1().ResticRepositories(), + s.arkClient.ArkV1(), + defaultBackupLocation, + s.resticManager, + ) + wg.Add(1) + go func() { + // TODO only having a single worker may be an issue since maintenance + // can take a long time. + resticRepoController.Run(ctx, 1) + wg.Done() + }() // SHARED INFORMERS HAVE TO BE STARTED AFTER ALL CONTROLLERS go s.sharedInformerFactory.Start(ctx.Done()) diff --git a/pkg/controller/pod_volume_restore_controller.go b/pkg/controller/pod_volume_restore_controller.go index 78b467240..73ed524fe 100644 --- a/pkg/controller/pod_volume_restore_controller.go +++ b/pkg/controller/pod_volume_restore_controller.go @@ -161,11 +161,9 @@ func (c *podVolumeRestoreController) podHandler(obj interface{}) { return } - selector, err := labels.Parse(fmt.Sprintf("%s=%s", arkv1api.PodUIDLabel, pod.UID)) - if err != nil { - log.WithError(err).Error("Unable to parse label selector %s", fmt.Sprintf("%s=%s", arkv1api.PodUIDLabel, pod.UID)) - return - } + selector := labels.Set(map[string]string{ + arkv1api.PodUIDLabel: string(pod.UID), + }).AsSelector() pvrs, err := c.podVolumeRestoreLister.List(selector) if err != nil { diff --git a/pkg/persistence/object_store.go b/pkg/persistence/object_store.go index 78634d039..15d247101 100644 --- a/pkg/persistence/object_store.go +++ b/pkg/persistence/object_store.go @@ -57,7 +57,7 @@ const DownloadURLTTL = 10 * time.Minute type objectBackupStore struct { objectStore cloudprovider.ObjectStore bucket string - layout *objectStoreLayout + layout *ObjectStoreLayout logger logrus.FieldLogger } @@ -103,7 +103,7 @@ func NewObjectBackupStore(location *arkv1api.BackupStorageLocation, objectStoreG return &objectBackupStore{ objectStore: objectStore, bucket: location.Spec.ObjectStorage.Bucket, - layout: newObjectStoreLayout(location.Spec.ObjectStorage.Prefix), + layout: NewObjectStoreLayout(location.Spec.ObjectStorage.Prefix), logger: log, }, nil } @@ -117,7 +117,7 @@ func (s *objectBackupStore) IsValid() error { var invalid []string for _, dir := range dirs { subdir := strings.TrimSuffix(strings.TrimPrefix(dir, s.layout.rootPrefix), "/") - if !validRootDirs.Has(subdir) { + if !s.layout.isValidSubdir(subdir) { invalid = append(invalid, subdir) } } @@ -134,7 +134,7 @@ func (s *objectBackupStore) IsValid() error { } func (s *objectBackupStore) ListBackups() ([]*arkv1api.Backup, error) { - prefixes, err := s.objectStore.ListCommonPrefixes(s.bucket, s.layout.backupsDir, "/") + prefixes, err := s.objectStore.ListCommonPrefixes(s.bucket, s.layout.subdirs["backups"], "/") if err != nil { return nil, err } @@ -149,7 +149,7 @@ func (s *objectBackupStore) ListBackups() ([]*arkv1api.Backup, error) { // ListCommonPrefixes method return the *full* prefix, inclusive // of s.backupsPrefix, and include the delimiter ("/") as a suffix. Trim // each of those off to get the backup name. - backupName := strings.TrimSuffix(strings.TrimPrefix(prefix, s.layout.backupsDir), "/") + backupName := strings.TrimSuffix(strings.TrimPrefix(prefix, s.layout.subdirs["backups"]), "/") backup, err := s.GetBackupMetadata(backupName) if err != nil { diff --git a/pkg/persistence/object_store_layout.go b/pkg/persistence/object_store_layout.go index 5c9bbbab7..2849b110d 100644 --- a/pkg/persistence/object_store_layout.go +++ b/pkg/persistence/object_store_layout.go @@ -20,57 +20,68 @@ import ( "fmt" "path" "strings" - - "k8s.io/apimachinery/pkg/util/sets" ) -var validRootDirs = sets.NewString("backups", "restores") - -type objectStoreLayout struct { - rootPrefix string - backupsDir string - restoresDir string +// ObjectStoreLayout defines how Ark's persisted files map to +// keys in an object storage bucket. +type ObjectStoreLayout struct { + rootPrefix string + subdirs map[string]string } -func newObjectStoreLayout(prefix string) *objectStoreLayout { +func NewObjectStoreLayout(prefix string) *ObjectStoreLayout { if prefix != "" && !strings.HasSuffix(prefix, "/") { prefix = prefix + "/" } - backupsDir := path.Join(prefix, "backups") + "/" - restoresDir := path.Join(prefix, "restores") + "/" + subdirs := map[string]string{ + "backups": path.Join(prefix, "backups") + "/", + "restores": path.Join(prefix, "restores") + "/", + "restic": path.Join(prefix, "restic") + "/", + } - return &objectStoreLayout{ - rootPrefix: prefix, - backupsDir: backupsDir, - restoresDir: restoresDir, + return &ObjectStoreLayout{ + rootPrefix: prefix, + subdirs: subdirs, } } -func (l *objectStoreLayout) getBackupDir(backup string) string { - return path.Join(l.backupsDir, backup) + "/" +// GetResticDir returns the full prefix representing the restic +// directory within an object storage bucket containing a backup +// store. +func (l *ObjectStoreLayout) GetResticDir() string { + return l.subdirs["restic"] } -func (l *objectStoreLayout) getRestoreDir(restore string) string { - return path.Join(l.restoresDir, restore) + "/" +func (l *ObjectStoreLayout) isValidSubdir(name string) bool { + _, ok := l.subdirs[name] + return ok } -func (l *objectStoreLayout) getBackupMetadataKey(backup string) string { - return path.Join(l.backupsDir, backup, "ark-backup.json") +func (l *ObjectStoreLayout) getBackupDir(backup string) string { + return path.Join(l.subdirs["backups"], backup) + "/" } -func (l *objectStoreLayout) getBackupContentsKey(backup string) string { - return path.Join(l.backupsDir, backup, fmt.Sprintf("%s.tar.gz", backup)) +func (l *ObjectStoreLayout) getRestoreDir(restore string) string { + return path.Join(l.subdirs["restores"], restore) + "/" } -func (l *objectStoreLayout) getBackupLogKey(backup string) string { - return path.Join(l.backupsDir, backup, fmt.Sprintf("%s-logs.gz", backup)) +func (l *ObjectStoreLayout) getBackupMetadataKey(backup string) string { + return path.Join(l.subdirs["backups"], backup, "ark-backup.json") } -func (l *objectStoreLayout) getRestoreLogKey(restore string) string { - return path.Join(l.restoresDir, restore, fmt.Sprintf("restore-%s-logs.gz", restore)) +func (l *ObjectStoreLayout) getBackupContentsKey(backup string) string { + return path.Join(l.subdirs["backups"], backup, fmt.Sprintf("%s.tar.gz", backup)) } -func (l *objectStoreLayout) getRestoreResultsKey(restore string) string { - return path.Join(l.restoresDir, restore, fmt.Sprintf("restore-%s-results.gz", restore)) +func (l *ObjectStoreLayout) getBackupLogKey(backup string) string { + return path.Join(l.subdirs["backups"], backup, fmt.Sprintf("%s-logs.gz", backup)) +} + +func (l *ObjectStoreLayout) getRestoreLogKey(restore string) string { + return path.Join(l.subdirs["restores"], restore, fmt.Sprintf("restore-%s-logs.gz", restore)) +} + +func (l *ObjectStoreLayout) getRestoreResultsKey(restore string) string { + return path.Join(l.subdirs["restores"], restore, fmt.Sprintf("restore-%s-results.gz", restore)) } diff --git a/pkg/persistence/object_store_test.go b/pkg/persistence/object_store_test.go index 15d84967e..fd5864dbe 100644 --- a/pkg/persistence/object_store_test.go +++ b/pkg/persistence/object_store_test.go @@ -53,7 +53,7 @@ func newObjectBackupStoreTestHarness(bucket, prefix string) *objectBackupStoreTe objectBackupStore: &objectBackupStore{ objectStore: objectStore, bucket: bucket, - layout: newObjectStoreLayout(prefix), + layout: NewObjectStoreLayout(prefix), logger: arktest.NewLogger(), }, objectStore: objectStore, @@ -363,7 +363,7 @@ func TestDeleteBackup(t *testing.T) { backupStore := &objectBackupStore{ objectStore: objectStore, bucket: "test-bucket", - layout: newObjectStoreLayout(test.prefix), + layout: NewObjectStoreLayout(test.prefix), logger: arktest.NewLogger(), } defer objectStore.AssertExpectations(t) diff --git a/pkg/restic/common.go b/pkg/restic/common.go index 3b8797430..d14c0db27 100644 --- a/pkg/restic/common.go +++ b/pkg/restic/common.go @@ -36,7 +36,6 @@ const ( DaemonSet = "restic" InitContainer = "restic-wait" DefaultMaintenanceFrequency = 24 * time.Hour - ResticLocationConfigKey = "restic-location" podAnnotationPrefix = "snapshot.ark.heptio.com/" volumesToBackupAnnotation = "backup.ark.heptio.com/backup-volumes" @@ -117,10 +116,9 @@ type SnapshotIdentifier struct { // GetSnapshotsInBackup returns a list of all restic snapshot ids associated with // a given Ark backup. func GetSnapshotsInBackup(backup *arkv1api.Backup, podVolumeBackupLister arkv1listers.PodVolumeBackupLister) ([]SnapshotIdentifier, error) { - selector, err := labels.Parse(fmt.Sprintf("%s=%s", arkv1api.BackupNameLabel, backup.Name)) - if err != nil { - return nil, errors.WithStack(err) - } + selector := labels.Set(map[string]string{ + arkv1api.BackupNameLabel: backup.Name, + }).AsSelector() podVolumeBackups, err := podVolumeBackupLister.List(selector) if err != nil { diff --git a/pkg/restic/config.go b/pkg/restic/config.go index 1e70d7d91..7d3e41732 100644 --- a/pkg/restic/config.go +++ b/pkg/restic/config.go @@ -18,10 +18,12 @@ package restic import ( "fmt" + "path" "strings" arkv1api "github.com/heptio/ark/pkg/apis/ark/v1" "github.com/heptio/ark/pkg/cloudprovider/aws" + "github.com/heptio/ark/pkg/persistence" ) type BackendType string @@ -39,18 +41,15 @@ var getAWSBucketRegion = aws.GetBucketRegion // getRepoPrefix returns the prefix of the value of the --repo flag for // restic commands, i.e. everything except the "/". func getRepoPrefix(location *arkv1api.BackupStorageLocation) string { - var ( - resticLocation = location.Spec.Config[ResticLocationConfigKey] - parts = strings.SplitN(resticLocation, "/", 2) - bucket, path, prefix string - ) + var provider, bucket, prefix, bucketAndPrefix string - if len(parts) >= 1 { - bucket = parts[0] - } - if len(parts) >= 2 { - path = parts[1] + if location.Spec.ObjectStorage != nil { + layout := persistence.NewObjectStoreLayout(location.Spec.ObjectStorage.Prefix) + + bucket = location.Spec.ObjectStorage.Bucket + prefix = layout.GetResticDir() } + bucketAndPrefix = path.Join(bucket, prefix) switch BackendType(location.Spec.Provider) { case AWSBackend: @@ -69,14 +68,14 @@ func getRepoPrefix(location *arkv1api.BackupStorageLocation) string { url = fmt.Sprintf("s3-%s.amazonaws.com", region) } - return fmt.Sprintf("s3:%s/%s", url, resticLocation) + return fmt.Sprintf("s3:%s/%s", url, bucketAndPrefix) case AzureBackend: - prefix = "azure" + provider = "azure" case GCPBackend: - prefix = "gs" + provider = "gs" } - return fmt.Sprintf("%s:%s:/%s", prefix, bucket, path) + return fmt.Sprintf("%s:%s:/%s", provider, bucket, prefix) } // GetRepoIdentifier returns the string to be used as the value of the --repo flag in diff --git a/pkg/restic/config_test.go b/pkg/restic/config_test.go index 982c1dca1..31ff68140 100644 --- a/pkg/restic/config_test.go +++ b/pkg/restic/config_test.go @@ -34,10 +34,15 @@ func TestGetRepoIdentifier(t *testing.T) { backupLocation := &arkv1api.BackupStorageLocation{ Spec: arkv1api.BackupStorageLocationSpec{ Provider: "aws", - Config: map[string]string{ResticLocationConfigKey: "bucket/prefix"}, + StorageType: arkv1api.StorageType{ + ObjectStorage: &arkv1api.ObjectStorageLocation{ + Bucket: "bucket", + Prefix: "prefix", + }, + }, }, } - assert.Equal(t, "s3:s3.amazonaws.com/bucket/prefix/repo-1", GetRepoIdentifier(backupLocation, "repo-1")) + assert.Equal(t, "s3:s3.amazonaws.com/bucket/prefix/restic/repo-1", GetRepoIdentifier(backupLocation, "repo-1")) // stub implementation of getAWSBucketRegion getAWSBucketRegion = func(string) (string, error) { @@ -47,43 +52,67 @@ func TestGetRepoIdentifier(t *testing.T) { backupLocation = &arkv1api.BackupStorageLocation{ Spec: arkv1api.BackupStorageLocationSpec{ Provider: "aws", - Config: map[string]string{ResticLocationConfigKey: "bucket"}, + StorageType: arkv1api.StorageType{ + ObjectStorage: &arkv1api.ObjectStorageLocation{ + Bucket: "bucket", + }, + }, }, } - assert.Equal(t, "s3:s3-us-west-2.amazonaws.com/bucket/repo-1", GetRepoIdentifier(backupLocation, "repo-1")) + assert.Equal(t, "s3:s3-us-west-2.amazonaws.com/bucket/restic/repo-1", GetRepoIdentifier(backupLocation, "repo-1")) backupLocation = &arkv1api.BackupStorageLocation{ Spec: arkv1api.BackupStorageLocationSpec{ Provider: "aws", - Config: map[string]string{ResticLocationConfigKey: "bucket/prefix"}, + StorageType: arkv1api.StorageType{ + ObjectStorage: &arkv1api.ObjectStorageLocation{ + Bucket: "bucket", + Prefix: "prefix", + }, + }, }, } - assert.Equal(t, "s3:s3-us-west-2.amazonaws.com/bucket/prefix/repo-1", GetRepoIdentifier(backupLocation, "repo-1")) + assert.Equal(t, "s3:s3-us-west-2.amazonaws.com/bucket/prefix/restic/repo-1", GetRepoIdentifier(backupLocation, "repo-1")) backupLocation = &arkv1api.BackupStorageLocation{ Spec: arkv1api.BackupStorageLocationSpec{ Provider: "aws", Config: map[string]string{ - ResticLocationConfigKey: "bucket/prefix", - "s3Url": "alternate-url", + "s3Url": "alternate-url", + }, + StorageType: arkv1api.StorageType{ + ObjectStorage: &arkv1api.ObjectStorageLocation{ + Bucket: "bucket", + Prefix: "prefix", + }, }, }, } - assert.Equal(t, "s3:alternate-url/bucket/prefix/repo-1", GetRepoIdentifier(backupLocation, "repo-1")) + assert.Equal(t, "s3:alternate-url/bucket/prefix/restic/repo-1", GetRepoIdentifier(backupLocation, "repo-1")) backupLocation = &arkv1api.BackupStorageLocation{ Spec: arkv1api.BackupStorageLocationSpec{ Provider: "azure", - Config: map[string]string{ResticLocationConfigKey: "bucket/prefix"}, + StorageType: arkv1api.StorageType{ + ObjectStorage: &arkv1api.ObjectStorageLocation{ + Bucket: "bucket", + Prefix: "prefix", + }, + }, }, } - assert.Equal(t, "azure:bucket:/prefix/repo-1", GetRepoIdentifier(backupLocation, "repo-1")) + assert.Equal(t, "azure:bucket:/prefix/restic/repo-1", GetRepoIdentifier(backupLocation, "repo-1")) backupLocation = &arkv1api.BackupStorageLocation{ Spec: arkv1api.BackupStorageLocationSpec{ Provider: "gcp", - Config: map[string]string{ResticLocationConfigKey: "bucket-2/prefix-2"}, + StorageType: arkv1api.StorageType{ + ObjectStorage: &arkv1api.ObjectStorageLocation{ + Bucket: "bucket-2", + Prefix: "prefix-2", + }, + }, }, } - assert.Equal(t, "gs:bucket-2:/prefix-2/repo-2", GetRepoIdentifier(backupLocation, "repo-2")) + assert.Equal(t, "gs:bucket-2:/prefix-2/restic/repo-2", GetRepoIdentifier(backupLocation, "repo-2")) }