Merge pull request #2611 from ashish-amarnath/restic-by-default
✨ Implement option to back up all volumes by default with restic
pull/2661/head
commit
ae1f4e284f
|
@ -0,0 +1 @@
|
|||
implement option to back up all volumes by default with restic
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2017, 2019 the Velero contributors.
|
||||
Copyright 2020 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.
|
||||
|
@ -82,6 +82,12 @@ type BackupSpec struct {
|
|||
// VolumeSnapshotLocations is a list containing names of VolumeSnapshotLocations associated with this backup.
|
||||
// +optional
|
||||
VolumeSnapshotLocations []string `json:"volumeSnapshotLocations,omitempty"`
|
||||
|
||||
// DefaultVolumesToRestic specifies whether restic should be used to take a
|
||||
// backup of all pod volumes by default.
|
||||
// +optional
|
||||
// + nullable
|
||||
DefaultVolumesToRestic *bool `json:"defaultVolumesToRestic,omitempty"`
|
||||
}
|
||||
|
||||
// BackupHooks contains custom behaviors that should be executed at different phases of the backup.
|
||||
|
|
|
@ -246,6 +246,11 @@ func (in *BackupSpec) DeepCopyInto(out *BackupSpec) {
|
|||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.DefaultVolumesToRestic != nil {
|
||||
in, out := &in.DefaultVolumesToRestic, &out.DefaultVolumesToRestic
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2017, 2020 the Velero contributors.
|
||||
Copyright 2020 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.
|
||||
|
@ -71,6 +71,7 @@ type kubernetesBackupper struct {
|
|||
podCommandExecutor podexec.PodCommandExecutor
|
||||
resticBackupperFactory restic.BackupperFactory
|
||||
resticTimeout time.Duration
|
||||
defaultVolumesToRestic bool
|
||||
}
|
||||
|
||||
type resolvedAction struct {
|
||||
|
@ -103,6 +104,7 @@ func NewKubernetesBackupper(
|
|||
podCommandExecutor podexec.PodCommandExecutor,
|
||||
resticBackupperFactory restic.BackupperFactory,
|
||||
resticTimeout time.Duration,
|
||||
defaultVolumesToRestic bool,
|
||||
) (Backupper, error) {
|
||||
return &kubernetesBackupper{
|
||||
backupClient: backupClient,
|
||||
|
@ -111,6 +113,7 @@ func NewKubernetesBackupper(
|
|||
podCommandExecutor: podCommandExecutor,
|
||||
resticBackupperFactory: resticBackupperFactory,
|
||||
resticTimeout: resticTimeout,
|
||||
defaultVolumesToRestic: defaultVolumesToRestic,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -239,6 +242,7 @@ func (kb *kubernetesBackupper) Backup(log logrus.FieldLogger, backupRequest *Req
|
|||
backupRequest.ResourceIncludesExcludes = getResourceIncludesExcludes(kb.discoveryHelper, backupRequest.Spec.IncludedResources, backupRequest.Spec.ExcludedResources)
|
||||
log.Infof("Including resources: %s", backupRequest.ResourceIncludesExcludes.IncludesString())
|
||||
log.Infof("Excluding resources: %s", backupRequest.ResourceIncludesExcludes.ExcludesString())
|
||||
log.Infof("Backing up all pod volumes using restic: %t", *backupRequest.Backup.Spec.DefaultVolumesToRestic)
|
||||
|
||||
var err error
|
||||
backupRequest.ResourceHooks, err = getResourceHooks(backupRequest.Spec.Hooks.Resources, kb.discoveryHelper)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2019 the Velero contributors.
|
||||
Copyright 2020 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.
|
||||
|
@ -2752,7 +2752,7 @@ func newSnapshotLocation(ns, name, provider string) *velerov1.VolumeSnapshotLoca
|
|||
}
|
||||
|
||||
func defaultBackup() *builder.BackupBuilder {
|
||||
return builder.ForBackup(velerov1.DefaultNamespace, "backup-1")
|
||||
return builder.ForBackup(velerov1.DefaultNamespace, "backup-1").DefaultVolumesToRestic(false)
|
||||
}
|
||||
|
||||
func toUnstructuredOrFail(t *testing.T, obj interface{}) map[string]interface{} {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2017 the Velero contributors.
|
||||
Copyright 2020 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.
|
||||
|
@ -141,7 +141,7 @@ func (ib *itemBackupper) backupItem(logger logrus.FieldLogger, obj runtime.Unstr
|
|||
// Get the list of volumes to back up using restic from the pod's annotations. Remove from this list
|
||||
// any volumes that use a PVC that we've already backed up (this would be in a read-write-many scenario,
|
||||
// where it's been backed up from another pod), since we don't need >1 backup per PVC.
|
||||
for _, volume := range restic.GetVolumesToBackup(pod) {
|
||||
for _, volume := range restic.GetPodVolumesUsingRestic(pod, boolptr.IsSetToTrue(ib.backupRequest.Spec.DefaultVolumesToRestic)) {
|
||||
if found, pvcName := ib.resticSnapshotTracker.HasPVCForPodVolume(pod, volume); found {
|
||||
log.WithFields(map[string]interface{}{
|
||||
"podVolume": volume,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2019 the Velero contributors.
|
||||
Copyright 2020 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.
|
||||
|
@ -128,6 +128,12 @@ func (b *BackupBuilder) SnapshotVolumes(val bool) *BackupBuilder {
|
|||
return b
|
||||
}
|
||||
|
||||
// DefaultVolumesToRestic sets the Backup's "DefaultVolumesToRestic" flag.
|
||||
func (b *BackupBuilder) DefaultVolumesToRestic(val bool) *BackupBuilder {
|
||||
b.object.Spec.DefaultVolumesToRestic = &val
|
||||
return b
|
||||
}
|
||||
|
||||
// Phase sets the Backup's phase.
|
||||
func (b *BackupBuilder) Phase(phase velerov1api.BackupPhase) *BackupBuilder {
|
||||
b.object.Status.Phase = phase
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2017 the Velero contributors.
|
||||
Copyright 2020 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.
|
||||
|
@ -81,6 +81,7 @@ type CreateOptions struct {
|
|||
Name string
|
||||
TTL time.Duration
|
||||
SnapshotVolumes flag.OptionalBool
|
||||
DefaultVolumesToRestic flag.OptionalBool
|
||||
IncludeNamespaces flag.StringArray
|
||||
ExcludeNamespaces flag.StringArray
|
||||
IncludeResources flag.StringArray
|
||||
|
@ -123,6 +124,9 @@ func (o *CreateOptions) BindFlags(flags *pflag.FlagSet) {
|
|||
|
||||
f = flags.VarPF(&o.IncludeClusterResources, "include-cluster-resources", "", "include cluster-scoped resources in the backup")
|
||||
f.NoOptDefVal = "true"
|
||||
|
||||
f = flags.VarPF(&o.DefaultVolumesToRestic, "default-volumes-to-restic", "", "use restic by default to backup all pod volumes")
|
||||
f.NoOptDefVal = "true"
|
||||
}
|
||||
|
||||
// BindWait binds the wait flag separately so it is not called by other create
|
||||
|
@ -295,6 +299,9 @@ func (o *CreateOptions) BuildBackup(namespace string) (*velerov1api.Backup, erro
|
|||
if o.IncludeClusterResources.Value != nil {
|
||||
backupBuilder.IncludeClusterResources(*o.IncludeClusterResources.Value)
|
||||
}
|
||||
if o.DefaultVolumesToRestic.Value != nil {
|
||||
backupBuilder.DefaultVolumesToRestic(*o.DefaultVolumesToRestic.Value)
|
||||
}
|
||||
}
|
||||
|
||||
backup := backupBuilder.ObjectMeta(builder.WithLabelsMap(o.Labels.Data())).Result()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2019,2020 the Velero contributors.
|
||||
Copyright 2020 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.
|
||||
|
@ -70,6 +70,7 @@ type InstallOptions struct {
|
|||
CRDsOnly bool
|
||||
CACertFile string
|
||||
Features string
|
||||
DefaultVolumesToRestic bool
|
||||
}
|
||||
|
||||
// BindFlags adds command line values to the options struct.
|
||||
|
@ -103,6 +104,7 @@ func (o *InstallOptions) BindFlags(flags *pflag.FlagSet) {
|
|||
flags.BoolVar(&o.CRDsOnly, "crds-only", o.CRDsOnly, "only generate CustomResourceDefinition resources. Useful for updating CRDs for an existing Velero install.")
|
||||
flags.StringVar(&o.CACertFile, "cacert", o.CACertFile, "file containing a certificate bundle to use when verifying TLS connections to the object store. Optional.")
|
||||
flags.StringVar(&o.Features, "features", o.Features, "comma separated list of Velero feature flags to be set on the Velero deployment and the restic daemonset, if restic is enabled")
|
||||
flags.BoolVar(&o.DefaultVolumesToRestic, "default-volumes-to-restic", o.DefaultVolumesToRestic, "bool flag to configure Velero server to use restic by default to backup all pod volumes on all backups. Optional.")
|
||||
}
|
||||
|
||||
// NewInstallOptions instantiates a new, default InstallOptions struct.
|
||||
|
@ -126,6 +128,7 @@ func NewInstallOptions() *InstallOptions {
|
|||
UseVolumeSnapshots: true,
|
||||
NoDefaultBackupLocation: false,
|
||||
CRDsOnly: false,
|
||||
DefaultVolumesToRestic: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -183,6 +186,7 @@ func (o *InstallOptions) AsVeleroOptions() (*install.VeleroOptions, error) {
|
|||
NoDefaultBackupLocation: o.NoDefaultBackupLocation,
|
||||
CACertData: caCertData,
|
||||
Features: strings.Split(o.Features, ","),
|
||||
DefaultVolumesToRestic: o.DefaultVolumesToRestic,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -374,6 +378,10 @@ func (o *InstallOptions) Validate(c *cobra.Command, args []string, f client.Fact
|
|||
}
|
||||
}
|
||||
|
||||
if o.DefaultVolumesToRestic && !o.UseRestic {
|
||||
return errors.New("--use-restic is required when using --default-volumes-to-restic")
|
||||
}
|
||||
|
||||
switch {
|
||||
case o.SecretFile == "" && !o.NoSecret:
|
||||
return errors.New("One of --secret-file or --no-secret is required")
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2017, 2019, 2020 the Velero contributors.
|
||||
Copyright 2020 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.
|
||||
|
@ -126,6 +126,7 @@ type serverConfig struct {
|
|||
profilerAddress string
|
||||
formatFlag *logging.FormatFlag
|
||||
defaultResticMaintenanceFrequency time.Duration
|
||||
defaultVolumesToRestic bool
|
||||
}
|
||||
|
||||
type controllerRunInfo struct {
|
||||
|
@ -152,6 +153,7 @@ func NewCommand(f client.Factory) *cobra.Command {
|
|||
resourceTerminatingTimeout: defaultResourceTerminatingTimeout,
|
||||
formatFlag: logging.NewFormatFlag(),
|
||||
defaultResticMaintenanceFrequency: restic.DefaultMaintenanceFrequency,
|
||||
defaultVolumesToRestic: restic.DefaultVolumesToRestic,
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -213,6 +215,7 @@ func NewCommand(f client.Factory) *cobra.Command {
|
|||
command.Flags().DurationVar(&config.resourceTerminatingTimeout, "terminating-resource-timeout", config.resourceTerminatingTimeout, "how long to wait on persistent volumes and namespaces to terminate during a restore before timing out")
|
||||
command.Flags().DurationVar(&config.defaultBackupTTL, "default-backup-ttl", config.defaultBackupTTL, "how long to wait by default before backups can be garbage collected")
|
||||
command.Flags().DurationVar(&config.defaultResticMaintenanceFrequency, "default-restic-prune-frequency", config.defaultResticMaintenanceFrequency, "how often 'restic prune' is run for restic repositories by default")
|
||||
command.Flags().BoolVar(&config.defaultVolumesToRestic, "default-volumes-to-restic", config.defaultVolumesToRestic, "backup all volumes with restic by default")
|
||||
|
||||
return command
|
||||
}
|
||||
|
@ -637,6 +640,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string
|
|||
podexec.NewPodCommandExecutor(s.kubeClientConfig, s.kubeClient.CoreV1().RESTClient()),
|
||||
s.resticManager,
|
||||
s.config.podVolumeOperationTimeout,
|
||||
s.config.defaultVolumesToRestic,
|
||||
)
|
||||
cmd.CheckError(err)
|
||||
|
||||
|
@ -651,6 +655,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string
|
|||
backupTracker,
|
||||
s.sharedInformerFactory.Velero().V1().BackupStorageLocations().Lister(),
|
||||
s.config.defaultBackupLocation,
|
||||
s.config.defaultVolumesToRestic,
|
||||
s.config.defaultBackupTTL,
|
||||
s.sharedInformerFactory.Velero().V1().VolumeSnapshotLocations().Lister(),
|
||||
defaultVolumeSnapshotLocations,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2017, 2019 the Velero contributors.
|
||||
Copyright 2020 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.
|
||||
|
@ -71,6 +71,7 @@ type backupController struct {
|
|||
backupTracker BackupTracker
|
||||
backupLocationLister velerov1listers.BackupStorageLocationLister
|
||||
defaultBackupLocation string
|
||||
defaultVolumesToRestic bool
|
||||
defaultBackupTTL time.Duration
|
||||
snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister
|
||||
defaultSnapshotLocations map[string]string
|
||||
|
@ -92,6 +93,7 @@ func NewBackupController(
|
|||
backupTracker BackupTracker,
|
||||
backupLocationLister velerov1listers.BackupStorageLocationLister,
|
||||
defaultBackupLocation string,
|
||||
defaultVolumesToRestic bool,
|
||||
defaultBackupTTL time.Duration,
|
||||
volumeSnapshotLocationLister velerov1listers.VolumeSnapshotLocationLister,
|
||||
defaultSnapshotLocations map[string]string,
|
||||
|
@ -112,6 +114,7 @@ func NewBackupController(
|
|||
backupTracker: backupTracker,
|
||||
backupLocationLister: backupLocationLister,
|
||||
defaultBackupLocation: defaultBackupLocation,
|
||||
defaultVolumesToRestic: defaultVolumesToRestic,
|
||||
defaultBackupTTL: defaultBackupTTL,
|
||||
snapshotLocationLister: volumeSnapshotLocationLister,
|
||||
defaultSnapshotLocations: defaultSnapshotLocations,
|
||||
|
@ -339,6 +342,10 @@ func (c *backupController) prepareBackupRequest(backup *velerov1api.Backup) *pkg
|
|||
request.Spec.StorageLocation = c.defaultBackupLocation
|
||||
}
|
||||
|
||||
if request.Spec.DefaultVolumesToRestic == nil {
|
||||
request.Spec.DefaultVolumesToRestic = &c.defaultVolumesToRestic
|
||||
}
|
||||
|
||||
// add the storage location as a label for easy filtering later.
|
||||
if request.Labels == nil {
|
||||
request.Labels = make(map[string]string)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2017, 2019 the Velero contributors.
|
||||
Copyright 2020 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.
|
||||
|
@ -48,6 +48,7 @@ import (
|
|||
pluginmocks "github.com/vmware-tanzu/velero/pkg/plugin/mocks"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
velerotest "github.com/vmware-tanzu/velero/pkg/test"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/boolptr"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/logging"
|
||||
)
|
||||
|
||||
|
@ -339,18 +340,20 @@ func TestProcessBackupCompletions(t *testing.T) {
|
|||
timestamp := metav1.NewTime(now)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
backup *velerov1api.Backup
|
||||
backupLocation *velerov1api.BackupStorageLocation
|
||||
expectedResult *velerov1api.Backup
|
||||
backupExists bool
|
||||
existenceCheckError error
|
||||
name string
|
||||
backup *velerov1api.Backup
|
||||
backupLocation *velerov1api.BackupStorageLocation
|
||||
defaultVolumesToRestic bool
|
||||
expectedResult *velerov1api.Backup
|
||||
backupExists bool
|
||||
existenceCheckError error
|
||||
}{
|
||||
// Completed
|
||||
{
|
||||
name: "backup with no backup location gets the default",
|
||||
backup: defaultBackup().Result(),
|
||||
backupLocation: defaultBackupLocation,
|
||||
name: "backup with no backup location gets the default",
|
||||
backup: defaultBackup().Result(),
|
||||
backupLocation: defaultBackupLocation,
|
||||
defaultVolumesToRestic: true,
|
||||
expectedResult: &velerov1api.Backup{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Backup",
|
||||
|
@ -369,7 +372,8 @@ func TestProcessBackupCompletions(t *testing.T) {
|
|||
},
|
||||
},
|
||||
Spec: velerov1api.BackupSpec{
|
||||
StorageLocation: defaultBackupLocation.Name,
|
||||
StorageLocation: defaultBackupLocation.Name,
|
||||
DefaultVolumesToRestic: boolptr.True(),
|
||||
},
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseCompleted,
|
||||
|
@ -382,9 +386,10 @@ func TestProcessBackupCompletions(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
name: "backup with a specific backup location keeps it",
|
||||
backup: defaultBackup().StorageLocation("alt-loc").Result(),
|
||||
backupLocation: builder.ForBackupStorageLocation("velero", "alt-loc").Bucket("store-1").Result(),
|
||||
name: "backup with a specific backup location keeps it",
|
||||
backup: defaultBackup().StorageLocation("alt-loc").Result(),
|
||||
backupLocation: builder.ForBackupStorageLocation("velero", "alt-loc").Bucket("store-1").Result(),
|
||||
defaultVolumesToRestic: false,
|
||||
expectedResult: &velerov1api.Backup{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Backup",
|
||||
|
@ -403,7 +408,8 @@ func TestProcessBackupCompletions(t *testing.T) {
|
|||
},
|
||||
},
|
||||
Spec: velerov1api.BackupSpec{
|
||||
StorageLocation: "alt-loc",
|
||||
StorageLocation: "alt-loc",
|
||||
DefaultVolumesToRestic: boolptr.False(),
|
||||
},
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseCompleted,
|
||||
|
@ -422,6 +428,7 @@ func TestProcessBackupCompletions(t *testing.T) {
|
|||
Bucket("store-1").
|
||||
AccessMode(velerov1api.BackupStorageLocationAccessModeReadWrite).
|
||||
Result(),
|
||||
defaultVolumesToRestic: true,
|
||||
expectedResult: &velerov1api.Backup{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Backup",
|
||||
|
@ -440,7 +447,8 @@ func TestProcessBackupCompletions(t *testing.T) {
|
|||
},
|
||||
},
|
||||
Spec: velerov1api.BackupSpec{
|
||||
StorageLocation: "read-write",
|
||||
StorageLocation: "read-write",
|
||||
DefaultVolumesToRestic: boolptr.True(),
|
||||
},
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseCompleted,
|
||||
|
@ -453,9 +461,10 @@ func TestProcessBackupCompletions(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
name: "backup with a TTL has expiration set",
|
||||
backup: defaultBackup().TTL(10 * time.Minute).Result(),
|
||||
backupLocation: defaultBackupLocation,
|
||||
name: "backup with a TTL has expiration set",
|
||||
backup: defaultBackup().TTL(10 * time.Minute).Result(),
|
||||
backupLocation: defaultBackupLocation,
|
||||
defaultVolumesToRestic: false,
|
||||
expectedResult: &velerov1api.Backup{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Backup",
|
||||
|
@ -474,8 +483,9 @@ func TestProcessBackupCompletions(t *testing.T) {
|
|||
},
|
||||
},
|
||||
Spec: velerov1api.BackupSpec{
|
||||
TTL: metav1.Duration{Duration: 10 * time.Minute},
|
||||
StorageLocation: defaultBackupLocation.Name,
|
||||
TTL: metav1.Duration{Duration: 10 * time.Minute},
|
||||
StorageLocation: defaultBackupLocation.Name,
|
||||
DefaultVolumesToRestic: boolptr.False(),
|
||||
},
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseCompleted,
|
||||
|
@ -488,10 +498,11 @@ func TestProcessBackupCompletions(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
name: "backup without an existing backup will succeed",
|
||||
backupExists: false,
|
||||
backup: defaultBackup().Result(),
|
||||
backupLocation: defaultBackupLocation,
|
||||
name: "backup without an existing backup will succeed",
|
||||
backupExists: false,
|
||||
backup: defaultBackup().Result(),
|
||||
backupLocation: defaultBackupLocation,
|
||||
defaultVolumesToRestic: true,
|
||||
expectedResult: &velerov1api.Backup{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Backup",
|
||||
|
@ -510,7 +521,160 @@ func TestProcessBackupCompletions(t *testing.T) {
|
|||
},
|
||||
},
|
||||
Spec: velerov1api.BackupSpec{
|
||||
StorageLocation: defaultBackupLocation.Name,
|
||||
StorageLocation: defaultBackupLocation.Name,
|
||||
DefaultVolumesToRestic: boolptr.True(),
|
||||
},
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseCompleted,
|
||||
Version: 1,
|
||||
FormatVersion: "1.1.0",
|
||||
StartTimestamp: ×tamp,
|
||||
CompletionTimestamp: ×tamp,
|
||||
Expiration: ×tamp,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "backup specifying a false value for 'DefaultVolumesToRestic' keeps it",
|
||||
backupExists: false,
|
||||
backup: defaultBackup().DefaultVolumesToRestic(false).Result(),
|
||||
backupLocation: defaultBackupLocation,
|
||||
// value set in the controller is different from that specified in the backup
|
||||
defaultVolumesToRestic: true,
|
||||
expectedResult: &velerov1api.Backup{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Backup",
|
||||
APIVersion: "velero.io/v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: velerov1api.DefaultNamespace,
|
||||
Name: "backup-1",
|
||||
Annotations: map[string]string{
|
||||
"velero.io/source-cluster-k8s-major-version": "1",
|
||||
"velero.io/source-cluster-k8s-minor-version": "16",
|
||||
"velero.io/source-cluster-k8s-gitversion": "v1.16.4",
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"velero.io/storage-location": "loc-1",
|
||||
},
|
||||
},
|
||||
Spec: velerov1api.BackupSpec{
|
||||
StorageLocation: defaultBackupLocation.Name,
|
||||
DefaultVolumesToRestic: boolptr.False(),
|
||||
},
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseCompleted,
|
||||
Version: 1,
|
||||
FormatVersion: "1.1.0",
|
||||
StartTimestamp: ×tamp,
|
||||
CompletionTimestamp: ×tamp,
|
||||
Expiration: ×tamp,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "backup specifying a true value for 'DefaultVolumesToRestic' keeps it",
|
||||
backupExists: false,
|
||||
backup: defaultBackup().DefaultVolumesToRestic(true).Result(),
|
||||
backupLocation: defaultBackupLocation,
|
||||
// value set in the controller is different from that specified in the backup
|
||||
defaultVolumesToRestic: false,
|
||||
expectedResult: &velerov1api.Backup{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Backup",
|
||||
APIVersion: "velero.io/v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: velerov1api.DefaultNamespace,
|
||||
Name: "backup-1",
|
||||
Annotations: map[string]string{
|
||||
"velero.io/source-cluster-k8s-major-version": "1",
|
||||
"velero.io/source-cluster-k8s-minor-version": "16",
|
||||
"velero.io/source-cluster-k8s-gitversion": "v1.16.4",
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"velero.io/storage-location": "loc-1",
|
||||
},
|
||||
},
|
||||
Spec: velerov1api.BackupSpec{
|
||||
StorageLocation: defaultBackupLocation.Name,
|
||||
DefaultVolumesToRestic: boolptr.True(),
|
||||
},
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseCompleted,
|
||||
Version: 1,
|
||||
FormatVersion: "1.1.0",
|
||||
StartTimestamp: ×tamp,
|
||||
CompletionTimestamp: ×tamp,
|
||||
Expiration: ×tamp,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "backup specifying no value for 'DefaultVolumesToRestic' gets the default true value",
|
||||
backupExists: false,
|
||||
backup: defaultBackup().Result(),
|
||||
backupLocation: defaultBackupLocation,
|
||||
// value set in the controller is different from that specified in the backup
|
||||
defaultVolumesToRestic: true,
|
||||
expectedResult: &velerov1api.Backup{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Backup",
|
||||
APIVersion: "velero.io/v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: velerov1api.DefaultNamespace,
|
||||
Name: "backup-1",
|
||||
Annotations: map[string]string{
|
||||
"velero.io/source-cluster-k8s-major-version": "1",
|
||||
"velero.io/source-cluster-k8s-minor-version": "16",
|
||||
"velero.io/source-cluster-k8s-gitversion": "v1.16.4",
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"velero.io/storage-location": "loc-1",
|
||||
},
|
||||
},
|
||||
Spec: velerov1api.BackupSpec{
|
||||
StorageLocation: defaultBackupLocation.Name,
|
||||
DefaultVolumesToRestic: boolptr.True(),
|
||||
},
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseCompleted,
|
||||
Version: 1,
|
||||
FormatVersion: "1.1.0",
|
||||
StartTimestamp: ×tamp,
|
||||
CompletionTimestamp: ×tamp,
|
||||
Expiration: ×tamp,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "backup specifying no value for 'DefaultVolumesToRestic' gets the default false value",
|
||||
backupExists: false,
|
||||
backup: defaultBackup().Result(),
|
||||
backupLocation: defaultBackupLocation,
|
||||
// value set in the controller is different from that specified in the backup
|
||||
defaultVolumesToRestic: false,
|
||||
expectedResult: &velerov1api.Backup{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Backup",
|
||||
APIVersion: "velero.io/v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: velerov1api.DefaultNamespace,
|
||||
Name: "backup-1",
|
||||
Annotations: map[string]string{
|
||||
"velero.io/source-cluster-k8s-major-version": "1",
|
||||
"velero.io/source-cluster-k8s-minor-version": "16",
|
||||
"velero.io/source-cluster-k8s-gitversion": "v1.16.4",
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"velero.io/storage-location": "loc-1",
|
||||
},
|
||||
},
|
||||
Spec: velerov1api.BackupSpec{
|
||||
StorageLocation: defaultBackupLocation.Name,
|
||||
DefaultVolumesToRestic: boolptr.False(),
|
||||
},
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseCompleted,
|
||||
|
@ -525,10 +689,11 @@ func TestProcessBackupCompletions(t *testing.T) {
|
|||
|
||||
// Failed
|
||||
{
|
||||
name: "backup with existing backup will fail",
|
||||
backupExists: true,
|
||||
backup: defaultBackup().Result(),
|
||||
backupLocation: defaultBackupLocation,
|
||||
name: "backup with existing backup will fail",
|
||||
backupExists: true,
|
||||
backup: defaultBackup().Result(),
|
||||
backupLocation: defaultBackupLocation,
|
||||
defaultVolumesToRestic: true,
|
||||
expectedResult: &velerov1api.Backup{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Backup",
|
||||
|
@ -547,7 +712,8 @@ func TestProcessBackupCompletions(t *testing.T) {
|
|||
},
|
||||
},
|
||||
Spec: velerov1api.BackupSpec{
|
||||
StorageLocation: defaultBackupLocation.Name,
|
||||
StorageLocation: defaultBackupLocation.Name,
|
||||
DefaultVolumesToRestic: boolptr.True(),
|
||||
},
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseFailed,
|
||||
|
@ -560,10 +726,11 @@ func TestProcessBackupCompletions(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
name: "error when checking if backup exists will cause backup to fail",
|
||||
backup: defaultBackup().Result(),
|
||||
existenceCheckError: errors.New("Backup already exists in object storage"),
|
||||
backupLocation: defaultBackupLocation,
|
||||
name: "error when checking if backup exists will cause backup to fail",
|
||||
backup: defaultBackup().Result(),
|
||||
existenceCheckError: errors.New("Backup already exists in object storage"),
|
||||
backupLocation: defaultBackupLocation,
|
||||
defaultVolumesToRestic: true,
|
||||
expectedResult: &velerov1api.Backup{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Backup",
|
||||
|
@ -582,7 +749,8 @@ func TestProcessBackupCompletions(t *testing.T) {
|
|||
},
|
||||
},
|
||||
Spec: velerov1api.BackupSpec{
|
||||
StorageLocation: defaultBackupLocation.Name,
|
||||
StorageLocation: defaultBackupLocation.Name,
|
||||
DefaultVolumesToRestic: boolptr.True(),
|
||||
},
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseFailed,
|
||||
|
@ -633,6 +801,7 @@ func TestProcessBackupCompletions(t *testing.T) {
|
|||
backupLocationLister: sharedInformers.Velero().V1().BackupStorageLocations().Lister(),
|
||||
snapshotLocationLister: sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(),
|
||||
defaultBackupLocation: defaultBackupLocation.Name,
|
||||
defaultVolumesToRestic: test.defaultVolumesToRestic,
|
||||
backupTracker: NewBackupTracker(),
|
||||
metrics: metrics.NewServerMetrics(),
|
||||
clock: clock.NewFakeClock(now),
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -36,6 +36,10 @@ spec:
|
|||
spec:
|
||||
description: BackupSpec defines the specification for a Velero backup.
|
||||
properties:
|
||||
defaultVolumesToRestic:
|
||||
description: DefaultVolumesToRestic specifies whether restic should
|
||||
be used to take a backup of all pod volumes by default.
|
||||
type: boolean
|
||||
excludedNamespaces:
|
||||
description: ExcludedNamespaces contains a list of namespaces that are
|
||||
not included in the backup.
|
||||
|
|
|
@ -44,6 +44,10 @@ spec:
|
|||
description: Template is the definition of the Backup to be run on the
|
||||
provided schedule
|
||||
properties:
|
||||
defaultVolumesToRestic:
|
||||
description: DefaultVolumesToRestic specifies whether restic should
|
||||
be used to take a backup of all pod volumes by default.
|
||||
type: boolean
|
||||
excludedNamespaces:
|
||||
description: ExcludedNamespaces contains a list of namespaces that
|
||||
are not included in the backup.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2018, 2019, 2020 the Velero contributors.
|
||||
Copyright 2020 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.
|
||||
|
@ -40,6 +40,7 @@ type podTemplateConfig struct {
|
|||
defaultResticMaintenanceFrequency time.Duration
|
||||
plugins []string
|
||||
features []string
|
||||
defaultVolumesToRestic bool
|
||||
}
|
||||
|
||||
func WithImage(image string) podTemplateOption {
|
||||
|
@ -107,6 +108,12 @@ func WithFeatures(features []string) podTemplateOption {
|
|||
}
|
||||
}
|
||||
|
||||
func WithDefaultVolumesToRestic() podTemplateOption {
|
||||
return func(c *podTemplateConfig) {
|
||||
c.defaultVolumesToRestic = true
|
||||
}
|
||||
}
|
||||
|
||||
func Deployment(namespace string, opts ...podTemplateOption) *appsv1.Deployment {
|
||||
// TODO: Add support for server args
|
||||
c := &podTemplateConfig{
|
||||
|
@ -129,6 +136,10 @@ func Deployment(namespace string, opts ...podTemplateOption) *appsv1.Deployment
|
|||
args = append(args, fmt.Sprintf("--features=%s", strings.Join(c.features, ",")))
|
||||
}
|
||||
|
||||
if c.defaultVolumesToRestic {
|
||||
args = append(args, "--default-volumes-to-restic=true")
|
||||
}
|
||||
|
||||
containerLabels := labels()
|
||||
containerLabels["deploy"] = "velero"
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2018, 2019, 2020 the Velero contributors.
|
||||
Copyright 2020 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.
|
||||
|
@ -220,6 +220,7 @@ type VeleroOptions struct {
|
|||
NoDefaultBackupLocation bool
|
||||
CACertData []byte
|
||||
Features []string
|
||||
DefaultVolumesToRestic bool
|
||||
}
|
||||
|
||||
func AllCRDs() *unstructured.UnstructuredList {
|
||||
|
@ -287,6 +288,10 @@ func AllResources(o *VeleroOptions) (*unstructured.UnstructuredList, error) {
|
|||
deployOpts = append(deployOpts, WithPlugins(o.Plugins))
|
||||
}
|
||||
|
||||
if o.DefaultVolumesToRestic {
|
||||
deployOpts = append(deployOpts, WithDefaultVolumesToRestic())
|
||||
}
|
||||
|
||||
deploy := Deployment(o.Namespace, deployOpts...)
|
||||
|
||||
appendUnstructured(resources, deploy)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2018 the Velero contributors.
|
||||
Copyright 2020 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.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2018 the Velero contributors.
|
||||
Copyright 2020 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.
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
corev1api "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
corev1listers "k8s.io/client-go/listers/core/v1"
|
||||
|
@ -45,6 +46,10 @@ const (
|
|||
// at which restic prune is run.
|
||||
DefaultMaintenanceFrequency = 7 * 24 * time.Hour
|
||||
|
||||
// DefaultVolumesToRestic specifies whether restic should be used, by default, to
|
||||
// take backup of all pod volumes.
|
||||
DefaultVolumesToRestic = false
|
||||
|
||||
// PVCNameAnnotation is the key for the annotation added to
|
||||
// pod volume backups when they're for a PVC.
|
||||
PVCNameAnnotation = "velero.io/pvc-name"
|
||||
|
@ -53,6 +58,10 @@ const (
|
|||
// need to be backed up using restic.
|
||||
VolumesToBackupAnnotation = "backup.velero.io/backup-volumes"
|
||||
|
||||
// VolumesToExcludeAnnotation is the annotation on a pod whose mounted volumes
|
||||
// should be excluded from restic backup.
|
||||
VolumesToExcludeAnnotation = "backup.velero.io/backup-volumes-excludes"
|
||||
|
||||
// Deprecated.
|
||||
//
|
||||
// TODO(2.0): remove
|
||||
|
@ -111,6 +120,7 @@ func GetVolumeBackupsForPod(podVolumeBackups []*velerov1api.PodVolumeBackup, pod
|
|||
|
||||
// GetVolumesToBackup returns a list of volume names to backup for
|
||||
// the provided pod.
|
||||
// Deprecated: Use GetPodVolumesUsingRestic instead.
|
||||
func GetVolumesToBackup(obj metav1.Object) []string {
|
||||
annotations := obj.GetAnnotations()
|
||||
if annotations == nil {
|
||||
|
@ -125,6 +135,51 @@ func GetVolumesToBackup(obj metav1.Object) []string {
|
|||
return strings.Split(backupsValue, ",")
|
||||
}
|
||||
|
||||
func getVolumesToExclude(obj metav1.Object) []string {
|
||||
annotations := obj.GetAnnotations()
|
||||
if annotations == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return strings.Split(annotations[VolumesToExcludeAnnotation], ",")
|
||||
}
|
||||
|
||||
func contains(list []string, k string) bool {
|
||||
for _, i := range list {
|
||||
if i == k {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetPodVolumesUsingRestic returns a list of volume names to backup for the provided pod.
|
||||
func GetPodVolumesUsingRestic(pod *corev1api.Pod, defaultVolumesToRestic bool) []string {
|
||||
if !defaultVolumesToRestic {
|
||||
return GetVolumesToBackup(pod)
|
||||
}
|
||||
|
||||
volsToExclude := getVolumesToExclude(pod)
|
||||
podVolumes := []string{}
|
||||
for _, pv := range pod.Spec.Volumes {
|
||||
// cannot backup hostpath volumes as they are not mounted into /var/lib/kubelet/pods
|
||||
// and therefore not accessible to the restic daemon set.
|
||||
if pv.HostPath != nil {
|
||||
continue
|
||||
}
|
||||
// don't backup volumes that are included in the exclude list.
|
||||
if contains(volsToExclude, pv.Name) {
|
||||
continue
|
||||
}
|
||||
// don't include volumes that mount the default service account token.
|
||||
if strings.HasPrefix(pv.Name, "default-token") {
|
||||
continue
|
||||
}
|
||||
podVolumes = append(podVolumes, pv.Name)
|
||||
}
|
||||
return podVolumes
|
||||
}
|
||||
|
||||
// SnapshotIdentifier uniquely identifies a restic snapshot
|
||||
// taken by Velero.
|
||||
type SnapshotIdentifier struct {
|
||||
|
|
|
@ -419,3 +419,105 @@ func TestTempCACertFile(t *testing.T) {
|
|||
|
||||
os.Remove(fileName)
|
||||
}
|
||||
|
||||
func TestGetPodVolumesUsingRestic(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
pod *corev1api.Pod
|
||||
expected []string
|
||||
defaultVolumesToRestic bool
|
||||
}{
|
||||
{
|
||||
name: "should get PVs from VolumesToBackupAnnotation when defaultVolumesToRestic is false",
|
||||
defaultVolumesToRestic: false,
|
||||
pod: &corev1api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
VolumesToBackupAnnotation: "resticPV1,resticPV2,resticPV3",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []string{"resticPV1", "resticPV2", "resticPV3"},
|
||||
},
|
||||
{
|
||||
name: "should get all pod volumes when defaultVolumesToRestic is true and no PVs are excluded",
|
||||
defaultVolumesToRestic: true,
|
||||
pod: &corev1api.Pod{
|
||||
Spec: corev1api.PodSpec{
|
||||
Volumes: []corev1api.Volume{
|
||||
// Restic Volumes
|
||||
{Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []string{"resticPV1", "resticPV2", "resticPV3"},
|
||||
},
|
||||
{
|
||||
name: "should get all pod volumes except ones excluded when defaultVolumesToRestic is true",
|
||||
defaultVolumesToRestic: true,
|
||||
pod: &corev1api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3",
|
||||
},
|
||||
},
|
||||
Spec: corev1api.PodSpec{
|
||||
Volumes: []corev1api.Volume{
|
||||
// Restic Volumes
|
||||
{Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"},
|
||||
/// Excluded from restic through annotation
|
||||
{Name: "nonResticPV1"}, {Name: "nonResticPV2"}, {Name: "nonResticPV3"},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []string{"resticPV1", "resticPV2", "resticPV3"},
|
||||
},
|
||||
{
|
||||
name: "should exclude default service account token from restic backup",
|
||||
defaultVolumesToRestic: true,
|
||||
pod: &corev1api.Pod{
|
||||
Spec: corev1api.PodSpec{
|
||||
Volumes: []corev1api.Volume{
|
||||
// Restic Volumes
|
||||
{Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"},
|
||||
/// Excluded from restic because colume mounting default service account token
|
||||
{Name: "default-token-5xq45"},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []string{"resticPV1", "resticPV2", "resticPV3"},
|
||||
},
|
||||
{
|
||||
name: "should exclude host path volumes from restic backups",
|
||||
defaultVolumesToRestic: true,
|
||||
pod: &corev1api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3",
|
||||
},
|
||||
},
|
||||
Spec: corev1api.PodSpec{
|
||||
Volumes: []corev1api.Volume{
|
||||
// Restic Volumes
|
||||
{Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"},
|
||||
/// Excluded from restic through annotation
|
||||
{Name: "nonResticPV1"}, {Name: "nonResticPV2"}, {Name: "nonResticPV3"},
|
||||
// Excluded from restic because hostpath
|
||||
{Name: "hostPath1", VolumeSource: corev1api.VolumeSource{HostPath: &corev1api.HostPathVolumeSource{Path: "/hostpathVol"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []string{"resticPV1", "resticPV2", "resticPV3"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
actual := GetPodVolumesUsingRestic(tc.pod, tc.defaultVolumesToRestic)
|
||||
|
||||
sort.Strings(tc.expected)
|
||||
sort.Strings(actual)
|
||||
assert.Equal(t, tc.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,6 +70,8 @@ spec:
|
|||
# a default value of 30 days will be used. The default can be configured on the velero server
|
||||
# by passing the flag --default-backup-ttl.
|
||||
ttl: 24h0m0s
|
||||
# Whether restic should be used to take a backup of all pod volumes by default.
|
||||
defaultVolumesToRestic: true
|
||||
# Actions to perform at different times during a backup. The only hook currently supported is
|
||||
# executing a command in a container in a pod using the pod exec API. Optional.
|
||||
hooks:
|
||||
|
|
|
@ -68,7 +68,7 @@ velero install \
|
|||
|
||||
Velero does not currently have a volume snapshot plugin for IBM Cloud, so creating volume snapshots is disabled.
|
||||
|
||||
Additionally, you can specify `--use-restic` to enable restic support, and `--wait` to wait for the deployment to be ready.
|
||||
Additionally, you can specify `--use-restic` to enable [restic support][16], and `--wait` to wait for the deployment to be ready.
|
||||
|
||||
(Optional) Specify [CPU and memory resource requests and limits][15] for the Velero/restic pods.
|
||||
|
||||
|
@ -94,3 +94,4 @@ Uncomment `storageClassName: <YOUR_STORAGE_CLASS_NAME>` and replace with your `S
|
|||
[5]: https://console.bluemix.net/docs/containers/container_index.html#container_index
|
||||
[14]: http://docs.aws.amazon.com/IAM/latest/UserGuide/introduction.html
|
||||
[15]: customize-installation.md#customize-resource-requests-and-limits
|
||||
[16]: restic.md
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
- [Install in any namespace](#install-in-any-namespace)
|
||||
- [Use non-file-based identity mechanisms](#use-non-file-based-identity-mechanisms)
|
||||
- [Enable restic integration](#enable-restic-integration)
|
||||
- [Enable default use of restic to backup pod volumes](#default-pod-volume-backup-to-restic)
|
||||
- [Enable features](#enable-features)
|
||||
- [Enable server side features](#enable-server-side-features)
|
||||
- [Enable client side features](#enable-client-side-features)
|
||||
|
@ -45,6 +46,14 @@ By default, `velero install` does not install Velero's [restic integration][3].
|
|||
|
||||
If you've already run `velero install` without the `--use-restic` flag, you can run the same command again, including the `--use-restic` flag, to add the restic integration to your existing install.
|
||||
|
||||
## Default Pod Volume backup to restic
|
||||
|
||||
By default, `velero install` does not enable use of restic to take backups of all pod volumes. An annotation has to be applied on every pod which contains volumes to be backed up by restic.
|
||||
|
||||
To backup all pod volumes using restic without having to apply annotation on the pod, run the `velero install` command with the `--default-volumes-to-restic` flag.
|
||||
|
||||
Using this flag requires restic integration to be enabled with the `--use-restic` flag. Please refer to the [restic integration][3] page for more information.
|
||||
|
||||
## Enable features
|
||||
|
||||
New features in Velero will be released as beta features behind feature flags which are not enabled by default. A full listing of Velero feature flags can be found [here][11].
|
||||
|
|
|
@ -158,7 +158,87 @@ kubectl patch storageclass/<YOUR_AZURE_FILE_STORAGE_CLASS_NAME> \
|
|||
|
||||
You're now ready to use Velero with restic.
|
||||
|
||||
## Back up
|
||||
## To back up
|
||||
|
||||
Velero supports two approaches of discovering pod volumes that need to be backed up using restic:
|
||||
|
||||
- Opt-in approach: Where every pod containing a volume to be backed up using restic must be annotated with the volume's name.
|
||||
- Opt-out approach: Where all pod volumes are backed up using restic, with the ability to opt-out any volumes that should not be backed up.
|
||||
|
||||
The following sections provide more details on the two approaches.
|
||||
|
||||
### Using the opt-out approach
|
||||
|
||||
In this approach, Velero will back up all pod volumes using restic with the exception of:
|
||||
|
||||
- Volumes mounting the default service account token
|
||||
- Hostpath volumes
|
||||
|
||||
It is possible to exclude volumes from being backed up using the `backup.velero.io/backup-volumes-excludes` annotation on the pod.
|
||||
|
||||
Instructions to back up using this approach are as follows:
|
||||
|
||||
1. Run the following command on each pod that contains volumes that should **not** be backed up using restic
|
||||
|
||||
```bash
|
||||
kubectl -n YOUR_POD_NAMESPACE annotate pod/YOUR_POD_NAME backup.velero.io/backup-volumes-excludes=YOUR_VOLUME_NAME_1,YOUR_VOLUME_NAME_2,...
|
||||
```
|
||||
where the volume names are the names of the volumes in the pod sepc.
|
||||
|
||||
For example, in the following pod:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: app1
|
||||
namespace: sample
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/test-webserver
|
||||
name: test-webserver
|
||||
volumeMounts:
|
||||
- name: pvc1-vm
|
||||
mountPath: /volume-1
|
||||
- name: pvc2-vm
|
||||
mountPath: /volume-2
|
||||
volumes:
|
||||
- name: pvc1-vm
|
||||
persistentVolumeClaim:
|
||||
claimName: pvc1
|
||||
- name: pvc2-vm
|
||||
claimName: pvc2
|
||||
```
|
||||
to exclude restic backup of volume `pvc1-vm`, you would run:
|
||||
|
||||
```bash
|
||||
kubectl -n sample annotate pod/app1 backup.velero.io/backup-volumes-excludes=pvc1-vm
|
||||
```
|
||||
|
||||
2. Take a Velero backup:
|
||||
|
||||
```bash
|
||||
velero backup create BACKUP_NAME --default-volumes-to-restic OTHER_OPTIONS
|
||||
```
|
||||
|
||||
The above steps uses the opt-out approach on a per backup basis.
|
||||
|
||||
Alternatively, this behavior may be enabled on all velero backups running the `velero install` command with the `--default-volumes-to-restic` flag. Refer [install overview][11] for details.
|
||||
|
||||
3. When the backup completes, view information about the backups:
|
||||
|
||||
```bash
|
||||
velero backup describe YOUR_BACKUP_NAME
|
||||
```
|
||||
```bash
|
||||
kubectl -n velero get podvolumebackups -l velero.io/backup-name=YOUR_BACKUP_NAME -o yaml
|
||||
```
|
||||
|
||||
### Using opt-in pod volume backup
|
||||
|
||||
Velero, by default, uses this approach to discover pod volumes that need to be backed up using restic, where every pod containing a volume to be backed up using restic must be annotated with the volume's name.
|
||||
|
||||
Instructions to back up using this approach are as follows:
|
||||
|
||||
1. Run the following for each pod that contains a volume to back up:
|
||||
|
||||
|
@ -216,7 +296,9 @@ You're now ready to use Velero with restic.
|
|||
kubectl -n velero get podvolumebackups -l velero.io/backup-name=YOUR_BACKUP_NAME -o yaml
|
||||
```
|
||||
|
||||
## Restore
|
||||
## To restore
|
||||
|
||||
Regardless of how volumes are discovered for backup using restic, the process of restoring remains the same.
|
||||
|
||||
1. Restore from your Velero backup:
|
||||
|
||||
|
@ -370,8 +452,7 @@ on that node. The controller executes `restic restore` commands to restore pod v
|
|||
|
||||
### Backup
|
||||
|
||||
1. The main Velero backup process checks each pod that it's backing up for the annotation specifying a restic backup
|
||||
should be taken (`backup.velero.io/backup-volumes`)
|
||||
1. Based on configuration, the main Velero backup process uses the opt-in or opt-out approach to check each pod that it's backing up for the volumes to be backed up using restic.
|
||||
1. When found, Velero first ensures a restic repository exists for the pod's namespace, by:
|
||||
- checking if a `ResticRepository` custom resource already exists
|
||||
- if not, creating a new one, and waiting for the `ResticRepository` controller to init/check it
|
||||
|
@ -418,12 +499,6 @@ Velero does not currently provide a mechanism to detect persistent volume claims
|
|||
|
||||
To solve this, a controller was written by Thomann Bits&Beats: [velero-pvc-watcher][7]
|
||||
|
||||
### Add backup annotation
|
||||
|
||||
Velero does not currently provide a single command or automatic way to backup all volume resources in the cluster without annotating pods or pod templates.
|
||||
|
||||
The [velero-volume-controller][10] written by duyanghao helps to solve this problem by adding backup annotation to pods with volumes automatically.
|
||||
|
||||
[1]: https://github.com/restic/restic
|
||||
[2]: customize-installation.md#enable-restic-integration
|
||||
[3]: https://github.com/vmware-tanzu/velero/releases/
|
||||
|
@ -433,4 +508,4 @@ The [velero-volume-controller][10] written by duyanghao helps to solve this prob
|
|||
[7]: https://github.com/bitsbeats/velero-pvc-watcher
|
||||
[8]: https://docs.microsoft.com/en-us/azure/aks/azure-files-dynamic-pv
|
||||
[9]: https://github.com/restic/restic/issues/1800
|
||||
[10]: https://github.com/duyanghao/velero-volume-controller
|
||||
[11]: customize-installation.md#default-pod-volume-backup-to-restic
|
||||
|
|
|
@ -19,6 +19,7 @@ velero install \
|
|||
--velero-pod-cpu-limit <CPU_LIMIT> \
|
||||
--velero-pod-mem-limit <MEMORY_LIMIT> \
|
||||
[--use-restic] \
|
||||
[--default-volumes-to-restic] \
|
||||
[--restic-pod-cpu-request <CPU_REQUEST>] \
|
||||
[--restic-pod-mem-request <MEMORY_REQUEST>] \
|
||||
[--restic-pod-cpu-limit <CPU_LIMIT>] \
|
||||
|
|
Loading…
Reference in New Issue