remove restic for repo

Signed-off-by: Lyndon-Li <lyonghui@vmware.com>
This commit is contained in:
Lyndon-Li
2026-04-07 07:35:25 +00:00
parent baf2491344
commit 235e579581
14 changed files with 32 additions and 505 deletions
@@ -69,9 +69,7 @@ spec:
- ""
type: string
resticIdentifier:
description: |-
ResticIdentifier is the full restic-compatible string for identifying
this repository. This field is only used when RepositoryType is "restic".
description: Deprecated
type: string
volumeNamespace:
description: |-
File diff suppressed because one or more lines are too long
@@ -35,8 +35,7 @@ type BackupRepositorySpec struct {
// +optional
RepositoryType string `json:"repositoryType"`
// ResticIdentifier is the full restic-compatible string for identifying
// this repository. This field is only used when RepositoryType is "restic".
// Deprecated
// +optional
ResticIdentifier string `json:"resticIdentifier,omitempty"`
@@ -58,8 +57,7 @@ const (
BackupRepositoryPhaseReady BackupRepositoryPhase = "Ready"
BackupRepositoryPhaseNotReady BackupRepositoryPhase = "NotReady"
BackupRepositoryTypeRestic string = "restic"
BackupRepositoryTypeKopia string = "kopia"
BackupRepositoryTypeKopia string = "kopia"
)
// BackupRepositoryStatus is the current status of a BackupRepository.
+4 -38
View File
@@ -238,6 +238,10 @@ func (r *BackupRepoReconciler) Reconcile(ctx context.Context, req ctrl.Request)
return ctrl.Result{}, err
}
if backupRepo.Spec.RepositoryType != velerov1api.BackupRepositoryTypeKopia {
return ctrl.Result{}, nil
}
bsl, bslErr := r.getBSL(ctx, backupRepo)
if bslErr != nil {
log.WithError(bslErr).Error("Fail to get BSL for BackupRepository. Skip reconciling.")
@@ -323,23 +327,6 @@ func (r *BackupRepoReconciler) getIdentifierByBSL(bsl *velerov1api.BackupStorage
func (r *BackupRepoReconciler) initializeRepo(ctx context.Context, req *velerov1api.BackupRepository, bsl *velerov1api.BackupStorageLocation, log logrus.FieldLogger) error {
log.WithField("repoConfig", r.backupRepoConfig).Info("Initializing backup repository")
var repoIdentifier string
// Only get restic identifier for restic repositories
if req.Spec.RepositoryType == "" || req.Spec.RepositoryType == velerov1api.BackupRepositoryTypeRestic {
var err error
repoIdentifier, err = r.getIdentifierByBSL(bsl, req)
if err != nil {
return r.patchBackupRepository(ctx, req, func(rr *velerov1api.BackupRepository) {
rr.Status.Message = err.Error()
rr.Status.Phase = velerov1api.BackupRepositoryPhaseNotReady
if rr.Spec.MaintenanceFrequency.Duration <= 0 {
rr.Spec.MaintenanceFrequency = metav1.Duration{Duration: r.getRepositoryMaintenanceFrequency(req)}
}
})
}
}
config, err := getBackupRepositoryConfig(ctx, r, r.backupRepoConfig, r.namespace, req.Name, req.Spec.RepositoryType, log)
if err != nil {
log.WithError(err).Warn("Failed to get repo config, repo config is ignored")
@@ -349,11 +336,6 @@ func (r *BackupRepoReconciler) initializeRepo(ctx context.Context, req *velerov1
// defaulting - if the patch fails, return an error so the item is returned to the queue
if err := r.patchBackupRepository(ctx, req, func(rr *velerov1api.BackupRepository) {
// Only set ResticIdentifier for restic repositories
if rr.Spec.RepositoryType == "" || rr.Spec.RepositoryType == velerov1api.BackupRepositoryTypeRestic {
rr.Spec.ResticIdentifier = repoIdentifier
}
if rr.Spec.MaintenanceFrequency.Duration <= 0 {
rr.Spec.MaintenanceFrequency = metav1.Duration{Duration: r.getRepositoryMaintenanceFrequency(req)}
}
@@ -579,22 +561,6 @@ func dueForMaintenance(req *velerov1api.BackupRepository, now time.Time) bool {
func (r *BackupRepoReconciler) checkNotReadyRepo(ctx context.Context, req *velerov1api.BackupRepository, bsl *velerov1api.BackupStorageLocation, log logrus.FieldLogger) (bool, error) {
log.Info("Checking backup repository for readiness")
// Only check and update restic identifier for restic repositories
if req.Spec.RepositoryType == "" || req.Spec.RepositoryType == velerov1api.BackupRepositoryTypeRestic {
repoIdentifier, err := r.getIdentifierByBSL(bsl, req)
if err != nil {
return false, r.patchBackupRepository(ctx, req, repoNotReady(err.Error()))
}
if repoIdentifier != req.Spec.ResticIdentifier {
if err := r.patchBackupRepository(ctx, req, func(rr *velerov1api.BackupRepository) {
rr.Spec.ResticIdentifier = repoIdentifier
}); err != nil {
return false, err
}
}
}
// we need to ensure it (first check, if check fails, attempt to init)
// because we don't know if it's been successfully initialized yet.
if err := ensureRepo(req, r.repositoryManager); err != nil {
@@ -98,32 +98,6 @@ func TestPatchBackupRepository(t *testing.T) {
}
func TestCheckNotReadyRepo(t *testing.T) {
// Test for restic repository
t.Run("restic repository", func(t *testing.T) {
rr := mockBackupRepositoryCR()
rr.Spec.BackupStorageLocation = "default"
rr.Spec.ResticIdentifier = "fake-identifier"
rr.Spec.VolumeNamespace = "volume-ns-1"
rr.Spec.RepositoryType = velerov1api.BackupRepositoryTypeRestic
reconciler := mockBackupRepoReconciler(t, "PrepareRepo", rr, nil)
err := reconciler.Client.Create(t.Context(), rr)
require.NoError(t, err)
location := velerov1api.BackupStorageLocation{
Spec: velerov1api.BackupStorageLocationSpec{
Config: map[string]string{"resticRepoPrefix": "s3:test.amazonaws.com/bucket/restic"},
},
ObjectMeta: metav1.ObjectMeta{
Namespace: velerov1api.DefaultNamespace,
Name: rr.Spec.BackupStorageLocation,
},
}
_, err = reconciler.checkNotReadyRepo(t.Context(), rr, &location, reconciler.logger)
require.NoError(t, err)
assert.Equal(t, velerov1api.BackupRepositoryPhaseReady, rr.Status.Phase)
assert.Equal(t, "s3:test.amazonaws.com/bucket/restic/volume-ns-1", rr.Spec.ResticIdentifier)
})
// Test for kopia repository
t.Run("kopia repository", func(t *testing.T) {
rr := mockBackupRepositoryCR()
@@ -149,32 +123,6 @@ func TestCheckNotReadyRepo(t *testing.T) {
// ResticIdentifier should remain empty for kopia
assert.Empty(t, rr.Spec.ResticIdentifier)
})
// Test for empty repository type (defaults to restic)
t.Run("empty repository type", func(t *testing.T) {
rr := mockBackupRepositoryCR()
rr.Spec.BackupStorageLocation = "default"
rr.Spec.ResticIdentifier = "fake-identifier"
rr.Spec.VolumeNamespace = "volume-ns-1"
// Deliberately leave RepositoryType empty
reconciler := mockBackupRepoReconciler(t, "PrepareRepo", rr, nil)
err := reconciler.Client.Create(t.Context(), rr)
require.NoError(t, err)
location := velerov1api.BackupStorageLocation{
Spec: velerov1api.BackupStorageLocationSpec{
Config: map[string]string{"resticRepoPrefix": "s3:test.amazonaws.com/bucket/restic"},
},
ObjectMeta: metav1.ObjectMeta{
Namespace: velerov1api.DefaultNamespace,
Name: rr.Spec.BackupStorageLocation,
},
}
_, err = reconciler.checkNotReadyRepo(t.Context(), rr, &location, reconciler.logger)
require.NoError(t, err)
assert.Equal(t, velerov1api.BackupRepositoryPhaseReady, rr.Status.Phase)
assert.Equal(t, "s3:test.amazonaws.com/bucket/restic/volume-ns-1", rr.Spec.ResticIdentifier)
})
}
func startMaintenanceJobFail(client.Client, context.Context, *velerov1api.BackupRepository, string, logrus.Level, *logging.FormatFlag, logrus.FieldLogger) (string, error) {
@@ -1605,58 +1553,6 @@ func TestInitializeRepoWithRepositoryTypes(t *testing.T) {
corev1api.AddToScheme(scheme)
velerov1api.AddToScheme(scheme)
// Test for restic repository
t.Run("restic repository", func(t *testing.T) {
rr := mockBackupRepositoryCR()
rr.Spec.BackupStorageLocation = "default"
rr.Spec.VolumeNamespace = "volume-ns-1"
rr.Spec.RepositoryType = velerov1api.BackupRepositoryTypeRestic
location := &velerov1api.BackupStorageLocation{
ObjectMeta: metav1.ObjectMeta{
Namespace: velerov1api.DefaultNamespace,
Name: "default",
},
Spec: velerov1api.BackupStorageLocationSpec{
Provider: "aws",
StorageType: velerov1api.StorageType{
ObjectStorage: &velerov1api.ObjectStorageLocation{
Bucket: "test-bucket",
Prefix: "test-prefix",
},
},
Config: map[string]string{
"region": "us-east-1",
},
},
}
fakeClient := clientFake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(rr, location).Build()
mgr := &repomokes.Manager{}
mgr.On("PrepareRepo", rr).Return(nil)
reconciler := NewBackupRepoReconciler(
velerov1api.DefaultNamespace,
velerotest.NewLogger(),
fakeClient,
mgr,
testMaintenanceFrequency,
"",
"",
logrus.InfoLevel,
nil,
nil,
)
err := reconciler.initializeRepo(t.Context(), rr, location, reconciler.logger)
require.NoError(t, err)
// Verify ResticIdentifier is set for restic
assert.NotEmpty(t, rr.Spec.ResticIdentifier)
assert.Contains(t, rr.Spec.ResticIdentifier, "volume-ns-1")
assert.Equal(t, velerov1api.BackupRepositoryPhaseReady, rr.Status.Phase)
})
// Test for kopia repository
t.Run("kopia repository", func(t *testing.T) {
rr := mockBackupRepositoryCR()
@@ -1707,58 +1603,6 @@ func TestInitializeRepoWithRepositoryTypes(t *testing.T) {
assert.Empty(t, rr.Spec.ResticIdentifier)
assert.Equal(t, velerov1api.BackupRepositoryPhaseReady, rr.Status.Phase)
})
// Test for empty repository type (defaults to restic)
t.Run("empty repository type", func(t *testing.T) {
rr := mockBackupRepositoryCR()
rr.Spec.BackupStorageLocation = "default"
rr.Spec.VolumeNamespace = "volume-ns-1"
// Leave RepositoryType empty
location := &velerov1api.BackupStorageLocation{
ObjectMeta: metav1.ObjectMeta{
Namespace: velerov1api.DefaultNamespace,
Name: "default",
},
Spec: velerov1api.BackupStorageLocationSpec{
Provider: "aws",
StorageType: velerov1api.StorageType{
ObjectStorage: &velerov1api.ObjectStorageLocation{
Bucket: "test-bucket",
Prefix: "test-prefix",
},
},
Config: map[string]string{
"region": "us-east-1",
},
},
}
fakeClient := clientFake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(rr, location).Build()
mgr := &repomokes.Manager{}
mgr.On("PrepareRepo", rr).Return(nil)
reconciler := NewBackupRepoReconciler(
velerov1api.DefaultNamespace,
velerotest.NewLogger(),
fakeClient,
mgr,
testMaintenanceFrequency,
"",
"",
logrus.InfoLevel,
nil,
nil,
)
err := reconciler.initializeRepo(t.Context(), rr, location, reconciler.logger)
require.NoError(t, err)
// Verify ResticIdentifier is set when type is empty (defaults to restic)
assert.NotEmpty(t, rr.Spec.ResticIdentifier)
assert.Contains(t, rr.Spec.ResticIdentifier, "volume-ns-1")
assert.Equal(t, velerov1api.BackupRepositoryPhaseReady, rr.Status.Phase)
})
}
func TestRepoMaintenanceMetricsRecording(t *testing.T) {
+3 -5
View File
@@ -251,11 +251,9 @@ func (fs *fileSystemBR) boostRepoConnect(ctx context.Context, repositoryType str
if err := repoProvider.NewUnifiedRepoProvider(*credentialGetter, repositoryType, fs.log).BoostRepoConnect(ctx, repoProvider.RepoParam{BackupLocation: fs.backupLocation, BackupRepo: fs.backupRepo, CacheDir: cacheDir}); err != nil {
return err
}
} else {
if err := repoProvider.NewResticRepositoryProvider(*credentialGetter, filesystem.NewFileSystem(), fs.log).BoostRepoConnect(ctx, repoProvider.RepoParam{BackupLocation: fs.backupLocation, BackupRepo: fs.backupRepo}); err != nil {
return err
}
return nil
}
return nil
return errors.Errorf("error getting provider for repo %s", repositoryType)
}
+2 -7
View File
@@ -272,7 +272,7 @@ func (b *backupper) BackupPodVolumes(backup *velerov1api.Backup, pod *corev1api.
return nil, pvcSummary, []error{err}
}
repositoryType := funcGetRepositoryType(b.uploaderType)
repositoryType := funcGetRepositoryType()
if repositoryType == "" {
err := errors.Errorf("empty repository type, uploader %s", b.uploaderType)
skipAllPodVolumes(pod, volumesToBackup, err, pvcSummary, log)
@@ -305,11 +305,6 @@ func (b *backupper) BackupPodVolumes(backup *velerov1api.Backup, pod *corev1api.
}
}
repoIdentifier := ""
if repositoryType == velerov1api.BackupRepositoryTypeRestic {
repoIdentifier = repo.Spec.ResticIdentifier
}
for _, volumeName := range volumesToBackup {
volume, ok := podVolumes[volumeName]
if !ok {
@@ -366,7 +361,7 @@ func (b *backupper) BackupPodVolumes(backup *velerov1api.Backup, pod *corev1api.
continue
}
volumeBackup := newPodVolumeBackup(backup, pod, volume, repoIdentifier, b.uploaderType, pvc)
volumeBackup := newPodVolumeBackup(backup, pod, volume, "", b.uploaderType, pvc)
// the PVB must be added into the indexer before creating it in API server otherwise unexpected behavior may happen:
// the PVB may be handled very quickly by the controller and the informer handler will insert the PVB before "b.pvbIndexer.Add(volumeBackup)" runs,
// this causes the PVB inserted by "b.pvbIndexer.Add(volumeBackup)" overrides the PVB in the indexer while the PVB inserted by "b.pvbIndexer.Add(volumeBackup)"
+1 -1
View File
@@ -580,7 +580,7 @@ func TestBackupPodVolumes(t *testing.T) {
require.NoError(t, err)
if test.mockGetRepositoryType {
funcGetRepositoryType = func(string) string { return "" }
funcGetRepositoryType = func() string { return "" }
} else {
funcGetRepositoryType = getRepositoryType
}
+1 -6
View File
@@ -166,11 +166,6 @@ func (r *restorer) RestorePodVolumes(data RestoreData, tracker *volume.RestoreVo
podVolumes[podVolume.Name] = podVolume
}
repoIdentifier := ""
if repositoryType == velerov1api.BackupRepositoryTypeRestic {
repoIdentifier = repo.Spec.ResticIdentifier
}
for volume, backupInfo := range volumesToRestore {
volumeObj, ok := podVolumes[volume]
var pvc *corev1api.PersistentVolumeClaim
@@ -185,7 +180,7 @@ func (r *restorer) RestorePodVolumes(data RestoreData, tracker *volume.RestoreVo
}
}
volumeRestore := newPodVolumeRestore(data.Restore, data.Pod, data.BackupLocation, volume, backupInfo.snapshotID, backupInfo.snapshotSize, repoIdentifier, backupInfo.uploaderType, data.SourceNamespace, pvc)
volumeRestore := newPodVolumeRestore(data.Restore, data.Pod, data.BackupLocation, volume, backupInfo.snapshotID, backupInfo.snapshotSize, "", backupInfo.uploaderType, data.SourceNamespace, pvc)
if err := veleroclient.CreateRetryGenerateName(r.crClient, r.ctx, volumeRestore); err != nil {
errs = append(errs, errors.WithStack(err))
continue
+10 -23
View File
@@ -62,12 +62,12 @@ func GetVolumeBackupsForPod(podVolumeBackups []*velerov1api.PodVolumeBackup, pod
// GetPvbRepositoryType returns the repositoryType according to the PVB information
func GetPvbRepositoryType(pvb *velerov1api.PodVolumeBackup) string {
return getRepositoryType(pvb.Spec.UploaderType)
return getRepositoryType()
}
// GetPvrRepositoryType returns the repositoryType according to the PVR information
func GetPvrRepositoryType(pvr *velerov1api.PodVolumeRestore) string {
return getRepositoryType(pvr.Spec.UploaderType)
return getRepositoryType()
}
// getVolumeBackupInfoForPod returns a map, of volume name -> VolumeBackupInfo,
@@ -97,7 +97,7 @@ func getVolumeBackupInfoForPod(podVolumeBackups []*velerov1api.PodVolumeBackup,
snapshotID: pvb.Status.SnapshotID,
snapshotSize: pvb.Status.Progress.TotalBytes,
uploaderType: getUploaderTypeOrDefault(pvb.Spec.UploaderType),
repositoryType: getRepositoryType(pvb.Spec.UploaderType),
repositoryType: getRepositoryType(),
}
}
@@ -111,7 +111,7 @@ func getVolumeBackupInfoForPod(podVolumeBackups []*velerov1api.PodVolumeBackup,
}
for k, v := range fromAnnntation {
volumes[k] = volumeBackupInfo{v, 0, uploader.ResticType, velerov1api.BackupRepositoryTypeRestic}
volumes[k] = volumeBackupInfo{v, 0, uploader.KopiaType, velerov1api.BackupRepositoryTypeKopia}
}
return volumes
@@ -135,7 +135,7 @@ func GetSnapshotIdentifier(podVolumeBackups *velerov1api.PodVolumeBackupList) ma
VolumeNamespace: item.Spec.Pod.Namespace,
BackupStorageLocation: item.Spec.BackupStorageLocation,
SnapshotID: item.Status.SnapshotID,
RepositoryType: getRepositoryType(item.Spec.UploaderType),
RepositoryType: getRepositoryType(),
UploaderType: item.Spec.UploaderType,
Source: item.Status.Path,
RepoIdentifier: item.Spec.RepoIdentifier,
@@ -167,24 +167,11 @@ func getUploaderTypeOrDefault(uploaderType string) string {
return uploader.ResticType
}
// getRepositoryType returns the hardcode repositoryType for different backup methods - Restic or Kopia,uploaderType
// indicates the method.
// For Restic backup method, it is always hardcode to BackupRepositoryTypeRestic, never changed.
// For Kopia backup method, this means we hardcode repositoryType as BackupRepositoryTypeKopia for Unified Repo,
// at present (Kopia backup method is using Unified Repo). However, it doesn't mean we could deduce repositoryType
// from uploaderType for Unified Repo.
// TODO: post v1.10, refactor this function for Kopia backup method. In future, when we have multiple implementations of
// Unified Repo (besides Kopia), we will add the repositoryType to BSL, because by then, we are not able to hardcode
// the repositoryType to BackupRepositoryTypeKopia for Unified Repo.
func getRepositoryType(uploaderType string) string {
switch uploaderType {
case "", uploader.ResticType:
return velerov1api.BackupRepositoryTypeRestic
case uploader.KopiaType:
return velerov1api.BackupRepositoryTypeKopia
default:
return ""
}
// getRepositoryType returns the hardcode repositoryType.
// TODO: In future, when we have multiple implementations of Unified Repo (besides Kopia), we will add the repositoryType to BSL,
// because by then, we are not able to hardcode the repositoryType to BackupRepositoryTypeKopia for Unified Repo.
func getRepositoryType() string {
return velerov1api.BackupRepositoryTypeKopia
}
func isPVBMatchPod(pvb *velerov1api.PodVolumeBackup, podName string, namespace string) bool {
-6
View File
@@ -109,10 +109,6 @@ func NewManager(
log: log,
}
mgr.providers[velerov1api.BackupRepositoryTypeRestic] = provider.NewResticRepositoryProvider(credentials.CredentialGetter{
FromFile: credentialFileStore,
FromSecret: credentialSecretStore,
}, mgr.fileSystem, mgr.log)
mgr.providers[velerov1api.BackupRepositoryTypeKopia] = provider.NewUnifiedRepoProvider(credentials.CredentialGetter{
FromFile: credentialFileStore,
FromSecret: credentialSecretStore,
@@ -275,8 +271,6 @@ func (m *manager) ClientSideCacheLimit(repo *velerov1api.BackupRepository) (int6
func (m *manager) getRepositoryProvider(repo *velerov1api.BackupRepository) (provider.Provider, error) {
switch repo.Spec.RepositoryType {
case "", velerov1api.BackupRepositoryTypeRestic:
return m.providers[velerov1api.BackupRepositoryTypeRestic], nil
case velerov1api.BackupRepositoryTypeKopia:
return m.providers[velerov1api.BackupRepositoryTypeKopia], nil
default:
+7 -9
View File
@@ -32,15 +32,13 @@ func TestGetRepositoryProvider(t *testing.T) {
repo := &velerov1.BackupRepository{}
// empty repository type
provider, err := mgr.getRepositoryProvider(repo)
require.NoError(t, err)
assert.NotNil(t, provider)
_, err := mgr.getRepositoryProvider(repo)
require.Error(t, err)
// valid repository type
repo.Spec.RepositoryType = velerov1.BackupRepositoryTypeRestic
provider, err = mgr.getRepositoryProvider(repo)
require.NoError(t, err)
assert.NotNil(t, provider)
// invalid repository type
repo.Spec.RepositoryType = "restic"
_, err = mgr.getRepositoryProvider(repo)
require.Error(t, err)
// invalid repository type
repo.Spec.RepositoryType = "unknown"
@@ -61,6 +59,6 @@ func TestGetRepositoryConfigProvider(t *testing.T) {
assert.NotNil(t, provider)
// invalid repository type
_, err = mgr.getRepositoryProvider(velerov1.BackupRepositoryTypeRestic)
_, err = mgr.getRepositoryProvider("restic")
require.Error(t, err)
}
-99
View File
@@ -1,99 +0,0 @@
/*
Copyright 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 provider
import (
"context"
"strings"
"time"
"github.com/sirupsen/logrus"
"github.com/vmware-tanzu/velero/internal/credentials"
"github.com/vmware-tanzu/velero/pkg/repository/restic"
"github.com/vmware-tanzu/velero/pkg/util/filesystem"
)
func NewResticRepositoryProvider(credGetter credentials.CredentialGetter, fs filesystem.Interface, log logrus.FieldLogger) Provider {
return &resticRepositoryProvider{
svc: restic.NewRepositoryService(credGetter, fs, log),
}
}
type resticRepositoryProvider struct {
svc *restic.RepositoryService
}
func (r *resticRepositoryProvider) InitRepo(ctx context.Context, param RepoParam) error {
return r.svc.InitRepo(param.BackupLocation, param.BackupRepo)
}
func (r *resticRepositoryProvider) ConnectToRepo(ctx context.Context, param RepoParam) error {
return r.svc.ConnectToRepo(param.BackupLocation, param.BackupRepo)
}
func (r *resticRepositoryProvider) PrepareRepo(ctx context.Context, param RepoParam) error {
if err := r.ConnectToRepo(ctx, param); err != nil {
// If the repository has not yet been initialized, the error message will always include
// the following string. This is the only scenario where we should try to initialize it.
// Other errors (e.g. "already locked") should be returned as-is since the repository
// does already exist, but it can't be connected to.
if strings.Contains(err.Error(), "Is there a repository at the following location?") {
return r.InitRepo(ctx, param)
}
return err
}
return nil
}
func (r *resticRepositoryProvider) BoostRepoConnect(ctx context.Context, param RepoParam) error {
return nil
}
func (r *resticRepositoryProvider) PruneRepo(ctx context.Context, param RepoParam) error {
return r.svc.PruneRepo(param.BackupLocation, param.BackupRepo)
}
func (r *resticRepositoryProvider) EnsureUnlockRepo(ctx context.Context, param RepoParam) error {
return r.svc.UnlockRepo(param.BackupLocation, param.BackupRepo)
}
func (r *resticRepositoryProvider) Forget(ctx context.Context, snapshotID string, param RepoParam) error {
return r.svc.Forget(param.BackupLocation, param.BackupRepo, snapshotID)
}
func (r *resticRepositoryProvider) BatchForget(ctx context.Context, snapshotIDs []string, param RepoParam) []error {
errs := []error{}
for _, snapshot := range snapshotIDs {
err := r.Forget(ctx, snapshot, param)
if err != nil {
errs = append(errs, err)
}
}
return errs
}
func (r *resticRepositoryProvider) DefaultMaintenanceFrequency() time.Duration {
return r.svc.DefaultMaintenanceFrequency()
}
func (r *resticRepositoryProvider) ClientSideCacheLimit(repoOption map[string]string) int64 {
return 0
}
-147
View File
@@ -1,147 +0,0 @@
/*
Copyright 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
import (
"os"
"time"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/vmware-tanzu/velero/internal/credentials"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
repokey "github.com/vmware-tanzu/velero/pkg/repository/keys"
"github.com/vmware-tanzu/velero/pkg/restic"
veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec"
"github.com/vmware-tanzu/velero/pkg/util/filesystem"
)
func NewRepositoryService(credGetter credentials.CredentialGetter, fs filesystem.Interface, log logrus.FieldLogger) *RepositoryService {
return &RepositoryService{
credGetter: credGetter,
fileSystem: fs,
log: log,
}
}
type RepositoryService struct {
credGetter credentials.CredentialGetter
fileSystem filesystem.Interface
log logrus.FieldLogger
}
func (r *RepositoryService) InitRepo(bsl *velerov1api.BackupStorageLocation, repo *velerov1api.BackupRepository) error {
return r.exec(restic.InitCommand(repo.Spec.ResticIdentifier), bsl)
}
func (r *RepositoryService) ConnectToRepo(bsl *velerov1api.BackupStorageLocation, repo *velerov1api.BackupRepository) error {
snapshotsCmd := restic.SnapshotsCommand(repo.Spec.ResticIdentifier)
// use the '--latest=1' flag to minimize the amount of data fetched since
// we're just validating that the repo exists and can be authenticated
// to.
// "--last" is replaced by "--latest=1" in restic v0.12.1
snapshotsCmd.ExtraFlags = append(snapshotsCmd.ExtraFlags, "--latest=1")
return r.exec(snapshotsCmd, bsl)
}
func (r *RepositoryService) PruneRepo(bsl *velerov1api.BackupStorageLocation, repo *velerov1api.BackupRepository) error {
return r.exec(restic.PruneCommand(repo.Spec.ResticIdentifier), bsl)
}
func (r *RepositoryService) UnlockRepo(bsl *velerov1api.BackupStorageLocation, repo *velerov1api.BackupRepository) error {
return r.exec(restic.UnlockCommand(repo.Spec.ResticIdentifier), bsl)
}
func (r *RepositoryService) Forget(bsl *velerov1api.BackupStorageLocation, repo *velerov1api.BackupRepository, snapshotID string) error {
return r.exec(restic.ForgetCommand(repo.Spec.ResticIdentifier, snapshotID), bsl)
}
func (r *RepositoryService) DefaultMaintenanceFrequency() time.Duration {
return restic.DefaultMaintenanceFrequency
}
func (r *RepositoryService) exec(cmd *restic.Command, bsl *velerov1api.BackupStorageLocation) error {
file, err := r.credGetter.FromFile.Path(repokey.RepoKeySelector())
if err != nil {
return err
}
// ignore error since there's nothing we can do and it's a temp file.
defer os.Remove(file)
cmd.PasswordFile = file
// if there's a caCert on the ObjectStorage, write it to disk so that it can be passed to restic
var caCertFile string
if bsl.Spec.ObjectStorage != nil {
var caCertData []byte
// Try CACertRef first (new method), then fall back to CACert (deprecated)
if bsl.Spec.ObjectStorage.CACertRef != nil {
caCertString, err := r.credGetter.FromSecret.Get(bsl.Spec.ObjectStorage.CACertRef)
if err != nil {
return errors.Wrap(err, "error getting CA certificate from secret")
}
caCertData = []byte(caCertString)
} else if bsl.Spec.ObjectStorage.CACert != nil {
caCertData = bsl.Spec.ObjectStorage.CACert
}
if caCertData != nil {
caCertFile, err = restic.TempCACertFile(caCertData, bsl.Name, r.fileSystem)
if err != nil {
return errors.Wrap(err, "error creating temp cacert file")
}
// ignore error since there's nothing we can do and it's a temp file.
defer os.Remove(caCertFile)
}
}
cmd.CACertFile = caCertFile
// CmdEnv uses credGetter.FromFile (not FromSecret) to get cloud provider credentials.
// FromFile materializes the BSL's Credential secret to a file path that cloud SDKs
// can read (e.g., AWS_SHARED_CREDENTIALS_FILE). This is different from caCertRef above,
// which uses FromSecret to read the CA certificate data directly into memory, then
// writes it to a temp file because restic CLI only accepts file paths (--cacert flag).
env, err := restic.CmdEnv(bsl, r.credGetter.FromFile)
if err != nil {
return err
}
cmd.Env = env
// #4820: restrieve insecureSkipTLSVerify from BSL configuration for
// AWS plugin. If nothing is return, that means insecureSkipTLSVerify
// is not enable for Restic command.
skipTLSRet := restic.GetInsecureSkipTLSVerifyFromBSL(bsl, r.log)
if len(skipTLSRet) > 0 {
cmd.ExtraFlags = append(cmd.ExtraFlags, skipTLSRet)
}
stdout, stderr, err := veleroexec.RunCommandWithLog(cmd.Cmd(), r.log)
r.log.WithFields(logrus.Fields{
"repository": cmd.RepoName(),
"command": cmd.String(),
"stdout": stdout,
"stderr": stderr,
}).Debugf("Ran restic command")
if err != nil {
return errors.Wrapf(err, "error running command=%s, stdout=%s, stderr=%s", cmd.String(), stdout, stderr)
}
return nil
}