Update tests to use object builders in pkg/builder ()

* add pkg/builder with BackupBuilder and ObjectMeta functional options

Signed-off-by: Steve Kriss <krisss@vmware.com>
pull/1694/head
Steve Kriss 2019-07-31 08:46:48 -06:00 committed by KubeKween
parent 22eca22ac8
commit 4543258970
43 changed files with 2575 additions and 2330 deletions

View File

@ -1,178 +0,0 @@
/*
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 backup
import (
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
)
// Builder is a helper for concisely constructing Backup API objects.
type Builder struct {
backup velerov1api.Backup
}
// NewBuilder returns a Builder for a Backup with no namespace/name.
func NewBuilder() *Builder {
return NewNamedBackupBuilder("", "")
}
// NewNamedBackupBuilder returns a Builder for a Backup with the specified namespace
// and name.
func NewNamedBackupBuilder(namespace, name string) *Builder {
return &Builder{
backup: velerov1api.Backup{
TypeMeta: metav1.TypeMeta{
APIVersion: velerov1api.SchemeGroupVersion.String(),
Kind: "Backup",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
Name: name,
},
},
}
}
// Backup returns the built Backup API object.
func (b *Builder) Backup() *velerov1api.Backup {
return &b.backup
}
// Namespace sets the Backup's namespace.
func (b *Builder) Namespace(namespace string) *Builder {
b.backup.Namespace = namespace
return b
}
// Name sets the Backup's name.
func (b *Builder) Name(name string) *Builder {
b.backup.Name = name
return b
}
// Labels sets the Backup's labels.
func (b *Builder) Labels(vals ...string) *Builder {
if b.backup.Labels == nil {
b.backup.Labels = map[string]string{}
}
// if we don't have an even number of values, e.g. a key and a value
// for each pair, add an empty-string value at the end to serve as
// the default value for the last key.
if len(vals)%2 != 0 {
vals = append(vals, "")
}
for i := 0; i < len(vals); i += 2 {
b.backup.Labels[vals[i]] = vals[i+1]
}
return b
}
// IncludedNamespaces sets the Backup's included namespaces.
func (b *Builder) IncludedNamespaces(namespaces ...string) *Builder {
b.backup.Spec.IncludedNamespaces = namespaces
return b
}
// ExcludedNamespaces sets the Backup's excluded namespaces.
func (b *Builder) ExcludedNamespaces(namespaces ...string) *Builder {
b.backup.Spec.ExcludedNamespaces = namespaces
return b
}
// IncludedResources sets the Backup's included resources.
func (b *Builder) IncludedResources(resources ...string) *Builder {
b.backup.Spec.IncludedResources = resources
return b
}
// ExcludedResources sets the Backup's excluded resources.
func (b *Builder) ExcludedResources(resources ...string) *Builder {
b.backup.Spec.ExcludedResources = resources
return b
}
// IncludeClusterResources sets the Backup's "include cluster resources" flag.
func (b *Builder) IncludeClusterResources(val bool) *Builder {
b.backup.Spec.IncludeClusterResources = &val
return b
}
// LabelSelector sets the Backup's label selector.
func (b *Builder) LabelSelector(selector *metav1.LabelSelector) *Builder {
b.backup.Spec.LabelSelector = selector
return b
}
// SnapshotVolumes sets the Backup's "snapshot volumes" flag.
func (b *Builder) SnapshotVolumes(val bool) *Builder {
b.backup.Spec.SnapshotVolumes = &val
return b
}
// Phase sets the Backup's phase.
func (b *Builder) Phase(phase velerov1api.BackupPhase) *Builder {
b.backup.Status.Phase = phase
return b
}
// StorageLocation sets the Backup's storage location.
func (b *Builder) StorageLocation(location string) *Builder {
b.backup.Spec.StorageLocation = location
return b
}
// VolumeSnapshotLocations sets the Backup's volume snapshot locations.
func (b *Builder) VolumeSnapshotLocations(locations ...string) *Builder {
b.backup.Spec.VolumeSnapshotLocations = locations
return b
}
// TTL sets the Backup's TTL.
func (b *Builder) TTL(ttl time.Duration) *Builder {
b.backup.Spec.TTL.Duration = ttl
return b
}
// Expiration sets the Backup's expiration.
func (b *Builder) Expiration(val time.Time) *Builder {
b.backup.Status.Expiration.Time = val
return b
}
// StartTimestamp sets the Backup's start timestamp.
func (b *Builder) StartTimestamp(val time.Time) *Builder {
b.backup.Status.StartTimestamp.Time = val
return b
}
// NoTypeMeta removes the type meta from the Backup.
func (b *Builder) NoTypeMeta() *Builder {
b.backup.TypeMeta = metav1.TypeMeta{}
return b
}
// Hooks sets the Backup's hooks.
func (b *Builder) Hooks(hooks velerov1api.BackupHooks) *Builder {
b.backup.Spec.Hooks = hooks
return b
}

File diff suppressed because it is too large Load Diff

View File

@ -1,92 +0,0 @@
/*
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 backup
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
)
// PodVolumeBackupBuilder is a helper for concisely constructing PodVolumeBackup API objects.
type PodVolumeBackupBuilder struct {
podVolumeBackup velerov1api.PodVolumeBackup
}
// NewPodVolumeBackupBuilder returns a PodVolumeBackupBuilder for a PodVolumeBackup with no namespace/name.
func NewPodVolumeBackupBuilder() *PodVolumeBackupBuilder {
return NewNamedPodVolumeBackupBuilder("", "")
}
// NewNamedPodVolumeBackupBuilder returns a PodVolumeBackupBuilder for a Backup with the specified namespace
// and name.
func NewNamedPodVolumeBackupBuilder(namespace, name string) *PodVolumeBackupBuilder {
return &PodVolumeBackupBuilder{
podVolumeBackup: velerov1api.PodVolumeBackup{
TypeMeta: metav1.TypeMeta{
APIVersion: velerov1api.SchemeGroupVersion.String(),
Kind: "PodVolumeBackup",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
Name: name,
},
},
}
}
// PodVolumeBackup returns the built PodVolumeBackup API object.
func (p *PodVolumeBackupBuilder) PodVolumeBackup() *velerov1api.PodVolumeBackup {
return &p.podVolumeBackup
}
// Namespace sets the PodVolumeBackup's namespace.
func (p *PodVolumeBackupBuilder) Namespace(namespace string) *PodVolumeBackupBuilder {
p.podVolumeBackup.Namespace = namespace
return p
}
// Name sets the PodVolumeBackup's name.
func (p *PodVolumeBackupBuilder) Name(name string) *PodVolumeBackupBuilder {
p.podVolumeBackup.Name = name
return p
}
// Labels sets the PodVolumeBackup's labels.
func (p *PodVolumeBackupBuilder) Labels(vals ...string) *PodVolumeBackupBuilder {
if p.podVolumeBackup.Labels == nil {
p.podVolumeBackup.Labels = map[string]string{}
}
// if we don't have an even number of values, e.g. a key and a value
// for each pair, add an empty-string value at the end to serve as
// the default value for the last key.
if len(vals)%2 != 0 {
vals = append(vals, "")
}
for i := 0; i < len(vals); i += 2 {
p.podVolumeBackup.Labels[vals[i]] = vals[i+1]
}
return p
}
// Phase sets the PodVolumeBackup's phase.
func (p *PodVolumeBackupBuilder) Phase(phase velerov1api.PodVolumeBackupPhase) *PodVolumeBackupBuilder {
p.podVolumeBackup.Status.Phase = phase
return p
}

View File

@ -0,0 +1,164 @@
/*
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 builder
import (
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
)
/*
Example usage:
var backup = builder.ForBackup("velero", "backup-1").
ObjectMeta(
builder.WithLabels("foo", "bar"),
builder.WithClusterName("cluster-1"),
).
SnapshotVolumes(true).
Result()
*/
// BackupBuilder builds Backup objects.
type BackupBuilder struct {
object *velerov1api.Backup
}
// ForBackup is the constructor for a BackupBuilder.
func ForBackup(ns, name string) *BackupBuilder {
return &BackupBuilder{
object: &velerov1api.Backup{
TypeMeta: metav1.TypeMeta{
APIVersion: velerov1api.SchemeGroupVersion.String(),
Kind: "Backup",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: ns,
Name: name,
},
},
}
}
// Result returns the built Backup.
func (b *BackupBuilder) Result() *velerov1api.Backup {
return b.object
}
// ObjectMeta applies functional options to the Backup's ObjectMeta.
func (b *BackupBuilder) ObjectMeta(opts ...ObjectMetaOpt) *BackupBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}
// IncludedNamespaces sets the Backup's included namespaces.
func (b *BackupBuilder) IncludedNamespaces(namespaces ...string) *BackupBuilder {
b.object.Spec.IncludedNamespaces = namespaces
return b
}
// ExcludedNamespaces sets the Backup's excluded namespaces.
func (b *BackupBuilder) ExcludedNamespaces(namespaces ...string) *BackupBuilder {
b.object.Spec.ExcludedNamespaces = namespaces
return b
}
// IncludedResources sets the Backup's included resources.
func (b *BackupBuilder) IncludedResources(resources ...string) *BackupBuilder {
b.object.Spec.IncludedResources = resources
return b
}
// ExcludedResources sets the Backup's excluded resources.
func (b *BackupBuilder) ExcludedResources(resources ...string) *BackupBuilder {
b.object.Spec.ExcludedResources = resources
return b
}
// IncludeClusterResources sets the Backup's "include cluster resources" flag.
func (b *BackupBuilder) IncludeClusterResources(val bool) *BackupBuilder {
b.object.Spec.IncludeClusterResources = &val
return b
}
// LabelSelector sets the Backup's label selector.
func (b *BackupBuilder) LabelSelector(selector *metav1.LabelSelector) *BackupBuilder {
b.object.Spec.LabelSelector = selector
return b
}
// SnapshotVolumes sets the Backup's "snapshot volumes" flag.
func (b *BackupBuilder) SnapshotVolumes(val bool) *BackupBuilder {
b.object.Spec.SnapshotVolumes = &val
return b
}
// Phase sets the Backup's phase.
func (b *BackupBuilder) Phase(phase velerov1api.BackupPhase) *BackupBuilder {
b.object.Status.Phase = phase
return b
}
// StorageLocation sets the Backup's storage location.
func (b *BackupBuilder) StorageLocation(location string) *BackupBuilder {
b.object.Spec.StorageLocation = location
return b
}
// VolumeSnapshotLocations sets the Backup's volume snapshot locations.
func (b *BackupBuilder) VolumeSnapshotLocations(locations ...string) *BackupBuilder {
b.object.Spec.VolumeSnapshotLocations = locations
return b
}
// TTL sets the Backup's TTL.
func (b *BackupBuilder) TTL(ttl time.Duration) *BackupBuilder {
b.object.Spec.TTL.Duration = ttl
return b
}
// Expiration sets the Backup's expiration.
func (b *BackupBuilder) Expiration(val time.Time) *BackupBuilder {
b.object.Status.Expiration.Time = val
return b
}
// StartTimestamp sets the Backup's start timestamp.
func (b *BackupBuilder) StartTimestamp(val time.Time) *BackupBuilder {
b.object.Status.StartTimestamp.Time = val
return b
}
// NoTypeMeta removes the type meta from the Backup.
func (b *BackupBuilder) NoTypeMeta() *BackupBuilder {
b.object.TypeMeta = metav1.TypeMeta{}
return b
}
// Hooks sets the Backup's hooks.
func (b *BackupBuilder) Hooks(hooks velerov1api.BackupHooks) *BackupBuilder {
b.object.Spec.Hooks = hooks
return b
}

View File

