diff --git a/pkg/apis/velero/v1/restore.go b/pkg/apis/velero/v1/restore.go index e758aa50e..3bad9329c 100644 --- a/pkg/apis/velero/v1/restore.go +++ b/pkg/apis/velero/v1/restore.go @@ -1,5 +1,5 @@ /* -Copyright 2017 the Velero contributors. +Copyright 2017, 2019 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. @@ -112,24 +112,6 @@ type RestoreStatus struct { FailureReason string `json:"failureReason"` } -// RestoreResult is a collection of messages that were generated -// during execution of a restore. This will typically store either -// warning or error messages. -type RestoreResult struct { - // Velero is a slice of messages related to the operation of Velero - // itself (for example, messages related to connecting to the - // cloud, reading a backup file, etc.) - Velero []string `json:"velero,omitempty"` - - // Cluster is a slice of messages related to restoring cluster- - // scoped resources. - Cluster []string `json:"cluster,omitempty"` - - // Namespaces is a map of namespace name to slice of messages - // related to restoring namespace-scoped resources. - Namespaces map[string][]string `json:"namespaces,omitempty"` -} - // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/apis/velero/v1/zz_generated.deepcopy.go b/pkg/apis/velero/v1/zz_generated.deepcopy.go index 6e61e6fe2..0d40817b3 100644 --- a/pkg/apis/velero/v1/zz_generated.deepcopy.go +++ b/pkg/apis/velero/v1/zz_generated.deepcopy.go @@ -974,47 +974,6 @@ func (in *RestoreList) DeepCopyObject() runtime.Object { return nil } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RestoreResult) DeepCopyInto(out *RestoreResult) { - *out = *in - if in.Velero != nil { - in, out := &in.Velero, &out.Velero - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Cluster != nil { - in, out := &in.Cluster, &out.Cluster - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Namespaces != nil { - in, out := &in.Namespaces, &out.Namespaces - *out = make(map[string][]string, len(*in)) - for key, val := range *in { - var outVal []string - if val == nil { - (*out)[key] = nil - } else { - in, out := &val, &outVal - *out = make([]string, len(*in)) - copy(*out, *in) - } - (*out)[key] = outVal - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RestoreResult. -func (in *RestoreResult) DeepCopy() *RestoreResult { - if in == nil { - return nil - } - out := new(RestoreResult) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RestoreSpec) DeepCopyInto(out *RestoreSpec) { *out = *in diff --git a/pkg/cmd/util/output/restore_describer.go b/pkg/cmd/util/output/restore_describer.go index dfc8dd957..cbeed3fe7 100644 --- a/pkg/cmd/util/output/restore_describer.go +++ b/pkg/cmd/util/output/restore_describer.go @@ -1,5 +1,5 @@ /* -Copyright 2017 the Velero contributors. +Copyright 2017, 2019 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. @@ -27,6 +27,7 @@ import ( v1 "github.com/heptio/velero/pkg/apis/velero/v1" "github.com/heptio/velero/pkg/cmd/util/downloadrequest" clientset "github.com/heptio/velero/pkg/generated/clientset/versioned" + pkgrestore "github.com/heptio/velero/pkg/restore" ) func DescribeRestore(restore *v1.Restore, podVolumeRestores []v1.PodVolumeRestore, details bool, veleroClient clientset.Interface) string { @@ -108,7 +109,7 @@ func describeRestoreResults(d *Describer, restore *v1.Restore, veleroClient clie } var buf bytes.Buffer - var resultMap map[string]v1.RestoreResult + var resultMap map[string]pkgrestore.Result if err := downloadrequest.Stream(veleroClient.VeleroV1(), restore.Namespace, restore.Name, v1.DownloadTargetKindRestoreResults, &buf, downloadRequestTimeout); err != nil { d.Printf("Warnings:\t\n\nErrors:\t\n", err, err) @@ -130,7 +131,7 @@ func describeRestoreResults(d *Describer, restore *v1.Restore, veleroClient clie } } -func describeRestoreResult(d *Describer, name string, result v1.RestoreResult) { +func describeRestoreResult(d *Describer, name string, result pkgrestore.Result) { d.Printf("%s:\n", name) d.DescribeSlice(1, "Velero", result.Velero) d.DescribeSlice(1, "Cluster", result.Cluster) diff --git a/pkg/controller/restore_controller.go b/pkg/controller/restore_controller.go index 1af0b0834..2dabf5b94 100644 --- a/pkg/controller/restore_controller.go +++ b/pkg/controller/restore_controller.go @@ -42,7 +42,7 @@ import ( "github.com/heptio/velero/pkg/metrics" "github.com/heptio/velero/pkg/persistence" "github.com/heptio/velero/pkg/plugin/clientmgmt" - "github.com/heptio/velero/pkg/restore" + pkgrestore "github.com/heptio/velero/pkg/restore" "github.com/heptio/velero/pkg/util/collections" kubeutil "github.com/heptio/velero/pkg/util/kube" "github.com/heptio/velero/pkg/util/logging" @@ -78,7 +78,7 @@ type restoreController struct { namespace string restoreClient velerov1client.RestoresGetter backupClient velerov1client.BackupsGetter - restorer restore.Restorer + restorer pkgrestore.Restorer backupLister listers.BackupLister restoreLister listers.RestoreLister backupLocationLister listers.BackupStorageLocationLister @@ -96,7 +96,7 @@ func NewRestoreController( restoreInformer informers.RestoreInformer, restoreClient velerov1client.RestoresGetter, backupClient velerov1client.BackupsGetter, - restorer restore.Restorer, + restorer pkgrestore.Restorer, backupInformer informers.BackupInformer, backupLocationInformer informers.BackupStorageLocationInformer, snapshotLocationInformer informers.VolumeSnapshotLocationInformer, @@ -449,7 +449,7 @@ func (c *restoreController) runValidatedRestore(restore *api.Restore, info backu restore.Status.Errors += len(e) } - m := map[string]api.RestoreResult{ + m := map[string]pkgrestore.Result{ "warnings": restoreWarnings, "errors": restoreErrors, } @@ -461,7 +461,7 @@ func (c *restoreController) runValidatedRestore(restore *api.Restore, info backu return nil } -func putResults(restore *api.Restore, results map[string]api.RestoreResult, backupStore persistence.BackupStore, log logrus.FieldLogger) error { +func putResults(restore *api.Restore, results map[string]pkgrestore.Result, backupStore persistence.BackupStore, log logrus.FieldLogger) error { buf := new(bytes.Buffer) gzw := gzip.NewWriter(buf) defer gzw.Close() diff --git a/pkg/controller/restore_controller_test.go b/pkg/controller/restore_controller_test.go index 63d00f367..e7f052d69 100644 --- a/pkg/controller/restore_controller_test.go +++ b/pkg/controller/restore_controller_test.go @@ -1,5 +1,5 @@ /* -Copyright 2017 the Velero contributors. +Copyright 2017, 2019 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. @@ -46,7 +46,7 @@ import ( "github.com/heptio/velero/pkg/plugin/clientmgmt" pluginmocks "github.com/heptio/velero/pkg/plugin/mocks" "github.com/heptio/velero/pkg/plugin/velero" - "github.com/heptio/velero/pkg/restore" + pkgrestore "github.com/heptio/velero/pkg/restore" velerotest "github.com/heptio/velero/pkg/util/test" "github.com/heptio/velero/pkg/volume" ) @@ -471,7 +471,7 @@ func TestProcessQueueItem(t *testing.T) { sharedInformers.Velero().V1().Backups().Informer().GetStore().Add(test.backup) } - var warnings, errors api.RestoreResult + var warnings, errors pkgrestore.Result if test.restorerError != nil { errors.Namespaces = map[string][]string{"ns-1": {test.restorerError.Error()}} } @@ -817,11 +817,11 @@ func (r *fakeRestorer) Restore( backupReader io.Reader, actions []velero.RestoreItemAction, snapshotLocationLister listers.VolumeSnapshotLocationLister, - volumeSnapshotterGetter restore.VolumeSnapshotterGetter, -) (api.RestoreResult, api.RestoreResult) { + volumeSnapshotterGetter pkgrestore.VolumeSnapshotterGetter, +) (pkgrestore.Result, pkgrestore.Result) { res := r.Called(log, restore, backup, backupReader, actions) r.calledWithArg = *restore - return res.Get(0).(api.RestoreResult), res.Get(1).(api.RestoreResult) + return res.Get(0).(pkgrestore.Result), res.Get(1).(pkgrestore.Result) } diff --git a/pkg/restore/restore.go b/pkg/restore/restore.go index 36411ea4a..7cb8f5461 100644 --- a/pkg/restore/restore.go +++ b/pkg/restore/restore.go @@ -71,7 +71,7 @@ type Restorer interface { actions []velero.RestoreItemAction, snapshotLocationLister listers.VolumeSnapshotLocationLister, volumeSnapshotterGetter VolumeSnapshotterGetter, - ) (api.RestoreResult, api.RestoreResult) + ) (Result, Result) } // kubernetesRestorer implements Restorer for restoring into a Kubernetes cluster. @@ -181,7 +181,7 @@ func (kr *kubernetesRestorer) Restore( actions []velero.RestoreItemAction, snapshotLocationLister listers.VolumeSnapshotLocationLister, volumeSnapshotterGetter VolumeSnapshotterGetter, -) (api.RestoreResult, api.RestoreResult) { +) (Result, Result) { // metav1.LabelSelectorAsSelector converts a nil LabelSelector to a // Nothing Selector, i.e. a selector that matches nothing. We want // a selector that matches everything. This can be accomplished by @@ -193,19 +193,19 @@ func (kr *kubernetesRestorer) Restore( selector, err := metav1.LabelSelectorAsSelector(ls) if err != nil { - return api.RestoreResult{}, api.RestoreResult{Velero: []string{err.Error()}} + return Result{}, Result{Velero: []string{err.Error()}} } // get resource includes-excludes resourceIncludesExcludes := getResourceIncludesExcludes(kr.discoveryHelper, restore.Spec.IncludedResources, restore.Spec.ExcludedResources) prioritizedResources, err := prioritizeResources(kr.discoveryHelper, kr.resourcePriorities, resourceIncludesExcludes, log) if err != nil { - return api.RestoreResult{}, api.RestoreResult{Velero: []string{err.Error()}} + return Result{}, Result{Velero: []string{err.Error()}} } resolvedActions, err := resolveActions(actions, kr.discoveryHelper) if err != nil { - return api.RestoreResult{}, api.RestoreResult{Velero: []string{err.Error()}} + return Result{}, Result{Velero: []string{err.Error()}} } podVolumeTimeout := kr.resticTimeout @@ -225,7 +225,7 @@ func (kr *kubernetesRestorer) Restore( if kr.resticRestorerFactory != nil { resticRestorer, err = kr.resticRestorerFactory.NewRestorer(ctx, restore) if err != nil { - return api.RestoreResult{}, api.RestoreResult{Velero: []string{err.Error()}} + return Result{}, Result{Velero: []string{err.Error()}} } } @@ -359,13 +359,13 @@ type resourceClientKey struct { namespace string } -func (ctx *context) execute() (api.RestoreResult, api.RestoreResult) { +func (ctx *context) execute() (Result, Result) { ctx.log.Infof("Starting restore of backup %s", kube.NamespaceAndName(ctx.backup)) dir, err := ctx.extractor.unzipAndExtractBackup(ctx.backupReader) if err != nil { ctx.log.Infof("error unzipping and extracting: %v", err) - return api.RestoreResult{}, api.RestoreResult{Velero: []string{err.Error()}} + return Result{}, Result{Velero: []string{err.Error()}} } defer ctx.fileSystem.RemoveAll(dir) @@ -377,8 +377,8 @@ func (ctx *context) execute() (api.RestoreResult, api.RestoreResult) { // restoreFromDir executes a restore based on backup data contained within a local // directory, ctx.restoreDir. -func (ctx *context) restoreFromDir() (api.RestoreResult, api.RestoreResult) { - warnings, errs := api.RestoreResult{}, api.RestoreResult{} +func (ctx *context) restoreFromDir() (Result, Result) { + warnings, errs := Result{}, Result{} namespaceFilter := collections.NewIncludesExcludes(). Includes(ctx.restore.Spec.IncludedNamespaces...). @@ -556,7 +556,7 @@ func getNamespace(logger logrus.FieldLogger, path, remappedName string) *v1.Name // merge combines two RestoreResult objects into one // by appending the corresponding lists to one another. -func merge(a, b *api.RestoreResult) { +func merge(a, b *Result) { a.Cluster = append(a.Cluster, b.Cluster...) a.Velero = append(a.Velero, b.Velero...) for k, v := range b.Namespaces { @@ -568,14 +568,14 @@ func merge(a, b *api.RestoreResult) { } // addVeleroError appends an error to the provided RestoreResult's Velero list. -func addVeleroError(r *api.RestoreResult, err error) { +func addVeleroError(r *Result, err error) { r.Velero = append(r.Velero, err.Error()) } // addToResult appends an error to the provided RestoreResult, either within // the cluster-scoped list (if ns == "") or within the provided namespace's // entry. -func addToResult(r *api.RestoreResult, ns string, e error) { +func addToResult(r *Result, ns string, e error) { if ns == "" { r.Cluster = append(r.Cluster, e.Error()) } else { @@ -703,8 +703,8 @@ func (ctx *context) shouldRestore(name string, pvClient client.Dynamic) (bool, e // restoreResource restores the specified cluster or namespace scoped resource. If namespace is // empty we are restoring a cluster level resource, otherwise into the specified namespace. -func (ctx *context) restoreResource(resource, namespace, resourcePath string) (api.RestoreResult, api.RestoreResult) { - warnings, errs := api.RestoreResult{}, api.RestoreResult{} +func (ctx *context) restoreResource(resource, namespace, resourcePath string) (Result, Result) { + warnings, errs := Result{}, Result{} if ctx.restore.Spec.IncludeClusterResources != nil && !*ctx.restore.Spec.IncludeClusterResources && namespace == "" { ctx.log.Infof("Skipping resource %s because it's cluster-scoped", resource) @@ -784,8 +784,8 @@ func getResourceID(groupResource schema.GroupResource, namespace, name string) s return fmt.Sprintf("%s/%s/%s", groupResource.String(), namespace, name) } -func (ctx *context) restoreItem(obj *unstructured.Unstructured, groupResource schema.GroupResource, namespace string) (api.RestoreResult, api.RestoreResult) { - warnings, errs := api.RestoreResult{}, api.RestoreResult{} +func (ctx *context) restoreItem(obj *unstructured.Unstructured, groupResource schema.GroupResource, namespace string) (Result, Result) { + warnings, errs := Result{}, Result{} resourceID := getResourceID(groupResource, namespace, obj.GetName()) // make a copy of object retrieved from backup diff --git a/pkg/restore/restore_test.go b/pkg/restore/restore_test.go index 9b55645e6..ff0adcfda 100644 --- a/pkg/restore/restore_test.go +++ b/pkg/restore/restore_test.go @@ -221,7 +221,7 @@ func TestRestorePriority(t *testing.T) { restore *api.Restore baseDir string prioritizedResources []schema.GroupResource - expectedErrors api.RestoreResult + expectedErrors Result expectedReadDirs []string }{ { @@ -272,7 +272,7 @@ func TestRestorePriority(t *testing.T) { {Resource: "b"}, {Resource: "c"}, }, - expectedErrors: api.RestoreResult{ + expectedErrors: Result{ Namespaces: map[string][]string{ "ns-1": {"error decoding \"bak/resources/a/namespaces/ns-1/invalid-json.json\": invalid character 'i' looking for beginning of value"}, }, @@ -390,7 +390,7 @@ func TestRestoreResourceForNamespace(t *testing.T) { includeClusterResources *bool fileSystem *velerotest.FakeFileSystem actions []resolvedAction - expectedErrors api.RestoreResult + expectedErrors Result expectedObjs []unstructured.Unstructured }{ { @@ -411,7 +411,7 @@ func TestRestoreResourceForNamespace(t *testing.T) { namespace: "ns-1", resourcePath: "configmaps", fileSystem: velerotest.NewFakeFileSystem(), - expectedErrors: api.RestoreResult{ + expectedErrors: Result{ Namespaces: map[string][]string{ "ns-1": {"error reading \"configmaps\" resource directory: open configmaps: file does not exist"}, }, @@ -431,7 +431,7 @@ func TestRestoreResourceForNamespace(t *testing.T) { fileSystem: velerotest.NewFakeFileSystem(). WithFile("configmaps/cm-1-invalid.json", []byte("this is not valid json")). WithFile("configmaps/cm-2.json", newNamedTestConfigMap("cm-2").ToJSON()), - expectedErrors: api.RestoreResult{ + expectedErrors: Result{ Namespaces: map[string][]string{ "ns-1": {"error decoding \"configmaps/cm-1-invalid.json\": invalid character 'h' in literal true (expecting 'r')"}, }, @@ -757,7 +757,7 @@ func TestRestoringExistingServiceAccount(t *testing.T) { assert.Empty(t, warnings.Velero) assert.Empty(t, warnings.Cluster) assert.Empty(t, warnings.Namespaces) - assert.Equal(t, api.RestoreResult{}, errors) + assert.Equal(t, Result{}, errors) }) } } @@ -1031,7 +1031,7 @@ status: assert.Empty(t, warnings.Velero) assert.Empty(t, warnings.Namespaces) - assert.Equal(t, api.RestoreResult{}, errors) + assert.Equal(t, Result{}, errors) assert.Empty(t, warnings.Cluster) // Prep PVC restore @@ -1063,7 +1063,7 @@ status: assert.Empty(t, warnings.Velero) assert.Empty(t, warnings.Cluster) assert.Empty(t, warnings.Namespaces) - assert.Equal(t, api.RestoreResult{}, errors) + assert.Equal(t, Result{}, errors) }) } } diff --git a/pkg/restore/result.go b/pkg/restore/result.go new file mode 100644 index 000000000..81ce3512d --- /dev/null +++ b/pkg/restore/result.go @@ -0,0 +1,35 @@ +/* +Copyright 2019 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 restore + +// Result is a collection of messages that were generated during +// execution of a restore. This will typically store either +// warning or error messages. +type Result struct { + // Velero is a slice of messages related to the operation of Velero + // itself (for example, messages related to connecting to the + // cloud, reading a backup file, etc.) + Velero []string `json:"velero,omitempty"` + + // Cluster is a slice of messages related to restoring cluster- + // scoped resources. + Cluster []string `json:"cluster,omitempty"` + + // Namespaces is a map of namespace name to slice of messages + // related to restoring namespace-scoped resources. + Namespaces map[string][]string `json:"namespaces,omitempty"` +}