mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-07-29 11:32:42 +00:00
Merge branch 'main' into 5048-CSI-snapshot-timeout-configurable
This commit is contained in:
@@ -70,6 +70,7 @@ import (
|
||||
"github.com/vmware-tanzu/velero/pkg/util/logging"
|
||||
"github.com/vmware-tanzu/velero/pkg/volume"
|
||||
|
||||
corev1api "k8s.io/api/core/v1"
|
||||
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
@@ -260,7 +261,6 @@ func (c *backupController) processBackup(key string) error {
|
||||
|
||||
log.Debug("Preparing backup request")
|
||||
request := c.prepareBackupRequest(original)
|
||||
|
||||
if len(request.Status.ValidationErrors) > 0 {
|
||||
request.Status.Phase = velerov1api.BackupPhaseFailedValidation
|
||||
} else {
|
||||
@@ -444,6 +444,17 @@ func (c *backupController) prepareBackupRequest(backup *velerov1api.Backup) *pkg
|
||||
request.Annotations[velerov1api.SourceClusterK8sMajorVersionAnnotation] = c.discoveryHelper.ServerVersion().Major
|
||||
request.Annotations[velerov1api.SourceClusterK8sMinorVersionAnnotation] = c.discoveryHelper.ServerVersion().Minor
|
||||
|
||||
// Add namespaces with label velero.io/exclude-from-backup=true into request.Spec.ExcludedNamespaces
|
||||
// Essentially, adding the label velero.io/exclude-from-backup=true to a namespace would be equivalent to setting spec.ExcludedNamespaces
|
||||
namespaces := corev1api.NamespaceList{}
|
||||
if err := c.kbClient.List(context.Background(), &namespaces, kbclient.MatchingLabels{"velero.io/exclude-from-backup": "true"}); err == nil {
|
||||
for _, ns := range namespaces.Items {
|
||||
request.Spec.ExcludedNamespaces = append(request.Spec.ExcludedNamespaces, ns.Name)
|
||||
}
|
||||
} else {
|
||||
request.Status.ValidationErrors = append(request.Status.ValidationErrors, fmt.Sprintf("error getting namespace list: %v", err))
|
||||
}
|
||||
|
||||
// validate the included/excluded resources
|
||||
for _, err := range collections.ValidateIncludesExcludes(request.Spec.IncludedResources, request.Spec.ExcludedResources) {
|
||||
request.Status.ValidationErrors = append(request.Status.ValidationErrors, fmt.Sprintf("Invalid included/excluded resource lists: %v", err))
|
||||
|
||||
@@ -198,7 +198,7 @@ func (r *backupDeletionReconciler) Reconcile(ctx context.Context, req ctrl.Reque
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
// if the request object has no labels defined, initialise an empty map since
|
||||
// if the request object has no labels defined, initialize an empty map since
|
||||
// we will be updating labels
|
||||
if dbr.Labels == nil {
|
||||
dbr.Labels = map[string]string{}
|
||||
|
||||
@@ -24,12 +24,10 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/builder"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
|
||||
"github.com/vmware-tanzu/velero/internal/storage"
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
@@ -39,7 +37,10 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
backupStorageLocationSyncPeriod = 1 * time.Minute
|
||||
// keep the enqueue period a smaller value to make sure the BSL can be validated as expected.
|
||||
// The BSL validation frequency is 1 minute by default, if we set the enqueue period as 1 minute,
|
||||
// this will cause the actual validation interval for each BSL to be 2 minutes
|
||||
bslValidationEnqueuePeriod = 10 * time.Second
|
||||
)
|
||||
|
||||
// BackupStorageLocationReconciler reconciles a BackupStorageLocation object
|
||||
@@ -185,7 +186,7 @@ func (r *BackupStorageLocationReconciler) SetupWithManager(mgr ctrl.Manager) err
|
||||
r.Log,
|
||||
mgr.GetClient(),
|
||||
&velerov1api.BackupStorageLocationList{},
|
||||
backupStorageLocationSyncPeriod,
|
||||
bslValidationEnqueuePeriod,
|
||||
// Add filter function to enqueue BSL per ValidationFrequency setting.
|
||||
func(object client.Object) bool {
|
||||
location := object.(*velerov1api.BackupStorageLocation)
|
||||
@@ -193,22 +194,8 @@ func (r *BackupStorageLocationReconciler) SetupWithManager(mgr ctrl.Manager) err
|
||||
},
|
||||
)
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&velerov1api.BackupStorageLocation{}).
|
||||
// Handle BSL's creation event and spec update event to let changed BSL got validation immediately.
|
||||
WithEventFilter(predicate.Funcs{
|
||||
CreateFunc: func(ce event.CreateEvent) bool {
|
||||
return true
|
||||
},
|
||||
UpdateFunc: func(ue event.UpdateEvent) bool {
|
||||
return ue.ObjectNew.GetGeneration() != ue.ObjectOld.GetGeneration()
|
||||
},
|
||||
DeleteFunc: func(de event.DeleteEvent) bool {
|
||||
return false
|
||||
},
|
||||
GenericFunc: func(ge event.GenericEvent) bool {
|
||||
return false
|
||||
},
|
||||
}).
|
||||
// As the "status.LastValidationTime" field is always updated, this triggers new reconciling process, skip the update event that include no spec change to avoid the reconcile loop
|
||||
For(&velerov1api.BackupStorageLocation{}, builder.WithPredicates(kube.SpecChangePredicate{})).
|
||||
Watches(g, nil).
|
||||
Complete(r)
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import (
|
||||
"github.com/vmware-tanzu/velero/internal/credentials"
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/metrics"
|
||||
repokey "github.com/vmware-tanzu/velero/pkg/repository/keys"
|
||||
"github.com/vmware-tanzu/velero/pkg/restic"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/filesystem"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/kube"
|
||||
@@ -124,7 +125,11 @@ func (r *PodVolumeBackupReconciler) Reconcile(ctx context.Context, req ctrl.Requ
|
||||
if err != nil {
|
||||
return r.updateStatusToFailed(ctx, &pvb, err, "building Restic command", log)
|
||||
}
|
||||
defer os.Remove(resticDetails.credsFile)
|
||||
|
||||
defer func() {
|
||||
os.Remove(resticDetails.credsFile)
|
||||
os.Remove(resticDetails.caCertFile)
|
||||
}()
|
||||
|
||||
backupLocation := &velerov1api.BackupStorageLocation{}
|
||||
if err := r.Client.Get(context.Background(), client.ObjectKey{
|
||||
@@ -204,19 +209,6 @@ func (r *PodVolumeBackupReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
Complete(r)
|
||||
}
|
||||
|
||||
func (r *PodVolumeBackupReconciler) singlePathMatch(path string) (string, error) {
|
||||
matches, err := r.FileSystem.Glob(path)
|
||||
if err != nil {
|
||||
return "", errors.WithStack(err)
|
||||
}
|
||||
|
||||
if len(matches) != 1 {
|
||||
return "", errors.Errorf("expected one matching path: %s, got %d", path, len(matches))
|
||||
}
|
||||
|
||||
return matches[0], nil
|
||||
}
|
||||
|
||||
// getParentSnapshot finds the most recent completed PodVolumeBackup for the
|
||||
// specified PVC and returns its Restic snapshot ID. Any errors encountered are
|
||||
// logged but not returned since they do not prevent a backup from proceeding.
|
||||
@@ -237,7 +229,7 @@ func (r *PodVolumeBackupReconciler) getParentSnapshot(ctx context.Context, log l
|
||||
|
||||
// Go through all the podvolumebackups for the PVC and look for the most
|
||||
// recent completed one to use as the parent.
|
||||
var mostRecentPVB *velerov1api.PodVolumeBackup
|
||||
var mostRecentPVB velerov1api.PodVolumeBackup
|
||||
for _, pvb := range pvbList.Items {
|
||||
if pvb.Status.Phase != velerov1api.PodVolumeBackupPhaseCompleted {
|
||||
continue
|
||||
@@ -254,12 +246,12 @@ func (r *PodVolumeBackupReconciler) getParentSnapshot(ctx context.Context, log l
|
||||
continue
|
||||
}
|
||||
|
||||
if mostRecentPVB == nil || pvb.Status.StartTimestamp.After(mostRecentPVB.Status.StartTimestamp.Time) {
|
||||
mostRecentPVB = &pvb
|
||||
if mostRecentPVB.Status == (velerov1api.PodVolumeBackupStatus{}) || pvb.Status.StartTimestamp.After(mostRecentPVB.Status.StartTimestamp.Time) {
|
||||
mostRecentPVB = pvb
|
||||
}
|
||||
}
|
||||
|
||||
if mostRecentPVB == nil {
|
||||
if mostRecentPVB.Status == (velerov1api.PodVolumeBackupStatus{}) {
|
||||
log.Info("No completed PodVolumeBackup found for PVC")
|
||||
return ""
|
||||
}
|
||||
@@ -313,14 +305,14 @@ func (r *PodVolumeBackupReconciler) buildResticCommand(ctx context.Context, log
|
||||
pathGlob := fmt.Sprintf("/host_pods/%s/volumes/*/%s", string(pvb.Spec.Pod.UID), volDir)
|
||||
log.WithField("pathGlob", pathGlob).Debug("Looking for path matching glob")
|
||||
|
||||
path, err := r.singlePathMatch(pathGlob)
|
||||
path, err := kube.SinglePathMatch(pathGlob, r.FileSystem, log)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "identifying unique volume path on host")
|
||||
}
|
||||
log.WithField("path", path).Debugf("Found path matching glob")
|
||||
|
||||
// Temporary credentials.
|
||||
details.credsFile, err = r.CredsFileStore.Path(restic.RepoKeySelector())
|
||||
details.credsFile, err = r.CredsFileStore.Path(repokey.RepoKeySelector())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "creating temporary Restic credentials file")
|
||||
}
|
||||
@@ -344,8 +336,6 @@ func (r *PodVolumeBackupReconciler) buildResticCommand(ctx context.Context, log
|
||||
if err != nil {
|
||||
log.WithError(err).Error("creating temporary caCert file")
|
||||
}
|
||||
defer os.Remove(details.caCertFile)
|
||||
|
||||
}
|
||||
cmd.CACertFile = details.caCertFile
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ import (
|
||||
|
||||
"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"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/boolptr"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/filesystem"
|
||||
@@ -215,19 +216,6 @@ func getResticInitContainerIndex(pod *corev1api.Pod) int {
|
||||
return -1
|
||||
}
|
||||
|
||||
func singlePathMatch(path string) (string, error) {
|
||||
matches, err := filepath.Glob(path)
|
||||
if err != nil {
|
||||
return "", errors.WithStack(err)
|
||||
}
|
||||
|
||||
if len(matches) != 1 {
|
||||
return "", errors.Errorf("expected one matching path: %s, got %d", path, len(matches))
|
||||
}
|
||||
|
||||
return matches[0], nil
|
||||
}
|
||||
|
||||
func (c *PodVolumeRestoreReconciler) processRestore(ctx context.Context, req *velerov1api.PodVolumeRestore, pod *corev1api.Pod, log logrus.FieldLogger) error {
|
||||
volumeDir, err := kube.GetVolumeDirectory(ctx, log, pod, req.Spec.Volume, c.Client)
|
||||
if err != nil {
|
||||
@@ -236,12 +224,14 @@ func (c *PodVolumeRestoreReconciler) processRestore(ctx context.Context, req *ve
|
||||
|
||||
// Get the full path of the new volume's directory as mounted in the daemonset pod, which
|
||||
// will look like: /host_pods/<new-pod-uid>/volumes/<volume-plugin-name>/<volume-dir>
|
||||
volumePath, err := singlePathMatch(fmt.Sprintf("/host_pods/%s/volumes/*/%s", string(req.Spec.Pod.UID), volumeDir))
|
||||
volumePath, err := kube.SinglePathMatch(
|
||||
fmt.Sprintf("/host_pods/%s/volumes/*/%s", string(req.Spec.Pod.UID), volumeDir),
|
||||
c.fileSystem, log)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error identifying path of volume")
|
||||
}
|
||||
|
||||
credsFile, err := c.credentialsFileStore.Path(restic.RepoKeySelector())
|
||||
credsFile, err := c.credentialsFileStore.Path(repokey.RepoKeySelector())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error creating temp restic credentials file")
|
||||
}
|
||||
|
||||
@@ -26,11 +26,11 @@ import (
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/clock"
|
||||
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
repoconfig "github.com/vmware-tanzu/velero/pkg/repository/config"
|
||||
"github.com/vmware-tanzu/velero/pkg/restic"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/kube"
|
||||
)
|
||||
@@ -68,16 +68,16 @@ func NewResticRepoReconciler(namespace string, logger logrus.FieldLogger, client
|
||||
}
|
||||
|
||||
func (r *ResticRepoReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
s := kube.NewPeriodicalEnqueueSource(r.logger, mgr.GetClient(), &velerov1api.ResticRepositoryList{}, repoSyncPeriod)
|
||||
s := kube.NewPeriodicalEnqueueSource(r.logger, mgr.GetClient(), &velerov1api.BackupRepositoryList{}, repoSyncPeriod)
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&velerov1api.ResticRepository{}).
|
||||
For(&velerov1api.BackupRepository{}).
|
||||
Watches(s, nil).
|
||||
Complete(r)
|
||||
}
|
||||
|
||||
func (r *ResticRepoReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
log := r.logger.WithField("resticRepo", req.String())
|
||||
resticRepo := &velerov1api.ResticRepository{}
|
||||
resticRepo := &velerov1api.BackupRepository{}
|
||||
if err := r.Get(ctx, req.NamespacedName, resticRepo); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
log.Warnf("restic repository %s in namespace %s is not found", req.Name, req.Namespace)
|
||||
@@ -87,7 +87,7 @@ func (r *ResticRepoReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
if resticRepo.Status.Phase == "" || resticRepo.Status.Phase == velerov1api.ResticRepositoryPhaseNew {
|
||||
if resticRepo.Status.Phase == "" || resticRepo.Status.Phase == velerov1api.BackupRepositoryPhaseNew {
|
||||
if err := r.initializeRepo(ctx, resticRepo, log); err != nil {
|
||||
log.WithError(err).Error("error initialize repository")
|
||||
return ctrl.Result{}, errors.WithStack(err)
|
||||
@@ -105,16 +105,16 @@ func (r *ResticRepoReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
||||
}
|
||||
|
||||
switch resticRepo.Status.Phase {
|
||||
case velerov1api.ResticRepositoryPhaseReady:
|
||||
case velerov1api.BackupRepositoryPhaseReady:
|
||||
return ctrl.Result{}, r.runMaintenanceIfDue(ctx, resticRepo, log)
|
||||
case velerov1api.ResticRepositoryPhaseNotReady:
|
||||
case velerov1api.BackupRepositoryPhaseNotReady:
|
||||
return ctrl.Result{}, r.checkNotReadyRepo(ctx, resticRepo, log)
|
||||
}
|
||||
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1api.ResticRepository, log logrus.FieldLogger) error {
|
||||
func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1api.BackupRepository, log logrus.FieldLogger) error {
|
||||
log.Info("Initializing restic repository")
|
||||
|
||||
// confirm the repo's BackupStorageLocation is valid
|
||||
@@ -127,11 +127,11 @@ func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1
|
||||
return r.patchResticRepository(ctx, req, repoNotReady(err.Error()))
|
||||
}
|
||||
|
||||
repoIdentifier, err := restic.GetRepoIdentifier(loc, req.Spec.VolumeNamespace)
|
||||
repoIdentifier, err := repoconfig.GetRepoIdentifier(loc, req.Spec.VolumeNamespace)
|
||||
if err != nil {
|
||||
return r.patchResticRepository(ctx, req, func(rr *velerov1api.ResticRepository) {
|
||||
return r.patchResticRepository(ctx, req, func(rr *velerov1api.BackupRepository) {
|
||||
rr.Status.Message = err.Error()
|
||||
rr.Status.Phase = velerov1api.ResticRepositoryPhaseNotReady
|
||||
rr.Status.Phase = velerov1api.BackupRepositoryPhaseNotReady
|
||||
|
||||
if rr.Spec.MaintenanceFrequency.Duration <= 0 {
|
||||
rr.Spec.MaintenanceFrequency = metav1.Duration{Duration: r.defaultMaintenanceFrequency}
|
||||
@@ -140,7 +140,7 @@ func (r *ResticRepoReconciler) 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.patchResticRepository(ctx, req, func(rr *velerov1api.ResticRepository) {
|
||||
if err := r.patchResticRepository(ctx, req, func(rr *velerov1api.BackupRepository) {
|
||||
rr.Spec.ResticIdentifier = repoIdentifier
|
||||
|
||||
if rr.Spec.MaintenanceFrequency.Duration <= 0 {
|
||||
@@ -154,8 +154,8 @@ func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1
|
||||
return r.patchResticRepository(ctx, req, repoNotReady(err.Error()))
|
||||
}
|
||||
|
||||
return r.patchResticRepository(ctx, req, func(rr *velerov1api.ResticRepository) {
|
||||
rr.Status.Phase = velerov1api.ResticRepositoryPhaseReady
|
||||
return r.patchResticRepository(ctx, req, func(rr *velerov1api.BackupRepository) {
|
||||
rr.Status.Phase = velerov1api.BackupRepositoryPhaseReady
|
||||
rr.Status.LastMaintenanceTime = &metav1.Time{Time: time.Now()}
|
||||
})
|
||||
}
|
||||
@@ -163,7 +163,7 @@ func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1
|
||||
// ensureRepo checks to see if a repository exists, and attempts to initialize it if
|
||||
// it does not exist. An error is returned if the repository can't be connected to
|
||||
// or initialized.
|
||||
func ensureRepo(repo *velerov1api.ResticRepository, repoManager restic.RepositoryManager) error {
|
||||
func ensureRepo(repo *velerov1api.BackupRepository, repoManager restic.RepositoryManager) error {
|
||||
if err := repoManager.ConnectToRepo(repo); 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.
|
||||
@@ -179,7 +179,7 @@ func ensureRepo(repo *velerov1api.ResticRepository, repoManager restic.Repositor
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ResticRepoReconciler) runMaintenanceIfDue(ctx context.Context, req *velerov1api.ResticRepository, log logrus.FieldLogger) error {
|
||||
func (r *ResticRepoReconciler) runMaintenanceIfDue(ctx context.Context, req *velerov1api.BackupRepository, log logrus.FieldLogger) error {
|
||||
log.Debug("resticRepositoryController.runMaintenanceIfDue")
|
||||
|
||||
now := r.clock.Now()
|
||||
@@ -196,21 +196,21 @@ func (r *ResticRepoReconciler) runMaintenanceIfDue(ctx context.Context, req *vel
|
||||
log.Debug("Pruning repo")
|
||||
if err := r.repositoryManager.PruneRepo(req); err != nil {
|
||||
log.WithError(err).Warn("error pruning repository")
|
||||
return r.patchResticRepository(ctx, req, func(rr *velerov1api.ResticRepository) {
|
||||
return r.patchResticRepository(ctx, req, func(rr *velerov1api.BackupRepository) {
|
||||
rr.Status.Message = err.Error()
|
||||
})
|
||||
}
|
||||
|
||||
return r.patchResticRepository(ctx, req, func(rr *velerov1api.ResticRepository) {
|
||||
return r.patchResticRepository(ctx, req, func(rr *velerov1api.BackupRepository) {
|
||||
rr.Status.LastMaintenanceTime = &metav1.Time{Time: now}
|
||||
})
|
||||
}
|
||||
|
||||
func dueForMaintenance(req *velerov1api.ResticRepository, now time.Time) bool {
|
||||
func dueForMaintenance(req *velerov1api.BackupRepository, now time.Time) bool {
|
||||
return req.Status.LastMaintenanceTime == nil || req.Status.LastMaintenanceTime.Add(req.Spec.MaintenanceFrequency.Duration).Before(now)
|
||||
}
|
||||
|
||||
func (r *ResticRepoReconciler) checkNotReadyRepo(ctx context.Context, req *velerov1api.ResticRepository, log logrus.FieldLogger) error {
|
||||
func (r *ResticRepoReconciler) checkNotReadyRepo(ctx context.Context, req *velerov1api.BackupRepository, log logrus.FieldLogger) error {
|
||||
// no identifier: can't possibly be ready, so just return
|
||||
if req.Spec.ResticIdentifier == "" {
|
||||
return nil
|
||||
@@ -226,16 +226,16 @@ func (r *ResticRepoReconciler) checkNotReadyRepo(ctx context.Context, req *veler
|
||||
return r.patchResticRepository(ctx, req, repoReady())
|
||||
}
|
||||
|
||||
func repoNotReady(msg string) func(*velerov1api.ResticRepository) {
|
||||
return func(r *velerov1api.ResticRepository) {
|
||||
r.Status.Phase = velerov1api.ResticRepositoryPhaseNotReady
|
||||
func repoNotReady(msg string) func(*velerov1api.BackupRepository) {
|
||||
return func(r *velerov1api.BackupRepository) {
|
||||
r.Status.Phase = velerov1api.BackupRepositoryPhaseNotReady
|
||||
r.Status.Message = msg
|
||||
}
|
||||
}
|
||||
|
||||
func repoReady() func(*velerov1api.ResticRepository) {
|
||||
return func(r *velerov1api.ResticRepository) {
|
||||
r.Status.Phase = velerov1api.ResticRepositoryPhaseReady
|
||||
func repoReady() func(*velerov1api.BackupRepository) {
|
||||
return func(r *velerov1api.BackupRepository) {
|
||||
r.Status.Phase = velerov1api.BackupRepositoryPhaseReady
|
||||
r.Status.Message = ""
|
||||
}
|
||||
}
|
||||
@@ -243,7 +243,7 @@ func repoReady() func(*velerov1api.ResticRepository) {
|
||||
// patchResticRepository mutates req with the provided mutate function, and patches it
|
||||
// through the Kube API. After executing this function, req will be updated with both
|
||||
// the mutation and the results of the Patch() API call.
|
||||
func (r *ResticRepoReconciler) patchResticRepository(ctx context.Context, req *velerov1api.ResticRepository, mutate func(*velerov1api.ResticRepository)) error {
|
||||
func (r *ResticRepoReconciler) patchResticRepository(ctx context.Context, req *velerov1api.BackupRepository, mutate func(*velerov1api.BackupRepository)) error {
|
||||
original := req.DeepCopy()
|
||||
mutate(req)
|
||||
if err := r.Patch(ctx, req, client.MergeFrom(original)); err != nil {
|
||||
|
||||
@@ -30,7 +30,7 @@ import (
|
||||
|
||||
const defaultMaintenanceFrequency = 10 * time.Minute
|
||||
|
||||
func mockResticRepoReconciler(t *testing.T, rr *velerov1api.ResticRepository, mockOn string, arg interface{}, ret interface{}) *ResticRepoReconciler {
|
||||
func mockResticRepoReconciler(t *testing.T, rr *velerov1api.BackupRepository, mockOn string, arg interface{}, ret interface{}) *ResticRepoReconciler {
|
||||
mgr := &resticmokes.RepositoryManager{}
|
||||
if mockOn != "" {
|
||||
mgr.On(mockOn, arg).Return(ret)
|
||||
@@ -44,13 +44,13 @@ func mockResticRepoReconciler(t *testing.T, rr *velerov1api.ResticRepository, mo
|
||||
)
|
||||
}
|
||||
|
||||
func mockResticRepositoryCR() *velerov1api.ResticRepository {
|
||||
return &velerov1api.ResticRepository{
|
||||
func mockResticRepositoryCR() *velerov1api.BackupRepository {
|
||||
return &velerov1api.BackupRepository{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: velerov1api.DefaultNamespace,
|
||||
Name: "repo",
|
||||
},
|
||||
Spec: velerov1api.ResticRepositorySpec{
|
||||
Spec: velerov1api.BackupRepositorySpec{
|
||||
MaintenanceFrequency: metav1.Duration{defaultMaintenanceFrequency},
|
||||
},
|
||||
}
|
||||
@@ -64,10 +64,10 @@ func TestPatchResticRepository(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
err = reconciler.patchResticRepository(context.Background(), rr, repoReady())
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, rr.Status.Phase, velerov1api.ResticRepositoryPhaseReady)
|
||||
assert.Equal(t, rr.Status.Phase, velerov1api.BackupRepositoryPhaseReady)
|
||||
err = reconciler.patchResticRepository(context.Background(), rr, repoNotReady("not ready"))
|
||||
assert.NoError(t, err)
|
||||
assert.NotEqual(t, rr.Status.Phase, velerov1api.ResticRepositoryPhaseReady)
|
||||
assert.NotEqual(t, rr.Status.Phase, velerov1api.BackupRepositoryPhaseReady)
|
||||
}
|
||||
|
||||
func TestCheckNotReadyRepo(t *testing.T) {
|
||||
@@ -77,11 +77,11 @@ func TestCheckNotReadyRepo(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
err = reconciler.checkNotReadyRepo(context.TODO(), rr, reconciler.logger)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, rr.Status.Phase, velerov1api.ResticRepositoryPhase(""))
|
||||
assert.Equal(t, rr.Status.Phase, velerov1api.BackupRepositoryPhase(""))
|
||||
rr.Spec.ResticIdentifier = "s3:test.amazonaws.com/bucket/restic"
|
||||
err = reconciler.checkNotReadyRepo(context.TODO(), rr, reconciler.logger)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, rr.Status.Phase, velerov1api.ResticRepositoryPhaseReady)
|
||||
assert.Equal(t, rr.Status.Phase, velerov1api.BackupRepositoryPhaseReady)
|
||||
}
|
||||
|
||||
func TestRunMaintenanceIfDue(t *testing.T) {
|
||||
@@ -121,23 +121,23 @@ func TestInitializeRepo(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
err = reconciler.initializeRepo(context.TODO(), rr, reconciler.logger)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, rr.Status.Phase, velerov1api.ResticRepositoryPhaseReady)
|
||||
assert.Equal(t, rr.Status.Phase, velerov1api.BackupRepositoryPhaseReady)
|
||||
}
|
||||
|
||||
func TestResticRepoReconcile(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
repo *velerov1api.ResticRepository
|
||||
repo *velerov1api.BackupRepository
|
||||
expectNil bool
|
||||
}{
|
||||
{
|
||||
name: "test on api server not found",
|
||||
repo: &velerov1api.ResticRepository{
|
||||
repo: &velerov1api.BackupRepository{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: velerov1api.DefaultNamespace,
|
||||
Name: "unknown",
|
||||
},
|
||||
Spec: velerov1api.ResticRepositorySpec{
|
||||
Spec: velerov1api.BackupRepositorySpec{
|
||||
MaintenanceFrequency: metav1.Duration{defaultMaintenanceFrequency},
|
||||
},
|
||||
},
|
||||
@@ -145,12 +145,12 @@ func TestResticRepoReconcile(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "test on initialize repo",
|
||||
repo: &velerov1api.ResticRepository{
|
||||
repo: &velerov1api.BackupRepository{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: velerov1api.DefaultNamespace,
|
||||
Name: "repo",
|
||||
},
|
||||
Spec: velerov1api.ResticRepositorySpec{
|
||||
Spec: velerov1api.BackupRepositorySpec{
|
||||
MaintenanceFrequency: metav1.Duration{defaultMaintenanceFrequency},
|
||||
},
|
||||
},
|
||||
@@ -158,16 +158,16 @@ func TestResticRepoReconcile(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "test on repo with new phase",
|
||||
repo: &velerov1api.ResticRepository{
|
||||
repo: &velerov1api.BackupRepository{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: velerov1api.DefaultNamespace,
|
||||
Name: "repo",
|
||||
},
|
||||
Spec: velerov1api.ResticRepositorySpec{
|
||||
Spec: velerov1api.BackupRepositorySpec{
|
||||
MaintenanceFrequency: metav1.Duration{defaultMaintenanceFrequency},
|
||||
},
|
||||
Status: velerov1api.ResticRepositoryStatus{
|
||||
Phase: velerov1api.ResticRepositoryPhaseNew,
|
||||
Status: velerov1api.BackupRepositoryStatus{
|
||||
Phase: velerov1api.BackupRepositoryPhaseNew,
|
||||
},
|
||||
},
|
||||
expectNil: true,
|
||||
|
||||
@@ -38,6 +38,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
hook "github.com/vmware-tanzu/velero/internal/hook"
|
||||
api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1"
|
||||
@@ -71,10 +72,14 @@ var nonRestorableResources = []string{
|
||||
// https://github.com/vmware-tanzu/velero/issues/622
|
||||
"restores.velero.io",
|
||||
|
||||
// TODO: Remove this in v1.11 or v1.12
|
||||
// Restic repositories are automatically managed by Velero and will be automatically
|
||||
// created as needed if they don't exist.
|
||||
// https://github.com/vmware-tanzu/velero/issues/1113
|
||||
"resticrepositories.velero.io",
|
||||
|
||||
// Backup repositories were renamed from Restic repositories
|
||||
"backuprepositories.velero.io",
|
||||
}
|
||||
|
||||
type restoreController struct {
|
||||
@@ -324,6 +329,22 @@ func (c *restoreController) validateAndComplete(restore *api.Restore, pluginMana
|
||||
return backupInfo{}
|
||||
}
|
||||
|
||||
// validate Restore Init Hook's InitContainers
|
||||
restoreHooks, err := hook.GetRestoreHooksFromSpec(&restore.Spec.Hooks)
|
||||
if err != nil {
|
||||
restore.Status.ValidationErrors = append(restore.Status.ValidationErrors, err.Error())
|
||||
}
|
||||
for _, resource := range restoreHooks {
|
||||
for _, h := range resource.RestoreHooks {
|
||||
for _, container := range h.Init.InitContainers {
|
||||
err = hook.ValidateContainer(container.Raw)
|
||||
if err != nil {
|
||||
restore.Status.ValidationErrors = append(restore.Status.ValidationErrors, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if ScheduleName is specified, fill in BackupName with the most recent successful backup from
|
||||
// the schedule
|
||||
if restore.Spec.ScheduleName != "" {
|
||||
|
||||
Reference in New Issue
Block a user