mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-01-06 21:36:30 +00:00
initial structure for refactored pkg/restore tests
Signed-off-by: Steve Kriss <krisss@vmware.com>
This commit is contained in:
78
pkg/restore/builder.go
Normal file
78
pkg/restore/builder.go
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
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
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
|
||||
)
|
||||
|
||||
// Builder is a helper for concisely constructing Restore API objects.
|
||||
type Builder struct {
|
||||
restore velerov1api.Restore
|
||||
}
|
||||
|
||||
// NewBuilder returns a Builder for a Restore with no namespace/name.
|
||||
func NewBuilder() *Builder {
|
||||
return NewNamedBuilder("", "")
|
||||
}
|
||||
|
||||
// NewNamedBuilder returns a Builder for a Restore with the specified namespace
|
||||
// and name.
|
||||
func NewNamedBuilder(namespace, name string) *Builder {
|
||||
return &Builder{
|
||||
restore: velerov1api.Restore{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: velerov1api.SchemeGroupVersion.String(),
|
||||
Kind: "Restore",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Restore returns the built Restore API object.
|
||||
func (b *Builder) Restore() *velerov1api.Restore {
|
||||
return &b.restore
|
||||
}
|
||||
|
||||
// Backup sets the Restore's backup name.
|
||||
func (b *Builder) Backup(name string) *Builder {
|
||||
b.restore.Spec.BackupName = name
|
||||
return b
|
||||
}
|
||||
|
||||
// NamespaceMappings sets the Restore's namespace mappings.
|
||||
func (b *Builder) NamespaceMappings(mapping ...string) *Builder {
|
||||
if b.restore.Spec.NamespaceMapping == nil {
|
||||
b.restore.Spec.NamespaceMapping = make(map[string]string)
|
||||
}
|
||||
|
||||
if len(mapping)%2 != 0 {
|
||||
panic("mapping must contain an even number of values")
|
||||
}
|
||||
|
||||
for i := 0; i < len(mapping); i += 2 {
|
||||
b.restore.Spec.NamespaceMapping[mapping[i]] = mapping[i+1]
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
232
pkg/restore/restore_new_test.go
Normal file
232
pkg/restore/restore_new_test.go
Normal file
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
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
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
|
||||
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
|
||||
"github.com/heptio/velero/pkg/backup"
|
||||
"github.com/heptio/velero/pkg/client"
|
||||
"github.com/heptio/velero/pkg/discovery"
|
||||
"github.com/heptio/velero/pkg/test"
|
||||
"github.com/heptio/velero/pkg/util/encode"
|
||||
testutil "github.com/heptio/velero/pkg/util/test"
|
||||
)
|
||||
|
||||
func TestRestoreNew(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
restore *velerov1api.Restore
|
||||
backup *velerov1api.Backup
|
||||
apiResources []*test.APIResource
|
||||
tarball io.Reader
|
||||
want map[*test.APIResource][]string
|
||||
}{
|
||||
{
|
||||
name: "base case - restore a single resource",
|
||||
restore: defaultRestore().Backup("backup-1").Restore(),
|
||||
backup: backup.NewNamedBuilder(velerov1api.DefaultNamespace, "backup-1").Backup(),
|
||||
tarball: newTarWriter(t).
|
||||
add("metadata/version", []byte("1")).
|
||||
add("resources/pods/namespaces/ns-1/pod-1.json", test.NewPod("ns-1", "pod-1")).
|
||||
done(),
|
||||
apiResources: []*test.APIResource{
|
||||
test.Pods(),
|
||||
},
|
||||
want: map[*test.APIResource][]string{
|
||||
test.Pods(): {"ns-1/pod-1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "restore a resource to a remapped namespace",
|
||||
restore: defaultRestore().Backup("backup-1").NamespaceMappings("ns-1", "ns-2").Restore(),
|
||||
backup: backup.NewNamedBuilder(velerov1api.DefaultNamespace, "backup-1").Backup(),
|
||||
tarball: newTarWriter(t).
|
||||
add("metadata/version", []byte("1")).
|
||||
add("resources/pods/namespaces/ns-1/pod-1.json", test.NewPod("ns-1", "pod-1")).
|
||||
done(),
|
||||
apiResources: []*test.APIResource{
|
||||
test.Pods(),
|
||||
},
|
||||
want: map[*test.APIResource][]string{
|
||||
test.Pods(): {"ns-2/pod-1"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
h := newHarness(t)
|
||||
|
||||
for _, r := range tc.apiResources {
|
||||
h.DiscoveryClient.WithAPIResource(r)
|
||||
}
|
||||
require.NoError(t, h.restorer.discoveryHelper.Refresh())
|
||||
|
||||
warnings, errs := h.restorer.Restore(
|
||||
h.log,
|
||||
tc.restore,
|
||||
tc.backup,
|
||||
nil, // volume snapshots
|
||||
tc.tarball,
|
||||
nil, // actions
|
||||
nil, // snapshot location lister
|
||||
nil, // volume snapshotter getter
|
||||
)
|
||||
|
||||
assertEmptyResults(t, warnings, errs)
|
||||
assertAPIContents(t, h, tc.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func defaultRestore() *Builder {
|
||||
return NewNamedBuilder(velerov1api.DefaultNamespace, "restore-1")
|
||||
}
|
||||
|
||||
// assertAPIContents asserts that the dynamic client on the provided harness contains
|
||||
// all of the items specified in 'want' (a map from an APIResource definition to a slice
|
||||
// of resource identifiers, formatted as <namespace>/<name>).
|
||||
func assertAPIContents(t *testing.T, h *harness, want map[*test.APIResource][]string) {
|
||||
for r, want := range want {
|
||||
res, err := h.DynamicClient.Resource(r.GVR()).List(metav1.ListOptions{})
|
||||
assert.NoError(t, err)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
got := sets.NewString()
|
||||
for _, item := range res.Items {
|
||||
got.Insert(fmt.Sprintf("%s/%s", item.GetNamespace(), item.GetName()))
|
||||
}
|
||||
|
||||
assert.Equal(t, sets.NewString(want...), got)
|
||||
}
|
||||
}
|
||||
|
||||
func assertEmptyResults(t *testing.T, res ...Result) {
|
||||
t.Helper()
|
||||
|
||||
for _, r := range res {
|
||||
assert.Empty(t, r.Cluster)
|
||||
assert.Empty(t, r.Namespaces)
|
||||
assert.Empty(t, r.Velero)
|
||||
}
|
||||
}
|
||||
|
||||
type tarWriter struct {
|
||||
t *testing.T
|
||||
buf *bytes.Buffer
|
||||
gzw *gzip.Writer
|
||||
tw *tar.Writer
|
||||
}
|
||||
|
||||
func newTarWriter(t *testing.T) *tarWriter {
|
||||
tw := new(tarWriter)
|
||||
tw.t = t
|
||||
tw.buf = new(bytes.Buffer)
|
||||
tw.gzw = gzip.NewWriter(tw.buf)
|
||||
tw.tw = tar.NewWriter(tw.gzw)
|
||||
|
||||
return tw
|
||||
}
|
||||
|
||||
func (tw *tarWriter) add(name string, obj interface{}) *tarWriter {
|
||||
tw.t.Helper()
|
||||
|
||||
var data []byte
|
||||
var err error
|
||||
|
||||
switch obj.(type) {
|
||||
case runtime.Object:
|
||||
data, err = encode.Encode(obj.(runtime.Object), "json")
|
||||
case []byte:
|
||||
data = obj.([]byte)
|
||||
default:
|
||||
data, err = json.Marshal(obj)
|
||||
}
|
||||
require.NoError(tw.t, err)
|
||||
|
||||
require.NoError(tw.t, tw.tw.WriteHeader(&tar.Header{
|
||||
Name: name,
|
||||
Size: int64(len(data)),
|
||||
Typeflag: tar.TypeReg,
|
||||
Mode: 0755,
|
||||
ModTime: time.Now(),
|
||||
}))
|
||||
|
||||
_, err = tw.tw.Write(data)
|
||||
require.NoError(tw.t, err)
|
||||
|
||||
return tw
|
||||
}
|
||||
|
||||
func (tw *tarWriter) done() *bytes.Buffer {
|
||||
require.NoError(tw.t, tw.tw.Close())
|
||||
require.NoError(tw.t, tw.gzw.Close())
|
||||
|
||||
return tw.buf
|
||||
}
|
||||
|
||||
type harness struct {
|
||||
*test.APIServer
|
||||
|
||||
restorer *kubernetesRestorer
|
||||
log logrus.FieldLogger
|
||||
}
|
||||
|
||||
func newHarness(t *testing.T) *harness {
|
||||
t.Helper()
|
||||
|
||||
apiServer := test.NewAPIServer(t)
|
||||
log := logrus.StandardLogger()
|
||||
|
||||
discoveryHelper, err := discovery.NewHelper(apiServer.DiscoveryClient, log)
|
||||
require.NoError(t, err)
|
||||
|
||||
return &harness{
|
||||
APIServer: apiServer,
|
||||
restorer: &kubernetesRestorer{
|
||||
discoveryHelper: discoveryHelper,
|
||||
dynamicFactory: client.NewDynamicFactory(apiServer.DynamicClient),
|
||||
namespaceClient: apiServer.KubeClient.CoreV1().Namespaces(),
|
||||
resourceTerminatingTimeout: time.Minute,
|
||||
logger: log,
|
||||
fileSystem: testutil.NewFakeFileSystem(),
|
||||
|
||||
// unsupported
|
||||
resticRestorerFactory: nil,
|
||||
resticTimeout: 0,
|
||||
},
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,6 @@ import (
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
|
||||
api "github.com/heptio/velero/pkg/apis/velero/v1"
|
||||
pkgclient "github.com/heptio/velero/pkg/client"
|
||||
@@ -1962,14 +1961,3 @@ func (r *fakeAction) Execute(input *velero.RestoreItemActionExecuteInput) (*vele
|
||||
|
||||
return velero.NewRestoreItemActionExecuteOutput(res), nil
|
||||
}
|
||||
|
||||
type fakeNamespaceClient struct {
|
||||
createdNamespaces []*v1.Namespace
|
||||
|
||||
corev1.NamespaceInterface
|
||||
}
|
||||
|
||||
func (nsc *fakeNamespaceClient) Create(ns *v1.Namespace) (*v1.Namespace, error) {
|
||||
nsc.createdNamespaces = append(nsc.createdNamespaces, ns)
|
||||
return ns, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user