@ -0,0 +1,79 @@
/*
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.
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 builder
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
)
// BackupStorageLocationBuilder builds BackupStorageLocation objects.
type BackupStorageLocationBuilder struct {
object *velerov1api.BackupStorageLocation
}
// ForBackupStorageLocation is the constructor for a BackupStorageLocationBuilder.
func ForBackupStorageLocation(ns, name string) *BackupStorageLocationBuilder {
return &BackupStorageLocationBuilder{
object: &velerov1api.BackupStorageLocation{
TypeMeta: metav1.TypeMeta{
APIVersion: velerov1api.SchemeGroupVersion.String(),
Kind: "BackupStorageLocation",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: ns,
Name: name,
},
},
}
}
// Result returns the built BackupStorageLocation.
func (b *BackupStorageLocationBuilder) Result() *velerov1api.BackupStorageLocation {
return b.object
}
// ObjectMeta applies functional options to the BackupStorageLocation's ObjectMeta.
func (b *BackupStorageLocationBuilder) ObjectMeta(opts ...ObjectMetaOpt) *BackupStorageLocationBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}
// Provider sets the BackupStorageLocation's provider.
func (b *BackupStorageLocationBuilder) Provider(name string) *BackupStorageLocationBuilder {
b.object.Spec.Provider = name
return b
}
// ObjectStorage sets the BackupStorageLocation's object storage.
func (b *BackupStorageLocationBuilder) ObjectStorage(bucketName string) *BackupStorageLocationBuilder {
if b.object.Spec.StorageType.ObjectStorage == nil {
b.object.Spec.StorageType.ObjectStorage = new(velerov1api.ObjectStorageLocation)
}
b.object.Spec.ObjectStorage.Bucket = bucketName
return b
}
// AccessMode sets the BackupStorageLocation's access mode.
func (b *BackupStorageLocationBuilder) AccessMode(accessMode velerov1api.BackupStorageLocationAccessMode) *BackupStorageLocationBuilder {
b.object.Spec.AccessMode = accessMode
return b
}

View File

@ -0,0 +1,63 @@
/*
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 builder
import (
corev1api "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// ConfigMapBuilder builds ConfigMap objects.
type ConfigMapBuilder struct {
object *corev1api.ConfigMap
}
// ForConfigMap is the constructor for a ConfigMapBuilder.
func ForConfigMap(ns, name string) *ConfigMapBuilder {
return &ConfigMapBuilder{
object: &corev1api.ConfigMap{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1api.SchemeGroupVersion.String(),
Kind: "ConfigMap",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: ns,
Name: name,
},
},
}
}
// Result returns the built ConfigMap.
func (b *ConfigMapBuilder) Result() *corev1api.ConfigMap {
return b.object
}
// ObjectMeta applies functional options to the ConfigMap's ObjectMeta.
func (b *ConfigMapBuilder) ObjectMeta(opts ...ObjectMetaOpt) *ConfigMapBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}
// Data set's the ConfigMap's data.
func (b *ConfigMapBuilder) Data(vals ...string) *ConfigMapBuilder {
b.object.Data = setMapEntries(b.object.Data, vals...)
return b
}

View File

@ -0,0 +1,57 @@
/*
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 builder
import (
appsv1api "k8s.io/api/apps/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// DeploymentBuilder builds Deployment objects.
type DeploymentBuilder struct {
object *appsv1api.Deployment
}
// ForDeployment is the constructor for a DeploymentBuilder.
func ForDeployment(ns, name string) *DeploymentBuilder {
return &DeploymentBuilder{
object: &appsv1api.Deployment{
TypeMeta: metav1.TypeMeta{
APIVersion: appsv1api.SchemeGroupVersion.String(),
Kind: "Deployment",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: ns,
Name: name,
},
},
}
}
// Result returns the built Deployment.
func (b *DeploymentBuilder) Result() *appsv1api.Deployment {
return b.object
}
// ObjectMeta applies functional options to the Deployment's ObjectMeta.
func (b *DeploymentBuilder) ObjectMeta(opts ...ObjectMetaOpt) *DeploymentBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}

View File

@ -0,0 +1,62 @@
/*
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 builder
import (
corev1api "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// NamespaceBuilder builds Namespace objects.
type NamespaceBuilder struct {
object *corev1api.Namespace
}
// ForNamespace is the constructor for a NamespaceBuilder.
func ForNamespace(name string) *NamespaceBuilder {
return &NamespaceBuilder{
object: &corev1api.Namespace{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1api.SchemeGroupVersion.String(),
Kind: "Namespace",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
},
}
}
// Result returns the built Namespace.
func (b *NamespaceBuilder) Result() *corev1api.Namespace {
return b.object
}
// ObjectMeta applies functional options to the Namespace's ObjectMeta.
func (b *NamespaceBuilder) ObjectMeta(opts ...ObjectMetaOpt) *NamespaceBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}
// Phase sets the namespace's phase
func (b *NamespaceBuilder) Phase(val corev1api.NamespacePhase) *NamespaceBuilder {
b.object.Status.Phase = val
return b
}

111
pkg/builder/object_meta.go Normal file
View File

@ -0,0 +1,111 @@
/*
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 builder
import (
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
)
// ObjectMetaOpt is a functional option for ObjectMeta.
type ObjectMetaOpt func(metav1.Object)
// WithName is a functional option that applies the specified
// name to an object.
func WithName(val string) func(obj metav1.Object) {
return func(obj metav1.Object) {
obj.SetName(val)
}
}
// WithLabels is a functional option that applies the specified
// label keys/values to an object.
func WithLabels(vals ...string) func(obj metav1.Object) {
return func(obj metav1.Object) {
obj.SetLabels(setMapEntries(obj.GetLabels(), vals...))
}
}
// WithAnnotations is a functional option that applies the specified
// annotation keys/values to an object.
func WithAnnotations(vals ...string) func(obj metav1.Object) {
return func(obj metav1.Object) {
obj.SetAnnotations(setMapEntries(obj.GetAnnotations(), vals...))
}
}
func setMapEntries(m map[string]string, vals ...string) map[string]string {
if m == nil {
m = make(map[string]string)
}
// if we don't have a value for every key, add an empty
// string at the end to serve as the value for the last
// key.
if len(vals)%2 != 0 {
vals = append(vals, "")
}
for i := 0; i < len(vals); i += 2 {
key := vals[i]
val := vals[i+1]
m[key] = val
}
return m
}
// WithClusterName is a functional option that applies the specified
// cluster name to an object.
func WithClusterName(val string) func(obj metav1.Object) {
return func(obj metav1.Object) {
obj.SetClusterName(val)
}
}
// WithFinalizers is a functional option that applies the specified
// finalizers to an object.
func WithFinalizers(vals ...string) func(obj metav1.Object) {
return func(obj metav1.Object) {
obj.SetFinalizers(vals)
}
}
// WithDeletionTimestamp is a functional option that applies the specified
// deletion timestamp to an object.
func WithDeletionTimestamp(val time.Time) func(obj metav1.Object) {
return func(obj metav1.Object) {
obj.SetDeletionTimestamp(&metav1.Time{Time: val})
}
}
// WithUID is a functional option that applies the specified UID to an object.
func WithUID(val string) func(obj metav1.Object) {
return func(obj metav1.Object) {
obj.SetUID(types.UID(val))
}
}
// WithGenerateName is a functional option that applies the specified generate name to an object.
func WithGenerateName(val string) func(obj metav1.Object) {
return func(obj metav1.Object) {
obj.SetGenerateName(val)
}
}

View File

@ -0,0 +1,96 @@
/*
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 builder
import (
corev1api "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// PersistentVolumeBuilder builds PersistentVolume objects.
type PersistentVolumeBuilder struct {
object *corev1api.PersistentVolume
}
// ForPersistentVolume is the constructor for a PersistentVolumeBuilder.
func ForPersistentVolume(name string) *PersistentVolumeBuilder {
return &PersistentVolumeBuilder{
object: &corev1api.PersistentVolume{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1api.SchemeGroupVersion.String(),
Kind: "PersistentVolume",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
},
}
}
// Result returns the built PersistentVolume.
func (b *PersistentVolumeBuilder) Result() *corev1api.PersistentVolume {
return b.object
}
// ObjectMeta applies functional options to the PersistentVolume's ObjectMeta.
func (b *PersistentVolumeBuilder) ObjectMeta(opts ...ObjectMetaOpt) *PersistentVolumeBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}
// ReclaimPolicy sets the PersistentVolume's reclaim policy.
func (b *PersistentVolumeBuilder) ReclaimPolicy(policy corev1api.PersistentVolumeReclaimPolicy) *PersistentVolumeBuilder {
b.object.Spec.PersistentVolumeReclaimPolicy = policy
return b
}
// ClaimRef sets the PersistentVolume's claim ref.
func (b *PersistentVolumeBuilder) ClaimRef(ns, name string) *PersistentVolumeBuilder {
b.object.Spec.ClaimRef = &corev1api.ObjectReference{
Namespace: ns,
Name: name,
}
return b
}
// AWSEBSVolumeID sets the PersistentVolume's AWSElasticBlockStore volume ID.
func (b *PersistentVolumeBuilder) AWSEBSVolumeID(volumeID string) *PersistentVolumeBuilder {
if b.object.Spec.AWSElasticBlockStore == nil {
b.object.Spec.AWSElasticBlockStore = new(corev1api.AWSElasticBlockStoreVolumeSource)
}
b.object.Spec.AWSElasticBlockStore.VolumeID = volumeID
return b
}
// CSI sets the PersistentVolume's CSI.
func (b *PersistentVolumeBuilder) CSI(driver, volumeHandle string) *PersistentVolumeBuilder {
if b.object.Spec.CSI == nil {
b.object.Spec.CSI = new(corev1api.CSIPersistentVolumeSource)
}
b.object.Spec.CSI.Driver = driver
b.object.Spec.CSI.VolumeHandle = volumeHandle
return b
}
// StorageClass sets the PersistentVolume's storage class name.
func (b *PersistentVolumeBuilder) StorageClass(name string) *PersistentVolumeBuilder {
b.object.Spec.StorageClassName = name
return b
}

View File

@ -0,0 +1,69 @@
/*
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 builder
import (
corev1api "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// PersistentVolumeClaimBuilder builds PersistentVolumeClaim objects.
type PersistentVolumeClaimBuilder struct {
object *corev1api.PersistentVolumeClaim
}
// ForPersistentVolumeClaim is the constructor for a PersistentVolumeClaimBuilder.
func ForPersistentVolumeClaim(ns, name string) *PersistentVolumeClaimBuilder {
return &PersistentVolumeClaimBuilder{
object: &corev1api.PersistentVolumeClaim{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1api.SchemeGroupVersion.String(),
Kind: "PersistentVolumeClaim",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: ns,
Name: name,
},
},
}
}
// Result returns the built PersistentVolumeClaim.
func (b *PersistentVolumeClaimBuilder) Result() *corev1api.PersistentVolumeClaim {
return b.object
}
// ObjectMeta applies functional options to the PersistentVolumeClaim's ObjectMeta.
func (b *PersistentVolumeClaimBuilder) ObjectMeta(opts ...ObjectMetaOpt) *PersistentVolumeClaimBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}
// VolumeName sets the PersistentVolumeClaim's volume name.
func (b *PersistentVolumeClaimBuilder) VolumeName(name string) *PersistentVolumeClaimBuilder {
b.object.Spec.VolumeName = name
return b
}
// StorageClass sets the PersistentVolumeClaim's storage class name.
func (b *PersistentVolumeClaimBuilder) StorageClass(name string) *PersistentVolumeClaimBuilder {
b.object.Spec.StorageClassName = &name
return b
}

View File

@ -0,0 +1,71 @@
/*
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 builder
import (
corev1api "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// PodBuilder builds Pod objects.
type PodBuilder struct {
object *corev1api.Pod
}
// ForPod is the constructor for a PodBuilder.
func ForPod(ns, name string) *PodBuilder {
return &PodBuilder{
object: &corev1api.Pod{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1api.SchemeGroupVersion.String(),
Kind: "Pod",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: ns,
Name: name,
},
},
}
}
// Result returns the built Pod.
func (b *PodBuilder) Result() *corev1api.Pod {
return b.object
}
// ObjectMeta applies functional options to the Pod's ObjectMeta.
func (b *PodBuilder) ObjectMeta(opts ...ObjectMetaOpt) *PodBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}
// Volumes appends to the pod's volumes
func (b *PodBuilder) Volumes(volumes ...*corev1api.Volume) *PodBuilder {
for _, v := range volumes {
b.object.Spec.Volumes = append(b.object.Spec.Volumes, *v)
}
return b
}
// NodeName sets the pod's node name
func (b *PodBuilder) NodeName(val string) *PodBuilder {
b.object.Spec.NodeName = val
return b
}

View File

@ -0,0 +1,64 @@
/*
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 builder
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
)
// PodVolumeBackupBuilder builds PodVolumeBackup objects
type PodVolumeBackupBuilder struct {
object *velerov1api.PodVolumeBackup
}
// ForPodVolumeBackup is the constructor for a PodVolumeBackupBuilder.
func ForPodVolumeBackup(ns, name string) *PodVolumeBackupBuilder {
return &PodVolumeBackupBuilder{
object: &velerov1api.PodVolumeBackup{
TypeMeta: metav1.TypeMeta{
APIVersion: velerov1api.SchemeGroupVersion.String(),
Kind: "PodVolumeBackup",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: ns,
Name: name,
},
},
}
}
// Result returns the built PodVolumeBackup.
func (b *PodVolumeBackupBuilder) Result() *velerov1api.PodVolumeBackup {
return b.object
}
// ObjectMeta applies functional options to the PodVolumeBackup's ObjectMeta.
func (b *PodVolumeBackupBuilder) ObjectMeta(opts ...ObjectMetaOpt) *PodVolumeBackupBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}
// Phase sets the PodVolumeBackup's phase.
func (b *PodVolumeBackupBuilder) Phase(phase velerov1api.PodVolumeBackupPhase) *PodVolumeBackupBuilder {
b.object.Status.Phase = phase
return b
}

View File

@ -0,0 +1,135 @@
/*
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 builder
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
)
// RestoreBuilder builds Restore objects.
type RestoreBuilder struct {
object *velerov1api.Restore
}
// ForRestore is the constructor for a RestoreBuilder.
func ForRestore(ns, name string) *RestoreBuilder {
return &RestoreBuilder{
object: &velerov1api.Restore{
TypeMeta: metav1.TypeMeta{
APIVersion: velerov1api.SchemeGroupVersion.String(),
Kind: "Restore",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: ns,
Name: name,
},
},
}
}
// Result returns the built Restore.
func (b *RestoreBuilder) Result() *velerov1api.Restore {
return b.object
}
// ObjectMeta applies functional options to the Restore's ObjectMeta.
func (b *RestoreBuilder) ObjectMeta(opts ...ObjectMetaOpt) *RestoreBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}
// Backup sets the Restore's backup name.
func (b *RestoreBuilder) Backup(name string) *RestoreBuilder {
b.object.Spec.BackupName = name
return b
}
// Schedule sets the Restore's schedule name.
func (b *RestoreBuilder) Schedule(name string) *RestoreBuilder {
b.object.Spec.ScheduleName = name
return b
}
// IncludedNamespaces appends to the Restore's included namespaces.
func (b *RestoreBuilder) IncludedNamespaces(namespaces ...string) *RestoreBuilder {
b.object.Spec.IncludedNamespaces = append(b.object.Spec.IncludedNamespaces, namespaces...)
return b
}
// ExcludedNamespaces appends to the Restore's excluded namespaces.
func (b *RestoreBuilder) ExcludedNamespaces(namespaces ...string) *RestoreBuilder {
b.object.Spec.ExcludedNamespaces = append(b.object.Spec.ExcludedNamespaces, namespaces...)
return b
}
// IncludedResources appends to the Restore's included resources.
func (b *RestoreBuilder) IncludedResources(resources ...string) *RestoreBuilder {
b.object.Spec.IncludedResources = append(b.object.Spec.IncludedResources, resources...)
return b
}
// ExcludedResources appends to the Restore's excluded resources.
func (b *RestoreBuilder) ExcludedResources(resources ...string) *RestoreBuilder {
b.object.Spec.ExcludedResources = append(b.object.Spec.ExcludedResources, resources...)
return b
}
// IncludeClusterResources sets the Restore's "include cluster resources" flag.
func (b *RestoreBuilder) IncludeClusterResources(val bool) *RestoreBuilder {
b.object.Spec.IncludeClusterResources = &val
return b
}
// LabelSelector sets the Restore's label selector.
func (b *RestoreBuilder) LabelSelector(selector *metav1.LabelSelector) *RestoreBuilder {
b.object.Spec.LabelSelector = selector
return b
}
// NamespaceMappings sets the Restore's namespace mappings.
func (b *RestoreBuilder) NamespaceMappings(mapping ...string) *RestoreBuilder {
if b.object.Spec.NamespaceMapping == nil {
b.object.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.object.Spec.NamespaceMapping[mapping[i]] = mapping[i+1]
}
return b
}
// Phase sets the Restore's phase.
func (b *RestoreBuilder) Phase(phase velerov1api.RestorePhase) *RestoreBuilder {
b.object.Status.Phase = phase
return b
}
// RestorePVs sets the Restore's restore PVs.
func (b *RestoreBuilder) RestorePVs(val bool) *RestoreBuilder {
b.object.Spec.RestorePVs = &val
return b
}

View File

@ -0,0 +1,91 @@
/*
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 builder
import (
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
)
// ScheduleBuilder builds Schedule objects.
type ScheduleBuilder struct {
object *velerov1api.Schedule
}
// ForSchedule is the constructor for a ScheduleBuilder.
func ForSchedule(ns, name string) *ScheduleBuilder {
return &ScheduleBuilder{
object: &velerov1api.Schedule{
TypeMeta: metav1.TypeMeta{
APIVersion: velerov1api.SchemeGroupVersion.String(),
Kind: "Schedule",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: ns,
Name: name,
},
},
}
}
// Result returns the built Schedule.
func (b *ScheduleBuilder) Result() *velerov1api.Schedule {
return b.object
}
// ObjectMeta applies functional options to the Schedule's ObjectMeta.
func (b *ScheduleBuilder) ObjectMeta(opts ...ObjectMetaOpt) *ScheduleBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}
// Phase sets the Schedule's phase.
func (b *ScheduleBuilder) Phase(phase velerov1api.SchedulePhase) *ScheduleBuilder {
b.object.Status.Phase = phase
return b
}
// ValidationError appends to the Schedule's validation errors.
func (b *ScheduleBuilder) ValidationError(err string) *ScheduleBuilder {
b.object.Status.ValidationErrors = append(b.object.Status.ValidationErrors, err)
return b
}
// CronSchedule sets the Schedule's cron schedule.
func (b *ScheduleBuilder) CronSchedule(expression string) *ScheduleBuilder {
b.object.Spec.Schedule = expression
return b
}
// LastBackupTime sets the Schedule's last backup time.
func (b *ScheduleBuilder) LastBackupTime(val string) *ScheduleBuilder {
t, _ := time.Parse("2006-01-02 15:04:05", val)
b.object.Status.LastBackup.Time = t
return b
}
// Template sets the Schedule's template.
func (b *ScheduleBuilder) Template(spec velerov1api.BackupSpec) *ScheduleBuilder {
b.object.Spec.Template = spec
return b
}

View File

@ -0,0 +1,57 @@
/*
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 builder
import (
corev1api "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// SecretBuilder builds Secret objects.
type SecretBuilder struct {
object *corev1api.Secret
}
// ForSecret is the constructor for a SecretBuilder.
func ForSecret(ns, name string) *SecretBuilder {
return &SecretBuilder{
object: &corev1api.Secret{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1api.SchemeGroupVersion.String(),
Kind: "Secret",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: ns,
Name: name,
},
},
}
}
// Result returns the built Secret.
func (b *SecretBuilder) Result() *corev1api.Secret {
return b.object
}
// ObjectMeta applies functional options to the Secret's ObjectMeta.
func (b *SecretBuilder) ObjectMeta(opts ...ObjectMetaOpt) *SecretBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}

View File

@ -0,0 +1,84 @@
/*
Copyright 2018 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 builder
import (
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
)
// ServerStatusRequestBuilder builds ServerStatusRequest objects.
type ServerStatusRequestBuilder struct {
object *velerov1api.ServerStatusRequest
}
// ForServerStatusRequest is the constructor for for a ServerStatusRequestBuilder.
func ForServerStatusRequest(ns, name string) *ServerStatusRequestBuilder {
return &ServerStatusRequestBuilder{
object: &velerov1api.ServerStatusRequest{
TypeMeta: metav1.TypeMeta{
APIVersion: velerov1api.SchemeGroupVersion.String(),
Kind: "ServerStatusRequest",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: ns,
Name: name,
},
},
}
}
// Result returns the built ServerStatusRequest.
func (b *ServerStatusRequestBuilder) Result() *velerov1api.ServerStatusRequest {
return b.object
}
// ObjectMeta applies functional options to the ServerStatusRequest's ObjectMeta.
func (b *ServerStatusRequestBuilder) ObjectMeta(opts ...ObjectMetaOpt) *ServerStatusRequestBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}
// Phase sets the ServerStatusRequest's phase.
func (b *ServerStatusRequestBuilder) Phase(phase velerov1api.ServerStatusRequestPhase) *ServerStatusRequestBuilder {
b.object.Status.Phase = phase
return b
}
// ProcessedTimestamp sets the ServerStatusRequest's processed timestamp.
func (b *ServerStatusRequestBuilder) ProcessedTimestamp(time time.Time) *ServerStatusRequestBuilder {
b.object.Status.ProcessedTimestamp.Time = time
return b
}
// ServerVersion sets the ServerStatusRequest's server version.
func (b *ServerStatusRequestBuilder) ServerVersion(version string) *ServerStatusRequestBuilder {
b.object.Status.ServerVersion = version
return b
}
// Plugins sets the ServerStatusRequest's plugins.
func (b *ServerStatusRequestBuilder) Plugins(plugins []velerov1api.PluginInfo) *ServerStatusRequestBuilder {
b.object.Status.Plugins = plugins
return b
}

View File

@ -0,0 +1,57 @@
/*
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 builder
import (
corev1api "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// ServiceAccountBuilder builds ServiceAccount objects.
type ServiceAccountBuilder struct {
object *corev1api.ServiceAccount
}
// ForServiceAccount is the constructor for a ServiceAccountBuilder.
func ForServiceAccount(ns, name string) *ServiceAccountBuilder {
return &ServiceAccountBuilder{
object: &corev1api.ServiceAccount{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1api.SchemeGroupVersion.String(),
Kind: "ServiceAccount",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: ns,
Name: name,
},
},
}
}
// Result returns the built ServiceAccount.
func (b *ServiceAccountBuilder) Result() *corev1api.ServiceAccount {
return b.object
}
// ObjectMeta applies functional options to the ServiceAccount's ObjectMeta.
func (b *ServiceAccountBuilder) ObjectMeta(opts ...ObjectMetaOpt) *ServiceAccountBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}

View File

@ -0,0 +1,56 @@
/*
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 builder
import (
storagev1api "k8s.io/api/storage/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// StorageClassBuilder builds StorageClass objects.
type StorageClassBuilder struct {
object *storagev1api.StorageClass
}
// ForStorageClass is the constructor for a StorageClassBuilder.
func ForStorageClass(name string) *StorageClassBuilder {
return &StorageClassBuilder{
object: &storagev1api.StorageClass{
TypeMeta: metav1.TypeMeta{
APIVersion: storagev1api.SchemeGroupVersion.String(),
Kind: "StorageClass",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
},
}
}
// Result returns the built StorageClass.
func (b *StorageClassBuilder) Result() *storagev1api.StorageClass {
return b.object
}
// ObjectMeta applies functional options to the StorageClass's ObjectMeta.
func (b *StorageClassBuilder) ObjectMeta(opts ...ObjectMetaOpt) *StorageClassBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}

View File

@ -0,0 +1,56 @@
/*
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 builder
import (
corev1api "k8s.io/api/core/v1"
)
// VolumeBuilder builds Volume objects.
type VolumeBuilder struct {
object *corev1api.Volume
}
// ForVolume is the constructor for a VolumeBuilder.
func ForVolume(name string) *VolumeBuilder {
return &VolumeBuilder{
object: &corev1api.Volume{
Name: name,
},
}
}
// Result returns the built Volume.
func (b *VolumeBuilder) Result() *corev1api.Volume {
return b.object
}
// PersistentVolumeClaimSource sets the Volume's persistent volume claim source.
func (b *VolumeBuilder) PersistentVolumeClaimSource(claimName string) *VolumeBuilder {
b.object.PersistentVolumeClaim = &corev1api.PersistentVolumeClaimVolumeSource{
ClaimName: claimName,
}
return b
}
// CSISource sets the Volume's CSI source.
func (b *VolumeBuilder) CSISource(driver string) *VolumeBuilder {
b.object.CSI = &corev1api.CSIVolumeSource{
Driver: driver,
}
return b
}

View File

@ -0,0 +1,64 @@
/*
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 builder
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
)
// VolumeSnapshotLocationBuilder builds VolumeSnapshotLocation objects.
type VolumeSnapshotLocationBuilder struct {
object *velerov1api.VolumeSnapshotLocation
}
// ForVolumeSnapshotLocation is the constructor for a VolumeSnapshotLocationBuilder.
func ForVolumeSnapshotLocation(ns, name string) *VolumeSnapshotLocationBuilder {
return &VolumeSnapshotLocationBuilder{
object: &velerov1api.VolumeSnapshotLocation{
TypeMeta: metav1.TypeMeta{
APIVersion: velerov1api.SchemeGroupVersion.String(),
Kind: "VolumeSnapshotLocation",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: ns,
Name: name,
},
},
}
}
// Result returns the built VolumeSnapshotLocation.
func (b *VolumeSnapshotLocationBuilder) Result() *velerov1api.VolumeSnapshotLocation {
return b.object
}
// ObjectMeta applies functional options to the VolumeSnapshotLocation's ObjectMeta.
func (b *VolumeSnapshotLocationBuilder) ObjectMeta(opts ...ObjectMetaOpt) *VolumeSnapshotLocationBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}
// Provider sets the VolumeSnapshotLocation's provider.
func (b *VolumeSnapshotLocationBuilder) Provider(name string) *VolumeSnapshotLocationBuilder {
b.object.Spec.Provider = name
return b
}

View File

@ -25,7 +25,7 @@ import (
v1 "k8s.io/api/core/v1"
"k8s.io/client-go/kubernetes/fake"
"github.com/heptio/velero/pkg/test"
"github.com/heptio/velero/pkg/builder"
testutil "github.com/heptio/velero/pkg/util/test"
)
@ -39,8 +39,8 @@ func Test_validatePodVolumesHostPath(t *testing.T) {
{
name: "no error when pod volumes are present",
pods: []*corev1.Pod{
test.NewPod("foo", "bar", test.WithUID("foo")),
test.NewPod("zoo", "raz", test.WithUID("zoo")),
builder.ForPod("foo", "bar").ObjectMeta(builder.WithUID("foo")).Result(),
builder.ForPod("zoo", "raz").ObjectMeta(builder.WithUID("zoo")).Result(),
},
dirs: []string{"foo", "zoo"},
wantErr: false,
@ -48,8 +48,8 @@ func Test_validatePodVolumesHostPath(t *testing.T) {
{
name: "no error when pod volumes are present and there are mirror pods",
pods: []*corev1.Pod{
test.NewPod("foo", "bar", test.WithUID("foo")),
test.NewPod("zoo", "raz", test.WithUID("zoo"), test.WithAnnotations(v1.MirrorPodAnnotationKey, "baz")),
builder.ForPod("foo", "bar").ObjectMeta(builder.WithUID("foo")).Result(),
builder.ForPod("zoo", "raz").ObjectMeta(builder.WithUID("zoo"), builder.WithAnnotations(v1.MirrorPodAnnotationKey, "baz")).Result(),
},
dirs: []string{"foo", "baz"},
wantErr: false,
@ -57,8 +57,8 @@ func Test_validatePodVolumesHostPath(t *testing.T) {
{
name: "error when all pod volumes missing",
pods: []*corev1.Pod{
test.NewPod("foo", "bar", test.WithUID("foo")),
test.NewPod("zoo", "raz", test.WithUID("zoo")),
builder.ForPod("foo", "bar").ObjectMeta(builder.WithUID("foo")).Result(),
builder.ForPod("zoo", "raz").ObjectMeta(builder.WithUID("zoo")).Result(),
},
dirs: []string{"unexpected-dir"},
wantErr: true,
@ -66,8 +66,8 @@ func Test_validatePodVolumesHostPath(t *testing.T) {
{
name: "error when some pod volumes missing",
pods: []*corev1.Pod{
test.NewPod("foo", "bar", test.WithUID("foo")),
test.NewPod("zoo", "raz", test.WithUID("zoo")),
builder.ForPod("foo", "bar").ObjectMeta(builder.WithUID("foo")).Result(),
builder.ForPod("zoo", "raz").ObjectMeta(builder.WithUID("zoo")).Result(),
},
dirs: []string{"foo"},
wantErr: true,

View File

@ -24,8 +24,8 @@ import (
"k8s.io/apimachinery/pkg/watch"
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
"github.com/heptio/velero/pkg/builder"
velerov1client "github.com/heptio/velero/pkg/generated/clientset/versioned/typed/velero/v1"
"github.com/heptio/velero/pkg/serverstatusrequest"
)
type ServerStatusGetter interface {
@ -38,7 +38,10 @@ type DefaultServerStatusGetter struct {
}
func (g *DefaultServerStatusGetter) GetServerStatus(client velerov1client.ServerStatusRequestsGetter) (*velerov1api.ServerStatusRequest, error) {
req := serverstatusrequest.NewBuilder().Namespace(g.Namespace).GenerateName("velero-cli-").ServerStatusRequest()
req := builder.ForServerStatusRequest(g.Namespace, "").
ObjectMeta(
builder.WithGenerateName("velero-cli-"),
).Result()
created, err := client.ServerStatusRequests(g.Namespace).Create(req)
if err != nil {

View File

@ -26,10 +26,10 @@ import (
"github.com/stretchr/testify/mock"
velerov1 "github.com/heptio/velero/pkg/apis/velero/v1"
"github.com/heptio/velero/pkg/builder"
"github.com/heptio/velero/pkg/buildinfo"
"github.com/heptio/velero/pkg/generated/clientset/versioned/fake"
v1 "github.com/heptio/velero/pkg/generated/clientset/versioned/typed/velero/v1"
"github.com/heptio/velero/pkg/serverstatusrequest"
)
func TestPrintVersion(t *testing.T) {
@ -73,7 +73,7 @@ func TestPrintVersion(t *testing.T) {
{
name: "server status getter returns normally",
clientOnly: false,
serverStatusRequest: serverstatusrequest.NewBuilder().ServerVersion("v1.0.1").ServerStatusRequest(),
serverStatusRequest: builder.ForServerStatusRequest("velero", "ssr-1").ServerVersion("v1.0.1").Result(),
getterError: nil,
want: clientVersion + "Server:\n\tVersion: v1.0.1\n",
},

View File

@ -35,6 +35,7 @@ import (
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
pkgbackup "github.com/heptio/velero/pkg/backup"
"github.com/heptio/velero/pkg/builder"
"github.com/heptio/velero/pkg/generated/clientset/versioned/fake"
informers "github.com/heptio/velero/pkg/generated/informers/externalversions"
"github.com/heptio/velero/pkg/metrics"
@ -44,7 +45,6 @@ import (
pluginmocks "github.com/heptio/velero/pkg/plugin/mocks"
"github.com/heptio/velero/pkg/plugin/velero"
"github.com/heptio/velero/pkg/util/logging"
velerotest "github.com/heptio/velero/pkg/util/test"
)
type fakeBackupper struct {
@ -56,8 +56,8 @@ func (b *fakeBackupper) Backup(logger logrus.FieldLogger, backup *pkgbackup.Requ
return args.Error(0)
}
func defaultBackup() *pkgbackup.Builder {
return pkgbackup.NewNamedBackupBuilder(velerov1api.DefaultNamespace, "backup-1")
func defaultBackup() *builder.BackupBuilder {
return builder.ForBackup(velerov1api.DefaultNamespace, "backup-1")
}
func TestProcessBackupNonProcessedItems(t *testing.T) {
@ -77,22 +77,22 @@ func TestProcessBackupNonProcessedItems(t *testing.T) {
{
name: "FailedValidation backup is not processed",
key: "velero/backup-1",
backup: defaultBackup().Phase(velerov1api.BackupPhaseFailedValidation).Backup(),
backup: defaultBackup().Phase(velerov1api.BackupPhaseFailedValidation).Result(),
},
{
name: "InProgress backup is not processed",
key: "velero/backup-1",
backup: defaultBackup().Phase(velerov1api.BackupPhaseInProgress).Backup(),
backup: defaultBackup().Phase(velerov1api.BackupPhaseInProgress).Result(),
},
{
name: "Completed backup is not processed",
key: "velero/backup-1",
backup: defaultBackup().Phase(velerov1api.BackupPhaseCompleted).Backup(),
backup: defaultBackup().Phase(velerov1api.BackupPhaseCompleted).Result(),
},
{
name: "Failed backup is not processed",
key: "velero/backup-1",
backup: defaultBackup().Phase(velerov1api.BackupPhaseFailed).Backup(),
backup: defaultBackup().Phase(velerov1api.BackupPhaseFailed).Result(),
},
}
@ -126,7 +126,7 @@ func TestProcessBackupNonProcessedItems(t *testing.T) {
}
func TestProcessBackupValidationFailures(t *testing.T) {
defaultBackupLocation := velerotest.NewTestBackupStorageLocation().WithName("loc-1").BackupStorageLocation
defaultBackupLocation := builder.ForBackupStorageLocation("velero", "loc-1").Result()
tests := []struct {
name string
@ -136,25 +136,25 @@ func TestProcessBackupValidationFailures(t *testing.T) {
}{
{
name: "invalid included/excluded resources fails validation",
backup: defaultBackup().IncludedResources("foo").ExcludedResources("foo").Backup(),
backup: defaultBackup().IncludedResources("foo").ExcludedResources("foo").Result(),
backupLocation: defaultBackupLocation,
expectedErrs: []string{"Invalid included/excluded resource lists: excludes list cannot contain an item in the includes list: foo"},
},
{
name: "invalid included/excluded namespaces fails validation",
backup: defaultBackup().IncludedNamespaces("foo").ExcludedNamespaces("foo").Backup(),
backup: defaultBackup().IncludedNamespaces("foo").ExcludedNamespaces("foo").Result(),
backupLocation: defaultBackupLocation,
expectedErrs: []string{"Invalid included/excluded namespace lists: excludes list cannot contain an item in the includes list: foo"},
},
{
name: "non-existent backup location fails validation",
backup: defaultBackup().StorageLocation("nonexistent").Backup(),
backup: defaultBackup().StorageLocation("nonexistent").Result(),
expectedErrs: []string{"a BackupStorageLocation CRD with the name specified in the backup spec needs to be created before this backup can be executed. Error: backupstoragelocation.velero.io \"nonexistent\" not found"},
},
{
name: "backup for read-only backup location fails validation",
backup: defaultBackup().StorageLocation("read-only").Backup(),
backupLocation: velerotest.NewTestBackupStorageLocation().WithName("read-only").WithAccessMode(velerov1api.BackupStorageLocationAccessModeReadOnly).BackupStorageLocation,
backup: defaultBackup().StorageLocation("read-only").Result(),
backupLocation: builder.ForBackupStorageLocation("velero", "read-only").AccessMode(velerov1api.BackupStorageLocationAccessModeReadOnly).Result(),
expectedErrs: []string{"backup can't be created because backup storage location read-only is currently in read-only mode"},
},
}
@ -214,15 +214,14 @@ func TestBackupLocationLabel(t *testing.T) {
}{
{
name: "valid backup location name should be used as a label",
backup: defaultBackup().Backup(),
backupLocation: velerotest.NewTestBackupStorageLocation().WithName("loc-1").BackupStorageLocation,
backup: defaultBackup().Result(),
backupLocation: builder.ForBackupStorageLocation("velero", "loc-1").Result(),
expectedBackupLocation: "loc-1",
},
{
name: "invalid storage location name should be handled while creating label",
backup: defaultBackup().Backup(),
backupLocation: velerotest.NewTestBackupStorageLocation().
WithName("defaultdefaultdefaultdefaultdefaultdefaultdefaultdefaultdefaultdefault").BackupStorageLocation,
name: "invalid storage location name should be handled while creating label",
backup: defaultBackup().Result(),
backupLocation: builder.ForBackupStorageLocation("velero", "defaultdefaultdefaultdefaultdefaultdefaultdefaultdefaultdefaultdefault").Result(),
expectedBackupLocation: "defaultdefaultdefaultdefaultdefaultdefaultdefaultdefaultd58343f",
},
}
@ -273,13 +272,13 @@ func TestDefaultBackupTTL(t *testing.T) {
}{
{
name: "backup with no TTL specified",
backup: defaultBackup().Backup(),
backup: defaultBackup().Result(),
expectedTTL: defaultBackupTTL,
expectedExpiration: metav1.NewTime(now.Add(defaultBackupTTL.Duration)),
},
{
name: "backup with TTL specified",
backup: defaultBackup().TTL(time.Hour).Backup(),
backup: defaultBackup().TTL(time.Hour).Result(),
expectedTTL: metav1.Duration{Duration: 1 * time.Hour},
expectedExpiration: metav1.NewTime(now.Add(1 * time.Hour)),
},
@ -312,7 +311,7 @@ func TestDefaultBackupTTL(t *testing.T) {
}
func TestProcessBackupCompletions(t *testing.T) {
defaultBackupLocation := velerotest.NewTestBackupStorageLocation().WithName("loc-1").WithObjectStorage("store-1").BackupStorageLocation
defaultBackupLocation := builder.ForBackupStorageLocation("velero", "loc-1").ObjectStorage("store-1").Result()
now, err := time.Parse(time.RFC1123Z, time.RFC1123Z)
require.NoError(t, err)
@ -329,7 +328,7 @@ func TestProcessBackupCompletions(t *testing.T) {
// Completed
{
name: "backup with no backup location gets the default",
backup: defaultBackup().Backup(),
backup: defaultBackup().Result(),
backupLocation: defaultBackupLocation,
expectedResult: &velerov1api.Backup{
TypeMeta: metav1.TypeMeta{
@ -357,8 +356,8 @@ func TestProcessBackupCompletions(t *testing.T) {
},
{
name: "backup with a specific backup location keeps it",
backup: defaultBackup().StorageLocation("alt-loc").Backup(),
backupLocation: velerotest.NewTestBackupStorageLocation().WithName("alt-loc").WithObjectStorage("store-1").BackupStorageLocation,
backup: defaultBackup().StorageLocation("alt-loc").Result(),
backupLocation: builder.ForBackupStorageLocation("velero", "alt-loc").ObjectStorage("store-1").Result(),
expectedResult: &velerov1api.Backup{
TypeMeta: metav1.TypeMeta{
Kind: "Backup",
@ -385,12 +384,11 @@ func TestProcessBackupCompletions(t *testing.T) {
},
{
name: "backup for a location with ReadWrite access mode gets processed",
backup: defaultBackup().StorageLocation("read-write").Backup(),
backupLocation: velerotest.NewTestBackupStorageLocation().
WithName("read-write").
WithObjectStorage("store-1").
WithAccessMode(velerov1api.BackupStorageLocationAccessModeReadWrite).
BackupStorageLocation,
backup: defaultBackup().StorageLocation("read-write").Result(),
backupLocation: builder.ForBackupStorageLocation("velero", "read-write").
ObjectStorage("store-1").
AccessMode(velerov1api.BackupStorageLocationAccessModeReadWrite).
Result(),
expectedResult: &velerov1api.Backup{
TypeMeta: metav1.TypeMeta{
Kind: "Backup",
@ -417,7 +415,7 @@ func TestProcessBackupCompletions(t *testing.T) {
},
{
name: "backup with a TTL has expiration set",
backup: defaultBackup().TTL(10 * time.Minute).Backup(),
backup: defaultBackup().TTL(10 * time.Minute).Result(),
backupLocation: defaultBackupLocation,
expectedResult: &velerov1api.Backup{
TypeMeta: metav1.TypeMeta{
@ -447,7 +445,7 @@ func TestProcessBackupCompletions(t *testing.T) {
{
name: "backup without an existing backup will succeed",
backupExists: false,
backup: defaultBackup().Backup(),
backup: defaultBackup().Result(),
backupLocation: defaultBackupLocation,
expectedResult: &velerov1api.Backup{
TypeMeta: metav1.TypeMeta{
@ -478,7 +476,7 @@ func TestProcessBackupCompletions(t *testing.T) {
{
name: "backup with existing backup will fail",
backupExists: true,
backup: defaultBackup().Backup(),
backup: defaultBackup().Result(),
backupLocation: defaultBackupLocation,
expectedResult: &velerov1api.Backup{
TypeMeta: metav1.TypeMeta{
@ -506,7 +504,7 @@ func TestProcessBackupCompletions(t *testing.T) {
},
{
name: "error when checking if backup exists will cause backup to fail",
backup: defaultBackup().Backup(),
backup: defaultBackup().Result(),
existenceCheckError: errors.New("Backup already exists in object storage"),
backupLocation: defaultBackupLocation,
expectedResult: &velerov1api.Backup{
@ -613,7 +611,7 @@ func TestValidateAndGetSnapshotLocations(t *testing.T) {
tests := []struct {
name string
backup *velerov1api.Backup
locations []*velerotest.TestVolumeSnapshotLocation
locations []*velerov1api.VolumeSnapshotLocation
defaultLocations map[string]string
expectedVolumeSnapshotLocationNames []string // adding these in the expected order will allow to test with better msgs in case of a test failure
expectedErrors string
@ -621,76 +619,76 @@ func TestValidateAndGetSnapshotLocations(t *testing.T) {
}{
{
name: "location name does not correspond to any existing location",
backup: defaultBackup().Phase(velerov1api.BackupPhaseNew).VolumeSnapshotLocations("random-name").Backup(),
locations: []*velerotest.TestVolumeSnapshotLocation{
velerotest.NewTestVolumeSnapshotLocation().WithProvider("aws").WithName("aws-us-east-1"),
velerotest.NewTestVolumeSnapshotLocation().WithProvider("aws").WithName("aws-us-west-1"),
velerotest.NewTestVolumeSnapshotLocation().WithProvider("fake-provider").WithName("some-name"),
backup: defaultBackup().Phase(velerov1api.BackupPhaseNew).VolumeSnapshotLocations("random-name").Result(),
locations: []*velerov1api.VolumeSnapshotLocation{
builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "aws-us-east-1").Provider("aws").Result(),
builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "aws-us-west-1").Provider("aws").Result(),
builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "some-name").Provider("fake-provider").Result(),
},
expectedErrors: "a VolumeSnapshotLocation CRD for the location random-name with the name specified in the backup spec needs to be created before this snapshot can be executed. Error: volumesnapshotlocation.velero.io \"random-name\" not found", expectedSuccess: false,
},
{
name: "duplicate locationName per provider: should filter out dups",
backup: defaultBackup().Phase(velerov1api.BackupPhaseNew).VolumeSnapshotLocations("aws-us-west-1", "aws-us-west-1").Backup(),
locations: []*velerotest.TestVolumeSnapshotLocation{
velerotest.NewTestVolumeSnapshotLocation().WithProvider("aws").WithName("aws-us-east-1"),
velerotest.NewTestVolumeSnapshotLocation().WithProvider("aws").WithName("aws-us-west-1"),
backup: defaultBackup().Phase(velerov1api.BackupPhaseNew).VolumeSnapshotLocations("aws-us-west-1", "aws-us-west-1").Result(),
locations: []*velerov1api.VolumeSnapshotLocation{
builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "aws-us-east-1").Provider("aws").Result(),
builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "aws-us-west-1").Provider("aws").Result(),
},
expectedVolumeSnapshotLocationNames: []string{"aws-us-west-1"},
expectedSuccess: true,
},
{
name: "multiple non-dupe location names per provider should error",
backup: defaultBackup().Phase(velerov1api.BackupPhaseNew).VolumeSnapshotLocations("aws-us-east-1", "aws-us-west-1").Backup(),
locations: []*velerotest.TestVolumeSnapshotLocation{
velerotest.NewTestVolumeSnapshotLocation().WithProvider("aws").WithName("aws-us-east-1"),
velerotest.NewTestVolumeSnapshotLocation().WithProvider("aws").WithName("aws-us-west-1"),
velerotest.NewTestVolumeSnapshotLocation().WithProvider("fake-provider").WithName("some-name"),
backup: defaultBackup().Phase(velerov1api.BackupPhaseNew).VolumeSnapshotLocations("aws-us-east-1", "aws-us-west-1").Result(),
locations: []*velerov1api.VolumeSnapshotLocation{
builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "aws-us-east-1").Provider("aws").Result(),
builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "aws-us-west-1").Provider("aws").Result(),
builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "some-name").Provider("fake-provider").Result(),
},
expectedErrors: "more than one VolumeSnapshotLocation name specified for provider aws: aws-us-west-1; unexpected name was aws-us-east-1",
expectedSuccess: false,
},
{
name: "no location name for the provider exists, only one VSL for the provider: use it",
backup: defaultBackup().Phase(velerov1api.BackupPhaseNew).Backup(),
locations: []*velerotest.TestVolumeSnapshotLocation{
velerotest.NewTestVolumeSnapshotLocation().WithProvider("aws").WithName("aws-us-east-1"),
backup: defaultBackup().Phase(velerov1api.BackupPhaseNew).Result(),
locations: []*velerov1api.VolumeSnapshotLocation{
builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "aws-us-east-1").Provider("aws").Result(),
},
expectedVolumeSnapshotLocationNames: []string{"aws-us-east-1"},
expectedSuccess: true,
},
{
name: "no location name for the provider exists, no default, more than one VSL for the provider: error",
backup: defaultBackup().Phase(velerov1api.BackupPhaseNew).Backup(),
locations: []*velerotest.TestVolumeSnapshotLocation{
velerotest.NewTestVolumeSnapshotLocation().WithProvider("aws").WithName("aws-us-east-1"),
velerotest.NewTestVolumeSnapshotLocation().WithProvider("aws").WithName("aws-us-west-1"),
backup: defaultBackup().Phase(velerov1api.BackupPhaseNew).Result(),
locations: []*velerov1api.VolumeSnapshotLocation{
builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "aws-us-east-1").Provider("aws").Result(),
builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "aws-us-west-1").Provider("aws").Result(),
},
expectedErrors: "provider aws has more than one possible volume snapshot location, and none were specified explicitly or as a default",
},
{
name: "no location name for the provider exists, more than one VSL for the provider: the provider's default should be added",
backup: defaultBackup().Phase(velerov1api.BackupPhaseNew).Backup(),
backup: defaultBackup().Phase(velerov1api.BackupPhaseNew).Result(),
defaultLocations: map[string]string{"aws": "aws-us-east-1"},
locations: []*velerotest.TestVolumeSnapshotLocation{
velerotest.NewTestVolumeSnapshotLocation().WithName("aws-us-east-1").WithProvider("aws"),
velerotest.NewTestVolumeSnapshotLocation().WithName("aws-us-west-1").WithProvider("aws"),
locations: []*velerov1api.VolumeSnapshotLocation{
builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "aws-us-east-1").Provider("aws").Result(),
builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "aws-us-west-1").Provider("aws").Result(),
},
expectedVolumeSnapshotLocationNames: []string{"aws-us-east-1"},
expectedSuccess: true,
},
{
name: "no existing location name and no default location name given",
backup: defaultBackup().Phase(velerov1api.BackupPhaseNew).Backup(),
backup: defaultBackup().Phase(velerov1api.BackupPhaseNew).Result(),
expectedSuccess: true,
},
{
name: "multiple location names for a provider, default location name for another provider",
backup: defaultBackup().Phase(velerov1api.BackupPhaseNew).VolumeSnapshotLocations("aws-us-west-1", "aws-us-west-1").Backup(),
backup: defaultBackup().Phase(velerov1api.BackupPhaseNew).VolumeSnapshotLocations("aws-us-west-1", "aws-us-west-1").Result(),
defaultLocations: map[string]string{"fake-provider": "some-name"},
locations: []*velerotest.TestVolumeSnapshotLocation{
velerotest.NewTestVolumeSnapshotLocation().WithProvider("aws").WithName("aws-us-west-1"),
velerotest.NewTestVolumeSnapshotLocation().WithProvider("fake-provider").WithName("some-name"),
locations: []*velerov1api.VolumeSnapshotLocation{
builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "aws-us-west-1").Provider("aws").Result(),
builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "some-name").Provider("fake-provider").Result(),
},
expectedVolumeSnapshotLocationNames: []string{"aws-us-west-1", "some-name"},
expectedSuccess: true,
@ -713,7 +711,7 @@ func TestValidateAndGetSnapshotLocations(t *testing.T) {
backup := test.backup.DeepCopy()
backup.Spec.VolumeSnapshotLocations = test.backup.Spec.VolumeSnapshotLocations
for _, location := range test.locations {
require.NoError(t, sharedInformers.Velero().V1().VolumeSnapshotLocations().Informer().GetStore().Add(location.VolumeSnapshotLocation))
require.NoError(t, sharedInformers.Velero().V1().VolumeSnapshotLocations().Informer().GetStore().Add(location))
}
providerLocations, errs := c.validateAndGetSnapshotLocations(backup)

View File

@ -34,6 +34,7 @@ import (
v1 "github.com/heptio/velero/pkg/apis/velero/v1"
pkgbackup "github.com/heptio/velero/pkg/backup"
"github.com/heptio/velero/pkg/builder"
"github.com/heptio/velero/pkg/generated/clientset/versioned/fake"
informers "github.com/heptio/velero/pkg/generated/informers/externalversions"
"github.com/heptio/velero/pkg/metrics"
@ -266,8 +267,8 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) {
})
t.Run("patching to InProgress fails", func(t *testing.T) {
backup := defaultBackup().Name("foo").StorageLocation("default").Backup()
location := velerotest.NewTestBackupStorageLocation().WithName("default").BackupStorageLocation
backup := builder.ForBackup(v1.DefaultNamespace, "foo").StorageLocation("default").Result()
location := builder.ForBackupStorageLocation("velero", "default").Result()
td := setupBackupDeletionControllerTest(backup)
@ -298,8 +299,8 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) {
})
t.Run("patching backup to Deleting fails", func(t *testing.T) {
backup := defaultBackup().Name("foo").StorageLocation("default").Backup()
location := velerotest.NewTestBackupStorageLocation().WithName("default").BackupStorageLocation
backup := builder.ForBackup(v1.DefaultNamespace, "foo").StorageLocation("default").Result()
location := builder.ForBackupStorageLocation("velero", "default").Result()
td := setupBackupDeletionControllerTest(backup)
@ -364,7 +365,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) {
})
t.Run("unable to find backup storage location", func(t *testing.T) {
backup := defaultBackup().Name("foo").StorageLocation("default").Backup()
backup := builder.ForBackup(v1.DefaultNamespace, "foo").StorageLocation("default").Result()
td := setupBackupDeletionControllerTest(backup)
@ -390,8 +391,8 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) {
})
t.Run("backup storage location is in read-only mode", func(t *testing.T) {
backup := defaultBackup().Name("foo").StorageLocation("default").Backup()
location := velerotest.NewTestBackupStorageLocation().WithName("default").WithAccessMode(v1.BackupStorageLocationAccessModeReadOnly).BackupStorageLocation
backup := builder.ForBackup(v1.DefaultNamespace, "foo").StorageLocation("default").Result()
location := builder.ForBackupStorageLocation("velero", "default").AccessMode(v1.BackupStorageLocationAccessModeReadOnly).Result()
td := setupBackupDeletionControllerTest(backup)
@ -419,13 +420,13 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) {
})
t.Run("full delete, no errors", func(t *testing.T) {
backup := defaultBackup().Name("foo").Backup()
backup := builder.ForBackup(v1.DefaultNamespace, "foo").Result()
backup.UID = "uid"
backup.Spec.StorageLocation = "primary"
restore1 := velerotest.NewTestRestore("velero", "restore-1", v1.RestorePhaseCompleted).WithBackup("foo").Restore
restore2 := velerotest.NewTestRestore("velero", "restore-2", v1.RestorePhaseCompleted).WithBackup("foo").Restore
restore3 := velerotest.NewTestRestore("velero", "restore-3", v1.RestorePhaseCompleted).WithBackup("some-other-backup").Restore
restore1 := builder.ForRestore("velero", "restore-1").Phase(v1.RestorePhaseCompleted).Backup("foo").Result()
restore2 := builder.ForRestore("velero", "restore-2").Phase(v1.RestorePhaseCompleted).Backup("foo").Result()
restore3 := builder.ForRestore("velero", "restore-3").Phase(v1.RestorePhaseCompleted).Backup("some-other-backup").Result()
td := setupBackupDeletionControllerTest(backup, restore1, restore2, restore3)
@ -565,16 +566,26 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) {
})
t.Run("full delete, no errors, with backup name greater than 63 chars", func(t *testing.T) {
backup := defaultBackup().Name("the-really-long-backup-name-that-is-much-more-than-63-characters").Backup()
backup := defaultBackup().
ObjectMeta(
builder.WithName("the-really-long-backup-name-that-is-much-more-than-63-characters"),
).
Result()
backup.UID = "uid"
backup.Spec.StorageLocation = "primary"
restore1 := velerotest.NewTestRestore("velero", "restore-1", v1.RestorePhaseCompleted).
WithBackup("the-really-long-backup-name-that-is-much-more-than-63-characters").Restore
restore2 := velerotest.NewTestRestore("velero", "restore-2", v1.RestorePhaseCompleted).
WithBackup("the-really-long-backup-name-that-is-much-more-than-63-characters").Restore
restore3 := velerotest.NewTestRestore("velero", "restore-3", v1.RestorePhaseCompleted).
WithBackup("some-other-backup").Restore
restore1 := builder.ForRestore("velero", "restore-1").
Phase(v1.RestorePhaseCompleted).
Backup("the-really-long-backup-name-that-is-much-more-than-63-characters").
Result()
restore2 := builder.ForRestore("velero", "restore-2").
Phase(v1.RestorePhaseCompleted).
Backup("the-really-long-backup-name-that-is-much-more-than-63-characters").
Result()
restore3 := builder.ForRestore("velero", "restore-3").
Phase(v1.RestorePhaseCompleted).
Backup("some-other-backup").
Result()
td := setupBackupDeletionControllerTest(backup, restore1, restore2, restore3)
td.req = pkgbackup.NewDeleteBackupRequest(backup.Name, string(backup.UID))

View File

@ -32,7 +32,7 @@ import (
core "k8s.io/client-go/testing"
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
pkgbackup "github.com/heptio/velero/pkg/backup"
"github.com/heptio/velero/pkg/builder"
"github.com/heptio/velero/pkg/generated/clientset/versioned/fake"
informers "github.com/heptio/velero/pkg/generated/informers/externalversions"
"github.com/heptio/velero/pkg/label"
@ -43,10 +43,6 @@ import (
velerotest "github.com/heptio/velero/pkg/util/test"
)
func defaultPodVolumeBackup() *pkgbackup.PodVolumeBackupBuilder {
return pkgbackup.NewNamedPodVolumeBackupBuilder(velerov1api.DefaultNamespace, "pvb-1")
}
func defaultLocationsList(namespace string) []*velerov1api.BackupStorageLocation {
return []*velerov1api.BackupStorageLocation{
{
@ -138,15 +134,15 @@ func TestBackupSyncControllerRun(t *testing.T) {
cloudBuckets: map[string][]*cloudBackupData{
"bucket-1": {
&cloudBackupData{
backup: defaultBackup().Namespace("ns-1").Name("backup-1").Backup(),
backup: builder.ForBackup("ns-1", "backup-1").Result(),
},
&cloudBackupData{
backup: defaultBackup().Namespace("ns-1").Name("backup-2").Backup(),
backup: builder.ForBackup("ns-1", "backup-2").Result(),
},
},
"bucket-2": {
&cloudBackupData{
backup: defaultBackup().Namespace("ns-1").Name("backup-3").Backup(),
backup: builder.ForBackup("ns-1", "backup-3").Result(),
},
},
},
@ -158,18 +154,18 @@ func TestBackupSyncControllerRun(t *testing.T) {
cloudBuckets: map[string][]*cloudBackupData{
"bucket-1": {
&cloudBackupData{
backup: defaultBackup().Namespace("ns-1").Name("backup-1").Backup(),
backup: builder.ForBackup("ns-1", "backup-1").Result(),
},
&cloudBackupData{
backup: defaultBackup().Namespace("ns-1").Name("backup-2").Backup(),
backup: builder.ForBackup("ns-1", "backup-2").Result(),
},
},
"bucket-2": {
&cloudBackupData{
backup: defaultBackup().Namespace("ns-2").Name("backup-3").Backup(),
backup: builder.ForBackup("ns-2", "backup-3").Result(),
},
&cloudBackupData{
backup: defaultBackup().Namespace("velero").Name("backup-4").Backup(),
backup: builder.ForBackup("velero", "backup-4").Result(),
},
},
},
@ -181,26 +177,26 @@ func TestBackupSyncControllerRun(t *testing.T) {
cloudBuckets: map[string][]*cloudBackupData{
"bucket-1": {
&cloudBackupData{
backup: defaultBackup().Namespace("ns-1").Name("backup-1").Backup(),
backup: builder.ForBackup("ns-1", "backup-1").Result(),
},
&cloudBackupData{
backup: defaultBackup().Namespace("ns-1").Name("backup-2").Backup(),
backup: builder.ForBackup("ns-1", "backup-2").Result(),
},
},
"bucket-2": {
&cloudBackupData{
backup: defaultBackup().Namespace("ns-1").Name("backup-3").Backup(),
backup: builder.ForBackup("ns-1", "backup-3").Result(),
},
&cloudBackupData{
backup: defaultBackup().Namespace("ns-1").Name("backup-4").Backup(),
backup: builder.ForBackup("ns-1", "backup-4").Result(),
},
},
},
existingBackups: []*velerov1api.Backup{
// add a label to each existing backup so we can differentiate it from the cloud
// backup during verification
defaultBackup().Namespace("ns-1").Name("backup-1").Labels("i-exist", "true").StorageLocation("location-1").Backup(),
defaultBackup().Namespace("ns-1").Name("backup-3").Labels("i-exist", "true").StorageLocation("location-2").Backup(),
builder.ForBackup("ns-1", "backup-1").StorageLocation("location-1").ObjectMeta(builder.WithLabels("i-exist", "true")).Result(),
builder.ForBackup("ns-1", "backup-3").StorageLocation("location-2").ObjectMeta(builder.WithLabels("i-exist", "true")).Result(),
},
},
{
@ -210,14 +206,14 @@ func TestBackupSyncControllerRun(t *testing.T) {
cloudBuckets: map[string][]*cloudBackupData{
"bucket-1": {
&cloudBackupData{
backup: defaultBackup().Namespace("ns-1").Name("backup-1").Backup(),
backup: builder.ForBackup("ns-1", "backup-1").Result(),
},
},
},
existingBackups: []*velerov1api.Backup{
// add a label to each existing backup so we can differentiate it from the cloud
// backup during verification
defaultBackup().Namespace("ns-1").Name("backup-1").Labels("i-exist", "true").StorageLocation("location-1").Backup(),
builder.ForBackup("ns-1", "backup-1").ObjectMeta(builder.WithLabels("i-exist", "true")).StorageLocation("location-1").Result(),
},
},
{
@ -227,15 +223,15 @@ func TestBackupSyncControllerRun(t *testing.T) {
cloudBuckets: map[string][]*cloudBackupData{
"bucket-1": {
&cloudBackupData{
backup: defaultBackup().Namespace("ns-1").Name("backup-1").StorageLocation("foo").Labels(velerov1api.StorageLocationLabel, "foo").Backup(),
backup: builder.ForBackup("ns-1", "backup-1").StorageLocation("foo").ObjectMeta(builder.WithLabels(velerov1api.StorageLocationLabel, "foo")).Result(),
},
&cloudBackupData{
backup: defaultBackup().Namespace("ns-1").Name("backup-2").Backup(),
backup: builder.ForBackup("ns-1", "backup-2").Result(),
},
},
"bucket-2": {
&cloudBackupData{
backup: defaultBackup().Namespace("ns-1").Name("backup-3").StorageLocation("bar").Labels(velerov1api.StorageLocationLabel, "bar").Backup(),
backup: builder.ForBackup("ns-1", "backup-3").StorageLocation("bar").ObjectMeta(builder.WithLabels(velerov1api.StorageLocationLabel, "bar")).Result(),
},
},
},
@ -248,15 +244,15 @@ func TestBackupSyncControllerRun(t *testing.T) {
cloudBuckets: map[string][]*cloudBackupData{
"bucket-1": {
&cloudBackupData{
backup: defaultBackup().Namespace("ns-1").Name("backup-1").StorageLocation("foo").Labels(velerov1api.StorageLocationLabel, "foo").Backup(),
backup: builder.ForBackup("ns-1", "backup-1").StorageLocation("foo").ObjectMeta(builder.WithLabels(velerov1api.StorageLocationLabel, "foo")).Result(),
},
&cloudBackupData{
backup: defaultBackup().Namespace("ns-1").Name("backup-2").Backup(),
backup: builder.ForBackup("ns-1", "backup-2").Result(),
},
},
"bucket-2": {
&cloudBackupData{
backup: defaultBackup().Namespace("ns-1").Name("backup-3").StorageLocation("bar").Labels(velerov1api.StorageLocationLabel, "bar").Backup(),
backup: builder.ForBackup("ns-1", "backup-3").StorageLocation("bar").ObjectMeta(builder.WithLabels(velerov1api.StorageLocationLabel, "bar")).Result(),
},
},
},
@ -268,28 +264,28 @@ func TestBackupSyncControllerRun(t *testing.T) {
cloudBuckets: map[string][]*cloudBackupData{
"bucket-1": {
&cloudBackupData{
backup: defaultBackup().Namespace("ns-1").Name("backup-1").Backup(),
backup: builder.ForBackup("ns-1", "backup-1").Result(),
podVolumeBackups: []*velerov1api.PodVolumeBackup{
defaultPodVolumeBackup().Namespace("ns-1").Name("pvb-1").PodVolumeBackup(),
builder.ForPodVolumeBackup("ns-1", "pvb-1").Result(),
},
},
&cloudBackupData{
backup: defaultBackup().Namespace("ns-1").Name("backup-2").Backup(),
backup: builder.ForBackup("ns-1", "backup-2").Result(),
podVolumeBackups: []*velerov1api.PodVolumeBackup{
defaultPodVolumeBackup().Namespace("ns-1").Name("pvb-2").PodVolumeBackup(),
builder.ForPodVolumeBackup("ns-1", "pvb-2").Result(),
},
},
},
"bucket-2": {
&cloudBackupData{
backup: defaultBackup().Namespace("ns-1").Name("backup-3").Backup(),
backup: builder.ForBackup("ns-1", "backup-3").Result(),
},
&cloudBackupData{
backup: defaultBackup().Namespace("ns-1").Name("backup-4").Backup(),
backup: builder.ForBackup("ns-1", "backup-4").Result(),
podVolumeBackups: []*velerov1api.PodVolumeBackup{
defaultPodVolumeBackup().Namespace("ns-1").Name("pvb-1").PodVolumeBackup(),
defaultPodVolumeBackup().Namespace("ns-1").Name("pvb-2").PodVolumeBackup(),
defaultPodVolumeBackup().Namespace("ns-1").Name("pvb-3").PodVolumeBackup(),
builder.ForPodVolumeBackup("ns-1", "pvb-1").Result(),
builder.ForPodVolumeBackup("ns-1", "pvb-2").Result(),
builder.ForPodVolumeBackup("ns-1", "pvb-3").Result(),
},
},
},
@ -302,35 +298,35 @@ func TestBackupSyncControllerRun(t *testing.T) {
cloudBuckets: map[string][]*cloudBackupData{
"bucket-1": {
&cloudBackupData{
backup: defaultBackup().Namespace("ns-1").Name("backup-1").Backup(),
backup: builder.ForBackup("ns-1", "backup-1").Result(),
podVolumeBackups: []*velerov1api.PodVolumeBackup{
defaultPodVolumeBackup().Namespace("ns-1").Name("pvb-1").PodVolumeBackup(),
builder.ForPodVolumeBackup("ns-1", "pvb-1").Result(),
},
},
&cloudBackupData{
backup: defaultBackup().Namespace("ns-1").Name("backup-2").Backup(),
backup: builder.ForBackup("ns-1", "backup-2").Result(),
podVolumeBackups: []*velerov1api.PodVolumeBackup{
defaultPodVolumeBackup().Namespace("ns-1").Name("pvb-3").PodVolumeBackup(),
builder.ForPodVolumeBackup("ns-1", "pvb-3").Result(),
},
},
},
"bucket-2": {
&cloudBackupData{
backup: defaultBackup().Namespace("ns-1").Name("backup-3").Backup(),
backup: builder.ForBackup("ns-1", "backup-3").Result(),
},
&cloudBackupData{
backup: defaultBackup().Namespace("ns-1").Name("backup-4").Backup(),
backup: builder.ForBackup("ns-1", "backup-4").Result(),
podVolumeBackups: []*velerov1api.PodVolumeBackup{
defaultPodVolumeBackup().Namespace("ns-1").Name("pvb-1").PodVolumeBackup(),
defaultPodVolumeBackup().Namespace("ns-1").Name("pvb-5").PodVolumeBackup(),
defaultPodVolumeBackup().Namespace("ns-1").Name("pvb-6").PodVolumeBackup(),
builder.ForPodVolumeBackup("ns-1", "pvb-1").Result(),
builder.ForPodVolumeBackup("ns-1", "pvb-5").Result(),
builder.ForPodVolumeBackup("ns-1", "pvb-6").Result(),
},
},
},
},
existingPodVolumeBackups: []*velerov1api.PodVolumeBackup{
defaultPodVolumeBackup().Namespace("ns-1").Name("pvb-1").PodVolumeBackup(),
defaultPodVolumeBackup().Namespace("ns-1").Name("pvb-2").PodVolumeBackup(),
builder.ForPodVolumeBackup("ns-1", "pvb-1").Result(),
builder.ForPodVolumeBackup("ns-1", "pvb-2").Result(),
},
},
}
@ -477,6 +473,10 @@ func TestBackupSyncControllerRun(t *testing.T) {
}
func TestDeleteOrphanedBackups(t *testing.T) {
baseBuilder := func(name string) *builder.BackupBuilder {
return builder.ForBackup("ns-1", name).ObjectMeta(builder.WithLabels(velerov1api.StorageLocationLabel, "default"))
}
tests := []struct {
name string
cloudBackups sets.String
@ -489,9 +489,9 @@ func TestDeleteOrphanedBackups(t *testing.T) {
namespace: "ns-1",
cloudBackups: sets.NewString("backup-1", "backup-2", "backup-3"),
k8sBackups: []*velerov1api.Backup{
defaultBackup().Namespace("ns-1").Name("backupA").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseCompleted).Backup(),
defaultBackup().Namespace("ns-1").Name("backupB").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseCompleted).Backup(),
defaultBackup().Namespace("ns-1").Name("backupC").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseCompleted).Backup(),
baseBuilder("backupA").Phase(velerov1api.BackupPhaseCompleted).Result(),
baseBuilder("backupB").Phase(velerov1api.BackupPhaseCompleted).Result(),
baseBuilder("backupC").Phase(velerov1api.BackupPhaseCompleted).Result(),
},
expectedDeletes: sets.NewString("backupA", "backupB", "backupC"),
},
@ -500,9 +500,9 @@ func TestDeleteOrphanedBackups(t *testing.T) {
namespace: "ns-1",
cloudBackups: sets.NewString("backup-1", "backup-2", "backup-3"),
k8sBackups: []*velerov1api.Backup{
defaultBackup().Namespace("ns-1").Name("backup-1").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseCompleted).Backup(),
defaultBackup().Namespace("ns-1").Name("backup-2").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseCompleted).Backup(),
defaultBackup().Namespace("ns-1").Name("backup-C").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseCompleted).Backup(),
baseBuilder("backup-1").Phase(velerov1api.BackupPhaseCompleted).Result(),
baseBuilder("backup-2").Phase(velerov1api.BackupPhaseCompleted).Result(),
baseBuilder("backup-C").Phase(velerov1api.BackupPhaseCompleted).Result(),
},
expectedDeletes: sets.NewString("backup-C"),
},
@ -511,9 +511,9 @@ func TestDeleteOrphanedBackups(t *testing.T) {
namespace: "ns-1",
cloudBackups: sets.NewString("backup-1", "backup-2", "backup-3"),
k8sBackups: []*velerov1api.Backup{
defaultBackup().Namespace("ns-1").Name("backup-1").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseCompleted).Backup(),
defaultBackup().Namespace("ns-1").Name("backup-2").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseCompleted).Backup(),
defaultBackup().Namespace("ns-1").Name("backup-3").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseCompleted).Backup(),
baseBuilder("backup-1").Phase(velerov1api.BackupPhaseCompleted).Result(),
baseBuilder("backup-2").Phase(velerov1api.BackupPhaseCompleted).Result(),
baseBuilder("backup-3").Phase(velerov1api.BackupPhaseCompleted).Result(),
},
expectedDeletes: sets.NewString(),
},
@ -522,12 +522,12 @@ func TestDeleteOrphanedBackups(t *testing.T) {
namespace: "ns-1",
cloudBackups: sets.NewString("backup-1", "backup-2", "backup-3"),
k8sBackups: []*velerov1api.Backup{
defaultBackup().Namespace("ns-1").Name("backupA").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseCompleted).Backup(),
defaultBackup().Namespace("ns-1").Name("Deleting").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseDeleting).Backup(),
defaultBackup().Namespace("ns-1").Name("Failed").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseFailed).Backup(),
defaultBackup().Namespace("ns-1").Name("FailedValidation").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseFailedValidation).Backup(),
defaultBackup().Namespace("ns-1").Name("InProgress").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseInProgress).Backup(),
defaultBackup().Namespace("ns-1").Name("New").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseNew).Backup(),
baseBuilder("backupA").Phase(velerov1api.BackupPhaseCompleted).Result(),
baseBuilder("Deleting").Phase(velerov1api.BackupPhaseDeleting).Result(),
baseBuilder("Failed").Phase(velerov1api.BackupPhaseFailed).Result(),
baseBuilder("FailedValidation").Phase(velerov1api.BackupPhaseFailedValidation).Result(),
baseBuilder("InProgress").Phase(velerov1api.BackupPhaseInProgress).Result(),
baseBuilder("New").Phase(velerov1api.BackupPhaseNew).Result(),
},
expectedDeletes: sets.NewString("backupA"),
},
@ -536,9 +536,9 @@ func TestDeleteOrphanedBackups(t *testing.T) {
namespace: "ns-1",
cloudBackups: sets.NewString("backup-1", "backup-2", "backup-3"),
k8sBackups: []*velerov1api.Backup{
defaultBackup().Namespace("ns-1").Name("backup-1").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseFailed).Backup(),
defaultBackup().Namespace("ns-1").Name("backup-2").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseFailedValidation).Backup(),
defaultBackup().Namespace("ns-1").Name("backup-3").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseInProgress).Backup(),
baseBuilder("backup-1").Phase(velerov1api.BackupPhaseFailed).Result(),
baseBuilder("backup-2").Phase(velerov1api.BackupPhaseFailedValidation).Result(),
baseBuilder("backup-3").Phase(velerov1api.BackupPhaseInProgress).Result(),
},
expectedDeletes: sets.NewString(),
},
@ -547,13 +547,13 @@ func TestDeleteOrphanedBackups(t *testing.T) {
namespace: "ns-1",
cloudBackups: sets.NewString("backup-1", "backup-2", "backup-3"),
k8sBackups: []*velerov1api.Backup{
defaultBackup().Namespace("ns-1").Name("backup-1").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseCompleted).Backup(),
defaultBackup().Namespace("ns-1").Name("backup-2").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseCompleted).Backup(),
defaultBackup().Namespace("ns-1").Name("backup-C").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseCompleted).Backup(),
baseBuilder("backup-1").Phase(velerov1api.BackupPhaseCompleted).Result(),
baseBuilder("backup-2").Phase(velerov1api.BackupPhaseCompleted).Result(),
baseBuilder("backup-C").Phase(velerov1api.BackupPhaseCompleted).Result(),
defaultBackup().Namespace("ns-1").Name("backup-4").Labels(velerov1api.StorageLocationLabel, "alternate").Phase(velerov1api.BackupPhaseCompleted).Backup(),
defaultBackup().Namespace("ns-1").Name("backup-5").Labels(velerov1api.StorageLocationLabel, "alternate").Phase(velerov1api.BackupPhaseCompleted).Backup(),
defaultBackup().Namespace("ns-1").Name("backup-6").Labels(velerov1api.StorageLocationLabel, "alternate").Phase(velerov1api.BackupPhaseCompleted).Backup(),
baseBuilder("backup-4").ObjectMeta(builder.WithLabels(velerov1api.StorageLocationLabel, "alternate")).Phase(velerov1api.BackupPhaseCompleted).Result(),
baseBuilder("backup-5").ObjectMeta(builder.WithLabels(velerov1api.StorageLocationLabel, "alternate")).Phase(velerov1api.BackupPhaseCompleted).Result(),
baseBuilder("backup-6").ObjectMeta(builder.WithLabels(velerov1api.StorageLocationLabel, "alternate")).Phase(velerov1api.BackupPhaseCompleted).Result(),
},
expectedDeletes: sets.NewString("backup-C"),
},
@ -628,12 +628,24 @@ func TestStorageLabelsInDeleteOrphanedBackups(t *testing.T) {
namespace: "ns-1",
cloudBackups: sets.NewString("backup-1", "backup-2", "backup-3"),
k8sBackups: []*velerov1api.Backup{
defaultBackup().Namespace("ns-1").Name("backup-1").
Labels(velerov1api.StorageLocationLabel, "the-really-long-location-name-that-is-much-more-than-63-c69e779").Phase(velerov1api.BackupPhaseCompleted).Backup(),
defaultBackup().Namespace("ns-1").Name("backup-2").
Labels(velerov1api.StorageLocationLabel, "the-really-long-location-name-that-is-much-more-than-63-c69e779").Phase(velerov1api.BackupPhaseCompleted).Backup(),
defaultBackup().Namespace("ns-1").Name("backup-C").
Labels(velerov1api.StorageLocationLabel, "the-really-long-location-name-that-is-much-more-than-63-c69e779").Phase(velerov1api.BackupPhaseCompleted).Backup(),
builder.ForBackup("ns-1", "backup-1").
ObjectMeta(
builder.WithLabels(velerov1api.StorageLocationLabel, "the-really-long-location-name-that-is-much-more-than-63-c69e779"),
).
Phase(velerov1api.BackupPhaseCompleted).
Result(),
builder.ForBackup("ns-1", "backup-2").
ObjectMeta(
builder.WithLabels(velerov1api.StorageLocationLabel, "the-really-long-location-name-that-is-much-more-than-63-c69e779"),
).
Phase(velerov1api.BackupPhaseCompleted).
Result(),
builder.ForBackup("ns-1", "backup-C").
ObjectMeta(
builder.WithLabels(velerov1api.StorageLocationLabel, "the-really-long-location-name-that-is-much-more-than-63-c69e779"),
).
Phase(velerov1api.BackupPhaseCompleted).
Result(),
},
expectedDeletes: sets.NewString("backup-C"),
},

View File

@ -28,6 +28,7 @@ import (
"k8s.io/apimachinery/pkg/util/clock"
v1 "github.com/heptio/velero/pkg/apis/velero/v1"
"github.com/heptio/velero/pkg/builder"
"github.com/heptio/velero/pkg/generated/clientset/versioned/fake"
informers "github.com/heptio/velero/pkg/generated/informers/externalversions"
"github.com/heptio/velero/pkg/persistence"
@ -119,6 +120,10 @@ func newBackupLocation(name, provider, bucket string) *v1.BackupStorageLocation
}
func TestProcessDownloadRequest(t *testing.T) {
defaultBackup := func() *v1.Backup {
return builder.ForBackup(v1.DefaultNamespace, "a-backup").StorageLocation("a-location").Result()
}
tests := []struct {
name string
key string
@ -145,94 +150,94 @@ func TestProcessDownloadRequest(t *testing.T) {
{
name: "backup contents request for nonexistent backup returns an error",
downloadRequest: newDownloadRequest("", v1.DownloadTargetKindBackupContents, "a-backup"),
backup: defaultBackup().Name("non-matching-backup").StorageLocation("a-location").Backup(),
backup: builder.ForBackup(v1.DefaultNamespace, "non-matching-backup").StorageLocation("a-location").Result(),
backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"),
expectedErr: "backup.velero.io \"a-backup\" not found",
},
{
name: "restore log request for nonexistent restore returns an error",
downloadRequest: newDownloadRequest("", v1.DownloadTargetKindRestoreLog, "a-backup-20170912150214"),
restore: velerotest.NewTestRestore(v1.DefaultNamespace, "non-matching-restore", v1.RestorePhaseCompleted).WithBackup("a-backup").Restore,
backup: defaultBackup().Name("a-backup").StorageLocation("a-location").Backup(),
restore: builder.ForRestore(v1.DefaultNamespace, "non-matching-restore").Phase(v1.RestorePhaseCompleted).Backup("a-backup").Result(),
backup: defaultBackup(),
backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"),
expectedErr: "error getting Restore: restore.velero.io \"a-backup-20170912150214\" not found",
},
{
name: "backup contents request for backup with nonexistent location returns an error",
downloadRequest: newDownloadRequest("", v1.DownloadTargetKindBackupContents, "a-backup"),
backup: defaultBackup().Name("a-backup").StorageLocation("a-location").Backup(),
backup: defaultBackup(),
backupLocation: newBackupLocation("non-matching-location", "a-provider", "a-bucket"),
expectedErr: "backupstoragelocation.velero.io \"a-location\" not found",
},
{
name: "backup contents request with phase '' gets a url",
downloadRequest: newDownloadRequest("", v1.DownloadTargetKindBackupContents, "a-backup"),
backup: defaultBackup().Name("a-backup").StorageLocation("a-location").Backup(),
backup: defaultBackup(),
backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"),
expectGetsURL: true,
},
{
name: "backup contents request with phase 'New' gets a url",
downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseNew, v1.DownloadTargetKindBackupContents, "a-backup"),
backup: defaultBackup().Name("a-backup").StorageLocation("a-location").Backup(),
backup: defaultBackup(),
backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"),
expectGetsURL: true,
},
{
name: "backup log request with phase '' gets a url",
downloadRequest: newDownloadRequest("", v1.DownloadTargetKindBackupLog, "a-backup"),
backup: defaultBackup().Name("a-backup").StorageLocation("a-location").Backup(),
backup: defaultBackup(),
backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"),
expectGetsURL: true,
},
{
name: "backup log request with phase 'New' gets a url",
downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseNew, v1.DownloadTargetKindBackupLog, "a-backup"),
backup: defaultBackup().Name("a-backup").StorageLocation("a-location").Backup(),
backup: defaultBackup(),
backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"),
expectGetsURL: true,
},
{
name: "restore log request with phase '' gets a url",
downloadRequest: newDownloadRequest("", v1.DownloadTargetKindRestoreLog, "a-backup-20170912150214"),
restore: velerotest.NewTestRestore(v1.DefaultNamespace, "a-backup-20170912150214", v1.RestorePhaseCompleted).WithBackup("a-backup").Restore,
backup: defaultBackup().Name("a-backup").StorageLocation("a-location").Backup(),
restore: builder.ForRestore(v1.DefaultNamespace, "a-backup-20170912150214").Phase(v1.RestorePhaseCompleted).Backup("a-backup").Result(),
backup: defaultBackup(),
backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"),
expectGetsURL: true,
},
{
name: "restore log request with phase 'New' gets a url",
downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseNew, v1.DownloadTargetKindRestoreLog, "a-backup-20170912150214"),
restore: velerotest.NewTestRestore(v1.DefaultNamespace, "a-backup-20170912150214", v1.RestorePhaseCompleted).WithBackup("a-backup").Restore,
backup: defaultBackup().Name("a-backup").StorageLocation("a-location").Backup(),
restore: builder.ForRestore(v1.DefaultNamespace, "a-backup-20170912150214").Phase(v1.RestorePhaseCompleted).Backup("a-backup").Result(),
backup: defaultBackup(),
backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"),
expectGetsURL: true,
},
{
name: "restore results request with phase '' gets a url",
downloadRequest: newDownloadRequest("", v1.DownloadTargetKindRestoreResults, "a-backup-20170912150214"),
restore: velerotest.NewTestRestore(v1.DefaultNamespace, "a-backup-20170912150214", v1.RestorePhaseCompleted).WithBackup("a-backup").Restore,
backup: defaultBackup().Name("a-backup").StorageLocation("a-location").Backup(),
restore: builder.ForRestore(v1.DefaultNamespace, "a-backup-20170912150214").Phase(v1.RestorePhaseCompleted).Backup("a-backup").Result(),
backup: defaultBackup(),
backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"),
expectGetsURL: true,
},
{
name: "restore results request with phase 'New' gets a url",
downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseNew, v1.DownloadTargetKindRestoreResults, "a-backup-20170912150214"),
restore: velerotest.NewTestRestore(v1.DefaultNamespace, "a-backup-20170912150214", v1.RestorePhaseCompleted).WithBackup("a-backup").Restore,
backup: defaultBackup().Name("a-backup").StorageLocation("a-location").Backup(),
restore: builder.ForRestore(v1.DefaultNamespace, "a-backup-20170912150214").Phase(v1.RestorePhaseCompleted).Backup("a-backup").Result(),
backup: defaultBackup(),
backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"),
expectGetsURL: true,
},
{
name: "request with phase 'Processed' is not deleted if not expired",
downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseProcessed, v1.DownloadTargetKindBackupLog, "a-backup-20170912150214"),
backup: defaultBackup().Name("a-backup").StorageLocation("a-location").Backup(),
backup: defaultBackup(),
},
{
name: "request with phase 'Processed' is deleted if expired",
downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseProcessed, v1.DownloadTargetKindBackupLog, "a-backup-20170912150214"),
backup: defaultBackup().Name("a-backup").StorageLocation("a-location").Backup(),
backup: defaultBackup(),
expired: true,
},
}

View File

@ -33,6 +33,7 @@ import (
core "k8s.io/client-go/testing"
api "github.com/heptio/velero/pkg/apis/velero/v1"
"github.com/heptio/velero/pkg/builder"
"github.com/heptio/velero/pkg/generated/clientset/versioned/fake"
informers "github.com/heptio/velero/pkg/generated/informers/externalversions"
"github.com/heptio/velero/pkg/util/kube"
@ -66,7 +67,7 @@ func TestGCControllerEnqueueAllBackups(t *testing.T) {
var expected []string
for i := 0; i < 3; i++ {
backup := defaultBackup().Name(fmt.Sprintf("backup-%d", i)).Backup()
backup := builder.ForBackup(api.DefaultNamespace, fmt.Sprintf("backup-%d", i)).Result()
sharedInformers.Velero().V1().Backups().Informer().GetStore().Add(backup)
expected = append(expected, kube.NamespaceAndName(backup))
}
@ -97,7 +98,7 @@ Loop:
}
func TestGCControllerHasUpdateFunc(t *testing.T) {
backup := defaultBackup().Backup()
backup := defaultBackup().Result()
expected := kube.NamespaceAndName(backup)
client := fake.NewSimpleClientset(backup)
@ -151,7 +152,7 @@ func TestGCControllerHasUpdateFunc(t *testing.T) {
func TestGCControllerProcessQueueItem(t *testing.T) {
fakeClock := clock.NewFakeClock(time.Now())
defaultBackupLocation := velerotest.NewTestBackupStorageLocation().WithName("default").BackupStorageLocation
defaultBackupLocation := builder.ForBackupStorageLocation("velero", "default").Result()
tests := []struct {
name string
@ -167,31 +168,31 @@ func TestGCControllerProcessQueueItem(t *testing.T) {
},
{
name: "unexpired backup is not deleted",
backup: defaultBackup().Expiration(fakeClock.Now().Add(time.Minute)).StorageLocation("default").Backup(),
backup: defaultBackup().Expiration(fakeClock.Now().Add(time.Minute)).StorageLocation("default").Result(),
backupLocation: defaultBackupLocation,
expectDeletion: false,
},
{
name: "expired backup in read-only storage location is not deleted",
backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Minute)).StorageLocation("read-only").Backup(),
backupLocation: velerotest.NewTestBackupStorageLocation().WithName("read-only").WithAccessMode(api.BackupStorageLocationAccessModeReadOnly).BackupStorageLocation,
backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Minute)).StorageLocation("read-only").Result(),
backupLocation: builder.ForBackupStorageLocation("velero", "read-only").AccessMode(api.BackupStorageLocationAccessModeReadOnly).Result(),
expectDeletion: false,
},
{
name: "expired backup in read-write storage location is deleted",
backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Minute)).StorageLocation("read-write").Backup(),
backupLocation: velerotest.NewTestBackupStorageLocation().WithName("read-write").WithAccessMode(api.BackupStorageLocationAccessModeReadWrite).BackupStorageLocation,
backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Minute)).StorageLocation("read-write").Result(),
backupLocation: builder.ForBackupStorageLocation("velero", "read-write").AccessMode(api.BackupStorageLocationAccessModeReadWrite).Result(),
expectDeletion: true,
},
{
name: "expired backup with no pending deletion requests is deleted",
backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Second)).StorageLocation("default").Backup(),
backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Second)).StorageLocation("default").Result(),
backupLocation: defaultBackupLocation,
expectDeletion: true,
},
{
name: "expired backup with a pending deletion request is not deleted",
backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Second)).StorageLocation("default").Backup(),
backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Second)).StorageLocation("default").Result(),
backupLocation: defaultBackupLocation,
deleteBackupRequests: []*api.DeleteBackupRequest{
{
@ -212,7 +213,7 @@ func TestGCControllerProcessQueueItem(t *testing.T) {
},
{
name: "expired backup with only processed deletion requests is deleted",
backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Second)).StorageLocation("default").Backup(),
backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Second)).StorageLocation("default").Result(),
backupLocation: defaultBackupLocation,
deleteBackupRequests: []*api.DeleteBackupRequest{
{
@ -233,7 +234,7 @@ func TestGCControllerProcessQueueItem(t *testing.T) {
},
{
name: "create DeleteBackupRequest error returns an error",
backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Second)).StorageLocation("default").Backup(),
backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Second)).StorageLocation("default").Result(),
backupLocation: defaultBackupLocation,
expectDeletion: true,
createDeleteBackupRequestError: true,

View File

@ -36,7 +36,7 @@ import (
"k8s.io/client-go/tools/cache"
api "github.com/heptio/velero/pkg/apis/velero/v1"
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
"github.com/heptio/velero/pkg/builder"
"github.com/heptio/velero/pkg/generated/clientset/versioned/fake"
informers "github.com/heptio/velero/pkg/generated/informers/externalversions"
listers "github.com/heptio/velero/pkg/generated/listers/velero/v1"
@ -66,17 +66,17 @@ func TestFetchBackupInfo(t *testing.T) {
{
name: "lister has backup",
backupName: "backup-1",
informerLocations: []*api.BackupStorageLocation{velerotest.NewTestBackupStorageLocation().WithName("default").WithProvider("myCloud").WithObjectStorage("bucket").BackupStorageLocation},
informerBackups: []*api.Backup{defaultBackup().StorageLocation("default").Backup()},
expectedRes: defaultBackup().StorageLocation("default").Backup(),
informerLocations: []*api.BackupStorageLocation{builder.ForBackupStorageLocation("velero", "default").Provider("myCloud").ObjectStorage("bucket").Result()},
informerBackups: []*api.Backup{defaultBackup().StorageLocation("default").Result()},
expectedRes: defaultBackup().StorageLocation("default").Result(),
},
{
name: "lister does not have a backup, but backupSvc does",
backupName: "backup-1",
backupStoreBackup: defaultBackup().StorageLocation("default").Backup(),
informerLocations: []*api.BackupStorageLocation{velerotest.NewTestBackupStorageLocation().WithName("default").WithProvider("myCloud").WithObjectStorage("bucket").BackupStorageLocation},
informerBackups: []*api.Backup{defaultBackup().StorageLocation("default").Backup()},
expectedRes: defaultBackup().StorageLocation("default").Backup(),
backupStoreBackup: defaultBackup().StorageLocation("default").Result(),
informerLocations: []*api.BackupStorageLocation{builder.ForBackupStorageLocation("velero", "default").Provider("myCloud").ObjectStorage("bucket").Result()},
informerBackups: []*api.Backup{defaultBackup().StorageLocation("default").Result()},
expectedRes: defaultBackup().StorageLocation("default").Result(),
},
{
name: "no backup",
@ -173,17 +173,17 @@ func TestProcessQueueItemSkips(t *testing.T) {
{
name: "restore with phase InProgress does not get processed",
restoreKey: "foo/bar",
restore: velerotest.NewTestRestore("foo", "bar", api.RestorePhaseInProgress).Restore,
restore: builder.ForRestore("foo", "bar").Phase(api.RestorePhaseInProgress).Result(),
},
{
name: "restore with phase Completed does not get processed",
restoreKey: "foo/bar",
restore: velerotest.NewTestRestore("foo", "bar", api.RestorePhaseCompleted).Restore,
restore: builder.ForRestore("foo", "bar").Phase(api.RestorePhaseCompleted).Result(),
},
{
name: "restore with phase FailedValidation does not get processed",
restoreKey: "foo/bar",
restore: velerotest.NewTestRestore("foo", "bar", api.RestorePhaseFailedValidation).Restore,
restore: builder.ForRestore("foo", "bar").Phase(api.RestorePhaseFailedValidation).Result(),
},
}
@ -227,6 +227,8 @@ func TestProcessQueueItemSkips(t *testing.T) {
}
func TestProcessQueueItem(t *testing.T) {
defaultStorageLocation := builder.ForBackupStorageLocation("velero", "default").Provider("myCloud").ObjectStorage("bucket").Result()
tests := []struct {
name string
restoreKey string
@ -246,48 +248,48 @@ func TestProcessQueueItem(t *testing.T) {
}{
{
name: "restore with both namespace in both includedNamespaces and excludedNamespaces fails validation",
location: velerotest.NewTestBackupStorageLocation().WithName("default").WithProvider("myCloud").WithObjectStorage("bucket").BackupStorageLocation,
restore: NewRestore("foo", "bar", "backup-1", "another-1", "*", api.RestorePhaseNew).WithExcludedNamespace("another-1").Restore,
backup: defaultBackup().StorageLocation("default").Backup(),
location: defaultStorageLocation,
restore: NewRestore("foo", "bar", "backup-1", "another-1", "*", api.RestorePhaseNew).ExcludedNamespaces("another-1").Result(),
backup: defaultBackup().StorageLocation("default").Result(),
expectedErr: false,
expectedPhase: string(api.RestorePhaseFailedValidation),
expectedValidationErrors: []string{"Invalid included/excluded namespace lists: excludes list cannot contain an item in the includes list: another-1"},
},
{
name: "restore with resource in both includedResources and excludedResources fails validation",
location: velerotest.NewTestBackupStorageLocation().WithName("default").WithProvider("myCloud").WithObjectStorage("bucket").BackupStorageLocation,
restore: NewRestore("foo", "bar", "backup-1", "*", "a-resource", api.RestorePhaseNew).WithExcludedResource("a-resource").Restore,
backup: defaultBackup().StorageLocation("default").Backup(),
location: defaultStorageLocation,
restore: NewRestore("foo", "bar", "backup-1", "*", "a-resource", api.RestorePhaseNew).ExcludedResources("a-resource").Result(),
backup: defaultBackup().StorageLocation("default").Result(),
expectedErr: false,
expectedPhase: string(api.RestorePhaseFailedValidation),
expectedValidationErrors: []string{"Invalid included/excluded resource lists: excludes list cannot contain an item in the includes list: a-resource"},
},
{
name: "new restore with empty backup and schedule names fails validation",
restore: NewRestore("foo", "bar", "", "ns-1", "", api.RestorePhaseNew).Restore,
restore: NewRestore("foo", "bar", "", "ns-1", "", api.RestorePhaseNew).Result(),
expectedErr: false,
expectedPhase: string(api.RestorePhaseFailedValidation),
expectedValidationErrors: []string{"Either a backup or schedule must be specified as a source for the restore, but not both"},
},
{
name: "new restore with backup and schedule names provided fails validation",
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseNew).WithSchedule("sched-1").Restore,
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseNew).Schedule("sched-1").Result(),
expectedErr: false,
expectedPhase: string(api.RestorePhaseFailedValidation),
expectedValidationErrors: []string{"Either a backup or schedule must be specified as a source for the restore, but not both"},
},
{
name: "valid restore with schedule name gets executed",
location: velerotest.NewTestBackupStorageLocation().WithName("default").WithProvider("myCloud").WithObjectStorage("bucket").BackupStorageLocation,
restore: NewRestore("foo", "bar", "", "ns-1", "", api.RestorePhaseNew).WithSchedule("sched-1").Restore,
backup: defaultBackup().StorageLocation("default").Labels(velerov1api.ScheduleNameLabel, "sched-1").Phase(api.BackupPhaseCompleted).Backup(),
location: defaultStorageLocation,
restore: NewRestore("foo", "bar", "", "ns-1", "", api.RestorePhaseNew).Schedule("sched-1").Result(),
backup: defaultBackup().StorageLocation("default").ObjectMeta(builder.WithLabels(api.ScheduleNameLabel, "sched-1")).Phase(api.BackupPhaseCompleted).Result(),
expectedErr: false,
expectedPhase: string(api.RestorePhaseInProgress),
expectedRestorerCall: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseInProgress).WithSchedule("sched-1").Restore,
expectedRestorerCall: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseInProgress).Schedule("sched-1").Result(),
},
{
name: "restore with non-existent backup name fails",
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "*", api.RestorePhaseNew).Restore,
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "*", api.RestorePhaseNew).Result(),
expectedErr: false,
expectedPhase: string(api.RestorePhaseFailedValidation),
expectedValidationErrors: []string{"Error retrieving backup: backup.velero.io \"backup-1\" not found"},
@ -295,30 +297,30 @@ func TestProcessQueueItem(t *testing.T) {
},
{
name: "restorer throwing an error causes the restore to fail",
location: velerotest.NewTestBackupStorageLocation().WithName("default").WithProvider("myCloud").WithObjectStorage("bucket").BackupStorageLocation,
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseNew).Restore,
backup: defaultBackup().StorageLocation("default").Backup(),
location: defaultStorageLocation,
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseNew).Result(),
backup: defaultBackup().StorageLocation("default").Result(),
restorerError: errors.New("blarg"),
expectedErr: false,
expectedPhase: string(api.RestorePhaseInProgress),
expectedFinalPhase: string(api.RestorePhasePartiallyFailed),
expectedRestoreErrors: 1,
expectedRestorerCall: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseInProgress).Restore,
expectedRestorerCall: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseInProgress).Result(),
},
{
name: "valid restore gets executed",
location: velerotest.NewTestBackupStorageLocation().WithName("default").WithProvider("myCloud").WithObjectStorage("bucket").BackupStorageLocation,
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseNew).Restore,
backup: defaultBackup().StorageLocation("default").Backup(),
location: defaultStorageLocation,
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseNew).Result(),
backup: defaultBackup().StorageLocation("default").Result(),
expectedErr: false,
expectedPhase: string(api.RestorePhaseInProgress),
expectedRestorerCall: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseInProgress).Restore,
expectedRestorerCall: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseInProgress).Result(),
},
{
name: "restoration of nodes is not supported",
location: velerotest.NewTestBackupStorageLocation().WithName("default").WithProvider("myCloud").WithObjectStorage("bucket").BackupStorageLocation,
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "nodes", api.RestorePhaseNew).Restore,
backup: defaultBackup().StorageLocation("default").Backup(),
location: defaultStorageLocation,
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "nodes", api.RestorePhaseNew).Result(),
backup: defaultBackup().StorageLocation("default").Result(),
expectedErr: false,
expectedPhase: string(api.RestorePhaseFailedValidation),
expectedValidationErrors: []string{
@ -328,9 +330,9 @@ func TestProcessQueueItem(t *testing.T) {
},
{
name: "restoration of events is not supported",
location: velerotest.NewTestBackupStorageLocation().WithName("default").WithProvider("myCloud").WithObjectStorage("bucket").BackupStorageLocation,
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "events", api.RestorePhaseNew).Restore,
backup: defaultBackup().StorageLocation("default").Backup(),
location: defaultStorageLocation,
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "events", api.RestorePhaseNew).Result(),
backup: defaultBackup().StorageLocation("default").Result(),
expectedErr: false,
expectedPhase: string(api.RestorePhaseFailedValidation),
expectedValidationErrors: []string{
@ -340,9 +342,9 @@ func TestProcessQueueItem(t *testing.T) {
},
{
name: "restoration of events.events.k8s.io is not supported",
location: velerotest.NewTestBackupStorageLocation().WithName("default").WithProvider("myCloud").WithObjectStorage("bucket").BackupStorageLocation,
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "events.events.k8s.io", api.RestorePhaseNew).Restore,
backup: defaultBackup().StorageLocation("default").Backup(),
location: defaultStorageLocation,
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "events.events.k8s.io", api.RestorePhaseNew).Result(),
backup: defaultBackup().StorageLocation("default").Result(),
expectedErr: false,
expectedPhase: string(api.RestorePhaseFailedValidation),
expectedValidationErrors: []string{
@ -352,9 +354,9 @@ func TestProcessQueueItem(t *testing.T) {
},
{
name: "restoration of backups.velero.io is not supported",
location: velerotest.NewTestBackupStorageLocation().WithName("default").WithProvider("myCloud").WithObjectStorage("bucket").BackupStorageLocation,
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "backups.velero.io", api.RestorePhaseNew).Restore,
backup: defaultBackup().StorageLocation("default").Backup(),
location: defaultStorageLocation,
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "backups.velero.io", api.RestorePhaseNew).Result(),
backup: defaultBackup().StorageLocation("default").Result(),
expectedErr: false,
expectedPhase: string(api.RestorePhaseFailedValidation),
expectedValidationErrors: []string{
@ -364,9 +366,9 @@ func TestProcessQueueItem(t *testing.T) {
},
{
name: "restoration of restores.velero.io is not supported",
location: velerotest.NewTestBackupStorageLocation().WithName("default").WithProvider("myCloud").WithObjectStorage("bucket").BackupStorageLocation,
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "restores.velero.io", api.RestorePhaseNew).Restore,
backup: defaultBackup().StorageLocation("default").Backup(),
location: defaultStorageLocation,
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "restores.velero.io", api.RestorePhaseNew).Result(),
backup: defaultBackup().StorageLocation("default").Result(),
expectedErr: false,
expectedPhase: string(api.RestorePhaseFailedValidation),
expectedValidationErrors: []string{
@ -376,12 +378,12 @@ func TestProcessQueueItem(t *testing.T) {
},
{
name: "backup download error results in failed restore",
location: velerotest.NewTestBackupStorageLocation().WithName("default").WithProvider("myCloud").WithObjectStorage("bucket").BackupStorageLocation,
restore: NewRestore(api.DefaultNamespace, "bar", "backup-1", "ns-1", "", api.RestorePhaseNew).Restore,
location: defaultStorageLocation,
restore: NewRestore(api.DefaultNamespace, "bar", "backup-1", "ns-1", "", api.RestorePhaseNew).Result(),
expectedPhase: string(api.RestorePhaseInProgress),
expectedFinalPhase: string(api.RestorePhaseFailed),
backupStoreGetBackupContentsErr: errors.New("Couldn't download backup"),
backup: defaultBackup().StorageLocation("default").Backup(),
backup: defaultBackup().StorageLocation("default").Result(),
},
}
@ -656,9 +658,9 @@ func TestvalidateAndCompleteWhenScheduleNameSpecified(t *testing.T) {
// no backups created from the schedule: fail validation
require.NoError(t, sharedInformers.Velero().V1().Backups().Informer().GetStore().Add(
defaultBackup().
Labels(velerov1api.ScheduleNameLabel, "non-matching-schedule").
Phase(velerov1api.BackupPhaseCompleted).
Backup(),
ObjectMeta(builder.WithLabels(api.ScheduleNameLabel, "non-matching-schedule")).
Phase(api.BackupPhaseCompleted).
Result(),
))
errs := c.validateAndComplete(restore, pluginManager)
@ -668,10 +670,12 @@ func TestvalidateAndCompleteWhenScheduleNameSpecified(t *testing.T) {
// no completed backups created from the schedule: fail validation
require.NoError(t, sharedInformers.Velero().V1().Backups().Informer().GetStore().Add(
defaultBackup().
Name("backup-2").
Labels(velerov1api.ScheduleNameLabel, "schedule-1").
Phase(velerov1api.BackupPhaseInProgress).
Backup(),
ObjectMeta(
builder.WithName("backup-2"),
builder.WithLabels(api.ScheduleNameLabel, "schedule-1"),
).
Phase(api.BackupPhaseInProgress).
Result(),
))
errs = c.validateAndComplete(restore, pluginManager)
@ -683,19 +687,23 @@ func TestvalidateAndCompleteWhenScheduleNameSpecified(t *testing.T) {
require.NoError(t, sharedInformers.Velero().V1().Backups().Informer().GetStore().Add(
defaultBackup().
Name("foo").
Labels(velerov1api.ScheduleNameLabel, "schedule-1").
Phase(velerov1api.BackupPhaseCompleted).
ObjectMeta(
builder.WithName("foo"),
builder.WithLabels(api.ScheduleNameLabel, "schedule-1"),
).
Phase(api.BackupPhaseCompleted).
StartTimestamp(now).
Backup(),
Result(),
))
require.NoError(t, sharedInformers.Velero().V1().Backups().Informer().GetStore().Add(
defaultBackup().
Name("foo").
Labels(velerov1api.ScheduleNameLabel, "schedule-1").
Phase(velerov1api.BackupPhaseCompleted).
ObjectMeta(
builder.WithName("foo"),
builder.WithLabels(api.ScheduleNameLabel, "schedule-1"),
).
Phase(api.BackupPhaseCompleted).
StartTimestamp(now.Add(time.Second)).
Backup(),
Result(),
))
errs = c.validateAndComplete(restore, pluginManager)
@ -792,20 +800,18 @@ func TestMostRecentCompletedBackup(t *testing.T) {
assert.Equal(t, expected, mostRecentCompletedBackup(backups))
}
func NewRestore(ns, name, backup, includeNS, includeResource string, phase api.RestorePhase) *velerotest.TestRestore {
restore := velerotest.NewTestRestore(ns, name, phase).WithBackup(backup)
func NewRestore(ns, name, backup, includeNS, includeResource string, phase api.RestorePhase) *builder.RestoreBuilder {
restore := builder.ForRestore(ns, name).Phase(phase).Backup(backup)
if includeNS != "" {
restore = restore.WithIncludedNamespace(includeNS)
restore = restore.IncludedNamespaces(includeNS)
}
if includeResource != "" {
restore = restore.WithIncludedResource(includeResource)
restore = restore.IncludedResources(includeResource)
}
for _, n := range nonRestorableResources {
restore = restore.WithExcludedResource(n)
}
restore.ExcludedResources(nonRestorableResources...)
return restore
}

View File

@ -31,8 +31,8 @@ import (
core "k8s.io/client-go/testing"
"k8s.io/client-go/tools/cache"
api "github.com/heptio/velero/pkg/apis/velero/v1"
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
"github.com/heptio/velero/pkg/builder"
"github.com/heptio/velero/pkg/generated/clientset/versioned/fake"
informers "github.com/heptio/velero/pkg/generated/informers/externalversions"
"github.com/heptio/velero/pkg/metrics"
@ -40,15 +40,19 @@ import (
)
func TestProcessSchedule(t *testing.T) {
newScheduleBuilder := func(phase velerov1api.SchedulePhase) *builder.ScheduleBuilder {
return builder.ForSchedule("ns", "name").Phase(phase)
}
tests := []struct {
name string
scheduleKey string
schedule *api.Schedule
schedule *velerov1api.Schedule
fakeClockTime string
expectedErr bool
expectedPhase string
expectedValidationErrors []string
expectedBackupCreate *api.Backup
expectedBackupCreate *velerov1api.Backup
expectedLastBackup string
}{
{
@ -63,54 +67,53 @@ func TestProcessSchedule(t *testing.T) {
},
{
name: "schedule with phase FailedValidation does not get processed",
schedule: velerotest.NewTestSchedule("ns", "name").WithPhase(api.SchedulePhaseFailedValidation).Schedule,
schedule: newScheduleBuilder(velerov1api.SchedulePhaseFailedValidation).Result(),
expectedErr: false,
},
{
name: "schedule with phase New gets validated and failed if invalid",
schedule: velerotest.NewTestSchedule("ns", "name").WithPhase(api.SchedulePhaseNew).Schedule,
schedule: newScheduleBuilder(velerov1api.SchedulePhaseNew).Result(),
expectedErr: false,
expectedPhase: string(api.SchedulePhaseFailedValidation),
expectedPhase: string(velerov1api.SchedulePhaseFailedValidation),
expectedValidationErrors: []string{"Schedule must be a non-empty valid Cron expression"},
},
{
name: "schedule with phase <blank> gets validated and failed if invalid",
schedule: velerotest.NewTestSchedule("ns", "name").Schedule,
schedule: newScheduleBuilder(velerov1api.SchedulePhase("")).Result(),
expectedErr: false,
expectedPhase: string(api.SchedulePhaseFailedValidation),
expectedPhase: string(velerov1api.SchedulePhaseFailedValidation),
expectedValidationErrors: []string{"Schedule must be a non-empty valid Cron expression"},
},
{
name: "schedule with phase Enabled gets re-validated and failed if invalid",
schedule: velerotest.NewTestSchedule("ns", "name").WithPhase(api.SchedulePhaseEnabled).Schedule,
schedule: newScheduleBuilder(velerov1api.SchedulePhaseEnabled).Result(),
expectedErr: false,
expectedPhase: string(api.SchedulePhaseFailedValidation),
expectedPhase: string(velerov1api.SchedulePhaseFailedValidation),
expectedValidationErrors: []string{"Schedule must be a non-empty valid Cron expression"},
},
{
name: "schedule with phase New gets validated and triggers a backup",
schedule: velerotest.NewTestSchedule("ns", "name").WithPhase(api.SchedulePhaseNew).WithCronSchedule("@every 5m").Schedule,
schedule: newScheduleBuilder(velerov1api.SchedulePhaseNew).CronSchedule("@every 5m").Result(),
fakeClockTime: "2017-01-01 12:00:00",
expectedErr: false,
expectedPhase: string(api.SchedulePhaseEnabled),
expectedBackupCreate: defaultBackup().Namespace("ns").Name("name-20170101120000").Labels(velerov1api.ScheduleNameLabel, "name").NoTypeMeta().Backup(),
expectedPhase: string(velerov1api.SchedulePhaseEnabled),
expectedBackupCreate: builder.ForBackup("ns", "name-20170101120000").ObjectMeta(builder.WithLabels(velerov1api.ScheduleNameLabel, "name")).NoTypeMeta().Result(),
expectedLastBackup: "2017-01-01 12:00:00",
},
{
name: "schedule with phase Enabled gets re-validated and triggers a backup if valid",
schedule: velerotest.NewTestSchedule("ns", "name").WithPhase(api.SchedulePhaseEnabled).WithCronSchedule("@every 5m").Schedule,
schedule: newScheduleBuilder(velerov1api.SchedulePhaseEnabled).CronSchedule("@every 5m").Result(),
fakeClockTime: "2017-01-01 12:00:00",
expectedErr: false,
expectedBackupCreate: defaultBackup().Namespace("ns").Name("name-20170101120000").Labels(velerov1api.ScheduleNameLabel, "name").NoTypeMeta().Backup(),
expectedBackupCreate: builder.ForBackup("ns", "name-20170101120000").ObjectMeta(builder.WithLabels(velerov1api.ScheduleNameLabel, "name")).NoTypeMeta().Result(),
expectedLastBackup: "2017-01-01 12:00:00",
},
{
name: "schedule that's already run gets LastBackup updated",
schedule: velerotest.NewTestSchedule("ns", "name").WithPhase(api.SchedulePhaseEnabled).
WithCronSchedule("@every 5m").WithLastBackupTime("2000-01-01 00:00:00").Schedule,
name: "schedule that's already run gets LastBackup updated",
schedule: newScheduleBuilder(velerov1api.SchedulePhaseEnabled).CronSchedule("@every 5m").LastBackupTime("2000-01-01 00:00:00").Result(),
fakeClockTime: "2017-01-01 12:00:00",
expectedErr: false,
expectedBackupCreate: defaultBackup().Namespace("ns").Name("name-20170101120000").Labels(velerov1api.ScheduleNameLabel, "name").NoTypeMeta().Backup(),
expectedBackupCreate: builder.ForBackup("ns", "name-20170101120000").ObjectMeta(builder.WithLabels(velerov1api.ScheduleNameLabel, "name")).NoTypeMeta().Result(),
expectedLastBackup: "2017-01-01 12:00:00",
},
}
@ -161,7 +164,7 @@ func TestProcessSchedule(t *testing.T) {
// these are the fields that may be updated by the controller
phase, found, err := unstructured.NestedString(patchMap, "status", "phase")
if err == nil && found {
res.Status.Phase = api.SchedulePhase(phase)
res.Status.Phase = velerov1api.SchedulePhase(phase)
}
lastBackupStr, found, err := unstructured.NestedString(patchMap, "status", "lastBackup")
@ -192,9 +195,9 @@ func TestProcessSchedule(t *testing.T) {
index := 0
type PatchStatus struct {
ValidationErrors []string `json:"validationErrors"`
Phase api.SchedulePhase `json:"phase"`
LastBackup time.Time `json:"lastBackup"`
ValidationErrors []string `json:"validationErrors"`
Phase velerov1api.SchedulePhase `json:"phase"`
LastBackup time.Time `json:"lastBackup"`
}
type Patch struct {
@ -214,7 +217,7 @@ func TestProcessSchedule(t *testing.T) {
expected := Patch{
Status: PatchStatus{
ValidationErrors: test.expectedValidationErrors,
Phase: api.SchedulePhase(test.expectedPhase),
Phase: velerov1api.SchedulePhase(test.expectedPhase),
},
}
@ -227,7 +230,7 @@ func TestProcessSchedule(t *testing.T) {
require.True(t, len(actions) > index, "len(actions) is too small")
action := core.NewCreateAction(
api.SchemeGroupVersion.WithResource("backups"),
velerov1api.SchemeGroupVersion.WithResource("backups"),
created.Namespace,
created)
@ -257,43 +260,47 @@ func parseTime(timeString string) time.Time {
}
func TestGetNextRunTime(t *testing.T) {
defaultSchedule := func() *velerov1api.Schedule {
return builder.ForSchedule("velero", "schedule-1").CronSchedule("@every 5m").Result()
}
tests := []struct {
name string
schedule *api.Schedule
schedule *velerov1api.Schedule
lastRanOffset string
expectedDue bool
expectedNextRunTimeOffset string
}{
{
name: "first run",
schedule: &api.Schedule{Spec: api.ScheduleSpec{Schedule: "@every 5m"}},
schedule: defaultSchedule(),
expectedDue: true,
expectedNextRunTimeOffset: "5m",
},
{
name: "just ran",
schedule: &api.Schedule{Spec: api.ScheduleSpec{Schedule: "@every 5m"}},
schedule: defaultSchedule(),
lastRanOffset: "0s",
expectedDue: false,
expectedNextRunTimeOffset: "5m",
},
{
name: "almost but not quite time to run",
schedule: &api.Schedule{Spec: api.ScheduleSpec{Schedule: "@every 5m"}},
schedule: defaultSchedule(),
lastRanOffset: "4m59s",
expectedDue: false,
expectedNextRunTimeOffset: "5m",
},
{
name: "time to run again",
schedule: &api.Schedule{Spec: api.ScheduleSpec{Schedule: "@every 5m"}},
schedule: defaultSchedule(),
lastRanOffset: "5m",
expectedDue: true,
expectedNextRunTimeOffset: "5m",
},
{
name: "several runs missed",
schedule: &api.Schedule{Spec: api.ScheduleSpec{Schedule: "@every 5m"}},
schedule: defaultSchedule(),
lastRanOffset: "5h",
expectedDue: true,
expectedNextRunTimeOffset: "5m",
@ -339,14 +346,7 @@ func TestParseCronSchedule(t *testing.T) {
// Start with a Schedule with:
// - schedule: once a day at 9am
// - last backup: 2017-08-10 12:27:00 (just happened)
s := &api.Schedule{
Spec: api.ScheduleSpec{
Schedule: "0 9 * * *",
},
Status: api.ScheduleStatus{
LastBackup: metav1.NewTime(now),
},
}
s := builder.ForSchedule("velero", "schedule-1").CronSchedule("0 9 * * *").LastBackupTime(now.Format("2006-01-02 15:04:05")).Result()
logger := velerotest.NewLogger()
@ -383,121 +383,51 @@ func TestParseCronSchedule(t *testing.T) {
func TestGetBackup(t *testing.T) {
tests := []struct {
name string
schedule *api.Schedule
schedule *velerov1api.Schedule
testClockTime string
expectedBackup *api.Backup
expectedBackup *velerov1api.Backup
}{
{
name: "ensure name is formatted correctly (AM time)",
schedule: &api.Schedule{
ObjectMeta: metav1.ObjectMeta{
Namespace: "foo",
Name: "bar",
},
Spec: api.ScheduleSpec{
Template: api.BackupSpec{},
},
},
testClockTime: "2017-07-25 09:15:00",
expectedBackup: &api.Backup{
ObjectMeta: metav1.ObjectMeta{
Namespace: "foo",
Name: "bar-20170725091500",
Labels: map[string]string{
velerov1api.ScheduleNameLabel: "bar",
},
},
Spec: api.BackupSpec{},
},
name: "ensure name is formatted correctly (AM time)",
schedule: builder.ForSchedule("foo", "bar").Result(),
testClockTime: "2017-07-25 09:15:00",
expectedBackup: builder.ForBackup("foo", "bar-20170725091500").ObjectMeta(builder.WithLabels(velerov1api.ScheduleNameLabel, "bar")).Result(),
},
{
name: "ensure name is formatted correctly (PM time)",
schedule: &api.Schedule{
ObjectMeta: metav1.ObjectMeta{
Namespace: "foo",
Name: "bar",
},
Spec: api.ScheduleSpec{
Template: api.BackupSpec{},
},
},
testClockTime: "2017-07-25 14:15:00",
expectedBackup: &api.Backup{
ObjectMeta: metav1.ObjectMeta{
Namespace: "foo",
Name: "bar-20170725141500",
Labels: map[string]string{
velerov1api.ScheduleNameLabel: "bar",
},
},
Spec: api.BackupSpec{},
},
name: "ensure name is formatted correctly (PM time)",
schedule: builder.ForSchedule("foo", "bar").Result(),
testClockTime: "2017-07-25 14:15:00",
expectedBackup: builder.ForBackup("foo", "bar-20170725141500").ObjectMeta(builder.WithLabels(velerov1api.ScheduleNameLabel, "bar")).Result(),
},
{
name: "ensure schedule backup template is copied",
schedule: &api.Schedule{
ObjectMeta: metav1.ObjectMeta{
Namespace: "foo",
Name: "bar",
},
Spec: api.ScheduleSpec{
Template: api.BackupSpec{
IncludedNamespaces: []string{"ns-1", "ns-2"},
ExcludedNamespaces: []string{"ns-3"},
IncludedResources: []string{"foo", "bar"},
ExcludedResources: []string{"baz"},
LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"label": "value"}},
TTL: metav1.Duration{Duration: time.Duration(300)},
},
},
},
schedule: builder.ForSchedule("foo", "bar").
Template(builder.ForBackup("", "").
IncludedNamespaces("ns-1", "ns-2").
ExcludedNamespaces("ns-3").
IncludedResources("foo", "bar").
ExcludedResources("baz").
LabelSelector(&metav1.LabelSelector{MatchLabels: map[string]string{"label": "value"}}).
TTL(time.Duration(300)).
Result().
Spec).
Result(),
testClockTime: "2017-07-25 09:15:00",
expectedBackup: &api.Backup{
ObjectMeta: metav1.ObjectMeta{
Namespace: "foo",
Name: "bar-20170725091500",
Labels: map[string]string{
velerov1api.ScheduleNameLabel: "bar",
},
},
Spec: api.BackupSpec{
IncludedNamespaces: []string{"ns-1", "ns-2"},
ExcludedNamespaces: []string{"ns-3"},
IncludedResources: []string{"foo", "bar"},
ExcludedResources: []string{"baz"},
LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"label": "value"}},
TTL: metav1.Duration{Duration: time.Duration(300)},
},
},
expectedBackup: builder.ForBackup("foo", "bar-20170725091500").
ObjectMeta(builder.WithLabels(velerov1api.ScheduleNameLabel, "bar")).
IncludedNamespaces("ns-1", "ns-2").
ExcludedNamespaces("ns-3").
IncludedResources("foo", "bar").
ExcludedResources("baz").
LabelSelector(&metav1.LabelSelector{MatchLabels: map[string]string{"label": "value"}}).
TTL(time.Duration(300)).
Result(),
},
{
name: "ensure schedule labels is copied",
schedule: &api.Schedule{
ObjectMeta: metav1.ObjectMeta{
Namespace: "foo",
Name: "bar",
Labels: map[string]string{
"foo": "bar",
"bar": "baz",
},
},
Spec: api.ScheduleSpec{
Template: api.BackupSpec{},
},
},
testClockTime: "2017-07-25 14:15:00",
expectedBackup: &api.Backup{
ObjectMeta: metav1.ObjectMeta{
Namespace: "foo",
Name: "bar-20170725141500",
Labels: map[string]string{
velerov1api.ScheduleNameLabel: "bar",
"bar": "baz",
"foo": "bar",
},
},
Spec: api.BackupSpec{},
},
name: "ensure schedule labels is copied",
schedule: builder.ForSchedule("foo", "bar").ObjectMeta(builder.WithLabels("foo", "bar", "bar", "baz")).Result(),
testClockTime: "2017-07-25 14:15:00",
expectedBackup: builder.ForBackup("foo", "bar-20170725141500").ObjectMeta(builder.WithLabels(velerov1api.ScheduleNameLabel, "bar", "bar", "baz", "foo", "bar")).Result(),
},
}

View File

@ -1,114 +0,0 @@
/*
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
}
// IncludedNamespaces sets the Restore's included namespaces.
func (b *Builder) IncludedNamespaces(namespaces ...string) *Builder {
b.restore.Spec.IncludedNamespaces = namespaces
return b
}
// ExcludedNamespaces sets the Restore's excluded namespaces.
func (b *Builder) ExcludedNamespaces(namespaces ...string) *Builder {
b.restore.Spec.ExcludedNamespaces = namespaces
return b
}
// IncludedResources sets the Restore's included resources.
func (b *Builder) IncludedResources(resources ...string) *Builder {
b.restore.Spec.IncludedResources = resources
return b
}
// ExcludedResources sets the Restore's excluded resources.
func (b *Builder) ExcludedResources(resources ...string) *Builder {
b.restore.Spec.ExcludedResources = resources
return b
}
// IncludeClusterResources sets the Restore's "include cluster resources" flag.
func (b *Builder) IncludeClusterResources(val bool) *Builder {
b.restore.Spec.IncludeClusterResources = &val
return b
}
// LabelSelector sets the Restore's label selector.
func (b *Builder) LabelSelector(selector *metav1.LabelSelector) *Builder {
b.restore.Spec.LabelSelector = selector
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
}

View File

@ -29,8 +29,8 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/fake"
"github.com/heptio/velero/pkg/builder"
"github.com/heptio/velero/pkg/plugin/velero"
"github.com/heptio/velero/pkg/test"
)
// TestChangeStorageClassActionExecute runs the ChangeStorageClassAction's Execute
@ -48,93 +48,93 @@ func TestChangeStorageClassActionExecute(t *testing.T) {
}{
{
name: "a valid mapping for a persistent volume is applied correctly",
pvOrPVC: test.NewPV("pv-1", test.WithStorageClassName("storageclass-1")),
configMap: test.NewConfigMap("velero", "change-storage-class",
test.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction"),
test.WithConfigMapData("storageclass-1", "storageclass-2"),
),
storageClass: test.NewStorageClass("storageclass-2"),
want: test.NewPV("pv-1", test.WithStorageClassName("storageclass-2")),
pvOrPVC: builder.ForPersistentVolume("pv-1").StorageClass("storageclass-1").Result(),
configMap: builder.ForConfigMap("velero", "change-storage-classs").
ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")).
Data("storageclass-1", "storageclass-2").
Result(),
storageClass: builder.ForStorageClass("storageclass-2").Result(),
want: builder.ForPersistentVolume("pv-1").StorageClass("storageclass-2").Result(),
},
{
name: "a valid mapping for a persistent volume claim is applied correctly",
pvOrPVC: test.NewPVC("velero", "pvc-1", test.WithStorageClassName("storageclass-1")),
configMap: test.NewConfigMap("velero", "change-storage-class",
test.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction"),
test.WithConfigMapData("storageclass-1", "storageclass-2"),
),
storageClass: test.NewStorageClass("storageclass-2"),
want: test.NewPVC("velero", "pvc-1", test.WithStorageClassName("storageclass-2")),
pvOrPVC: builder.ForPersistentVolumeClaim("velero", "pvc-1").StorageClass("storageclass-1").Result(),
configMap: builder.ForConfigMap("velero", "change-storage-classs").
ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")).
Data("storageclass-1", "storageclass-2").
Result(),
storageClass: builder.ForStorageClass("storageclass-2").Result(),
want: builder.ForPersistentVolumeClaim("velero", "pvc-1").StorageClass("storageclass-2").Result(),
},
{
name: "when no config map exists for the plugin, the item is returned as-is",
pvOrPVC: test.NewPV("pv-1", test.WithStorageClassName("storageclass-1")),
configMap: test.NewConfigMap("velero", "change-storage-class",
test.WithLabels("velero.io/plugin-config", "true", "velero.io/some-other-plugin", "RestoreItemAction"),
test.WithConfigMapData("storageclass-1", "storageclass-2"),
),
want: test.NewPV("pv-1", test.WithStorageClassName("storageclass-1")),
pvOrPVC: builder.ForPersistentVolume("pv-1").StorageClass("storageclass-1").Result(),
configMap: builder.ForConfigMap("velero", "change-storage-classs").
ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/some-other-plugin", "RestoreItemAction")).
Data("storageclass-1", "storageclass-2").
Result(),
want: builder.ForPersistentVolume("pv-1").StorageClass("storageclass-1").Result(),
},
{
name: "when no storage class mappings exist in the plugin config map, the item is returned as-is",
pvOrPVC: test.NewPV("pv-1", test.WithStorageClassName("storageclass-1")),
configMap: test.NewConfigMap("velero", "change-storage-class",
test.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction"),
),
want: test.NewPV("pv-1", test.WithStorageClassName("storageclass-1")),
pvOrPVC: builder.ForPersistentVolume("pv-1").StorageClass("storageclass-1").Result(),
configMap: builder.ForConfigMap("velero", "change-storage-classs").
ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")).
Result(),
want: builder.ForPersistentVolume("pv-1").StorageClass("storageclass-1").Result(),
},
{
name: "when persistent volume has no storage class, the item is returned as-is",
pvOrPVC: test.NewPV("pv-1"),
configMap: test.NewConfigMap("velero", "change-storage-class",
test.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction"),
test.WithConfigMapData("storageclass-1", "storageclass-2"),
),
want: test.NewPV("pv-1"),
pvOrPVC: builder.ForPersistentVolume("pv-1").Result(),
configMap: builder.ForConfigMap("velero", "change-storage-classs").
ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")).
Data("storageclass-1", "storageclass-2").
Result(),
want: builder.ForPersistentVolume("pv-1").Result(),
},
{
name: "when persistent volume claim has no storage class, the item is returned as-is",
pvOrPVC: test.NewPVC("velero", "pvc-1"),
configMap: test.NewConfigMap("velero", "change-storage-class",
test.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction"),
test.WithConfigMapData("storageclass-1", "storageclass-2"),
),
want: test.NewPVC("velero", "pvc-1"),
pvOrPVC: builder.ForPersistentVolumeClaim("velero", "pvc-1").Result(),
configMap: builder.ForConfigMap("velero", "change-storage-classs").
ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")).
Data("storageclass-1", "storageclass-2").
Result(),
want: builder.ForPersistentVolumeClaim("velero", "pvc-1").Result(),
},
{
name: "when persistent volume's storage class has no mapping in the config map, the item is returned as-is",
pvOrPVC: test.NewPV("pv-1", test.WithStorageClassName("storageclass-1")),
configMap: test.NewConfigMap("velero", "change-storage-class",
test.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction"),
test.WithConfigMapData("storageclass-3", "storageclass-4"),
),
want: test.NewPV("pv-1", test.WithStorageClassName("storageclass-1")),
pvOrPVC: builder.ForPersistentVolume("pv-1").StorageClass("storageclass-1").Result(),
configMap: builder.ForConfigMap("velero", "change-storage-classs").
ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")).
Data("storageclass-3", "storageclass-4").
Result(),
want: builder.ForPersistentVolume("pv-1").StorageClass("storageclass-1").Result(),
},
{
name: "when persistent volume claim's storage class has no mapping in the config map, the item is returned as-is",
pvOrPVC: test.NewPVC("velero", "pvc-1", test.WithStorageClassName("storageclass-1")),
configMap: test.NewConfigMap("velero", "change-storage-class",
test.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction"),
test.WithConfigMapData("storageclass-3", "storageclass-4"),
),
want: test.NewPVC("velero", "pvc-1", test.WithStorageClassName("storageclass-1")),
pvOrPVC: builder.ForPersistentVolumeClaim("velero", "pvc-1").StorageClass("storageclass-1").Result(),
configMap: builder.ForConfigMap("velero", "change-storage-classs").
ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")).
Data("storageclass-3", "storageclass-4").
Result(),
want: builder.ForPersistentVolumeClaim("velero", "pvc-1").StorageClass("storageclass-1").Result(),
},
{
name: "when persistent volume's storage class is mapped to a nonexistent storage class, an error is returned",
pvOrPVC: test.NewPV("pv-1", test.WithStorageClassName("storageclass-1")),
configMap: test.NewConfigMap("velero", "change-storage-class",
test.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction"),
test.WithConfigMapData("storageclass-1", "nonexistent-storage-class"),
),
pvOrPVC: builder.ForPersistentVolume("pv-1").StorageClass("storageclass-1").Result(),
configMap: builder.ForConfigMap("velero", "change-storage-classs").
ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")).
Data("storageclass-1", "nonexistent-storage-class").
Result(),
wantErr: errors.New("error getting storage class nonexistent-storage-class from API: storageclasses.storage.k8s.io \"nonexistent-storage-class\" not found"),
},
{
name: "when persistent volume claim's storage class is mapped to a nonexistent storage class, an error is returned",
pvOrPVC: test.NewPVC("velero", "pvc-1", test.WithStorageClassName("storageclass-1")),
configMap: test.NewConfigMap("velero", "change-storage-class",
test.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction"),
test.WithConfigMapData("storageclass-1", "nonexistent-storage-class"),
),
pvOrPVC: builder.ForPersistentVolumeClaim("velero", "pvc-1").StorageClass("storageclass-1").Result(),
configMap: builder.ForConfigMap("velero", "change-storage-classs").
ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")).
Data("storageclass-1", "nonexistent-storage-class").
Result(),
wantErr: errors.New("error getting storage class nonexistent-storage-class from API: storageclasses.storage.k8s.io \"nonexistent-storage-class\" not found"),
},
}

View File

@ -26,7 +26,7 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
api "github.com/heptio/velero/pkg/apis/velero/v1"
"github.com/heptio/velero/pkg/backup"
"github.com/heptio/velero/pkg/builder"
cloudprovidermocks "github.com/heptio/velero/pkg/cloudprovider/mocks"
"github.com/heptio/velero/pkg/generated/clientset/versioned/fake"
informers "github.com/heptio/velero/pkg/generated/informers/externalversions"
@ -35,8 +35,8 @@ import (
"github.com/heptio/velero/pkg/volume"
)
func defaultBackup() *backup.Builder {
return backup.NewNamedBackupBuilder(api.DefaultNamespace, "backup-1")
func defaultBackup() *builder.BackupBuilder {
return builder.ForBackup(api.DefaultNamespace, "backup-1")
}
func TestExecutePVAction_NoSnapshotRestores(t *testing.T) {
@ -53,46 +53,46 @@ func TestExecutePVAction_NoSnapshotRestores(t *testing.T) {
{
name: "no name should error",
obj: NewTestUnstructured().WithMetadata().Unstructured,
restore: velerotest.NewDefaultTestRestore().Restore,
restore: builder.ForRestore(api.DefaultNamespace, "").Result(),
expectedErr: true,
},
{
name: "no spec should error",
obj: NewTestUnstructured().WithName("pv-1").Unstructured,
restore: velerotest.NewDefaultTestRestore().Restore,
restore: builder.ForRestore(api.DefaultNamespace, "").Result(),
expectedErr: true,
},
{
name: "ensure spec.claimRef is deleted",
obj: NewTestUnstructured().WithName("pv-1").WithAnnotations("a", "b").WithSpec("claimRef", "someOtherField").Unstructured,
restore: velerotest.NewDefaultTestRestore().WithRestorePVs(false).Restore,
backup: defaultBackup().Phase(api.BackupPhaseInProgress).Backup(),
restore: builder.ForRestore(api.DefaultNamespace, "").RestorePVs(false).Result(),
backup: defaultBackup().Phase(api.BackupPhaseInProgress).Result(),
expectedRes: NewTestUnstructured().WithAnnotations("a", "b").WithName("pv-1").WithSpec("someOtherField").Unstructured,
},
{
name: "ensure spec.storageClassName is retained",
obj: NewTestUnstructured().WithName("pv-1").WithAnnotations("a", "b").WithSpec("storageClassName", "someOtherField").Unstructured,
restore: velerotest.NewDefaultTestRestore().WithRestorePVs(false).Restore,
backup: defaultBackup().Phase(api.BackupPhaseInProgress).Backup(),
restore: builder.ForRestore(api.DefaultNamespace, "").RestorePVs(false).Result(),
backup: defaultBackup().Phase(api.BackupPhaseInProgress).Result(),
expectedRes: NewTestUnstructured().WithAnnotations("a", "b").WithName("pv-1").WithSpec("storageClassName", "someOtherField").Unstructured,
},
{
name: "if backup.spec.snapshotVolumes is false, ignore restore.spec.restorePVs and return early",
obj: NewTestUnstructured().WithName("pv-1").WithAnnotations("a", "b").WithSpec("claimRef", "storageClassName", "someOtherField").Unstructured,
restore: velerotest.NewDefaultTestRestore().WithRestorePVs(true).Restore,
backup: defaultBackup().Phase(api.BackupPhaseInProgress).SnapshotVolumes(false).Backup(),
restore: builder.ForRestore(api.DefaultNamespace, "").RestorePVs(true).Result(),
backup: defaultBackup().Phase(api.BackupPhaseInProgress).SnapshotVolumes(false).Result(),
expectedRes: NewTestUnstructured().WithName("pv-1").WithAnnotations("a", "b").WithSpec("storageClassName", "someOtherField").Unstructured,
},
{
name: "restore.spec.restorePVs=false, return early",
obj: NewTestUnstructured().WithName("pv-1").WithSpec().Unstructured,
restore: velerotest.NewDefaultTestRestore().WithRestorePVs(false).Restore,
backup: defaultBackup().Phase(api.BackupPhaseInProgress).Backup(),
restore: builder.ForRestore(api.DefaultNamespace, "").RestorePVs(false).Result(),
backup: defaultBackup().Phase(api.BackupPhaseInProgress).Result(),
volumeSnapshots: []*volume.Snapshot{
newSnapshot("pv-1", "loc-1", "gp", "az-1", "snap-1", 1000),
},
locations: []*api.VolumeSnapshotLocation{
velerotest.NewTestVolumeSnapshotLocation().WithName("loc-1").VolumeSnapshotLocation,
builder.ForVolumeSnapshotLocation(api.DefaultNamespace, "loc-1").Result(),
},
expectedErr: false,
expectedRes: NewTestUnstructured().WithName("pv-1").WithSpec().Unstructured,
@ -100,11 +100,11 @@ func TestExecutePVAction_NoSnapshotRestores(t *testing.T) {
{
name: "volumeSnapshots is empty: return early",
obj: NewTestUnstructured().WithName("pv-1").WithSpec().Unstructured,
restore: velerotest.NewDefaultTestRestore().WithRestorePVs(true).Restore,
backup: defaultBackup().Backup(),
restore: builder.ForRestore(api.DefaultNamespace, "").RestorePVs(true).Result(),
backup: defaultBackup().Result(),
locations: []*api.VolumeSnapshotLocation{
velerotest.NewTestVolumeSnapshotLocation().WithName("loc-1").VolumeSnapshotLocation,
velerotest.NewTestVolumeSnapshotLocation().WithName("loc-2").VolumeSnapshotLocation,
builder.ForVolumeSnapshotLocation(api.DefaultNamespace, "loc-1").Result(),
builder.ForVolumeSnapshotLocation(api.DefaultNamespace, "loc-2").Result(),
},
volumeSnapshots: []*volume.Snapshot{},
expectedRes: NewTestUnstructured().WithName("pv-1").WithSpec().Unstructured,
@ -112,11 +112,11 @@ func TestExecutePVAction_NoSnapshotRestores(t *testing.T) {
{
name: "volumeSnapshots doesn't have a snapshot for PV: return early",
obj: NewTestUnstructured().WithName("pv-1").WithSpec().Unstructured,
restore: velerotest.NewDefaultTestRestore().WithRestorePVs(true).Restore,
backup: defaultBackup().Backup(),
restore: builder.ForRestore(api.DefaultNamespace, "").RestorePVs(true).Result(),
backup: defaultBackup().Result(),
locations: []*api.VolumeSnapshotLocation{
velerotest.NewTestVolumeSnapshotLocation().WithName("loc-1").VolumeSnapshotLocation,
velerotest.NewTestVolumeSnapshotLocation().WithName("loc-2").VolumeSnapshotLocation,
builder.ForVolumeSnapshotLocation(api.DefaultNamespace, "loc-1").Result(),
builder.ForVolumeSnapshotLocation(api.DefaultNamespace, "loc-2").Result(),
},
volumeSnapshots: []*volume.Snapshot{
newSnapshot("non-matching-pv-1", "loc-1", "type-1", "az-1", "snap-1", 1),
@ -178,11 +178,11 @@ func TestExecutePVAction_SnapshotRestores(t *testing.T) {
{
name: "backup with a matching volume.Snapshot for PV executes restore",
obj: NewTestUnstructured().WithName("pv-1").WithSpec().Unstructured,
restore: velerotest.NewDefaultTestRestore().WithRestorePVs(true).Restore,
backup: defaultBackup().Backup(),
restore: builder.ForRestore(api.DefaultNamespace, "").RestorePVs(true).Result(),
backup: defaultBackup().Result(),
locations: []*api.VolumeSnapshotLocation{
velerotest.NewTestVolumeSnapshotLocation().WithName("loc-1").WithProvider("provider-1").VolumeSnapshotLocation,
velerotest.NewTestVolumeSnapshotLocation().WithName("loc-2").WithProvider("provider-2").VolumeSnapshotLocation,
builder.ForVolumeSnapshotLocation(api.DefaultNamespace, "loc-1").Provider("provider-1").Result(),
builder.ForVolumeSnapshotLocation(api.DefaultNamespace, "loc-2").Provider("provider-2").Result(),
},
volumeSnapshots: []*volume.Snapshot{
newSnapshot("pv-1", "loc-1", "type-1", "az-1", "snap-1", 1),

File diff suppressed because it is too large Load Diff

View File

@ -1,81 +0,0 @@
/*
Copyright 2018 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 serverstatusrequest
import (
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
)
type Builder struct {
serverStatusRequest velerov1api.ServerStatusRequest
}
// NewBuilder returns a Builder for a ServerStatusRequest.
func NewBuilder() *Builder {
return &Builder{
serverStatusRequest: velerov1api.ServerStatusRequest{
TypeMeta: metav1.TypeMeta{
APIVersion: velerov1api.SchemeGroupVersion.String(),
Kind: "ServerStatusRequest",
},
},
}
}
// ServerStatusRequest returns the built ServerStatusRequest API object.
func (b *Builder) ServerStatusRequest() *velerov1api.ServerStatusRequest {
return &b.serverStatusRequest
}
func (b *Builder) Namespace(namespace string) *Builder {
b.serverStatusRequest.Namespace = namespace
return b
}
func (b *Builder) Name(name string) *Builder {
b.serverStatusRequest.Name = name
return b
}
func (b *Builder) GenerateName(name string) *Builder {
b.serverStatusRequest.GenerateName = name
return b
}
func (b *Builder) Phase(phase velerov1api.ServerStatusRequestPhase) *Builder {
b.serverStatusRequest.Status.Phase = phase
return b
}
func (b *Builder) ProcessedTimestamp(time time.Time) *Builder {
b.serverStatusRequest.Status.ProcessedTimestamp.Time = time
return b
}
func (b *Builder) ServerVersion(version string) *Builder {
b.serverStatusRequest.Status.ServerVersion = version
return b
}
func (b *Builder) Plugins(plugins []velerov1api.PluginInfo) *Builder {
b.serverStatusRequest.Status.Plugins = plugins
return b
}

View File

@ -29,13 +29,14 @@ import (
"k8s.io/apimachinery/pkg/util/clock"
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
"github.com/heptio/velero/pkg/builder"
"github.com/heptio/velero/pkg/buildinfo"
"github.com/heptio/velero/pkg/generated/clientset/versioned/fake"
"github.com/heptio/velero/pkg/plugin/framework"
)
func statusRequestBuilder() *Builder {
return NewBuilder().Namespace(velerov1api.DefaultNamespace).Name("sr-1")
func statusRequestBuilder() *builder.ServerStatusRequestBuilder {
return builder.ForServerStatusRequest(velerov1api.DefaultNamespace, "sr-1")
}
func TestProcess(t *testing.T) {
@ -56,7 +57,7 @@ func TestProcess(t *testing.T) {
}{
{
name: "server status request with empty phase gets processed",
req: statusRequestBuilder().ServerStatusRequest(),
req: statusRequestBuilder().Result(),
reqPluginLister: &fakePluginLister{
plugins: []framework.PluginIdentifier{
{
@ -75,13 +76,13 @@ func TestProcess(t *testing.T) {
Kind: "VolumeSnapshotter",
},
}).
ServerStatusRequest(),
Result(),
},
{
name: "server status request with phase=New gets processed",
req: statusRequestBuilder().
Phase(velerov1api.ServerStatusRequestPhaseNew).
ServerStatusRequest(),
Result(),
reqPluginLister: &fakePluginLister{
plugins: []framework.PluginIdentifier{
{
@ -108,14 +109,14 @@ func TestProcess(t *testing.T) {
Kind: "VolumeSnapshotter",
},
}).
ServerStatusRequest(),
Result(),
},
{
name: "server status request with phase=Processed gets deleted if expired",
req: statusRequestBuilder().
Phase(velerov1api.ServerStatusRequestPhaseProcessed).
ProcessedTimestamp(now.Add(-61 * time.Second)).
ServerStatusRequest(),
Result(),
reqPluginLister: &fakePluginLister{
plugins: []framework.PluginIdentifier{
{
@ -131,20 +132,20 @@ func TestProcess(t *testing.T) {
req: statusRequestBuilder().
Phase(velerov1api.ServerStatusRequestPhaseProcessed).
ProcessedTimestamp(now.Add(-59 * time.Second)).
ServerStatusRequest(),
Result(),
expected: statusRequestBuilder().
Phase(velerov1api.ServerStatusRequestPhaseProcessed).
ProcessedTimestamp(now.Add(-59 * time.Second)).
ServerStatusRequest(),
Result(),
},
{
name: "server status request with invalid phase returns an error",
req: statusRequestBuilder().
Phase(velerov1api.ServerStatusRequestPhase("an-invalid-phase")).
ServerStatusRequest(),
Result(),
expected: statusRequestBuilder().
Phase(velerov1api.ServerStatusRequestPhase("an-invalid-phase")).
ServerStatusRequest(),
Result(),
expectedErrMsg: "unexpected ServerStatusRequest phase \"an-invalid-phase\"",
},
}

View File

@ -17,14 +17,8 @@ limitations under the License.
package test
import (
"time"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
storagev1 "k8s.io/api/storage/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
)
// APIResource stores information about a specific Kubernetes API
@ -135,386 +129,3 @@ func ServiceAccounts(items ...metav1.Object) *APIResource {
Items: items,
}
}
type ObjectOpts func(metav1.Object)
func NewPod(ns, name string, opts ...ObjectOpts) *corev1.Pod {
obj := &corev1.Pod{
TypeMeta: metav1.TypeMeta{
Kind: "Pod",
APIVersion: "v1",
},
ObjectMeta: objectMeta(ns, name),
}
for _, opt := range opts {
opt(obj)
}
return obj
}
func NewPVC(ns, name string, opts ...ObjectOpts) *corev1.PersistentVolumeClaim {
obj := &corev1.PersistentVolumeClaim{
TypeMeta: metav1.TypeMeta{
Kind: "PersistentVolumeClaim",
APIVersion: "v1",
},
ObjectMeta: objectMeta(ns, name),
}
for _, opt := range opts {
opt(obj)
}
return obj
}
func NewPV(name string, opts ...ObjectOpts) *corev1.PersistentVolume {
obj := &corev1.PersistentVolume{
TypeMeta: metav1.TypeMeta{
Kind: "PersistentVolume",
APIVersion: "v1",
},
ObjectMeta: objectMeta("", name),
}
for _, opt := range opts {
opt(obj)
}
return obj
}
func NewSecret(ns, name string, opts ...ObjectOpts) *corev1.Secret {
obj := &corev1.Secret{
TypeMeta: metav1.TypeMeta{
Kind: "Secret",
APIVersion: "v1",
},
ObjectMeta: objectMeta(ns, name),
}
for _, opt := range opts {
opt(obj)
}
return obj
}
func NewDeployment(ns, name string, opts ...ObjectOpts) *appsv1.Deployment {
obj := &appsv1.Deployment{
TypeMeta: metav1.TypeMeta{
Kind: "Deployment",
APIVersion: "apps/v1",
},
ObjectMeta: objectMeta(ns, name),
}
for _, opt := range opts {
opt(obj)
}
return obj
}
func NewServiceAccount(ns, name string, opts ...ObjectOpts) *corev1.ServiceAccount {
obj := &corev1.ServiceAccount{
TypeMeta: metav1.TypeMeta{
Kind: "ServiceAccount",
APIVersion: "v1",
},
ObjectMeta: objectMeta(ns, name),
}
for _, opt := range opts {
opt(obj)
}
return obj
}
func NewNamespace(name string, opts ...ObjectOpts) *corev1.Namespace {
obj := &corev1.Namespace{
TypeMeta: metav1.TypeMeta{
Kind: "Namespace",
APIVersion: "v1",
},
ObjectMeta: objectMeta("", name),
}
for _, opt := range opts {
opt(obj)
}
return obj
}
func NewConfigMap(ns, name string, opts ...ObjectOpts) *corev1.ConfigMap {
obj := &corev1.ConfigMap{
TypeMeta: metav1.TypeMeta{
Kind: "ConfigMap",
APIVersion: "v1",
},
ObjectMeta: objectMeta(ns, name),
}
for _, opt := range opts {
opt(obj)
}
return obj
}
func NewStorageClass(name string, opts ...ObjectOpts) *storagev1.StorageClass {
obj := &storagev1.StorageClass{
TypeMeta: metav1.TypeMeta{
Kind: "StorageClass",
APIVersion: "storage/v1",
},
ObjectMeta: objectMeta("", name),
}
for _, opt := range opts {
opt(obj)
}
return obj
}
// VolumeOpts exists because corev1.Volume does not implement metav1.Object
type VolumeOpts func(*corev1.Volume)
func NewVolume(name string, opts ...VolumeOpts) *corev1.Volume {
obj := &corev1.Volume{Name: name}
for _, opt := range opts {
opt(obj)
}
return obj
}
func objectMeta(ns, name string) metav1.ObjectMeta {
return metav1.ObjectMeta{
Namespace: ns,
Name: name,
}
}
// WithLabels is a functional option that applies the specified
// label keys/values to an object.
func WithLabels(labels ...string) func(obj metav1.Object) {
return func(obj metav1.Object) {
objLabels := obj.GetLabels()
if objLabels == nil {
objLabels = make(map[string]string)
}
if len(labels)%2 != 0 {
labels = append(labels, "")
}
for i := 0; i < len(labels); i += 2 {
objLabels[labels[i]] = labels[i+1]
}
obj.SetLabels(objLabels)
}
}
// WithAnnotations is a functional option that applies the specified
// annotation keys/values to an object.
func WithAnnotations(vals ...string) func(obj metav1.Object) {
return func(obj metav1.Object) {
objAnnotations := obj.GetAnnotations()
if objAnnotations == nil {
objAnnotations = make(map[string]string)
}
if len(vals)%2 != 0 {
vals = append(vals, "")
}
for i := 0; i < len(vals); i += 2 {
objAnnotations[vals[i]] = vals[i+1]
}
obj.SetAnnotations(objAnnotations)
}
}
// WithClusterName is a functional option that applies the specified
// cluster name to an object.
func WithClusterName(val string) func(obj metav1.Object) {
return func(obj metav1.Object) {
obj.SetClusterName(val)
}
}
// WithFinalizers is a functional option that applies the specified
// finalizers to an object.
func WithFinalizers(vals ...string) func(obj metav1.Object) {
return func(obj metav1.Object) {
obj.SetFinalizers(vals)
}
}
// WithDeletionTimestamp is a functional option that applies the specified
// deletion timestamp to an object.
func WithDeletionTimestamp(val time.Time) func(obj metav1.Object) {
return func(obj metav1.Object) {
obj.SetDeletionTimestamp(&metav1.Time{Time: val})
}
}
// WithUID is a functional option that applies the specified UID to an object.
func WithUID(val string) func(obj metav1.Object) {
return func(obj metav1.Object) {
obj.SetUID(types.UID(val))
}
}
// WithReclaimPolicy is a functional option for persistent volumes that sets
// the specified reclaim policy. It panics if the object is not a persistent
// volume.
func WithReclaimPolicy(policy corev1.PersistentVolumeReclaimPolicy) func(obj metav1.Object) {
return func(obj metav1.Object) {
pv, ok := obj.(*corev1.PersistentVolume)
if !ok {
panic("WithReclaimPolicy is only valid for persistent volumes")
}
pv.Spec.PersistentVolumeReclaimPolicy = policy
}
}
// WithClaimRef is a functional option for persistent volumes that sets the specified
// claim ref. It panics if the object is not a persistent volume.
func WithClaimRef(ns, name string) func(obj metav1.Object) {
return func(obj metav1.Object) {
pv, ok := obj.(*corev1.PersistentVolume)
if !ok {
panic("WithClaimRef is only valid for persistent volumes")
}
pv.Spec.ClaimRef = &corev1.ObjectReference{
Namespace: ns,
Name: name,
}
}
}
// WithAWSEBSVolumeID is a functional option for persistent volumes that sets the specified
// AWS EBS volume ID. It panics if the object is not a persistent volume.
func WithAWSEBSVolumeID(volumeID string) func(obj metav1.Object) {
return func(obj metav1.Object) {
pv, ok := obj.(*corev1.PersistentVolume)
if !ok {
panic("WithClaimRef is only valid for persistent volumes")
}
if pv.Spec.AWSElasticBlockStore == nil {
pv.Spec.AWSElasticBlockStore = new(corev1.AWSElasticBlockStoreVolumeSource)
}
pv.Spec.AWSElasticBlockStore.VolumeID = volumeID
}
}
// WithCSI is a functional option for persistent volumes that sets the specified CSI driver name
// and volume handle. It panics if the object is not a persistent volume.
func WithCSI(driver, volumeHandle string) func(object metav1.Object) {
return func(obj metav1.Object) {
pv, ok := obj.(*corev1.PersistentVolume)
if !ok {
panic("WithCSI is only valid for persistent volumes")
}
if pv.Spec.CSI == nil {
pv.Spec.CSI = new(corev1.CSIPersistentVolumeSource)
}
pv.Spec.CSI.Driver = driver
pv.Spec.CSI.VolumeHandle = volumeHandle
}
}
// WithPVName is a functional option for persistent volume claims that sets the specified
// persistent volume name. It panics if the object is not a persistent volume claim.
func WithPVName(name string) func(obj metav1.Object) {
return func(obj metav1.Object) {
pvc, ok := obj.(*corev1.PersistentVolumeClaim)
if !ok {
panic("WithPVName is only valid for persistent volume claims")
}
pvc.Spec.VolumeName = name
}
}
// WithStorageClassName is a functional option for persistent volumes or
// persistent volume claims that sets the specified storage class name.
// It panics if the object is not a persistent volume or persistent volume
// claim.
func WithStorageClassName(name string) func(obj metav1.Object) {
return func(obj metav1.Object) {
switch obj.(type) {
case *corev1.PersistentVolume:
obj.(*corev1.PersistentVolume).Spec.StorageClassName = name
case *corev1.PersistentVolumeClaim:
obj.(*corev1.PersistentVolumeClaim).Spec.StorageClassName = &name
default:
panic("WithStorageClassName is only valid for persistent volumes and persistent volume claims")
}
}
}
// WithVolume is a functional option for pods that sets the specified
// volume on the pod's Spec. It panics if the object is not a pod.
func WithVolume(volume *corev1.Volume) func(obj metav1.Object) {
return func(obj metav1.Object) {
pod, ok := obj.(*corev1.Pod)
if !ok {
panic("WithVolume is only valid for pods")
}
pod.Spec.Volumes = append(pod.Spec.Volumes, *volume)
}
}
// WithPVCSource is a functional option for volumes that creates a
// PersistentVolumeClaimVolumeSource with the specified name.
func WithPVCSource(claimName string) func(vol *corev1.Volume) {
return func(vol *corev1.Volume) {
vol.VolumeSource.PersistentVolumeClaim = &corev1.PersistentVolumeClaimVolumeSource{ClaimName: claimName}
}
}
// WithCSISource is a functional option for volumes that creates a
// CSIVolumeSource with the specified driver name.
func WithCSISource(driverName string) func(vol *corev1.Volume) {
return func(vol *corev1.Volume) {
vol.VolumeSource.CSI = &corev1.CSIVolumeSource{Driver: driverName}
}
}
// WithConfigMapData is a functional option for config maps that puts the specified
// values in the Data field. It panics if the object is not a config map.
func WithConfigMapData(vals ...string) func(obj metav1.Object) {
return func(obj metav1.Object) {
cm, ok := obj.(*corev1.ConfigMap)
if !ok {
panic("WithConfigMapData is only valid for config maps")
}
if cm.Data == nil {
cm.Data = make(map[string]string)
}
if len(vals)%2 != 0 {
vals = append(vals, "")
}
for i := 0; i < len(vals); i += 2 {
cm.Data[vals[i]] = vals[i+1]
}
}
}

View File

@ -29,6 +29,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
kubeinformers "k8s.io/client-go/informers"
"github.com/heptio/velero/pkg/builder"
"github.com/heptio/velero/pkg/test"
velerotest "github.com/heptio/velero/pkg/util/test"
)
@ -152,30 +153,26 @@ func TestGetVolumeDirectorySuccess(t *testing.T) {
}{
{
name: "Non-CSI volume with a PVC/PV returns the volume's name",
pod: test.NewPod("ns-1", "my-pod",
test.WithVolume(test.NewVolume("my-vol", test.WithPVCSource("my-pvc")))),
pvc: test.NewPVC("ns-1", "my-pvc", test.WithPVName("a-pv")),
pv: test.NewPV("a-pv"),
pod: builder.ForPod("ns-1", "my-pod").Volumes(builder.ForVolume("my-vol").PersistentVolumeClaimSource("my-pvc").Result()).Result(),
pvc: builder.ForPersistentVolumeClaim("ns-1", "my-pvc").VolumeName("a-pv").Result(),
pv: builder.ForPersistentVolume("a-pv").Result(),
want: "a-pv",
},
{
name: "CSI volume with a PVC/PV appends '/mount' to the volume name",
pod: test.NewPod("ns-1", "my-pod",
test.WithVolume(test.NewVolume("my-vol", test.WithPVCSource("my-pvc")))),
pvc: test.NewPVC("ns-1", "my-pvc", test.WithPVName("a-pv")),
pv: test.NewPV("a-pv", test.WithCSI("csi.test.com", "provider-volume-id")),
pod: builder.ForPod("ns-1", "my-pod").Volumes(builder.ForVolume("my-vol").PersistentVolumeClaimSource("my-pvc").Result()).Result(),
pvc: builder.ForPersistentVolumeClaim("ns-1", "my-pvc").VolumeName("a-pv").Result(),
pv: builder.ForPersistentVolume("a-pv").CSI("csi.test.com", "provider-volume-id").Result(),
want: "a-pv/mount",
},
{
name: "CSI volume mounted without a PVC appends '/mount' to the volume name",
pod: test.NewPod("ns-1", "my-pod",
test.WithVolume(test.NewVolume("my-vol", test.WithCSISource("csi.test.com")))),
pod: builder.ForPod("ns-1", "my-pod").Volumes(builder.ForVolume("my-vol").CSISource("csi.test.com").Result()).Result(),
want: "my-vol/mount",
},
{
name: "Non-CSI volume without a PVC returns the volume name",
pod: test.NewPod("ns-1", "my-pod",
test.WithVolume(test.NewVolume("my-vol"))),
pod: builder.ForPod("ns-1", "my-pod").Volumes(builder.ForVolume("my-vol").Result()).Result(),
want: "my-vol",
},
}

View File

@ -1,73 +0,0 @@
/*
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.
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 test
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1 "github.com/heptio/velero/pkg/apis/velero/v1"
)
type TestBackupStorageLocation struct {
*v1.BackupStorageLocation
}
func NewTestBackupStorageLocation() *TestBackupStorageLocation {
return &TestBackupStorageLocation{
BackupStorageLocation: &v1.BackupStorageLocation{
ObjectMeta: metav1.ObjectMeta{
Namespace: v1.DefaultNamespace,
},
},
}
}
func (b *TestBackupStorageLocation) WithNamespace(namespace string) *TestBackupStorageLocation {
b.Namespace = namespace
return b
}
func (b *TestBackupStorageLocation) WithName(name string) *TestBackupStorageLocation {
b.Name = name
return b
}
func (b *TestBackupStorageLocation) WithLabel(key, value string) *TestBackupStorageLocation {
if b.Labels == nil {
b.Labels = make(map[string]string)
}
b.Labels[key] = value
return b
}
func (b *TestBackupStorageLocation) WithProvider(name string) *TestBackupStorageLocation {
b.Spec.Provider = name
return b
}
func (b *TestBackupStorageLocation) WithObjectStorage(bucketName string) *TestBackupStorageLocation {
if b.Spec.StorageType.ObjectStorage == nil {
b.Spec.StorageType.ObjectStorage = &v1.ObjectStorageLocation{}
}
b.Spec.ObjectStorage.Bucket = bucketName
return b
}
func (b *TestBackupStorageLocation) WithAccessMode(accessMode v1.BackupStorageLocationAccessMode) *TestBackupStorageLocation {
b.Spec.AccessMode = accessMode
return b
}

View File

@ -1,99 +0,0 @@
/*
Copyright 2017 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 test
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
api "github.com/heptio/velero/pkg/apis/velero/v1"
)
type TestRestore struct {
*api.Restore
}
func NewTestRestore(ns, name string, phase api.RestorePhase) *TestRestore {
return &TestRestore{
Restore: &api.Restore{
ObjectMeta: metav1.ObjectMeta{
Namespace: ns,
Name: name,
},
Spec: api.RestoreSpec{},
Status: api.RestoreStatus{
Phase: phase,
},
},
}
}
func NewDefaultTestRestore() *TestRestore {
return NewTestRestore(api.DefaultNamespace, "", api.RestorePhase(""))
}
func (r *TestRestore) WithIncludedNamespace(name string) *TestRestore {
r.Spec.IncludedNamespaces = append(r.Spec.IncludedNamespaces, name)
return r
}
func (r *TestRestore) WithExcludedNamespace(name string) *TestRestore {
r.Spec.ExcludedNamespaces = append(r.Spec.ExcludedNamespaces, name)
return r
}
func (r *TestRestore) WithValidationError(err string) *TestRestore {
r.Status.ValidationErrors = append(r.Status.ValidationErrors, err)
return r
}
func (r *TestRestore) WithBackup(name string) *TestRestore {
r.Spec.BackupName = name
return r
}
func (r *TestRestore) WithSchedule(name string) *TestRestore {
r.Spec.ScheduleName = name
return r
}
func (r *TestRestore) WithErrors(i int) *TestRestore {
r.Status.Errors = i
return r
}
func (r *TestRestore) WithRestorePVs(value bool) *TestRestore {
r.Spec.RestorePVs = &value
return r
}
func (r *TestRestore) WithMappedNamespace(from string, to string) *TestRestore {
if r.Spec.NamespaceMapping == nil {
r.Spec.NamespaceMapping = make(map[string]string)
}
r.Spec.NamespaceMapping[from] = to
return r
}
func (r *TestRestore) WithIncludedResource(resource string) *TestRestore {
r.Spec.IncludedResources = append(r.Spec.IncludedResources, resource)
return r
}
func (r *TestRestore) WithExcludedResource(resource string) *TestRestore {
r.Spec.ExcludedResources = append(r.Spec.ExcludedResources, resource)
return r
}

View File

@ -1,61 +0,0 @@
/*
Copyright 2017 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 test
import (
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
api "github.com/heptio/velero/pkg/apis/velero/v1"
)
type TestSchedule struct {
*api.Schedule
}
func NewTestSchedule(namespace, name string) *TestSchedule {
return &TestSchedule{
Schedule: &api.Schedule{
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
Name: name,
},
},
}
}
func (s *TestSchedule) WithPhase(phase api.SchedulePhase) *TestSchedule {
s.Status.Phase = phase
return s
}
func (s *TestSchedule) WithValidationError(msg string) *TestSchedule {
s.Status.ValidationErrors = append(s.Status.ValidationErrors, msg)
return s
}
func (s *TestSchedule) WithCronSchedule(cronExpression string) *TestSchedule {
s.Spec.Schedule = cronExpression
return s
}
func (s *TestSchedule) WithLastBackupTime(timeString string) *TestSchedule {
t, _ := time.Parse("2006-01-02 15:04:05", timeString)
s.Status.LastBackup = metav1.Time{Time: t}
return s
}

View File

@ -1,77 +0,0 @@
/*
Copyright 2018 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 test
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1 "github.com/heptio/velero/pkg/apis/velero/v1"
)
type TestVolumeSnapshotLocation struct {
*v1.VolumeSnapshotLocation
}
func NewTestVolumeSnapshotLocation() *TestVolumeSnapshotLocation {
return &TestVolumeSnapshotLocation{
VolumeSnapshotLocation: &v1.VolumeSnapshotLocation{
ObjectMeta: metav1.ObjectMeta{
Namespace: v1.DefaultNamespace,
},
Spec: v1.VolumeSnapshotLocationSpec{
Provider: "aws",
Config: map[string]string{"region": "us-west-1"},
},
},
}
}
func (location *TestVolumeSnapshotLocation) WithName(name string) *TestVolumeSnapshotLocation {
location.Name = name
return location
}
func (location *TestVolumeSnapshotLocation) WithProvider(name string) *TestVolumeSnapshotLocation {
location.Spec.Provider = name
return location
}
func (location *TestVolumeSnapshotLocation) WithProviderConfig(info []LocationInfo) []*TestVolumeSnapshotLocation {
var locations []*TestVolumeSnapshotLocation
for _, v := range info {
location := &TestVolumeSnapshotLocation{
VolumeSnapshotLocation: &v1.VolumeSnapshotLocation{
ObjectMeta: metav1.ObjectMeta{
Name: v.Name,
Namespace: v1.DefaultNamespace,
},
Spec: v1.VolumeSnapshotLocationSpec{
Provider: v.Provider,
Config: v.Config,
},
},
}
locations = append(locations, location)
}
return locations
}
type LocationInfo struct {
Name, Provider string
Config map[string]string
}