mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-07-25 17:43:00 +00:00
Update CRDs and CLI to support in-place restore (#10038)
Update CRDs(Restore, DataDownload, PodVolumeRestore) and restore create CLI to support in-place restore Signed-off-by: Wenkai Yin(尹文开) <yinw@vmware.com>
This commit is contained in:
@@ -132,6 +132,9 @@ spec:
|
||||
repoIdentifier:
|
||||
description: RepoIdentifier is the backup repository identifier.
|
||||
type: string
|
||||
restoreType:
|
||||
description: RestoreType indicates the type of the restore.
|
||||
type: string
|
||||
snapshotID:
|
||||
description: SnapshotID is the ID of the volume snapshot to be restored.
|
||||
type: string
|
||||
@@ -167,6 +170,7 @@ spec:
|
||||
- backupStorageLocation
|
||||
- pod
|
||||
- repoIdentifier
|
||||
- restoreType
|
||||
- snapshotID
|
||||
- sourceNamespace
|
||||
- volume
|
||||
|
||||
@@ -69,6 +69,11 @@ spec:
|
||||
for the Kubernetes resource to be restored
|
||||
nullable: true
|
||||
type: string
|
||||
existingVolumeDataPolicy:
|
||||
description: ExistingVolumeDataPolicy specifies the restore behavior
|
||||
for the volume data to be restored
|
||||
nullable: true
|
||||
type: string
|
||||
hooks:
|
||||
description: Hooks represent custom behaviors that should be executed
|
||||
during or post restore.
|
||||
@@ -471,6 +476,12 @@ spec:
|
||||
description: UploaderConfig specifies the configuration for the restore.
|
||||
nullable: true
|
||||
properties:
|
||||
deleteExtraFiles:
|
||||
description: |-
|
||||
DeleteExtraFiles specifies whether to delete the extra files in the target volume that do not exist in the backup.
|
||||
This setting is only applicable to File System restores (PodVolumeBackup or CSI File System Data Move) and has no effect on Block Data Move restores.
|
||||
nullable: true
|
||||
type: boolean
|
||||
parallelFilesDownload:
|
||||
description: ParallelFilesDownload is the concurrency number setting
|
||||
for restore.
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -83,6 +83,30 @@ spec:
|
||||
Cancel indicates request to cancel the ongoing DataDownload. It can be set
|
||||
when the DataDownload is in InProgress phase
|
||||
type: boolean
|
||||
csiSnapshot:
|
||||
description: CSISnapshot provides the information of the CSI snapshot
|
||||
used to do the incremental restore.
|
||||
nullable: true
|
||||
properties:
|
||||
driver:
|
||||
description: Driver is the driver used by the VolumeSnapshotContent
|
||||
type: string
|
||||
snapshotClass:
|
||||
description: SnapshotClass is the name of the snapshot class that
|
||||
the volume snapshot is created with
|
||||
type: string
|
||||
storageClass:
|
||||
description: StorageClass is the name of the storage class of
|
||||
the PVC that the volume snapshot is created from
|
||||
type: string
|
||||
volumeSnapshot:
|
||||
description: VolumeSnapshot is the name of the volume snapshot
|
||||
to be backed up
|
||||
type: string
|
||||
required:
|
||||
- storageClass
|
||||
- volumeSnapshot
|
||||
type: object
|
||||
dataMoverConfig:
|
||||
additionalProperties:
|
||||
type: string
|
||||
@@ -106,6 +130,9 @@ spec:
|
||||
OperationTimeout specifies the time used to wait internal operations,
|
||||
before returning error as timeout.
|
||||
type: string
|
||||
restoreType:
|
||||
description: RestoreType indicates the type of the restore.
|
||||
type: string
|
||||
snapshotID:
|
||||
description: SnapshotID is the ID of the Velero backup snapshot to
|
||||
be restored from.
|
||||
@@ -145,6 +172,7 @@ spec:
|
||||
required:
|
||||
- backupStorageLocation
|
||||
- operationTimeout
|
||||
- restoreType
|
||||
- snapshotID
|
||||
- sourceNamespace
|
||||
- targetVolume
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -46,6 +46,9 @@ type PodVolumeRestoreSpec struct {
|
||||
// SnapshotID is the ID of the volume snapshot to be restored.
|
||||
SnapshotID string `json:"snapshotID"`
|
||||
|
||||
// RestoreType indicates the type of the restore.
|
||||
RestoreType string `json:"restoreType"`
|
||||
|
||||
// SourceNamespace is the original namespace for namaspace mapping.
|
||||
SourceNamespace string `json:"sourceNamespace"`
|
||||
|
||||
|
||||
@@ -113,7 +113,12 @@ type RestoreSpec struct {
|
||||
// ExistingResourcePolicy specifies the restore behavior for the Kubernetes resource to be restored
|
||||
// +optional
|
||||
// +nullable
|
||||
ExistingResourcePolicy PolicyType `json:"existingResourcePolicy,omitempty"`
|
||||
ExistingResourcePolicy ResourcePolicyType `json:"existingResourcePolicy,omitempty"`
|
||||
|
||||
// ExistingVolumeDataPolicy specifies the restore behavior for the volume data to be restored
|
||||
// +optional
|
||||
// +nullable
|
||||
ExistingVolumeDataPolicy VolumeDataPolicyType `json:"existingVolumeDataPolicy,omitempty"`
|
||||
|
||||
// ItemOperationTimeout specifies the time used to wait for RestoreItemAction operations
|
||||
// The default value is 4 hour.
|
||||
@@ -141,6 +146,10 @@ type RestoreSpec struct {
|
||||
UploaderConfig *UploaderConfigForRestore `json:"uploaderConfig,omitempty"`
|
||||
}
|
||||
|
||||
func (r *RestoreSpec) IsVolumeDataInplaceRestore() bool {
|
||||
return r.ExistingVolumeDataPolicy == VolumeDataPolicyTypeFull || r.ExistingVolumeDataPolicy == VolumeDataPolicyTypeIncremental
|
||||
}
|
||||
|
||||
// UploaderConfigForRestore defines the configuration for the restore.
|
||||
type UploaderConfigForRestore struct {
|
||||
// WriteSparseFiles is a flag to indicate whether write files sparsely or not.
|
||||
@@ -150,6 +159,11 @@ type UploaderConfigForRestore struct {
|
||||
// ParallelFilesDownload is the concurrency number setting for restore.
|
||||
// +optional
|
||||
ParallelFilesDownload int `json:"parallelFilesDownload,omitempty"`
|
||||
// DeleteExtraFiles specifies whether to delete the extra files in the target volume that do not exist in the backup.
|
||||
// This setting is only applicable to File System restores (PodVolumeBackup or CSI File System Data Move) and has no effect on Block Data Move restores.
|
||||
// +optional
|
||||
// +nullable
|
||||
DeleteExtraFiles *bool `json:"deleteExtraFiles,omitempty"`
|
||||
}
|
||||
|
||||
// RestoreHooks contains custom behaviors that should be executed during or post restore.
|
||||
@@ -316,13 +330,22 @@ const (
|
||||
// The failing error is recorded in status.FailureReason.
|
||||
RestorePhaseFailed RestorePhase = "Failed"
|
||||
|
||||
// PolicyTypeNone means velero will not overwrite the resource
|
||||
// ResourcePolicyTypeNone means velero will not overwrite the resource
|
||||
// in cluster with the one in backup whether changed/unchanged.
|
||||
PolicyTypeNone PolicyType = "none"
|
||||
ResourcePolicyTypeNone ResourcePolicyType = "none"
|
||||
|
||||
// PolicyTypeUpdate means velero will try to attempt a patch on
|
||||
// ResourcePolicyTypeUpdate means velero will try to attempt a patch on
|
||||
// the changed resources.
|
||||
PolicyTypeUpdate PolicyType = "update"
|
||||
ResourcePolicyTypeUpdate ResourcePolicyType = "update"
|
||||
|
||||
// VolumeDataPolicyTypeNone means velero will skip and not overwrite the volume data if the volume already exists
|
||||
VolumeDataPolicyTypeNone VolumeDataPolicyType = "none"
|
||||
|
||||
// VolumeDataPolicyTypeFull means velero will try to restore the volume data fully if the volume already exists.
|
||||
VolumeDataPolicyTypeFull VolumeDataPolicyType = "full"
|
||||
|
||||
// VolumeDataPolicyTypeIncremental means velero will try to restore the volume data incrementally if the volume already exists.
|
||||
VolumeDataPolicyTypeIncremental VolumeDataPolicyType = "incremental"
|
||||
)
|
||||
|
||||
// RestoreStatus captures the current status of a Velero restore
|
||||
@@ -440,5 +463,8 @@ type RestoreList struct {
|
||||
Items []Restore `json:"items"`
|
||||
}
|
||||
|
||||
// PolicyType helps specify the ExistingResourcePolicy
|
||||
type PolicyType string
|
||||
// ResourcePolicyType helps specify the ExistingResourcePolicy
|
||||
type ResourcePolicyType string
|
||||
|
||||
// VolumeDataPolicyType helps specify the ExistingVolumeDataPolicy
|
||||
type VolumeDataPolicyType string
|
||||
|
||||
@@ -1754,6 +1754,11 @@ func (in *UploaderConfigForRestore) DeepCopyInto(out *UploaderConfigForRestore)
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
if in.DeleteExtraFiles != nil {
|
||||
in, out := &in.DeleteExtraFiles, &out.DeleteExtraFiles
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UploaderConfigForRestore.
|
||||
|
||||
@@ -39,6 +39,14 @@ type DataDownloadSpec struct {
|
||||
// SnapshotID is the ID of the Velero backup snapshot to be restored from.
|
||||
SnapshotID string `json:"snapshotID"`
|
||||
|
||||
// RestoreType indicates the type of the restore.
|
||||
RestoreType string `json:"restoreType"`
|
||||
|
||||
// CSISnapshot provides the information of the CSI snapshot used to do the incremental restore.
|
||||
// +optional
|
||||
// +nullable
|
||||
CSISnapshot *CSISnapshotSpec `json:"csiSnapshot"`
|
||||
|
||||
// SourceNamespace is the original namespace where the volume is backed up from.
|
||||
// It may be different from SourcePVC's namespace if namespace is remapped during restore.
|
||||
SourceNamespace string `json:"sourceNamespace"`
|
||||
|
||||
@@ -86,6 +86,11 @@ func (in *DataDownloadList) DeepCopyObject() runtime.Object {
|
||||
func (in *DataDownloadSpec) DeepCopyInto(out *DataDownloadSpec) {
|
||||
*out = *in
|
||||
out.TargetVolume = in.TargetVolume
|
||||
if in.CSISnapshot != nil {
|
||||
in, out := &in.CSISnapshot, &out.CSISnapshot
|
||||
*out = new(CSISnapshotSpec)
|
||||
**out = **in
|
||||
}
|
||||
if in.DataMoverConfig != nil {
|
||||
in, out := &in.DataMoverConfig, &out.DataMoverConfig
|
||||
*out = make(map[string]string, len(*in))
|
||||
|
||||
@@ -98,7 +98,13 @@ func (b *RestoreBuilder) ExcludedResources(resources ...string) *RestoreBuilder
|
||||
|
||||
// ExistingResourcePolicy sets the Restore's resource policy.
|
||||
func (b *RestoreBuilder) ExistingResourcePolicy(policy string) *RestoreBuilder {
|
||||
b.object.Spec.ExistingResourcePolicy = velerov1api.PolicyType(policy)
|
||||
b.object.Spec.ExistingResourcePolicy = velerov1api.ResourcePolicyType(policy)
|
||||
return b
|
||||
}
|
||||
|
||||
// ExistingVolumeDataPolicy sets the Restore's volume data policy.
|
||||
func (b *RestoreBuilder) ExistingVolumeDataPolicy(policy string) *RestoreBuilder {
|
||||
b.object.Spec.ExistingVolumeDataPolicy = velerov1api.VolumeDataPolicyType(policy)
|
||||
return b
|
||||
}
|
||||
|
||||
|
||||
@@ -95,6 +95,7 @@ type CreateOptions struct {
|
||||
IncludeNamespaces flag.StringArray
|
||||
ExcludeNamespaces flag.StringArray
|
||||
ExistingResourcePolicy string
|
||||
ExistingVolumeDataPolicy string
|
||||
IncludeResources flag.StringArray
|
||||
ExcludeResources flag.StringArray
|
||||
StatusIncludeResources flag.StringArray
|
||||
@@ -110,6 +111,7 @@ type CreateOptions struct {
|
||||
ResourcePoliciesConfigMap string
|
||||
WriteSparseFiles flag.OptionalBool
|
||||
ParallelFilesDownload int
|
||||
DeleteExtraFiles flag.OptionalBool
|
||||
client kbclient.WithWatch
|
||||
}
|
||||
|
||||
@@ -123,6 +125,7 @@ func NewCreateOptions() *CreateOptions {
|
||||
PreserveNodePorts: flag.NewOptionalBool(nil),
|
||||
IncludeClusterResources: flag.NewOptionalBool(nil),
|
||||
WriteSparseFiles: flag.NewOptionalBool(nil),
|
||||
DeleteExtraFiles: flag.NewOptionalBool(nil),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +139,8 @@ func (o *CreateOptions) BindFlags(flags *pflag.FlagSet) {
|
||||
flags.Var(&o.Annotations, "annotations", "Annotations to apply to the restore.")
|
||||
flags.Var(&o.IncludeResources, "include-resources", "Resources to include in the restore, formatted as resource.group, such as storageclasses.storage.k8s.io (use '*' for all resources).")
|
||||
flags.Var(&o.ExcludeResources, "exclude-resources", "Resources to exclude from the restore, formatted as resource.group, such as storageclasses.storage.k8s.io.")
|
||||
flags.StringVar(&o.ExistingResourcePolicy, "existing-resource-policy", "", "Restore Policy to be used during the restore workflow, can be - none or update")
|
||||
flags.StringVar(&o.ExistingResourcePolicy, "existing-resource-policy", "", "Restore Policy to be used during the restore workflow for Kubernetes resources, can be - none or update")
|
||||
flags.StringVar(&o.ExistingVolumeDataPolicy, "existing-volume-data-policy", "", "Restore Policy to be used during the restore workflow for volume data, can be - none, full or incremental")
|
||||
flags.Var(&o.StatusIncludeResources, "status-include-resources", "Resources to include in the restore status, formatted as resource.group, such as storageclasses.storage.k8s.io.")
|
||||
flags.Var(&o.StatusExcludeResources, "status-exclude-resources", "Resources to exclude from the restore status, formatted as resource.group, such as storageclasses.storage.k8s.io.")
|
||||
flags.VarP(&o.Selector, "selector", "l", "Only restore resources matching this label selector.")
|
||||
@@ -168,6 +172,9 @@ func (o *CreateOptions) BindFlags(flags *pflag.FlagSet) {
|
||||
f.NoOptDefVal = cmd.TRUE
|
||||
|
||||
flags.IntVar(&o.ParallelFilesDownload, "parallel-files-download", 0, "The number of restore operations to run in parallel. If set to 0, the default parallelism will be the number of CPUs for the node that node agent pod is running.")
|
||||
|
||||
f = flags.VarPF(&o.DeleteExtraFiles, "delete-extra-files", "", "Whether to delete extra files in the target volume that do not exist in the backup during file system restore. This setting is only applicable to File System restores (PodVolumeBackup or CSI File System Data Move) and has no effect on Block Data Move restores.")
|
||||
f.NoOptDefVal = cmd.TRUE
|
||||
}
|
||||
|
||||
func (o *CreateOptions) Complete(args []string, f client.Factory) error {
|
||||
@@ -217,6 +224,10 @@ func (o *CreateOptions) Validate(c *cobra.Command, args []string, f client.Facto
|
||||
return errors.New("existing-resource-policy has invalid value, it accepts only none, update as value")
|
||||
}
|
||||
|
||||
if len(o.ExistingVolumeDataPolicy) > 0 && !restore.IsVolumeDataPolicyValid(o.ExistingVolumeDataPolicy) {
|
||||
return errors.New("existing-volume-data-policy has invalid value, it accepts only none, full, incremental as value")
|
||||
}
|
||||
|
||||
if o.ParallelFilesDownload < 0 {
|
||||
return errors.New("parallel-files-download cannot be negative")
|
||||
}
|
||||
@@ -337,27 +348,29 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error {
|
||||
Annotations: o.Annotations.Data(),
|
||||
},
|
||||
Spec: api.RestoreSpec{
|
||||
BackupName: o.BackupName,
|
||||
ScheduleName: o.ScheduleName,
|
||||
IncludedNamespaces: o.IncludeNamespaces,
|
||||
ExcludedNamespaces: o.ExcludeNamespaces,
|
||||
IncludedResources: o.IncludeResources,
|
||||
ExcludedResources: o.ExcludeResources,
|
||||
ExistingResourcePolicy: api.PolicyType(o.ExistingResourcePolicy),
|
||||
NamespaceMapping: o.NamespaceMappings.Data(),
|
||||
LabelSelector: o.Selector.LabelSelector,
|
||||
OrLabelSelectors: o.OrSelector.OrLabelSelectors,
|
||||
RestorePVs: o.RestoreVolumes.Value,
|
||||
PreserveNodePorts: o.PreserveNodePorts.Value,
|
||||
IncludeClusterResources: o.IncludeClusterResources.Value,
|
||||
ResourceModifier: resModifiers,
|
||||
ResourcePolicy: resPolicies,
|
||||
BackupName: o.BackupName,
|
||||
ScheduleName: o.ScheduleName,
|
||||
IncludedNamespaces: o.IncludeNamespaces,
|
||||
ExcludedNamespaces: o.ExcludeNamespaces,
|
||||
IncludedResources: o.IncludeResources,
|
||||
ExcludedResources: o.ExcludeResources,
|
||||
ExistingResourcePolicy: api.ResourcePolicyType(o.ExistingResourcePolicy),
|
||||
ExistingVolumeDataPolicy: api.VolumeDataPolicyType(o.ExistingVolumeDataPolicy),
|
||||
NamespaceMapping: o.NamespaceMappings.Data(),
|
||||
LabelSelector: o.Selector.LabelSelector,
|
||||
OrLabelSelectors: o.OrSelector.OrLabelSelectors,
|
||||
RestorePVs: o.RestoreVolumes.Value,
|
||||
PreserveNodePorts: o.PreserveNodePorts.Value,
|
||||
IncludeClusterResources: o.IncludeClusterResources.Value,
|
||||
ResourceModifier: resModifiers,
|
||||
ResourcePolicy: resPolicies,
|
||||
ItemOperationTimeout: metav1.Duration{
|
||||
Duration: o.ItemOperationTimeout,
|
||||
},
|
||||
UploaderConfig: &api.UploaderConfigForRestore{
|
||||
WriteSparseFiles: o.WriteSparseFiles.Value,
|
||||
ParallelFilesDownload: o.ParallelFilesDownload,
|
||||
DeleteExtraFiles: o.DeleteExtraFiles.Value,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@ func TestCreateCommand(t *testing.T) {
|
||||
includeNamespaces := "app1,app2"
|
||||
excludeNamespaces := "pod1,pod2,pod3"
|
||||
existingResourcePolicy := "none"
|
||||
existingVolumeDataPolicy := "none"
|
||||
includeResources := "sc,sts"
|
||||
excludeResources := "job"
|
||||
statusIncludeResources := "sc,sts"
|
||||
@@ -80,6 +81,7 @@ func TestCreateCommand(t *testing.T) {
|
||||
resourceModifierConfigMap := "modifier-cm"
|
||||
ResourcePoliciesConfigMap := "policies-cm"
|
||||
writeSparseFiles := "true"
|
||||
deleteExtraFiles := "true"
|
||||
parallel := 2
|
||||
flags := new(pflag.FlagSet)
|
||||
o := NewCreateOptions()
|
||||
@@ -92,6 +94,7 @@ func TestCreateCommand(t *testing.T) {
|
||||
flags.Parse([]string{"--labels", labels})
|
||||
flags.Parse([]string{"--annotations", annotations})
|
||||
flags.Parse([]string{"--existing-resource-policy", existingResourcePolicy})
|
||||
flags.Parse([]string{"--existing-volume-data-policy", existingVolumeDataPolicy})
|
||||
flags.Parse([]string{"--include-namespaces", includeNamespaces})
|
||||
flags.Parse([]string{"--exclude-namespaces", excludeNamespaces})
|
||||
flags.Parse([]string{"--include-resources", includeResources})
|
||||
@@ -106,6 +109,7 @@ func TestCreateCommand(t *testing.T) {
|
||||
flags.Parse([]string{"--resource-modifier-configmap", resourceModifierConfigMap})
|
||||
flags.Parse([]string{"--resource-policies-configmap", ResourcePoliciesConfigMap})
|
||||
flags.Parse([]string{"--write-sparse-files", writeSparseFiles})
|
||||
flags.Parse([]string{"--delete-extra-files", deleteExtraFiles})
|
||||
flags.Parse([]string{"--parallel-files-download", "2"})
|
||||
client := velerotest.NewFakeControllerRuntimeClient(t).(kbclient.WithWatch)
|
||||
|
||||
@@ -133,6 +137,7 @@ func TestCreateCommand(t *testing.T) {
|
||||
require.Equal(t, includeNamespaces, o.IncludeNamespaces.String())
|
||||
require.Equal(t, excludeNamespaces, o.ExcludeNamespaces.String())
|
||||
require.Equal(t, existingResourcePolicy, o.ExistingResourcePolicy)
|
||||
require.Equal(t, existingVolumeDataPolicy, o.ExistingVolumeDataPolicy)
|
||||
require.Equal(t, includeResources, o.IncludeResources.String())
|
||||
require.Equal(t, excludeResources, o.ExcludeResources.String())
|
||||
|
||||
@@ -147,6 +152,7 @@ func TestCreateCommand(t *testing.T) {
|
||||
require.Equal(t, ResourcePoliciesConfigMap, o.ResourcePoliciesConfigMap)
|
||||
require.Equal(t, writeSparseFiles, o.WriteSparseFiles.String())
|
||||
require.Equal(t, parallel, o.ParallelFilesDownload)
|
||||
require.Equal(t, deleteExtraFiles, o.DeleteExtraFiles.String())
|
||||
})
|
||||
|
||||
t.Run("create a restore from schedule", func(t *testing.T) {
|
||||
|
||||
@@ -361,10 +361,15 @@ func (r *restoreReconciler) validateAndComplete(ctx context.Context, restore *ap
|
||||
}
|
||||
|
||||
// validate ExistingResourcePolicy
|
||||
if restore.Spec.ExistingResourcePolicy != "" && !pkgrestoreUtil.IsResourcePolicyValid(string(restore.Spec.ExistingResourcePolicy)) {
|
||||
if !pkgrestoreUtil.IsResourcePolicyValid(string(restore.Spec.ExistingResourcePolicy)) {
|
||||
restore.Status.ValidationErrors = append(restore.Status.ValidationErrors, fmt.Sprintf("Invalid ExistingResourcePolicy: %s", restore.Spec.ExistingResourcePolicy))
|
||||
}
|
||||
|
||||
// validate ExistingVolumeDataPolicy
|
||||
if !pkgrestoreUtil.IsVolumeDataPolicyValid(string(restore.Spec.ExistingVolumeDataPolicy)) {
|
||||
restore.Status.ValidationErrors = append(restore.Status.ValidationErrors, fmt.Sprintf("Invalid ExistingVolumeDataPolicy: %s", restore.Spec.ExistingVolumeDataPolicy))
|
||||
}
|
||||
|
||||
// if ScheduleName is specified, fill in BackupName with the most recent successful backup from
|
||||
// the schedule
|
||||
if restore.Spec.ScheduleName != "" {
|
||||
|
||||
@@ -348,6 +348,39 @@ func TestRestoreReconcile(t *testing.T) {
|
||||
expectedCompletedTime: ×tamp,
|
||||
expectedRestorerCall: nil, // this restore should fail validation and not be passed to the restorer
|
||||
},
|
||||
{
|
||||
name: "valid restore with update existingvolumedatapolicy(full) gets executed",
|
||||
location: defaultStorageLocation,
|
||||
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "", velerov1api.RestorePhaseNew).ExistingVolumeDataPolicy("full").Result(),
|
||||
backup: defaultBackup().StorageLocation("default").Phase(velerov1api.BackupPhaseCompleted).Result(),
|
||||
expectedErr: false,
|
||||
expectedPhase: string(velerov1api.RestorePhaseInProgress),
|
||||
expectedStartTime: ×tamp,
|
||||
expectedCompletedTime: ×tamp,
|
||||
expectedRestorerCall: NewRestore("foo", "bar", "backup-1", "ns-1", "", velerov1api.RestorePhaseInProgress).ExistingVolumeDataPolicy("full").Result(),
|
||||
},
|
||||
{
|
||||
name: "valid restore with update existingvolumedatapolicy(incremental) gets executed",
|
||||
location: defaultStorageLocation,
|
||||
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "", velerov1api.RestorePhaseNew).ExistingVolumeDataPolicy("incremental").Result(),
|
||||
backup: defaultBackup().StorageLocation("default").Phase(velerov1api.BackupPhaseCompleted).Result(),
|
||||
expectedErr: false,
|
||||
expectedPhase: string(velerov1api.RestorePhaseInProgress),
|
||||
expectedStartTime: ×tamp,
|
||||
expectedCompletedTime: ×tamp,
|
||||
expectedRestorerCall: NewRestore("foo", "bar", "backup-1", "ns-1", "", velerov1api.RestorePhaseInProgress).ExistingVolumeDataPolicy("incremental").Result(),
|
||||
},
|
||||
{
|
||||
name: "invalid restore with invalid existingvolumedatapolicy errors",
|
||||
location: defaultStorageLocation,
|
||||
restore: NewRestore("foo", "invalidexistingvolumedatapolicy", "backup-1", "ns-1", "", velerov1api.RestorePhaseNew).ExistingVolumeDataPolicy("invalid").Result(),
|
||||
backup: defaultBackup().StorageLocation("default").Result(),
|
||||
expectedErr: false,
|
||||
expectedPhase: string(velerov1api.RestorePhaseFailedValidation),
|
||||
expectedStartTime: ×tamp,
|
||||
expectedCompletedTime: ×tamp,
|
||||
expectedRestorerCall: nil, // this restore should fail validation and not be passed to the restorer
|
||||
},
|
||||
{
|
||||
name: "valid restore gets executed",
|
||||
location: defaultStorageLocation,
|
||||
|
||||
@@ -1890,7 +1890,7 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso
|
||||
if err != nil {
|
||||
warnings.Add(namespace, err)
|
||||
// check if there is existingResourcePolicy and if it is set to update policy
|
||||
if len(ctx.restore.Spec.ExistingResourcePolicy) > 0 && ctx.restore.Spec.ExistingResourcePolicy == velerov1api.PolicyTypeUpdate {
|
||||
if len(ctx.restore.Spec.ExistingResourcePolicy) > 0 && ctx.restore.Spec.ExistingResourcePolicy == velerov1api.ResourcePolicyTypeUpdate {
|
||||
// remove restore labels so that we apply the latest backup/restore names on the object via patch
|
||||
removeRestoreLabels(fromCluster)
|
||||
//try patching just the backup/restore labels
|
||||
@@ -1910,12 +1910,12 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso
|
||||
restoreLogger.Infof("restore API has resource policy defined %s, executing restore workflow accordingly for changed resource %s %s", resourcePolicy, fromCluster.GroupVersionKind().Kind, kube.NamespaceAndName(fromCluster))
|
||||
|
||||
// existingResourcePolicy is set as none, add warning
|
||||
if resourcePolicy == velerov1api.PolicyTypeNone {
|
||||
if resourcePolicy == velerov1api.ResourcePolicyTypeNone {
|
||||
e := errors.Errorf("could not restore, %s %q already exists. Warning: the in-cluster version is different than the backed-up version",
|
||||
obj.GetKind(), obj.GetName())
|
||||
warnings.Add(namespace, e)
|
||||
// existingResourcePolicy is set as update, attempt patch on the resource and add warning if it fails
|
||||
} else if resourcePolicy == velerov1api.PolicyTypeUpdate {
|
||||
} else if resourcePolicy == velerov1api.ResourcePolicyTypeUpdate {
|
||||
// processing update as existingResourcePolicy
|
||||
warningsFromUpdateRP, errsFromUpdateRP := ctx.processUpdateResourcePolicy(fromCluster, fromClusterWithLabels, obj, namespace, resourceClient)
|
||||
if warningsFromUpdateRP.IsEmpty() && errsFromUpdateRP.IsEmpty() {
|
||||
@@ -1936,7 +1936,7 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso
|
||||
}
|
||||
|
||||
//update backup/restore labels on the unchanged resources if existingResourcePolicy is set as update
|
||||
if ctx.restore.Spec.ExistingResourcePolicy == velerov1api.PolicyTypeUpdate {
|
||||
if ctx.restore.Spec.ExistingResourcePolicy == velerov1api.ResourcePolicyTypeUpdate {
|
||||
resourcePolicy := ctx.restore.Spec.ExistingResourcePolicy
|
||||
restoreLogger.Infof("restore API has resource policy defined %s, executing restore workflow accordingly for unchanged resource %s %s ", resourcePolicy, obj.GroupVersionKind().Kind, kube.NamespaceAndName(fromCluster))
|
||||
// remove restore labels so that we apply the latest backup/restore names on the object via patch
|
||||
|
||||
@@ -5,8 +5,14 @@ import (
|
||||
)
|
||||
|
||||
func IsResourcePolicyValid(resourcePolicy string) bool {
|
||||
if resourcePolicy == string(api.PolicyTypeNone) || resourcePolicy == string(api.PolicyTypeUpdate) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return resourcePolicy == "" ||
|
||||
resourcePolicy == string(api.ResourcePolicyTypeNone) ||
|
||||
resourcePolicy == string(api.ResourcePolicyTypeUpdate)
|
||||
}
|
||||
|
||||
func IsVolumeDataPolicyValid(volumeDataPolicy string) bool {
|
||||
return volumeDataPolicy == "" ||
|
||||
volumeDataPolicy == string(api.VolumeDataPolicyTypeNone) ||
|
||||
volumeDataPolicy == string(api.VolumeDataPolicyTypeFull) ||
|
||||
volumeDataPolicy == string(api.VolumeDataPolicyTypeIncremental)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,16 @@ import (
|
||||
)
|
||||
|
||||
func TestIsResourcePolicyValid(t *testing.T) {
|
||||
require.True(t, IsResourcePolicyValid(string(velerov1api.PolicyTypeNone)))
|
||||
require.True(t, IsResourcePolicyValid(string(velerov1api.PolicyTypeUpdate)))
|
||||
require.False(t, IsResourcePolicyValid(""))
|
||||
require.True(t, IsResourcePolicyValid(string(velerov1api.ResourcePolicyTypeNone)))
|
||||
require.True(t, IsResourcePolicyValid(string(velerov1api.ResourcePolicyTypeUpdate)))
|
||||
require.True(t, IsResourcePolicyValid(""))
|
||||
require.False(t, IsResourcePolicyValid("invalid"))
|
||||
}
|
||||
|
||||
func TestIsVolumeDataPolicyValid(t *testing.T) {
|
||||
require.True(t, IsVolumeDataPolicyValid(string(velerov1api.VolumeDataPolicyTypeNone)))
|
||||
require.True(t, IsVolumeDataPolicyValid(string(velerov1api.VolumeDataPolicyTypeFull)))
|
||||
require.True(t, IsVolumeDataPolicyValid(string(velerov1api.VolumeDataPolicyTypeIncremental)))
|
||||
require.True(t, IsVolumeDataPolicyValid(""))
|
||||
require.False(t, IsVolumeDataPolicyValid("invalid"))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user