diff --git a/apis/stash/types.go b/apis/stash/types.go index e74f0a180..ac5db1afe 100644 --- a/apis/stash/types.go +++ b/apis/stash/types.go @@ -155,11 +155,12 @@ type Recovery struct { } type RecoverySpec struct { - Restic string `json:"restic,omitempty"` - Workload LocalTypedReference `json:"workload,omitempty"` - PodOrdinal string `json:"podOrdinal,omitempty"` - NodeName string `json:"nodeName,omitempty"` - Volumes []core.Volume `json:"volumes,omitempty"` + Backend Backend `json:"backend,omitempty"` + Paths []string `json:"paths,omitempty"` + Workload LocalTypedReference `json:"workload,omitempty"` + PodOrdinal string `json:"podOrdinal,omitempty"` + NodeName string `json:"nodeName,omitempty"` + RecoveredVolumes []RecoveredVolume `json:"recoveredVolumes,omitempty"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -170,6 +171,12 @@ type RecoveryList struct { Items []Recovery `json:"items,omitempty"` } +type RecoveredVolume struct { + VolumeSource core.VolumeSource `json:",inline"` + MountPath string `json:"mountPath,omitempty"` + SubPath string `json:"subPath,omitempty"` +} + // LocalTypedReference contains enough information to let you inspect or modify the referred object. type LocalTypedReference struct { // Kind of the referent. diff --git a/apis/stash/v1alpha1/types.go b/apis/stash/v1alpha1/types.go index 6b52bb397..ae4738418 100644 --- a/apis/stash/v1alpha1/types.go +++ b/apis/stash/v1alpha1/types.go @@ -155,11 +155,12 @@ type Recovery struct { } type RecoverySpec struct { - Restic string `json:"restic,omitempty"` - Workload LocalTypedReference `json:"workload,omitempty"` - PodOrdinal string `json:"podOrdinal,omitempty"` - NodeName string `json:"nodeName,omitempty"` - Volumes []core.Volume `json:"volumes,omitempty"` + Backend Backend `json:"backend,omitempty"` + Paths []string `json:"paths,omitempty"` + Workload LocalTypedReference `json:"workload,omitempty"` + PodOrdinal string `json:"podOrdinal,omitempty"` + NodeName string `json:"nodeName,omitempty"` + RecoveredVolumes []RecoveredVolume `json:"recoveredVolumes,omitempty"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -170,6 +171,12 @@ type RecoveryList struct { Items []Recovery `json:"items,omitempty"` } +type RecoveredVolume struct { + VolumeSource core.VolumeSource `json:",inline"` + MountPath string `json:"mountPath,omitempty"` + SubPath string `json:"subPath,omitempty"` +} + // LocalTypedReference contains enough information to let you inspect or modify the referred object. type LocalTypedReference struct { // Kind of the referent. diff --git a/apis/stash/v1alpha1/validator.go b/apis/stash/v1alpha1/validator.go index a83a49f98..f40467484 100644 --- a/apis/stash/v1alpha1/validator.go +++ b/apis/stash/v1alpha1/validator.go @@ -35,11 +35,14 @@ func (r Restic) IsValid() error { } func (r Recovery) IsValid() error { - if r.Spec.Restic == "" { - return fmt.Errorf("missing restic name") + if r.Spec.Backend.StorageSecretName == "" { + return fmt.Errorf("missing repository secret name") + } + if len(r.Spec.Paths) == 0 { + return fmt.Errorf("missing filegroup paths") } - if len(r.Spec.Volumes) == 0 { - return fmt.Errorf("missing target vollume") + if len(r.Spec.RecoveredVolumes) == 0 { + return fmt.Errorf("missing recovery vollume") } if err := r.Spec.Workload.Canonicalize(); err != nil { diff --git a/apis/stash/v1alpha1/zz_generated.conversion.go b/apis/stash/v1alpha1/zz_generated.conversion.go index 78ed64e49..7ae70830d 100644 --- a/apis/stash/v1alpha1/zz_generated.conversion.go +++ b/apis/stash/v1alpha1/zz_generated.conversion.go @@ -52,6 +52,8 @@ func RegisterConversions(scheme *runtime.Scheme) error { Convert_stash_LocalSpec_To_v1alpha1_LocalSpec, Convert_v1alpha1_LocalTypedReference_To_stash_LocalTypedReference, Convert_stash_LocalTypedReference_To_v1alpha1_LocalTypedReference, + Convert_v1alpha1_RecoveredVolume_To_stash_RecoveredVolume, + Convert_stash_RecoveredVolume_To_v1alpha1_RecoveredVolume, Convert_v1alpha1_Recovery_To_stash_Recovery, Convert_stash_Recovery_To_v1alpha1_Recovery, Convert_v1alpha1_RecoveryList_To_stash_RecoveryList, @@ -247,6 +249,30 @@ func Convert_stash_LocalTypedReference_To_v1alpha1_LocalTypedReference(in *stash return autoConvert_stash_LocalTypedReference_To_v1alpha1_LocalTypedReference(in, out, s) } +func autoConvert_v1alpha1_RecoveredVolume_To_stash_RecoveredVolume(in *RecoveredVolume, out *stash.RecoveredVolume, s conversion.Scope) error { + out.VolumeSource = in.VolumeSource + out.MountPath = in.MountPath + out.SubPath = in.SubPath + return nil +} + +// Convert_v1alpha1_RecoveredVolume_To_stash_RecoveredVolume is an autogenerated conversion function. +func Convert_v1alpha1_RecoveredVolume_To_stash_RecoveredVolume(in *RecoveredVolume, out *stash.RecoveredVolume, s conversion.Scope) error { + return autoConvert_v1alpha1_RecoveredVolume_To_stash_RecoveredVolume(in, out, s) +} + +func autoConvert_stash_RecoveredVolume_To_v1alpha1_RecoveredVolume(in *stash.RecoveredVolume, out *RecoveredVolume, s conversion.Scope) error { + out.VolumeSource = in.VolumeSource + out.MountPath = in.MountPath + out.SubPath = in.SubPath + return nil +} + +// Convert_stash_RecoveredVolume_To_v1alpha1_RecoveredVolume is an autogenerated conversion function. +func Convert_stash_RecoveredVolume_To_v1alpha1_RecoveredVolume(in *stash.RecoveredVolume, out *RecoveredVolume, s conversion.Scope) error { + return autoConvert_stash_RecoveredVolume_To_v1alpha1_RecoveredVolume(in, out, s) +} + func autoConvert_v1alpha1_Recovery_To_stash_Recovery(in *Recovery, out *stash.Recovery, s conversion.Scope) error { out.ObjectMeta = in.ObjectMeta if err := Convert_v1alpha1_RecoverySpec_To_stash_RecoverySpec(&in.Spec, &out.Spec, s); err != nil { @@ -302,13 +328,16 @@ func Convert_stash_RecoveryList_To_v1alpha1_RecoveryList(in *stash.RecoveryList, } func autoConvert_v1alpha1_RecoverySpec_To_stash_RecoverySpec(in *RecoverySpec, out *stash.RecoverySpec, s conversion.Scope) error { - out.Restic = in.Restic + if err := Convert_v1alpha1_Backend_To_stash_Backend(&in.Backend, &out.Backend, s); err != nil { + return err + } + out.Paths = *(*[]string)(unsafe.Pointer(&in.Paths)) if err := Convert_v1alpha1_LocalTypedReference_To_stash_LocalTypedReference(&in.Workload, &out.Workload, s); err != nil { return err } out.PodOrdinal = in.PodOrdinal out.NodeName = in.NodeName - out.Volumes = *(*[]v1.Volume)(unsafe.Pointer(&in.Volumes)) + out.RecoveredVolumes = *(*[]stash.RecoveredVolume)(unsafe.Pointer(&in.RecoveredVolumes)) return nil } @@ -318,13 +347,16 @@ func Convert_v1alpha1_RecoverySpec_To_stash_RecoverySpec(in *RecoverySpec, out * } func autoConvert_stash_RecoverySpec_To_v1alpha1_RecoverySpec(in *stash.RecoverySpec, out *RecoverySpec, s conversion.Scope) error { - out.Restic = in.Restic + if err := Convert_stash_Backend_To_v1alpha1_Backend(&in.Backend, &out.Backend, s); err != nil { + return err + } + out.Paths = *(*[]string)(unsafe.Pointer(&in.Paths)) if err := Convert_stash_LocalTypedReference_To_v1alpha1_LocalTypedReference(&in.Workload, &out.Workload, s); err != nil { return err } out.PodOrdinal = in.PodOrdinal out.NodeName = in.NodeName - out.Volumes = *(*[]v1.Volume)(unsafe.Pointer(&in.Volumes)) + out.RecoveredVolumes = *(*[]RecoveredVolume)(unsafe.Pointer(&in.RecoveredVolumes)) return nil } diff --git a/apis/stash/v1alpha1/zz_generated.deepcopy.go b/apis/stash/v1alpha1/zz_generated.deepcopy.go index 11ca36be6..bd90340aa 100644 --- a/apis/stash/v1alpha1/zz_generated.deepcopy.go +++ b/apis/stash/v1alpha1/zz_generated.deepcopy.go @@ -67,6 +67,10 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error { in.(*LocalTypedReference).DeepCopyInto(out.(*LocalTypedReference)) return nil }, InType: reflect.TypeOf(&LocalTypedReference{})}, + conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { + in.(*RecoveredVolume).DeepCopyInto(out.(*RecoveredVolume)) + return nil + }, InType: reflect.TypeOf(&RecoveredVolume{})}, conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { in.(*Recovery).DeepCopyInto(out.(*Recovery)) return nil @@ -285,6 +289,23 @@ func (in *LocalTypedReference) DeepCopy() *LocalTypedReference { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RecoveredVolume) DeepCopyInto(out *RecoveredVolume) { + *out = *in + in.VolumeSource.DeepCopyInto(&out.VolumeSource) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RecoveredVolume. +func (in *RecoveredVolume) DeepCopy() *RecoveredVolume { + if in == nil { + return nil + } + out := new(RecoveredVolume) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Recovery) DeepCopyInto(out *Recovery) { *out = *in @@ -351,10 +372,16 @@ func (in *RecoveryList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RecoverySpec) DeepCopyInto(out *RecoverySpec) { *out = *in + in.Backend.DeepCopyInto(&out.Backend) + if in.Paths != nil { + in, out := &in.Paths, &out.Paths + *out = make([]string, len(*in)) + copy(*out, *in) + } out.Workload = in.Workload - if in.Volumes != nil { - in, out := &in.Volumes, &out.Volumes - *out = make([]v1.Volume, len(*in)) + if in.RecoveredVolumes != nil { + in, out := &in.RecoveredVolumes, &out.RecoveredVolumes + *out = make([]RecoveredVolume, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } diff --git a/apis/stash/zz_generated.deepcopy.go b/apis/stash/zz_generated.deepcopy.go index d4e2e6dc4..6a5ab8a44 100644 --- a/apis/stash/zz_generated.deepcopy.go +++ b/apis/stash/zz_generated.deepcopy.go @@ -67,6 +67,10 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error { in.(*LocalTypedReference).DeepCopyInto(out.(*LocalTypedReference)) return nil }, InType: reflect.TypeOf(&LocalTypedReference{})}, + conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { + in.(*RecoveredVolume).DeepCopyInto(out.(*RecoveredVolume)) + return nil + }, InType: reflect.TypeOf(&RecoveredVolume{})}, conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { in.(*Recovery).DeepCopyInto(out.(*Recovery)) return nil @@ -285,6 +289,23 @@ func (in *LocalTypedReference) DeepCopy() *LocalTypedReference { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RecoveredVolume) DeepCopyInto(out *RecoveredVolume) { + *out = *in + in.VolumeSource.DeepCopyInto(&out.VolumeSource) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RecoveredVolume. +func (in *RecoveredVolume) DeepCopy() *RecoveredVolume { + if in == nil { + return nil + } + out := new(RecoveredVolume) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Recovery) DeepCopyInto(out *Recovery) { *out = *in @@ -351,10 +372,16 @@ func (in *RecoveryList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RecoverySpec) DeepCopyInto(out *RecoverySpec) { *out = *in + in.Backend.DeepCopyInto(&out.Backend) + if in.Paths != nil { + in, out := &in.Paths, &out.Paths + *out = make([]string, len(*in)) + copy(*out, *in) + } out.Workload = in.Workload - if in.Volumes != nil { - in, out := &in.Volumes, &out.Volumes - *out = make([]v1.Volume, len(*in)) + if in.RecoveredVolumes != nil { + in, out := &in.RecoveredVolumes, &out.RecoveredVolumes + *out = make([]RecoveredVolume, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } diff --git a/pkg/backup/backup.go b/pkg/backup/backup.go index e8f4acba5..6771ff851 100644 --- a/pkg/backup/backup.go +++ b/pkg/backup/backup.go @@ -158,7 +158,7 @@ func (c *Controller) setup() (*api.Restic, error) { log.Infof("Found repository secret %s\n", secret.Name) // setup restic-cli - if err = c.resticCLI.SetupEnv(resource, secret, c.opt.SmartPrefix); err != nil { + if err = c.resticCLI.SetupEnv(resource.Spec.Backend, secret, c.opt.SmartPrefix); err != nil { return nil, err } if err = c.resticCLI.InitRepositoryIfAbsent(); err != nil { diff --git a/pkg/backup/scheduler.go b/pkg/backup/scheduler.go index 13aa096fa..f9ca303cf 100644 --- a/pkg/backup/scheduler.go +++ b/pkg/backup/scheduler.go @@ -153,7 +153,7 @@ func (c *Controller) runOnceForScheduler() error { } // setup restic again, previously done in setup() - if err = c.resticCLI.SetupEnv(resource, secret, c.opt.SmartPrefix); err != nil { + if err = c.resticCLI.SetupEnv(resource.Spec.Backend, secret, c.opt.SmartPrefix); err != nil { return err } if err = c.resticCLI.InitRepositoryIfAbsent(); err != nil { diff --git a/pkg/check/check.go b/pkg/check/check.go index 7640203fc..c1fbd1a67 100644 --- a/pkg/check/check.go +++ b/pkg/check/check.go @@ -74,7 +74,7 @@ func (c *Controller) Run() (err error) { } cli := cli.New("/tmp", false, c.opt.HostName) - if err = cli.SetupEnv(restic, secret, c.opt.SmartPrefix); err != nil { + if err = cli.SetupEnv(restic.Spec.Backend, secret, c.opt.SmartPrefix); err != nil { return } diff --git a/pkg/cli/env.go b/pkg/cli/env.go index ffe36f586..f02fd196e 100644 --- a/pkg/cli/env.go +++ b/pkg/cli/env.go @@ -54,9 +54,9 @@ const ( OS_AUTH_TOKEN = "OS_AUTH_TOKEN" ) -func (w *ResticWrapper) SetupEnv(resource *api.Restic, secret *core.Secret, autoPrefix string) error { +func (w *ResticWrapper) SetupEnv(backend api.Backend, secret *core.Secret, autoPrefix string) error { if v, ok := secret.Data[RESTIC_PASSWORD]; !ok { - return errors.New("Missing repository password") + return errors.New("missing repository password") } else { w.sh.SetEnv(RESTIC_PASSWORD, string(v)) } @@ -67,7 +67,6 @@ func (w *ResticWrapper) SetupEnv(resource *api.Restic, secret *core.Secret, auto } w.sh.SetEnv(TMPDIR, tmpDir) - backend := resource.Spec.Backend if backend.Local != nil { r := filepath.Join(backend.Local.Path, autoPrefix) if err := os.MkdirAll(r, 0755); err != nil { diff --git a/pkg/cmds/snapshot_handler.go b/pkg/cmds/snapshot_handler.go index 1e0980e78..c99b5798b 100644 --- a/pkg/cmds/snapshot_handler.go +++ b/pkg/cmds/snapshot_handler.go @@ -70,7 +70,7 @@ func (e PrometheusExporter) ServeHTTP(w http.ResponseWriter, r *http.Request) { http.Error(w, err.Error(), http.StatusInternalServerError) return } - err = resticCLI.SetupEnv(resource, secret, r.URL.Query().Get(QueryParamAutoPrefix)) + err = resticCLI.SetupEnv(resource.Spec.Backend, secret, r.URL.Query().Get(QueryParamAutoPrefix)) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return diff --git a/pkg/controller/recoveries.go b/pkg/controller/recoveries.go index ce4a3bb80..3eabdd2d9 100644 --- a/pkg/controller/recoveries.go +++ b/pkg/controller/recoveries.go @@ -166,29 +166,14 @@ func (c *StashController) runRecoveryJob(rec *api.Recovery) error { return nil } - restic, err := c.stashClient.Restics(rec.Namespace).Get(rec.Spec.Restic, metav1.GetOptions{}) - if err != nil { - log.Errorln(err) - stash_util.SetRecoveryStatusPhase(c.stashClient, rec, api.RecoveryFailed) - c.recorder.Event(rec.ObjectReference(), core.EventTypeWarning, eventer.EventReasonFailedToRecover, err.Error()) - return err - } - - if err = restic.IsValid(); err != nil { - log.Errorln(err) - stash_util.SetRecoveryStatusPhase(c.stashClient, rec, api.RecoveryFailed) - c.recorder.Event(rec.ObjectReference(), core.EventTypeWarning, eventer.EventReasonFailedToRecover, err.Error()) - return err - } - - job := util.CreateRecoveryJob(rec, restic, c.options.SidecarImageTag) + job := util.CreateRecoveryJob(rec, c.options.SidecarImageTag) if c.options.EnableRBAC { - if err = c.ensureRecoveryRBAC(job.Name, job.Namespace); err != nil { + if err := c.ensureRecoveryRBAC(job.Name, job.Namespace); err != nil { return fmt.Errorf("error ensuring rbac for recovery job %s, reason: %s\n", job.Name, err) } job.Spec.Template.Spec.ServiceAccountName = job.Name } - if job, err = c.k8sClient.BatchV1().Jobs(rec.Namespace).Create(job); err != nil { + if _, err := c.k8sClient.BatchV1().Jobs(rec.Namespace).Create(job); err != nil { if kerr.IsAlreadyExists(err) { return nil } diff --git a/pkg/recovery/recovery.go b/pkg/recovery/recovery.go index 59297f0d3..840eceb8f 100644 --- a/pkg/recovery/recovery.go +++ b/pkg/recovery/recovery.go @@ -86,18 +86,7 @@ func (c *Controller) Run() { } func (c *Controller) RecoverOrErr(recovery *api.Recovery) error { - restic, err := c.stashClient.Restics(c.namespace).Get(recovery.Spec.Restic, metav1.GetOptions{}) - if err != nil { - return err - } - if err = restic.IsValid(); err != nil { - return err - } - if restic.Status.BackupCount < 1 { - return fmt.Errorf("no backup found") - } - - secret, err := c.k8sClient.CoreV1().Secrets(c.namespace).Get(restic.Spec.Backend.StorageSecretName, metav1.GetOptions{}) + secret, err := c.k8sClient.CoreV1().Secrets(c.namespace).Get(recovery.Spec.Backend.StorageSecretName, metav1.GetOptions{}) if err != nil { return err } @@ -110,13 +99,13 @@ func (c *Controller) RecoverOrErr(recovery *api.Recovery) error { } cli := cli.New("/tmp", false, hostname) - if err = cli.SetupEnv(restic, secret, smartPrefix); err != nil { + if err = cli.SetupEnv(recovery.Spec.Backend, secret, smartPrefix); err != nil { return err } var errRec error - for _, fg := range restic.Spec.FileGroups { - d, err := c.measure(cli.Restore, fg.Path, hostname) + for _, path := range recovery.Spec.Paths { + d, err := c.measure(cli.Restore, path, hostname) if err != nil { errRec = err eventer.CreateEventWithLog( @@ -125,11 +114,11 @@ func (c *Controller) RecoverOrErr(recovery *api.Recovery) error { recovery.ObjectReference(), core.EventTypeWarning, eventer.EventReasonFailedToRecover, - fmt.Sprintf("failed to recover FileGroup %s, reason: %v", fg.Path, err), + fmt.Sprintf("failed to recover FileGroup %s, reason: %v", path, err), ) - stash_util.SetRecoveryStats(c.stashClient, recovery, fg.Path, d, api.RecoveryFailed) + stash_util.SetRecoveryStats(c.stashClient, recovery, path, d, api.RecoveryFailed) } else { - stash_util.SetRecoveryStats(c.stashClient, recovery, fg.Path, d, api.RecoverySucceeded) + stash_util.SetRecoveryStats(c.stashClient, recovery, path, d, api.RecoverySucceeded) } } diff --git a/pkg/util/kubernetes.go b/pkg/util/kubernetes.go index 35dd21395..a5553e29d 100644 --- a/pkg/util/kubernetes.go +++ b/pkg/util/kubernetes.go @@ -355,7 +355,21 @@ func RecoveryEqual(old, new *api.Recovery) bool { return reflect.DeepEqual(oldSpec, newSpec) } -func CreateRecoveryJob(recovery *api.Recovery, restic *api.Restic, tag string) *batch.Job { +func CreateRecoveryJob(recovery *api.Recovery, tag string) *batch.Job { + volumes := make([]core.Volume, 0) + volumeMounts := make([]core.VolumeMount, 0) + for i, recVol := range recovery.Spec.RecoveredVolumes { + volumes = append(volumes, core.Volume{ + Name: fmt.Sprintf("vol-%d", i), + VolumeSource: recVol.VolumeSource, + }) + volumeMounts = append(volumeMounts, core.VolumeMount{ + Name: fmt.Sprintf("vol-%d", i), + MountPath: recVol.MountPath, + SubPath: recVol.SubPath, + }) + } + job := &batch.Job{ ObjectMeta: metav1.ObjectMeta{ Name: RecoveryJobPrefix + recovery.Name, @@ -370,7 +384,6 @@ func CreateRecoveryJob(recovery *api.Recovery, restic *api.Restic, tag string) * }, Labels: map[string]string{ "app": AppLabelStash, - AnnotationRestic: restic.Name, AnnotationRecovery: recovery.Name, AnnotationOperation: OperationRecovery, }, @@ -387,14 +400,14 @@ func CreateRecoveryJob(recovery *api.Recovery, restic *api.Restic, tag string) * "--recovery-name=" + recovery.Name, "--v=10", }, - VolumeMounts: append(restic.Spec.VolumeMounts, core.VolumeMount{ + VolumeMounts: append(volumeMounts, core.VolumeMount{ Name: ScratchDirVolumeName, MountPath: "/tmp", - }), // use volume mounts specified in restic + }), }, }, RestartPolicy: core.RestartPolicyOnFailure, - Volumes: append(recovery.Spec.Volumes, core.Volume{ + Volumes: append(volumes, core.Volume{ Name: ScratchDirVolumeName, VolumeSource: core.VolumeSource{ EmptyDir: &core.EmptyDirVolumeSource{}, @@ -407,18 +420,18 @@ func CreateRecoveryJob(recovery *api.Recovery, restic *api.Restic, tag string) * } // local backend - if restic.Spec.Backend.Local != nil { + if recovery.Spec.Backend.Local != nil { job.Spec.Template.Spec.Containers[0].VolumeMounts = append(job.Spec.Template.Spec.Containers[0].VolumeMounts, core.VolumeMount{ Name: LocalVolumeName, - MountPath: restic.Spec.Backend.Local.Path, + MountPath: recovery.Spec.Backend.Local.Path, }) // user don't need to specify "stash-local" volume, we collect it from restic-spec job.Spec.Template.Spec.Volumes = append(job.Spec.Template.Spec.Volumes, core.Volume{ Name: LocalVolumeName, - VolumeSource: restic.Spec.Backend.Local.VolumeSource, + VolumeSource: recovery.Spec.Backend.Local.VolumeSource, }) } diff --git a/test/e2e/daemonset_test.go b/test/e2e/daemonset_test.go index 29396000c..ecd4acdfd 100644 --- a/test/e2e/daemonset_test.go +++ b/test/e2e/daemonset_test.go @@ -31,6 +31,7 @@ var _ = Describe("DaemonSet", func() { Skip("Missing repository credential") } restic.Spec.Backend.StorageSecretName = cred.Name + recovery.Spec.Backend.StorageSecretName = cred.Name daemon = f.DaemonSet() }) @@ -134,7 +135,7 @@ var _ = Describe("DaemonSet", func() { }, BeNumerically(">=", 1))) By("Removing labels of DaemonSet " + daemon.Name) - _, err = ext_util.PatchDaemonSet(f.KubeClient, &daemon, func(in *extensions.DaemonSet) *extensions.DaemonSet { + _, _, err = ext_util.PatchDaemonSet(f.KubeClient, &daemon, func(in *extensions.DaemonSet) *extensions.DaemonSet { in.Labels = map[string]string{ "app": "unmatched", } @@ -378,7 +379,7 @@ var _ = Describe("DaemonSet", func() { BeforeEach(func() { cred = f.SecretForLocalBackend() restic = f.ResticForHostPathLocalBackend() - recovery = f.RecoveryForRestic(restic.Name) + recovery = f.RecoveryForRestic(restic) }) It(`should restore local daemonset backup`, shouldRestoreDemonset) }) @@ -387,7 +388,7 @@ var _ = Describe("DaemonSet", func() { BeforeEach(func() { cred = f.SecretForS3Backend() restic = f.ResticForS3Backend() - recovery = f.RecoveryForRestic(restic.Name) + recovery = f.RecoveryForRestic(restic) }) It(`should restore s3 daemonset backup`, shouldRestoreDemonset) }) diff --git a/test/e2e/deployment_test.go b/test/e2e/deployment_test.go index 2fc4c1097..8e36624c8 100644 --- a/test/e2e/deployment_test.go +++ b/test/e2e/deployment_test.go @@ -35,6 +35,7 @@ var _ = Describe("Deployment", func() { Skip("Missing repository credential") } restic.Spec.Backend.StorageSecretName = cred.Name + recovery.Spec.Backend.StorageSecretName = cred.Name deployment = f.Deployment() }) @@ -138,7 +139,7 @@ var _ = Describe("Deployment", func() { }, BeNumerically(">=", 1))) By("Removing labels of Deployment " + deployment.Name) - _, err = apps_util.PatchDeployment(f.KubeClient, &deployment, func(in *apps.Deployment) *apps.Deployment { + _, _, err = apps_util.PatchDeployment(f.KubeClient, &deployment, func(in *apps.Deployment) *apps.Deployment { in.Labels = map[string]string{ "app": "unmatched", } @@ -412,7 +413,7 @@ var _ = Describe("Deployment", func() { BeforeEach(func() { cred = f.SecretForLocalBackend() restic = f.ResticForHostPathLocalBackend() - recovery = f.RecoveryForRestic(restic.Name) + recovery = f.RecoveryForRestic(restic) }) It(`should restore local deployment backup`, shouldRestoreDeployment) }) @@ -421,7 +422,7 @@ var _ = Describe("Deployment", func() { BeforeEach(func() { cred = f.SecretForS3Backend() restic = f.ResticForS3Backend() - recovery = f.RecoveryForRestic(restic.Name) + recovery = f.RecoveryForRestic(restic) }) It(`should restore s3 deployment backup`, shouldRestoreDeployment) }) @@ -440,7 +441,7 @@ var _ = Describe("Deployment", func() { BeforeEach(func() { cred = f.SecretForLocalBackend() restic = f.ResticForHostPathLocalBackend() - recovery = f.RecoveryForRestic(restic.Name) + recovery = f.RecoveryForRestic(restic) }) It(`should delete job after recovery deleted`, func() { By("Creating restic " + restic.Name) diff --git a/test/e2e/framework/recovery.go b/test/e2e/framework/recovery.go index 745c69174..b84f3e557 100644 --- a/test/e2e/framework/recovery.go +++ b/test/e2e/framework/recovery.go @@ -10,7 +10,11 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func (fi *Invocation) RecoveryForRestic(resticName string) api.Recovery { +func (fi *Invocation) RecoveryForRestic(restic api.Restic) api.Recovery { + paths := make([]string, 0) + for _, fg := range restic.Spec.FileGroups { + paths = append(paths, fg.Path) + } return api.Recovery{ TypeMeta: metav1.TypeMeta{ APIVersion: api.SchemeGroupVersion.String(), @@ -21,10 +25,11 @@ func (fi *Invocation) RecoveryForRestic(resticName string) api.Recovery { Namespace: fi.namespace, }, Spec: api.RecoverySpec{ - Restic: resticName, - Volumes: []core.Volume{ + Paths: paths, + Backend: restic.Spec.Backend, + RecoveredVolumes: []api.RecoveredVolume{ { - Name: TestSourceDataVolumeName, + MountPath: restic.Spec.VolumeMounts[0].MountPath, VolumeSource: core.VolumeSource{ HostPath: &core.HostPathVolumeSource{ Path: "/data/stash-test/restic-restored", diff --git a/test/e2e/rc_test.go b/test/e2e/rc_test.go index a819ed628..082efcbf2 100644 --- a/test/e2e/rc_test.go +++ b/test/e2e/rc_test.go @@ -31,6 +31,7 @@ var _ = Describe("ReplicationController", func() { Skip("Missing repository credential") } restic.Spec.Backend.StorageSecretName = cred.Name + recovery.Spec.Backend.StorageSecretName = cred.Name rc = f.ReplicationController() }) @@ -134,7 +135,7 @@ var _ = Describe("ReplicationController", func() { }, BeNumerically(">=", 1))) By("Removing labels of ReplicationController " + rc.Name) - _, err = core_util.PatchRC(f.KubeClient, &rc, func(in *core.ReplicationController) *core.ReplicationController { + _, _, err = core_util.PatchRC(f.KubeClient, &rc, func(in *core.ReplicationController) *core.ReplicationController { in.Labels = map[string]string{ "app": "unmatched", } @@ -405,7 +406,7 @@ var _ = Describe("ReplicationController", func() { BeforeEach(func() { cred = f.SecretForLocalBackend() restic = f.ResticForHostPathLocalBackend() - recovery = f.RecoveryForRestic(restic.Name) + recovery = f.RecoveryForRestic(restic) }) It(`should restore local rc backup`, shouldRestoreRC) }) @@ -414,7 +415,7 @@ var _ = Describe("ReplicationController", func() { BeforeEach(func() { cred = f.SecretForS3Backend() restic = f.ResticForS3Backend() - recovery = f.RecoveryForRestic(restic.Name) + recovery = f.RecoveryForRestic(restic) }) It(`should restore s3 rc backup`, shouldRestoreRC) }) diff --git a/test/e2e/replicaset_test.go b/test/e2e/replicaset_test.go index 7853d0bd3..3a926bc92 100644 --- a/test/e2e/replicaset_test.go +++ b/test/e2e/replicaset_test.go @@ -32,6 +32,7 @@ var _ = Describe("ReplicaSet", func() { Skip("Missing repository credential") } restic.Spec.Backend.StorageSecretName = cred.Name + recovery.Spec.Backend.StorageSecretName = cred.Name rs = f.ReplicaSet() }) @@ -135,7 +136,7 @@ var _ = Describe("ReplicaSet", func() { }, BeNumerically(">=", 1))) By("Removing labels of ReplicaSet " + rs.Name) - _, err = ext_util.PatchReplicaSet(f.KubeClient, &rs, func(in *extensions.ReplicaSet) *extensions.ReplicaSet { + _, _, err = ext_util.PatchReplicaSet(f.KubeClient, &rs, func(in *extensions.ReplicaSet) *extensions.ReplicaSet { in.Labels = map[string]string{ "app": "unmatched", } @@ -406,7 +407,7 @@ var _ = Describe("ReplicaSet", func() { BeforeEach(func() { cred = f.SecretForLocalBackend() restic = f.ResticForHostPathLocalBackend() - recovery = f.RecoveryForRestic(restic.Name) + recovery = f.RecoveryForRestic(restic) }) It(`should restore local replicaset backup`, shouldRestoreReplicaSet) }) @@ -415,7 +416,7 @@ var _ = Describe("ReplicaSet", func() { BeforeEach(func() { cred = f.SecretForS3Backend() restic = f.ResticForS3Backend() - recovery = f.RecoveryForRestic(restic.Name) + recovery = f.RecoveryForRestic(restic) }) It(`should restore s3 replicaset backup`, shouldRestoreReplicaSet) }) diff --git a/test/e2e/statefulset_test.go b/test/e2e/statefulset_test.go index 0c5e21753..f1029b7f1 100644 --- a/test/e2e/statefulset_test.go +++ b/test/e2e/statefulset_test.go @@ -32,6 +32,7 @@ var _ = Describe("StatefulSet", func() { Skip("Missing repository credential") } restic.Spec.Backend.StorageSecretName = cred.Name + recovery.Spec.Backend.StorageSecretName = cred.Name svc = f.HeadlessService() ss = f.StatefulSet(restic, TestSidecarImageTag) }) @@ -152,7 +153,7 @@ var _ = Describe("StatefulSet", func() { }, BeNumerically(">=", 1))) By("Removing labels of StatefulSet " + ss.Name) - _, err = apps_util.PatchStatefulSet(f.KubeClient, &ss, func(in *apps.StatefulSet) *apps.StatefulSet { + _, _, err = apps_util.PatchStatefulSet(f.KubeClient, &ss, func(in *apps.StatefulSet) *apps.StatefulSet { in.Labels = map[string]string{ "app": "unmatched", } @@ -408,7 +409,7 @@ var _ = Describe("StatefulSet", func() { BeforeEach(func() { cred = f.SecretForLocalBackend() restic = f.ResticForHostPathLocalBackend() - recovery = f.RecoveryForRestic(restic.Name) + recovery = f.RecoveryForRestic(restic) }) It(`should restore local StatefulSet backup`, shouldRestoreStatefulSet) }) @@ -417,7 +418,7 @@ var _ = Describe("StatefulSet", func() { BeforeEach(func() { cred = f.SecretForS3Backend() restic = f.ResticForS3Backend() - recovery = f.RecoveryForRestic(restic.Name) + recovery = f.RecoveryForRestic(restic) }) It(`should restore s3 StatefulSet backup`, shouldRestoreStatefulSet) })