diff --git a/changelogs/unreleased/2611-ashish-amarnath b/changelogs/unreleased/2611-ashish-amarnath new file mode 100644 index 000000000..6c9ad598f --- /dev/null +++ b/changelogs/unreleased/2611-ashish-amarnath @@ -0,0 +1 @@ +implement option to back up all volumes by default with restic diff --git a/pkg/apis/velero/v1/backup.go b/pkg/apis/velero/v1/backup.go index 72aafc95e..d1e2cb48c 100644 --- a/pkg/apis/velero/v1/backup.go +++ b/pkg/apis/velero/v1/backup.go @@ -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. diff --git a/pkg/apis/velero/v1/zz_generated.deepcopy.go b/pkg/apis/velero/v1/zz_generated.deepcopy.go index d8e111b71..7e5e8ad39 100644 --- a/pkg/apis/velero/v1/zz_generated.deepcopy.go +++ b/pkg/apis/velero/v1/zz_generated.deepcopy.go @@ -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 } diff --git a/pkg/backup/backup.go b/pkg/backup/backup.go index 18a5657d5..026aba6b7 100644 --- a/pkg/backup/backup.go +++ b/pkg/backup/backup.go @@ -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) diff --git a/pkg/backup/backup_test.go b/pkg/backup/backup_test.go index 5d9f5c8b2..d4bdc9aa5 100644 --- a/pkg/backup/backup_test.go +++ b/pkg/backup/backup_test.go @@ -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{} { diff --git a/pkg/backup/item_backupper.go b/pkg/backup/item_backupper.go index e0889ff0c..d9fd2822b 100644 --- a/pkg/backup/item_backupper.go +++ b/pkg/backup/item_backupper.go @@ -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, diff --git a/pkg/builder/backup_builder.go b/pkg/builder/backup_builder.go index 6d0b1dc68..75f95d950 100644 --- a/pkg/builder/backup_builder.go +++ b/pkg/builder/backup_builder.go @@ -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 diff --git a/pkg/cmd/cli/backup/create.go b/pkg/cmd/cli/backup/create.go index 2ba0986c9..d73928c0d 100644 --- a/pkg/cmd/cli/backup/create.go +++ b/pkg/cmd/cli/backup/create.go @@ -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() diff --git a/pkg/cmd/cli/install/install.go b/pkg/cmd/cli/install/install.go index ce8185348..1065a1729 100644 --- a/pkg/cmd/cli/install/install.go +++ b/pkg/cmd/cli/install/install.go @@ -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") diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index f4ecc866f..48c032b09 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -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, diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 05f549df5..85380144b 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -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) diff --git a/pkg/controller/backup_controller_test.go b/pkg/controller/backup_controller_test.go index 87d457a93..b746cb734 100644 --- a/pkg/controller/backup_controller_test.go +++ b/pkg/controller/backup_controller_test.go @@ -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), diff --git a/pkg/generated/crds/crds.go b/pkg/generated/crds/crds.go index 11a95305e..3cd478009 100644 --- a/pkg/generated/crds/crds.go +++ b/pkg/generated/crds/crds.go @@ -29,7 +29,7 @@ import ( ) var rawCRDs = [][]byte{ - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\xaf?\xd5\r\u0087Qr\xb7\xc2a\x0e&\xa8\xa1\xad\x14\xba\xf8\xa2\x9c\xfdKc\xd7\xd7#\x80k)\x84\x1dR\x89-*p\xa80\xf3\xc6\x0e\xb1aZ\xa8a\xcc\xfb\xa8\x11\xde\rx\xab\xe84\xa3\vm;*3\n\x13\xe0\xf5 \xb3CؼH_\x18\n\xe4\x06\x1dۯ(K\xf5>L\x1cLK:\x8c\t\x13nƤ1\x9f\xc2\xea\x9bu3f\xfd\\3f<^\x97\x97\xb5\xe8\xff~X\x99\x1c\xf7ي\xb9\xe9-\xfcH\xc5$&J\n@7;\xc0\xa2\xf4\xef\xd7 }zJ1\xae\xe0\xf4i\x94=\xf5\xbb\u007fs\x828W\xa77\xa7\xeb>P\xa7\u007f\xa5\x14\xeaW\xfff\x84\xc0\xce\xfe1\xfa\xfa\x85\x02\xf8\xa5\xbd\xe6\x1a\xe4\xae\x16@~\r;\xa9<\xda\x13IL\x91k\xa6%\xf1kY0\xbfS\xd1(\x84\xcf\x0e\xf7o\x14\x1d\xb9\xa6걈\x1b\xa7KCL\x99\xa2\xea\xeef:\t\x158e\x92\x16\x8b\x90\x88=1\a\x9b'\x1c\xf9|\xfe\xfa\x05\xf3q\xa6\xc0\x12\r\xeb\x91\xf0\xf9\x04\xcd\xf6kc\x88\xbc\x8c\x80\x18\xa4\xd4\xd9EH\xaa\xafA\xc0\v\xbe\x87\xe8\x82R\xfc\x12\xad\xa0\xd7\xd0\xe4Y\x88\x169\xb3g\x85z\xc1w\x06\x12\x93\xf5\x99\xb5\xcbD\x1f\xc6\v\xbe\xcfO:a\x1ba#],>\x10\xff\xe8\x013\x803\xbd\xa5,\x03.\xb5$\x0f3G\x14,u\x11i$n\x9fM^-\xa6\xa6:\x10\x04y\xe5\x82PH\xdb\x0f\xb2\\D \xb9Np\xc86\x91J-\xcfBɼ~M\xd0\uf37e\x86\xaf\xc6o\xf4X\xb0\xda\x1d\xf7o\xd2\xc5\n\xd7\x17\x83\xee\xab\xf1\xfc\xe4Ù\x18P>\x9b\x85a\x19\x9b\x90\x0en\x98\xe8oWlf\x958\x8cMȰj\x91H\a\x1bM9D\xe0U\xa8\xb9\x85\x97My\xfb\xee(*\xc7%\x19m\xf4\x8a7\xbb\xf5\xd0{\"\x8b\x17*r[\n}\xb4\xeaW\x86\xd7-\x82\xf8D\xfbBX\x1d\xea\x87Jd\x98C^1\x13\xb9\xfe%<\xeee\x06\x05\xda\xfd\xf8F\xd0\x1e%\xf9\xec%\xaf_\xe4K\xc38K\x9f\x96l\xcdiDg\x9cϡ\xb1\"ۜ\x9d\x93D;3q\xb0\xe05>q\x8e\x0e\xde$9n\x98\xe1\xa6\xc8s>\x8e\x10\xeaa\xb1\xf7^\xcc\xf9\xfe\xbe\x1dP\n{\\!J\xb2\xce\xff\xa5\xad\x8a\x95\xf6\xff\xa0\x14\xd2\xceZ\xe8g>WP\xd8Y\x19\xabB\xed\x97\x10|逤y\x14\xea\xb4l:@\x96!\xaf\x81*l\xc3f\u05cb4\xae\xe1\xf5`\\\xd8\x15w\x12U\x0er*Ңq\xf9\x82\xef\x97\xd7=\x1b\xbf\xdc\xe8˰=\xf7,6\xed\xe53\x80\x8dV\xefp\xc9+/\xbf=tY\xa4u\v&\xf1!Ӳ`\x96\xb2\xb9\xb4\x8bӲ\xfa\xa4\x82B\xd1ql\x17\xe8\\i\x9c_\x88ăq>T\xe8:\xc1\xe3@mh:\xa7\x895!\x10\xbbp:dl:\a GvR\xaa$)9\x1c,p\xf6 \xe6\x11\xa4P\n.\x1b\x1b\r\xfe\xf12\x1c\x0e\xf0+D\xc6a\xc1\x04DR\x85Қ\f\x9d\x9bR\x87Y\xcf;Sp\xab\x8bm\"$\x15\xa1\xd4>U\xdcKci\xd8H\xac9+̾\u007fk\xd5\x00ɴ\xe9\xffi5;\x0f#\xe0\x93ڢ\x10zv\xb3\xe8!w\x17\xd6%S\x88`B\xc8n\xf7\x15\x9b\xf1\xd2H/*\xcd\xf7\xdd`\v\xa97\f\x1c>}\xe8v\f\xc9%\xe2\xf9!\xf5]Zٰ\xb9~\x10l\xb34\xfd\x92\xfb\xd0x=\xa0Ŏ\xa4\xfa\x95a\x0e\xe7\xb4\xf1\xad\xf4|\x19\xa3\x03\x1eW\x0ev\xd2:\xdfF\xd2A5i\xb5- g\xe5(\xfa\xde\xdaoHQ\xfe\x18ֵ\n@\a\xf3\x9a\xce\xd3\x02C\x16Q\xcc\xc7 \br\a\xd2\x03\xea\xccT\x9a\x8b\x18d\xa4\xfc\x82\xc0\xd2\xe0Lg7\xd90\x96\x186\r\xd4U\xb1\x84\xf0\x15k\x8f\xd4\x13\xb5\x8e\xf6䟅\x9c\xaaT\xa5q\x96\x98\xbc,\xd0T\x13\x9bZ3:bz\n\xeb:\a\xa1\x85x\x93EU\x80(\x88ً8J;\xb3,\xb0+_x\x15ҳw'\xa8\xec\xea\xbd!\xa3(\x15\xfae\xd9\xc0\x16wƲ-:\x99c\xbdeF\x99\x1b\r\x02vB\xaa\xca.\xf2hgptyd\x1f\x8d\xfcc\x82\xf6%\xaf]1\xf9\xb3e\xcaE\xa1ڔW-\xed\xd2@\xed\xc1\xe2G\x86H\xa5\x95\xa43\xe6c\xa3\xa4\xa8JB\xbf\xff\b\x93Z\xbc\xf9\x11&\xf5Ə0\xa93~\x84I?¤\xc9\xf1#L\xfa\x11&\xfd\xbd\x86IӘ\xac\xb8n5\xf8\xd3\xcc\xdbg\x8fP\xc7\x11\x1b\x85\x1cO\xf5\xefB\x87\U000b2fbc\xcd\U0001a5bfz=\xa0?\xa0M\x8d\xcf+\xee\xcb\xee˹9\xfao\xdc|ݨGʟ\x947\xb4_N\xb6\xee-h\xc4\xdb\x1a\xa3P\xe8!\xfa\x97\xf6f\x0e4\x95t{\x12\xebƎԔh\xd2+zԧ~o\n3\xdb\x1d\fB\xa9vo\x8a\xb0\rS\xbeS\xbf\xe2l\xeb\xc7L\xc3\xc7t\xdb\xe68\x87NB\xfb.\x8bl\xa7\xc5\xf0;sh\xb2/c\xbc\x1b#\x9ed\xa0\x17\xc7O\xeb\xee/\xde\xc4\xde\fx\x95\xfe\xd0#\x80\x9b&)e\xd1\xfbvsdҩ\xd8d\u007f\xca90\x16\xb4T׃}1\xf5\xfd\x836;\xe1\x8feH\x8aβ\xb7\xa9\xd0~I\xef\xc67wlt{2\x06\x9d\xecy\x87\x1dK[H\x97\xf7dt{.F6\x99\x05\x9d\x18gwZ\xcc\xe7[\x93]\x15\xdf\xd0K\x91\xfa$\xa66܉\x0e\x8a\x051\xc7|\xb7\xc47\xf5H\xf0a\xde\x04\xd6guF\xb4\xba\x1e&@.\xeb\x87X\xc0\x92\xb9އ\xb3;\x1eN\xbb\f&\x88\x98\xebs\x18\xefa\x98\x00:\xd8ݰ\xa4sa\x02f\xdd\xd3\xf0\x81\xfd\n3]\n\x1f\xd3I\xf8kcϱ\x9e\x83\x99N\x83\x99\xc8t\n\xab\x99^\x82\xe5\x1d\x043\xfc\xf9\xc6n\x81\xba\x1f`\xf0\x9d\xe7\xf6\bt\xbb\x00\x06A.\xec\f\x189\xfb\x1f\x04\xb9\xa0\x1f`\xe6\xc4\u007f\x10\xec\xe4\xc68\xa1\x11\xa3?9-Jw0\xfe\x99\xaf4NG\x91\x8fݹ\x03\xc9\x05\xc58\xe2\x05!S\xa6\xcak\xd8}R\xf8\xa2\xe2;<<\xb3\x93\xe7\xab0Ys\x11(\xba\xf2\x14\xfc\x9c\xde\x13\xfa\xe9#\x93\r\xe7\x8d\x15{\xfc\xc5d\xad\xfb\xa8c\xf4w\xe7v.\x0fF\xa1\xa6\x94>\xf5A\x88t\x8b\xad\xbbt(v\x8cU\xb6\x10\x1f\xb6\xb2/°/\xefQ\xcb\xf3^M\x12\xf1\xf4\xf4K@\xdc\xcb\x02\xd7_\xaa\x90ȭJa\x1d\x12\xff\x12Aaі\xfe<\x98\xd7\x1e\xc2\xcaDJ\u007f:\xc5\xd7\"\xd7\xf08[\\\x8cu\xb8Q\x9b\x14,\xb1iZ\x1d\x9f\x87״bіPBbcvc\xabz\x04\xb6\xae\xfbR\xb4\x1f:Z>\xea\x8aڰs\x1e\xbe\"酯\xdc\xdc%I\x9e\x94\xae<NJoe\xf9\x86Y\x00\x10\x94\xf1\xec{\x92\xb1\xbcչ\x87>%\x93\xbb\xfe|\xbepl\xf3\x80\x14\x97\xd5DR\xf2W\xe1\xea\x02ڀGk\x80\x85u\x1c\f\x10,\xcc\x01\x8f\xa8\xc1h\xae\x97\xf1\r\xaep\x1b\xfetM?\u007fm\xc1\x88帪TF\xe4\xc9r#j\xe9\x12\xf5\x13\xfb#{D{\xe5F!V.\x16G\x06\xc8?լ\x9d\xb1\x85\xf0\xb7\x90\v\x8f\xab\x01\x80\v\xfc\u0600Jq\xf1x\xe6\xea&O\t\xd6\xc1ugV\t\xa5b\xe1\xb9@\xe7\xc4>\xdd\xd9|%w\xb4GM\x9b\xdc@\x95(\x86bM\xe1\xb2{\u007f1dt\"\xf3\x94\xff\x06\xd4b\nۚuշ9e\xf6\x94a\xf3\xc4x\xaf:\xfa\xe7aG\"\xb5\xc7=v\xc3#|+\xa5\x9d\xf7\xe5\xf7\xf54\xe2\b\xa7\xeel\xe1\xcdg\x06Pɽ$\x87H\x82\xdd\v\xbb\x15{\\eFQ\x1e%\x8d>\xc5\xe8\xaf#\xd7\x00u\xe0\x1b\x02=\x82~n\xcfL\x89`T\xe6\x00%}R\xe0:\xee\xa8$\xc1B\xfc\xc5\xd8\xfeaM!\xb5\xb1!|\xe1\x10:-]\xec\xcf\xf9Z\xeb$\xbe\x0f4\xa3>\x9dj\xf9*L\xca4\xbc\xcf\x0f\x9db\xac\xe0+\x9enQ\xe1`\x02\xf3\xe7\xfa[\x13\xbd\t\x1b\xfd`͞\"\xbe\xdeOw\xc9+\xf5~y\x10\xd6K\xa1\xd4{\x00?\xf2\xd6\xde\xe3/H~ad#\x18b`\xc4l\x9a\x87qR\x13RJ\x1dd͇\t[S\xf9\x8e\xc15\x06ۓxzߚ\xf2DLu\x03مH; :\xbf\xc2\xdd\xceX\x1f\xe2\xd7\xd5\n\xe4.n,=\xa8䝹\xf2\x15\xbeY\x00\xd27Y\\\xa3\x9b\x1c\vZ\x14\x8eu\xd3\xf3w\x13\xb8\xec,\xb2\x8c\xe2\x13\xbcq^\xa8\x9e\x0f\xf8\xe6r\x17\xefפ]\x98\xff\xb9\xb7\x9d\xf5\x98\xbciϮ\xfb\\\xabb\x8b\x964\x95\x81\x05~\xf1\xd1^\xf0zj8\xa5\xdb\"jx\xb5\xd2{\xf27\xed\x82 x\xf20J\x813\xb0\x13\x83w\x86\xc7}\x1e\xffj\xbcP\x9b\xb1\x84\xb6\x1b\x02\xd6S\x139\xbc\xb8O\x94!1l\x99\xf4ArBs\x87ti%\t.;\b\xbd'\x05\xb2\xa6\xda\x1f\x92\x06\x8e\xec\x14\xc3E\xbc\x8a\x10\x82RU{R\xe9XX\xf3\x95խ\xcc4\x96\xda\xf2\x16\xaa\"{\x81\xaa\x1c>y\x0e_T\x89\x1fĹ\x89w\x81W;k\x8aU\xe4?\xd7̮c\xc5\xceJC!\x13\xe74\xf1:\xde\bX\x16{Y\xa2\x06\xe1\".\xb3}'S\x82\x1cOԼ\xb0~Y\x10\xf6ؙ:\x13\u007f1\\\xcc\xd7\xf0\x88\xa5 c\xeb\x1b\xb15\x05ܝ~\x8e\xe8\x9ar\xf4\xf4\xed\x9d\xf0\xe9\x94 z\xcak\xc1\"\xa7-\xe1\x0e^\x0fb'\xa0\xea\x04P]\xd4\xff6\xb1S\xf35\xa2\xfb\xf9(\xea\xf9d\xf2\xc9A\nYp\x03/\xc5>\xbf\x93\xbb~~Q\x96Jf\x84\xed\xef\xbf\xd3\x01\xc9qATq5\x19Pp\xf4P\xc7\x06\xf0\x05K\x8b\x19Ye\x1f\xf9\a\x85\xb4\xdf;\xc4n\xa4r\xb58\xb0릈\xee\xb3\xf7X\x94\x03\xef\x9a\xc8\x11\x9bEc\x8eO\xa4\t=\x02\xd27\x9f\x12\xa8\xd8\t0\x9a\x14.&\xa4\x0e5\xce!\xa4^4F\x88\xab2r@\xbbjh+\xaas\xae\x0f\xa4\xeaUXJ\xb4\xa7\xad\xe7?⤁,$\xae\xff\xd8<\xa4\x95\x86$\xfc\xfeF\x89Ȁ\x1f?y\xd4|\xf2\xedS\xf3\x1f\xb3o\x15?\xf1v\f\xfdd\xec-\xf3\x96iGTⓦ@ \xb2\fIw\xbf\x9e~\xed\xed\xf2\x92\xffI\x1ft\xe3\u007f3\xa3\xc3^\xean\xe1?\xff\xeb\x02b\x9d\xe99\xe1A\x0f\xff?\x00\x00\xff\xffً\xab\xc5\x1eO\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xecwà\x18C\xe7\xf2(\xf3J(\x90\xday\xa1\xb3@\x87\xa8q:\xa5\x03\xc6\xc5\xd9\xc36\xb8\xb5\x843\xf1\xbe\xe3\xe2\x8cF0\x16\n\xf2\xe6\xfd\xa9n\x10>\x8c\x92\xbb\x15\xe4kLPC[)t\xf1E9{\xceƮ\xafG\x00\xd7R\b{\xbf\x12[T\xe0Pa\xe6\x8d\x1dbôPØ\xf7Q#\xbc\x1b\xf0V\x8d\xff%\x12ێʌ\xc2\x04x=\xc8\xec\x10\xb6e\xd2\x17\x86\x02\xb9A\xc7\xf6+\xcaR\xbd\x0f\x13\aӒ\x0ec\u0084\x9b1i̧\xb0\xfaf\u074cY?\u05cc\x19\x8f\xd7\xe5e-\xfa\xdf\x0f+\x93\xe3>[17\xbd\x85\x1f\xa9\x98\xc4DI\xa1\xf5f\aX\x94\xfe\xfd\x1a\xa4OO)\x92\x10\x9c\x18\x8e\xb2\xa7~\xf7oN\x10\xe7\xea\xf4\xe6t\xdd\a\xea\xf4/\x94B\xfd\xeaߌ\x10\xd8\xd9?F_\xbfP\x00?\xb5\xd7\\\x83\xdc\xd5\x02ȯa'\x95G{\"\x89)rʹ$~)\v\xe6w*\x1a\x85\xf0\xd9\xe1\xfe\x8d\xa2#\xd7\xd4s\x16q\xe3ti\x88)ST\xdd\xddL'\xa1\x02'\x83\xd2b\x11R\xcc'\xe6`\xf3\x84#\x9f\xcf_\xbf`>\xce\x14X\xa2a=\x12>\x9f\xa0\xd9~m\f\x91\x97\x11\x10\x83\x94:\xbb\b\xe5\x82k\x10\xf0\x82\xef!\xba\x10\x1aH \x82^C\x93g!Z\xe4\x9a\x05+\xd4\v\xbe3\x90X\x86\x98Y\xbbL\xf4a\xbc\xe0\xfb\xfc\xa4\x13\xb6\x116\xd2Ų\n\xf1\x8f\x1e0\x038\x87]\xca2\xe0\"R\xf20sD\xc1R\x17\x91F\xe2\xf6\xd9\xe4\xd5bj\xea\x1eA\x90W.\b\x85\xb4\xfd \xcbE\x04\x92\xeb\x04\x87l\x13\xa9\x88\xf4,\x94\xcc\xeb\xd7\x04\xfd\xde\xe8k\xf8j\xfcF\x8f\x05\xab\xddq\xff&]\xac\xdd}1\xe8\xbe\x1a\xcfO>\x9c\x89\x01\xe5\xb3Y\x18\x96\xb1\t\xe9\xe0\x86\x89\xfev-jV\x89\xc3\u0604\f\xab\x16\x89t\xb0єC\x04^\x85jbxٔ\xb7\uf3a2r\\l\xd2F\xafx\xb3[\x0f\xbd'\xb2x\xa1\"\xb7\xa5\xd0G\xab~ex\xdd\"\x88O\xb4/\x84ա2\xaaD\x869\xe4\x153\x91+{\xc2\xe3^fP\xa0ݏo\x04\xedQ\x92\xcf^\xf2\xfaE\xbe4\x8c\xb3\xf4i\xc9֜Ft\xc6\xf9\x1c\x1a+\xb2\xcd\xd99I\xb43\x13\aKy\xe3\x13\xe7\xe8\xe0M\x92\xe3\x86\x19n\x8a<\xe7\x83\x16\xa1\x1e\x16{\xefŜ\xef\xef\xdb\x01\xa5\xb0\xc7\x15\x82\vt\xffK[\x15+\xed\xffA)\xa4\x9d\xb5\xd0\xcf|b\xa2\xb0\xb32V\x85\xda/!\xf8\xd2\x01I\xf3(\xd4iAx\x80,C^\x03U؆ͮ\x17i\\\xc3\xeb\xc1\xb8\xb0+\xee$\xaa\x1c\xe4T\xa4E\xe3\xf2\x05\xdf/\xaf{6~\xb9їa{\xeeYl\xda\xcbg\x00\x1b\xad\xde\xe1\x92W^~{\xe8\xb2H\xeb\x16L\xe2\xe3\xb3e\xc1,esi\x17\xa7e\xf5\x19\f\x85\xa2\xe3\xd8.й\xd28\xbf\x10\x89\a\xe3|\xa8\xd0u\x82ǁ\xda\xd0tN\x13kB v\xe1\xdc\xcb\xd8t\xc2A\x8e\xec\xa4TIRr8X\xe0\xecA\xcc#H\xa1\x14\\66\x1a\xfc\xe3e8\xf6\xe0W\x88\x8cÂ\t\x88\xa4\n\xa55\x19:7\xa5\x0e\xb3\x9ew\xa6\xe0V\x17\xdbDH*\xc2!\xc2Tq/\x8d\xa5a#\xb1\xe6\xac0\xfb\xfe\xadU\x03$Ӧ\xff\xa7\xd5\xec<\x8c\x80Ϡ\x8bB\xe8\xd9͢\x87\xdc]X\x97L!\x82\t!\xbb\xddWl\xc6K#\xbd\xa84\xbf\xee\x06[H\xbda\xe0\xf0\xe9C\xb7cH.\x11\xcf\x0f\xa9\xef\xd2ʆ\xcd\xf5\x83`\x9b\xa5\xe9\x97܇\xc6\xeb\x01-v$կ\fs8\xa7\x8do\xa5\xe7\xcb\x18\x1d\xf0\xb8r\xb0\x93\xd6\xf96\x92\x8e\x0f\xb6>>G\xd1\xf7\xd6~C\x8a\xf2簮U\x00:\x98\xd7tR8r874\xf8\x18\x04A\xee@z@\x9d\x99Js\x11\x83\x8c\x94_\x10X\x1a\x9c\xe9\xec&\x1b\xc6\x12æ\x81\xba*\x96\x10\xbeb\xed\x91z\xa2\xd6ў\xfc\xa3\x90S\x95\xaa4\xce\x12\x93\x97\x05\x9ajbSkFGLOa]爷\x10o\xb2\xa8\n\x10\x051{\x11Gig\x96\x05v\xe5\v\xafBz\xf6\xee\x04\x95]\xbd7d\x14\xa5B\xbf,\x1b\xd8\xe2\xceX\xb6E's\xac\xb7\xcc(s\xa3A\xc0NHU\xd9E\x1e\xed\f\x8e.\x8f죑\u007fLо\xe4\xb5+&\u007f\xb6L\xb9(T\x9b\xf2\xaa\xa5]\x1a\xa8=X\xfc\xc8\x10\xa9\xb4\x92t\xc6|l\x94\x14UI\xe8\xf7\xefaR\x8b7\xdfä\xde\xf8\x1e&u\xc6\xf70\xe9{\x9849\xbe\x87I\xdfä\xdfk\x984\x8dɊ\xebV\x83?ͼ}\xf6\bu\x1c\xb1Q\xc8\xf1T\xff.\xf4^/\xeb\xcb\xdb\f\xaf\x19軌-\xdd+\xee8\xef˹9\xfao\xdc|ݨGʟ\x9474\x96N\xb6\xee-h\xc4\x1b\xea͜o/\x99k*\xe9\xf6$֍\x1d\xa9)ѤW\xf4\xa8O\x9d\xec\x14f\xb6;\x18\x84R\xed\xde\x14a\x1b\xa6\xfcJ\xfd\x8a\xb3\xad\x1f3\r\x1f\xd3m\x9b\xe3\x1c:\t\xed\xbb,\xb2\x9d\x16\xc3_\x99C\x93}\x19\xe3\xdd\x18\xf1$\x03\xbd8~Zw\u007f\xf1&\xf6f\xc0\xab\xf4\x87\x1e\x01\xdc4I)\x8b\u07b7\x9b#\x93N\xc5\xeb\x03\xa7\x9c\x03cAKu=\xd8\x17S߬h\xb3\x13\xfe\\\x86\xa4\xe8,{\x9b\n\xed\x97\xf4n|s\xc7F\xb7'c\xd0ɞwر\xb4\x85tyOF\xb7\xe7bd\x93YЉqv\xa7\xc5|\xbe5\xd9U\xf1\r\xbd\x14\xa9ObjÝ\xe8\xa0X\x10s\xccwK|S\x8f\x04\x1f\xe6M`}VgD\xab\xeba\x02\xe4\xb2~\x88\x05,\x99\xeb}8\xbb\xe3\xe1\xb4\xcb`\x82\x88\xb9>\x87\xf1\x1e\x86\t\xa0\x83\xdd\rK:\x17&`\xd6=\r\x1fد0ӥ\xf01\x9d\x84\xbf4\xf6\x1c\xeb9\x98\xe94\x98\x89L\xa7\xb0\x9a\xe9%X\xdeA0ßo\xec\x16\xa8\xfb\x01\x06\xdfyn\x8f@\xb7\v`\x10\xe4\xc2\u0380\x91\xb3\xffA\x90\v\xfa\x01fN\xfc\a\xc1Nn\x8c\x13\x1a1\xfa\x93Ӣt\a\x93.lM\xc6I\x8fݹ\x03\xc9E\xba\xae\x95)S\xe55\xec>)|\x05\xf3\x1d\x1e\x9e\xd9\xc9\xf3U\x98\xac\xb9\b\x14]y\n~N\xef\t\xfd\xf0\x91Ɇ\xf3Ɗ=\xfed\xb2\xd6M\xdb1\xfa\xbbs;\xd7\"\xa3PSJ\x9f\xfa D\xba\x9f\xd7]:\x14;\xc6*[\xbc\xe7\xd6d_\x84a_ޣ\x96罚$\xe2\xe9駀\xb8\x97\x05\xae\xbfT!\x91[\x95\xc2:$\xfe%\x82¢-\xfdy0\xaf=\x84\x95\x89\x94\xfep\x8a\xafE\xae\xe1q\xb6\xb8\x18\xebp\x97/)XbӴ:>\x0f\xafiŢ-\xa1\x84\xc4\xc6\xec\xc6V\xf5\bl]d\xa6h?t\xb4|\xd4\x15\xb5a\xe7<|\xf9\xd3\v_\xb9\xb9\xeb\x9f<)]\xe6\x8e\x15\xdf\xca\xf2\r\xb3\x00 (\xe3\xd97@cy\xabs\xc3~J&w\xfd\xf9|\x95\xda\xe6\x01).\xab\u05579_\x85\xab\vh\x03\x1e\xad\x01\x16\xd6q0@\xb00\a<\xa2\x06\xa3\xb9^\xc67\xb8\xc2=\xff\xd35\xfd\xfc\xb5\x05#\x96\xe3\xaaR\x19\x91'\xcbM\xf7L\xe3\xf5\xf0'\xf6G\xf6\x88\xf6ʍB䫪;c\x87\xc8?լ\x9d\xb1\x85\xf0\xb7\x90\v\x8f\xab\x01\x80\v\xfc\u0600Jq\xf1x\xe6\xea&O\t\xd6\xc1u\xe7t\x976\x14\x9e\vtN\xecӝ\xcdWrG{Դ\xc9\rT\x89b(\xd6\x14.\xbb\xf7\x17CF'2O\xf9o@-\xa6\xb0\xadYW}\x9bSfO\x196O\x8c7ƣ\u007f\x1ev$R{\xdcc7<·R\xday_~_O#\x8ep\xea\xce\x16\xde|@\x01\x95\xdcKr\x88$ؽ\xb0[\xb1\xc7Uf\x14\xe5Q\xd2\xe8S\x8c\xfe6r\rP\a\xbe\x8e\xd0#\xe8\xc7\xf6̔\bFe\x0eP\xd2\xc7\x12\xae\xe3\x8eJ\x12,\xc4_\x8d\xed\x1f\xd6\x14R\x1b\x1b\xc2\x17\x0e\xa1\xd3\xd2\xc5\xfe\x9c\xaf\xb5N\xe2\xfb@3\xeaө\x96\xaf¤L\xc3\xfb\xfc\xd0)\xc6\n\xbe\xe2\xe9\x16\x15\x0e&0\u007f\xae\xbf\xa2ћ\xb0\xd1\x0f\xd6\xec)\xe2\xeb\xfdt\x97\xbcR\xef\x97\aa\xbd\x14J\xbd\a\xf0#o\xed=\xfe\x82\xe4\x17F6\x82!\x06F̦y\x18'5!\xa5\xd4A\xd6|\x98\xb05\x95\xef\x18\\c\xb0=\x89\xa7\xf7\xad)O\xc4T7\x90]\x88\xb4\x03\xa2\xf3+\xdc\xed\x8c\xf5!~]\xad@\xee\xe2\xc6҃Jޙ+_\xe1k\f }\x93\xc55\xbaɱ\xa0E\xe1X7=\u007f\x11\x82\xcb\xce\"\xcb(>\xc1\x1b\xe7\x85\xea\xf9\x80o.w\xf1~Mڅ\xf9_z\xdbY\x8fɛ\xf6\xec\xbaϵ*\xb6hIS\x19X\xe0\x17\x1f\xed\x05\xaf\xa7\x86S\xba-\xa2\x86W+\xbd'\u007f\xd3.\b\x82'\x0f\xa3\x148\x03;1xgx\xdc\xe7\xf1\xaf\xc6\v\xb5\x19Kh\xbb!`=5\x91Ë\xfbD\x19\x12ÖI\x1f$'4wH\x97V\x92ಃ\xd0{R k\xaa\xfd!i\xe0\xc8N1\\ī\b!(U\xb5'\x95\x8e\x855_Y\xdd\xcaLc\xa9-o\xa1*\xb2\x17\xa8\xca\xe1\x93\xe7\xf0\xad\x98\xf8\xa9\x9f\x9bx\x17x\xb5\xb3\xa6XE\xfes\xcd\xec:V\xec\xac4\x142qN\x13\xaf㍀e\xb1\x97%j\x10.\xe22\xdbw2%\xc8\xf1D\xcd\v\xeb\x97\x05a\x8f\x9d\xa93\xf1\x17\xc3\xc5|\r\x8fX\n2\xb6\xbe\x11[S\xc0\xdd釖\xae)GO_\x15\n\x1f\x85\t\xa2\xa7\xbc\x96\xbf\xeea,\x86;x=\x88\x9d\x80\xaa\x13@uQ\xff\xfb\xc4N\xcdw\x96\xee磨\xe7\x93\xc9'\a)d\xc1\r\xbc\x14\xfb\xfcA\xee\xfa\xf9EY*\x99\x11\xb6\u007f\xfc\x95\x0eH\x8e\v\xa2\x8a\xabɀ\x82\xa3\x87:6\x80/XZ\xcc\xc8*\xfb\xc8?(\xa4\xfd\xde!v#\x95\xabŁ]7Et\x9f\xbdǢ\x1cx\xd7D\x8e\xd8,\x1as|\"M\xe8\x11\x90\xbef\x95@\xc5N\x80Ѥp1!u\xa8q\x0e!\xf5\xa21B\\\x95\x91\x03\xdaUC[Q\x9ds} U\xaf\xc2R\xa2=m=\xff\x11'\rd!q\xfd\xc7\xe6!\xad4$\xe1\xf7wJD\x06\xfc\xf8ɣ\xe6cv\x9f\x9a\xff\x98}\xab\xf8\xf1\xbac\xe8'co\x99\xb7L;\xa2\x12\x9f4\x05\x02\x91eH\xba\xfb\xf5\xf4;v\x97\x97\xfcO\xfaT\x1d\xff\x9b\x19\x1d\xf6Rw\v\xff\xf9_\x17\x10\xebL\xcf\t\x0fz\xf8\xff\x01\x00\x00\xff\xff\xff\x8cC\xfd\xf8O\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xbcX\xcdr\xdb6\x10\xbe\xeb)v܃/\x15\x95\xa4=txs\x94v&S'\xf1X\x89{h;\x13\bXJ\xa8A\x80\xc5\x02rԧ\xef,\bR\"E\xd9\xceLS\xde\b,\x17\xdf~\xfb\v\xce\xe6\xf3\xf9L4\xfa\x0e=igK\x10\x8d\xc6/\x01-\xbfQq\xff\x13\x15\xda-v/\xd7\x18\xc4\xcbٽ\xb6\xaa\x84e\xa4\xe0\xea[$\x17\xbd\xc47Xi\xab\x83vvVc\x10J\x04Q\xce\x00\x84\xb5.\b^&~\x05\x90\xce\x06\xef\x8cA?ߠ-\xee\xe3\x1a\xd7Q\x1b\x85>\x9dН\xbf{Q\xbc*~\x9c\x01H\x8f\xe9\xf3\x8f\xbaF\n\xa2nJ\xb0ј\x19\x80\x155\x96\xb0\x16\xf2>6\x14\x9c\x17\x1b4N\xb6g\x15;4\xe8]\xa1\u074c\x1a\x94|\xf4ƻؔp\xd8h5dX\xadI\xaf\x93\xb2U\xab\xec:+K\xfbFS\xf8\xf5\xbc̵\xa6\x90\xe4\x1a\x13\xbd0\xe7`%\x11\xd2v\x13\x8d\xf0g\x84f\x00\x8dGB\xbf\xc3O\xf6\u07ba\a\xfb\x8bF\xa3\xa8\x84J\x18\xc2\x19\x00I\xd7`\t\xef\x19}#$\xaa\x19\xc0N\x18\xad\xd2\xf7\xad=\xaeA{u\xf3\xf6\ue1d5\xdcb-\xdaE\x00\x85$\xbdn\x92ܴ%\xa0\t\x04t`\xe0a\x8b\x1e\xe1.\x91\x06\x8c\x14)\xc3\xce\x1a\x01\xdc\xfa/\x94\x81\x8a\xbc\xd0xנ\x0f\xbac\x96\x9f\xa3\xc8\xea\xd7F`.\x19m+\x03\x8ac\t\t\xc2\x16!G\x04*\xa0d\t\xb8\n\xc2V\x13xL4\xd9ppR\x8f\xa8\x02a3\xae\x02VL\xa5'\xa0\xad\x8bFq\x00\xee\xd0\a\xf0(\xdd\xc6\xea\u007fz\xcd\x04\xc1\xa5#\x8d\b\x98\xdd\xd9=\xda\x06\xf4V\x18\xe69\xe2\xf7 \xac\x82Z\xec\xc1#\x9f\x01\xd1\x1eiK\"T\xc0;\xe7\x11\xb4\xad\\\t\xdb\x10\x1a*\x17\x8b\x8d\x0e].IW\xd7\xd1\xea\xb0_\xa4\x8c\xd0\xeb\x18\x9c\xa7\x85\xc2\x1d\x9a\x05\xe9\xcd\\x\xb9\xd5\x01e\x88\x1e\x17\xa2\xd1\xf3\x04ܶ\xe1]\xab\xef|N<\xba\xa3\x03\xf1L\x94\xb6\x15\xfa\xd6q\x95wu҈V5Nې^\xa4\xd1h\x87\xa4S\\\xd7:\xb0\xa7\xff\x8eH\x81\xfdS\xc02U\x14X#\xc4F\x89\x80\xaa\x80\xb7\x16\x96\xa2F\xb3\x14\x84ߜvf\x98\xe6L\xe9\xd3\xc4\x1f\x17¡`\xcbV\xbf\xdcըI\x0fM\xa6\xe9\xaaA9\xc8\x13V\xa1+\x9dӶr\x1eDN\xdb\x01\xa7\x93ʊ#\x91\xa9\xf4M),%\x12\xbds\n\x87\xeb#\xb0W\xbd\xd8\x00]\x83\xbe֔\x9aI\xc2\xc6km\x19\x81\\\xfeFJ\xa1/A\xc5h\am\xac\xc7\x10\xe6p\x8bB}\xb0f?\xb9\xf1\x9b\xd7a|\xc0\xa4\xc3\xf8ia\xad\xf6Vޠ\xd7N=j\xee\xeb\x91po\xf4\xd6=@\x95\x02\xd7\x06\xb3\xe7\xcaB{+ǥ\xb3{\xaen\xdeve\xb4M\x8f\x9cM\x99\x9b\x02\xaerV\xba\n^\x80\xd2$\xd6\x06)\xa9\x1c\xd3\xc3͑wK\b>>\xdbh\xe9l\xa57cS\x85R\xa9\xa3\vss&*\x1eU:\xe2j\x99\xce\xe0R\xc3\x11\xd0x\xb7\xd3\n\xfd\xbc\v܌!\xfa\x1c\xc1\xa9鍭\x9b\xcc\x1e\xe8\xcbO\x8e\xebG]\xf6\xe1X\xb2\xefz\x19E\x97L\x18\xb8\xee\x11X\xe4p\x16~\x1cW\xc0\x1e\x95\xceZ\xf6Rp z{.i\xec\xbcѧ\xe7\x12\x8c\x9fu\x94\xf7\x18N\xd7\xc7Q\x97ĘɔG\xed[p\x10\t\x13\xb7\x8f\x03x\xc2g\x00R,\xd1?\x8dby\xc5b}\xc4\vX^\xc1:Ze\xb0\xc3\xf2\xb0E\xcb\r\\W{\xee\"\x1f\xafW\x13:\xa1\xe31\x15\x87܂;6\xa7\xb0W\xce\xd7\"\x94\xb0ޟ$\xf5\x93\xa65\x1e+\xfd\xe5I\xd3n\x92XGp#\xc2\x16\xb4%\xad\x10\xc4\x04\xdd\x13e\xb6{\xfa\x04\xfeд\x89\xf4\x95\xce\xe0\n\xa2=\x9e\x14\xa1y\x86\xf1\xdc\xf4\xe8\xf8|43n\xb2Pow\xf7\x9eF\xaeq\xc1\x9eN\xcd\x13+\xa6,\x98\x0f\xd3u\xb0\xd3\x1d\xfad\xcb\f\"D\xfaʦ\x99\xbeɂ\xeb\x9c\xeb2z\x8f6d\x85\xe0\xaaa\xdb\xebf\xdfo\xdd8/\x8e:'Oc\x16\xa2\x8d\x84\xaa-\x84\x05\xfca\xe1\r\xcfV\x92g\x9e\x92\x91\xf3\x98C'\xe1d\xdd\x03\u007f|\xa4-)\x00g\x93\xb5in\xe0\xe9\xb5\x1d\xc5\xd2փ6\x86\a*\x8f\xb5ۥ\xdb\xc4\xf0\xe1\xe9ǣك`z`\xf7\xaaxQ\\\xfc\xcf]\xd9\b\n\xdcfQ\xdd\xe2N\x8fo\x12\xa7l^\x9f\xc8wQ\xdd7R~\xf9܍h\v\x9f\xc5>\x9f\x98_i\xc3\x03\xe5D\n\x1c\xaeI\xed\xb5\x81\x02\x04]cz{\xbd\xba\xbe\xa4t\xdb\xe5a\xf8D\xe9\x03\xbb\x8f\x12@\xbe\\\xb8<\x03G\n\xe8'\x9c\xdd\xfbJ\x13X\a\xc6\xd9\xcd E\xda'O\xc4\xe0<\xb4\xa1\xe3<(\xe4a\x96˯\xdc\n\xbb\xc1\xc3-'c?BɁq\x8at\x18\x1d\x87h\xd0v:\x14\x9e\xe1C\xbe\xcc?\xea\xbf\xeb\x81h\xe7\xba!\xc3=\xea\xecKs|_\u007f6\xd7#鮹0\x91s>\xea?\x19\xaf\x9a\xad\xa0\xc7\r\xbea\x89\xce\xce\xe3\x92ԇ\xea\x93\x05\bΦ\xe1\xd5N\xe8\x84\xfad\xe7\x93\x15g\xf6\xce\xd82Q\x8bGK\x87_8/\x0fo\xa9&\xce\xf3/\x9b\xb4\x01\x90~q\xa8#\"sV\xe5\x95C\x81\xe7\n\xda\x04T\xefǿk..\x06\xff\\ҫt\xb6\x1dZ\xa9\x84\xdf\xff\x9c\xb5ZQ\xddu8x\xf1\xdf\x00\x00\x00\xff\xff\xc1e\xcb@\xee\x12\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4\x96\xcdr$5\f\x80\xef\xfd\x14\xaa\xe5\xb0\x17\xa6\x87\x00\a\xaao\x90]\xaaR@*\x95,\xb9P\x1c<\xb6fZ\xc4m\x1bI\x9e\x10\x9e\x9e\xb2\xbb;\xf3\x93Ne9l\xdf,\xcb\xfa\xf9$\xb9ݬV\xab\xc6$\xbaG\x16\x8a\xa1\x03\x93\b\xffQ\fe%\xed\xc3\x0f\xd2R\\\xef/6\xa8\xe6\xa2y\xa0\xe0:\xb8̢q\xb8E\x89\x99-~\xc0-\x05R\x8a\xa1\x19P\x8d3j\xba\x06\xc0\x84\x10\xd5\x14\xb1\x94%\x80\x8dA9z\x8f\xbc\xdaah\x1f\xf2\x067\x99\xbcC\xae\x1ef\xff\xfbo\xdao\xdb\xef\x1b\x00\xcbX\x8f\u007f\xa2\x01E͐:\b\xd9\xfb\x06 \x98\x01;p\xe8Qqc\xecCN\x8c\u007fg\x14\x95v\x8f\x1e9\xb6\x14\x1bIh\x8b\xe3\x1dǜ:8l\x8c秠Ƅ>TS?US\xb7\xa3\xa9\xba\xebI\xf4\x97\xd74~\xa5I+\xf9\xcc\xc6/\aT\x15\x84\xc2.{Ë*\r@b\x14\xe4=\xfe\x1e\x1eB|\f?\x13z'\x1dl\x8d\x17l\x00\xc4Ƅ\x1d\\\x97\xa8\x93\xb1\xe8\x1a\x80\xbd\xf1\xe4*\x9e1\x8f\x980\xfcxsu\xffݝ\xedq0\xa3\x10\xc0\xa1X\xa6T\xf5\x96r\x00\x1200E\x02\x1a\xa7\x00!\x06\x84\xc80DF\x18\xa3\x95v2\x998&d\xa5\x99`\xf9\x8e\xfa\xe7Yv\xe6\xfc}\x89n\xd4\x01W:\x06\x05\xb4G\x98\xea\x8e\x0e\xa4F\x0eq\vړ\x00c\xc5\x12\xc6\x1e:2\vE\xc5\x04\x88\x9b\xbf\xd0j\vw\x05\x1d\vH\x1f\xb3w\xa5\xcd\xf6\xc8\n\x8c6\xee\x02\xfd\xfblYJ~ť7:\x17x\xfe((r0\xbep\xcd\xf85\x98\xe0`0O\xc0X|@\x0eG֪\x8a\xb4\xf0[\x81Ca\x1b;\xe8U\x93t\xeb\xf5\x8et\x9e\x18\x1b\x87!\aҧu\xed{\xdad\x8d,k\x87{\xf4k\xa1\xddʰ\xedI\xd1jf\\\x9bD\xab\x1ax\xa8\x03\xd3\x0e\xee+\x9e\xc6K\xde\x1fE\xaaO\xa5\x13D\x99\xc2\xeeY\\{\xf8U\xee\xa5\u007f\xc72\x8f\xc7\xc6\xf8\x0fx\x8b\xa8P\xb9\xfdx\xf7\tf\xa7\xb5\x04\xa7\xcc+\xed\xc319\x80/\xa0(l\x91\xc7\xc2m9\x0e\xd5\"\x06\x97\"\x05\xad\v\xeb\t\xc3)tɛ\x81T\xe6\xf6+\xf5i\xe1\xb2\xde\x1b\xb0A\xc8\xc9\x19E\xd7\xc2U\x80K3\xa0\xbf4\x82_\x1c{!,\xab\x82\xf4m\xf0\xc7\xd7ݩ\xe2H\xebY<\xdfE\x8b\x15Z\x18˻\x84\xb6Ԭ\x80+giK\xb6\x8e\x01l#\xc3cO\xb6\x9f\xc7\xf2\x84\xe8\xf3\x00\xb7G⥁-\xdfh\xa0\xdc*\xa7\xf2W\x92\x85Z'b<\xe9\xb5Ց\x997)\xa8\xd1,\xff\x8bC=1\x93\xb0\x99\x19\x83Nv\xea-\xb0t\xe8srG\xe6\xc8r\x9e\xf7I8\x1f\xabJ\xfdk\x19\n\x02&t\xb1\xb1\x16\x93\xa2\xbb>\u007fN\xbc{w\xf2.\xa8K\x1b\x83\xa3\xf15\x04\u007f\xfcٌV\xd1\xdd\xcfq\x14\xe1\u007f\x01\x00\x00\xff\xff\xcb0\x9b\f\x8c\t\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WOoܶ\x13\xbd\xebS\f\xf2;\xe4W \xd26m\x0f\x85n\xad\x93\x02A\xd3 \xb0\x13_\x8a\x1e\xb8\xe4\xacĚ\"Y\xcep\x1d\xf7\xd3\x17CI\xfbG+\xdb\xe9\xa1{\xd3p8|||oȭ꺮T\xb4\xb7\x98\xc8\x06߂\x8a\x16\xbf0z\xf9\xa2\xe6\xeeGjl\xd8\xec_o\x91\xd5\xeb\xea\xcez\xd3\xc2U&\x0e\xc35R\xc8I\xe3\x1b\xdcYo\xd9\x06_\r\xc8\xca(Vm\x05\xa0\xbc\x0f\xac$L\xf2\t\xa0\x83\xe7\x14\x9c\xc3Tw蛻\xbc\xc5m\xb6\xce`*+\xcc\xeb\xef\xbfm\xbek~\xa8\x00t\xc22\xfd\x93\x1d\x90X\r\xb1\x05\x9f\x9d\xab\x00\xbc\x1a\xb0\x05\x13\xee\xbd\v\xca$\xfc+#15{t\x98BcCE\x11\xb5,ڥ\x90c\vǁq\xee\x04h\xdc̛\xa9\xcc\xf5X\xa6\x8c8K\xfc\xeb\xda\xe8{;eD\x97\x93r\x97 \xca Y\xdfe\xa7\xd2\xc5p\x05\x10\x13\x12\xa6=~\xf6w>\xdc\xfb_,:C-\xec\x94#\xac\x00H\x87\x88-|\x10\x94Qi4\x15\xc0^9k\n\x15#\xee\x10\xd1\xff\xf4\xf1\xdd\xed\xf77\xba\xc7A\x8dA\x00\x83\xa4\x93\x8d%o\x89\x1b,\x81\x82\t\x05p8\x00\x03\xe5A%\xb6;\xa5\x19v)\f\xb0U\xfa.ǩ&@\xd8\xfe\x89\x9a\x818$\xd5\xe1+\xa0\xac{PRmL\x04\x17:\xd8Y\x87\xcd4%\xa6\x101\xb1\x9dY\x96߉\xbe\x0e\xb1\x05\xe0\x97\xb2\xa31\a\x8c(\n\t\xb8G\x98t\x81\x06\xa8\xec\x16\xc2\x0e\xb8\xb7\x04\t\v\x95~\xd4\xd8IY\x90\x14\xe5'\xe4\r\xdc\b݉\x80\xfa\x90\x9d\x11\x19\xee11$ԡ\xf3\xf6\xefCe\x12^dI\xa7x\x16\xc2\xfc\xb3\x9e1y\xe5\xe4,2\xbe\x02\xe5\r\f\xea\x01\x12\x16v\xb2?\xa9VR\xa8\x81\xdfBB\xb0~\x17Z\xe8\x99#\xb5\x9bMgyv\x94\x0eÐ\xbd\xe5\x87M\xf1\x85\xddf\x0e\x896\x06\xf7\xe86d\xbbZ%\xdd[F\xcd9\xe1FE[\x17\xe0\xbe\x18\xaa\x19\xcc\xff\xd2d?zy\x82\x94\x1fD=\xc4\xc9\xfa\xee\x10.:\u007f\x94w\xd1\xf9(\x8fqڈ\xffH\xaf\x84\x84\x95\xeb\xb77\x9f`^\xb4\x1c\xc19\xe7\xa3N\x0e\xd3\xe8H\xbc\x10e\xfd\x0e\xd3xpEeR\x11\xbd\x89\xc1z.\x1f\xdaY\xf4\xe7\xa4S\xde\x0e\x96i\x96\xad\x9cO\x03W\xa5\xaf\xc0\x16!G\xa3\x18M\x03\xef<\\\xa9\x01ݕ\"\xfc\xcfi\x17\x86\xa9\x16J\x9f'\xfe\xb4\x1d\x9e'\x8el\x1d\xc2s\xbfZ=\xa1\x85\x95o\"j9/!M\xe6ٝ\xd5\xc5\x02\xb0\v\t\xd4\xd1\xd9\x13m\xcdI\xdd5o\x16P*u\xc8\xe7\xb1\x05\x8aO%E\x16\xbe\xef\xd5y\v\xf9?6]#}\x80&\bcg\xf8\xa6Y\xd4{l\xf55\x8d\xaeb\x98\xa5*[\x17\x1e\xc5\xe8\xd2zN\xd1,\x17\x95\x1f\xfa<\xac\x15\xaf\xe1\xe7\x82\xf4}\xe8\x9e\x18\xbd\n\x9eE\xd0O\xa4\xdc\x06\x97\a\xbc\xf1*R\x1f\x9e̜/\xcd\xc3E\xb2L\xbbFi\xb5\xf8\x18\xa4i\xf8\x1a)\xbbՅV\x858\xff\xca\xc5\xf9\x1c\xcbr\xf7\xcc,˄\xb1\xe3\"ȅ\x9d<2ұ\r\xdc[\xeeᾷ\xba_\xa9\neZ9 \xe9/DA\xdb\xe2\xd8\u007f\a[tl\x13^ȣ.\xa2\xb9\b\n\xe4j\xad\xf8\xc2s\xeb\x85\xeb\xc9\v\xcf:\x96\x15g\xfajϖ\xec\x99T\x9dSB\xcfS\x8dr[-'|\x8dig\xc5\u007f\xbe~\xff\xa4s\xdf\x1c\xf3\xca\x1bLY?\xe2\x88\tk\xb2\x9dܭ2&\xde-\xceZ\x120\xfeN\xef\xf8gO\r\xbfD\x9bN\x9e,\x8f@{{H\x1b\x1b\v\xfa\xf1\x8aX\xbe^J9\xa4r\xedj\xe5/\xb0m\x11\f:d4\xb0}\x18;\xe3\x031\x0eK\xbc\xbb\x90\x06\xc5-\xc8\xc5Q\xb3\xbd\x10\x8a\xbc/\xd5\xd6a\v\x9c\xf2\xba\x8aV6\x1b{E\x17\xb6:\xdb\xe7G\xc9X;\xfe\x83\xb9\x9e8\u007fx\xa4\x83\xd5\xf0\x01\xef/b\x1fS\xd0H\x84Kc<\x82~E܋\xd0\xf1a\xfe\xfa\xf8U\xa4XO\x0f\xf12\x00P\x9e\xb5愺\xe9\xcd8E\x8e\x8eQZcd4\x1f\x96O\xf1\x17/\xce\xde\xd6\xe5S\ao\xec\xf8/\x02~\xff\xa3\x1a\xab\xa2\xb9\x9dqH\xf0\x9f\x00\x00\x00\xff\xff\xbbظ3\xc4\f\x00\x00"), @@ -37,7 +37,7 @@ var rawCRDs = [][]byte{ []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Y_s۸\x11\u007fק\xd8\xf1=\xb87\x13RMz\xd3\xe9\xe8\xed\xcen:n\xef\x1cO\xe4\xcbK&\x0f\x10\xb1\x14Q\x93\x00\x8b]HV;\xfd\xee\x9d\x05HI\x94(Y\xce\xf4rzI\b,\x16\xbf\xfd\xed\x1f,\xe0I\x96e\x13՚O\xe8\xc98;\x03\xd5\x1a|f\xb4\xf2E\xf9\xd3_(7n\xbaz\xbb@Vo'O\xc6\xea\x19\xdc\x04b\xd7|Dr\xc1\x17x\x8b\xa5\xb1\x86\x8d\xb3\x93\x06Yi\xc5j6\x01P\xd6:V2L\xf2\tP8\xcb\xde\xd55\xfal\x896\u007f\n\v\\\x04Sk\xf4q\x87~\xff\xd5\x1f\xf3w\xf9\x0f\x13\x80\xc2c\\\xfeh\x1a$VM;\x03\x1b\xeaz\x02`U\x833h\x9d^\xb9:4\xe8\x91\xd8y\xa4|\x855z\x97\x1b7\xa1\x16\v\xd9u\xe9]hg\xb0\x9bH\x8b;Dɚ\a\xa7?E=\x1f\x93\x9e8U\x1b\xe2\u007f\x8cN\xffl\x88\xa3H[\a\xaf\xea\x11\x1cq\x96\x8c]\x86Z\xf9\xe3\xf9\t@\xeb\x91Я\xf0W\xfbd\xddھ7Xk\x9aA\xa9j\x92i*\\\x8b3\xb8\x17\xa4\xad*PO\x00V\xaa6:\U00091c3b\x16\xed\x8f\x0fw\x9f\xfe4/*lT\x1a\x14ͮEϦ7Q~{\xdeݎ\x01h\xa4\u009b6j\x84kQ\x95d@\x8b?\x91\x80+\x84\xce+\xa8\x81\xe26\xe0J\xe0\xca\x10x\x8c6\xd8\xe4\xe1=\xb5 \"ʂ[\xfc\x13\v\xcea.vz\x02\xaa\\\xa8\xb5\x04\xc1\n=\x83\xc7\xc2-\xad\xf9\xf7V3\x01\xbb\xb8e\xad\x18;\x86\xfb\x9f\xb1\x8cުZH\b\xf8\x06\x94\xd5Ш\rx\x94= \xd8=mQ\x84r\xf8\xc5y\x04cK7\x83\x8a\xb9\xa5\xd9t\xba4\xdc\xc7s\xe1\x9a&XÛi\x8cJ\xb3\b\xec\xdc\xf1,!\xf2+\xa5\xca?(\xae^\xdc\xf5\xfa\xaeL\xdbĊ\xc0\x0e\x14\xb4\x06\v\x1c\x94V0\x96\x18\x95N\x83#*\x01$q\xbf\xfb2\xc6\x19\xc0{\xe7\x01\x9fU\xd3\xd6\xf8\x06Lby[\xd0\xfa\xf80\x94\x88\xd8ꃵ\xe1ʌ\x1b\xae$\x8e:\x83\xd7\xd1PVO\b\xae34 \xd4\xe6\tgp%\x19\xbc\a\xf1?\x92:\xff\xbd\x1a\xd5\xf9\x87\x94\"W\"r\x95\x80mϬ\xfd\x8c\xdb\x01\xe4J1\xb07\xcb%z\x1cg3\x16b)p߃\xf3b\xbbu{\n\xa2Z\xf1Y\xaa3\xa8\x8f\x00\u007f~\xf7\xe5\x04\xda!O`\xac\xc6gx\a\xc6&VZ\xa7\xbf\xcf\xe11F\xc4Ʋz\x96}\x8a\xca\x11Zp\xb6ތ\xa3uP\xa9\x15\x02\xb9\x06a\x8du\x9d\xa5^A\xc3Zm\xc4\xfe\xde]\x12a\nZ\xe5y\xd8\r\x8cj}\xfcp\xfba\x96PI\b-c\x1d\x93S\xa64r\xe6\xcba\x9fN.\x89\xc9HGH\xc1\xc1\x0e\x8aJّ\xb2\x06\xb1i\x88\xec\x96AΒ\xfc\xfa\xb5\xd9zxl\xf7\xbf\x91\xe3\xfb\xb00\xfcN\x87\xe0Ef\xc5\xd6\xf9E\xb3\xee\xf7\xe2\xf9\xacY\xd2\xc4{\x8b\x8c\xd12\xed\n\x12\xa3\nl\x99\xa6n\x85~ep=];\xffd\xec2\x93@\xccR$\xd04\xb6\xe1\xd3\xef\xe2?_eE\xec\x8c/3%\x8a~\v{d\x1f\x9a\xbeڜ\xbe\xaf\xbb\xf4T\xba\x9ew\x8d\xc7\xe1JI\x89ue\x8a\xaao\xd2w\xd5s4G\x1a\xa5S\xc9Uv\U000db1ed\x10\x19\xbc\xe0\xd9d\xdd]0SV\xcb\xff\xc9\x10\xcb\xf8\xab\x99\v\xe6\x82$\xfd\xf5\xee\xf6\xdb\x04s0\xaf\xce\xc8ц4\xc5D\xeb\xee\xb4\xd0W\x1a\xf4g\xbb\xa9\x8f\x03Ѿ\v\x1c\xe9\xe3\xb62\x177rdUK\x95\xe3\xbb۳\b\xe6[\xb1~\xf7\x1d\xe5]\xfb\xd6k\x92\x10=ӷ\x9dD\x92ԜE\x91\xfa\xee\xb1.\xb8Ð:\x868\"\x1d\xe8W!\x91됴9\xfbH\xb2\xf1\x0e~ \xd1:=\xf8\x1e\xfaw0\xb5#}0\x9c\x8cx\xf12Ê\x03]~\x9d\x89\xe2=g)?\xb9S\x12\xcf\uebfa\xd0\x14N\x9a\xb9\xe1\xe3\xcd9\xcf\xdd\x1c\xcb\xc7\x17\x02\xaf\x13.6\r\xc6\xdbBD\x00kE\xfd\x16\xc7~\x83=mia\xac\x84\xa2\ful\xb6\xa4\x0f,\x95\xa9Q\xc3\xf6\xe9\b\x1e\xe5>\x17\xaf\xcc\xd7ǵ\xb2W\x13\bu\xbc\xe7\x8d\x00>\\U:\xdf(\x9e\x81\\\x933Qp0oC]\xabE\x8d3`\x1f\x0e'O\xa6A\x83Djy>\x0f~I2\xe9\x86\xd5-\x00\xb5p\x81\xb7W\xac.!:\xf3\xaf\xa9\xf3\xf8\xe5\x17\xbcJ\xd1y\x10\x0f\"1\x16Wۤ<\x17X\x10o/\xa19\xdc\"\x83{\\\x1f\x8d\xdd\xd9\a\xef\x96\x1e\xe9\xd0\aY﨣\xf6;\x83\xf71\x02.6\xb8\xdb\xe0\xbc͝\x10T\xae\xee#ױ\xaa\xc1\x86f\x81^\f_l\x18\xa9g\xa0O\xf4\xe3\x1bj\xecyw\xbc\xed\xd6\xf7\xd5*)\xea:\xf8B\xd9\xf8$#\xd1\xc9\x0e\xb4\xa1\xb6V\xc7-|oC<\xf6$8%Cvq\xd1g\x97\xa4t\x9c{͝:¹uv\xb4#\xebS\xc1X\xfe\xf3\x0f'\xcfGc\x19\x97\x83R\xd8\xcd\n\x85?\x89\xfe\xff\xb7\ue4c7/\xb1\xf2|Y\xe9\x9a\x0fD_\xaaZQ\xf1X\xcd\xda/?\xc7\xe5f\xb8ɷ\xa84#\xd4\x1c\f\xed\x1e\xec\xdf\uefa2\x8b\xb2\xee\x81>N@2K\xefm\xde=Fu#\xbb\x03K\x15\xd2k\xa1\xbe?|\xa1\xbf\xba\x1a<\xb8\xc7\xcf\xc2Ym\xd2_\x17\xe0\xf3\x97\ttOT\x9fz\x1c2\xf8\xbf\x00\x00\x00\xff\xff6\x10(\x86\xdc\x18\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4W\xcdn\xe36\x10\xbe\xfb)\x06\xdb\xc3^*\xb9i{(tk\xd3.\x104\t\x16\xce6\x97\xa2\a\x8a\x1a\xd9\xd3P$\xcb\x19:u\x9f\xbe %ٲ-{\x83\x05V7\x0e\x873\xdf|\xf3CjQ\x14\xc5Byz\xc6\xc0\xe4l\x05\xca\x13\xfe+hӊ˗\x9f\xb8$\xb7\xdc\xde\xd4(\xeaf\xf1B\xb6\xa9\xe06\xb2\xb8n\x85\xecb\xd0\xf8+\xb6dI\xc8\xd9E\x87\xa2\x1a%\xaaZ\x00(k\x9d\xa8$\xe6\xb4\x04\xd0\xceJp\xc6`(\xd6h˗Xc\x1d\xc94\x18\xb2\x87\xd1\xff\xf6\xbb\xf2\xfb\xf2\xc7\x05\x80\x0e\x98\x8f\u007f\xa2\x0eYT\xe7+\xb0ј\x05\x80U\x1dV\x10\x90\x85t@\xef\x98\xc4\x05B.\xb7h0\xb8\x92܂=\xea\xe4v\x1d\\\xf4\x15\x1c6\xfa\xd3\x03\xa4>\x9cU6\xb4\x1a\r\xed\xf2\x96!\x96\xdfg\xb7\xef\x89%\xabx\x13\x832s@\xf26\x93]G\xa3\u0099Br\xe0\x032\x86-\xfea_\xac{\xb5\x1f\bM\xc3\x15\xb4\xca0.\x00X;\x8f\x15<&\xa8^il\x16\x00[e\xa8Ɍ\xf4\xe0\x9dG\xfb\xf3ǻ\xe7\x1f\x9e\xf4\x06;\xd5\v\x93e\xe71\b\x8d1\xa6o\x92߽\f\xa0Aց|\xb6\b\uf4e9^\a\x9a\x94Qd\x90\r\u0090\x17l\x80\xb3\x1bp-Ȇ\x18\x02\xe6\x18l\x9f\xe3\x89YH*ʂ\xab\xffF-%<\xa58\x03\x03o\\4M*\x83-\x06\x81\x80ڭ-\xfd\xb7\xb7\xcc .\xbb4Jp\xa0x\xfc\xc8\n\x06\xabL\"!ⷠl\x03\x9d\xdaA\xc0\xe4\x03\xa2\x9dX\xcb*\\\u0083\v\bd[W\xc1F\xc4s\xb5\\\xaeIƊ֮\xeb\xa2%\xd9-s]R\x1d\xc5\x05^6\xb8E\xb3dZ\x17*\xe8\r\tj\x89\x01\x97\xcaS\x91\x81\xdb\\\xd0e\xd7|\x13\x86\xf2\xe7\xf7\x13\xa4\xb2Kic\td\xd7{q\xae\xb2\x8b\xbc\xa7\"\x03bPñ\x1e\xff\x81\xde$J\xac\xac~{\xfa\x04\xa3Ӝ\x82c\xce3ۇc| >\x11E\xb6\xc5\xd0'\xae\r\xae\xcb\x16\xd16ޑ\x95\xbcІ\xd0\x1e\x93α\xeeHR\xa6\xff\x89Ȓ\xf2S\xc2m\xeek\xa8\x11\xa2o\x94`S\u009d\x85[ա\xb9U\x8c_\x9d\xf6\xc40\x17\x89\xd2\xcf\x13?\x1dGNJ=[{\xf18-f3t\xda\xffO\x1euJXb-\x1d\xa4\x96t\xee\x01h]\x00u\xa6_N\f\xcf5g\xfaj\xa5_\xa2\u007f\x12\x17\xd4\x1a\uf75e\xb4\xf9\x05T\xbf̝\x18a\xa5\x11\xd77*\xce+\x9eX\x06\x90\x8d\x92I\x87\x8a\"\xbbo\xf3\x998.R\x9eiW\xa9]\xad\xb2\x1a?\xe4ڱzw5\x96\x87\x99\x03)\x94\x8d{\x05\xd7\nک\xc9\x11e\x8dgA\x84h\xdf\f\xb2\x9f\xc9wM*\xad\x960\\\x05\xb8:Q\x1eyn\xa31\x83\xa5B\xbb\xce+\xa1\xda\xe0\xd8ȭ\vg\x10\xa9\xb7\xb1\xeb\xbb\xfa\xcb\xf8\xdd:\x13;\xdc\xdf\rW\x91?\x1f\xebN\v\xa4\x17\f R\b\x10\x8e\xaf\xc0\xe97\xd4\x04\x83w\xcd\x00`(ZNq\xbe\x11{J.\x05<\x9a\x86\xc5|\xf1\x1fi\xccUԑ\xc2i6\x8f6O\xf8\xfa\xec0\x10%\x91\xdf>\x0e\xb2\xfaH\xac\x8e!\xa0\x95\xc1H\xbe\t\xbfh \x18\xc52i\x8b\xf4\x06\xba\x9a\xe7\xfbs\xfd\x11R2\x05\x92\x04\xd3.zU<\xd7/\xad\v\x9d\x92\n\xd2h/ҡ\x93\xfd\xf4\x02S\xb5\xc1\n$\xc4\xd3\xcd\xcb\x13\x01\x99\xd5\xfaz\x04\x0f\xbdN\u007f\x15\x0e\a@\xd5.\xca\x05b\xf3\xa5x\x85ګ\x88\xfcF\xf1u<\x1f\x93\xc6\\Z\xf1\xad\xce\xd1\xc6\xee\xd4E\x01\x8f\xf8z&[\xa1jN{\xae\x80G's\x1b\x17b\x9a\xa9\xe5\x13\xd1\xe1\x89}sX\xe5\xba+\x86'u\xde\x00\xc8/\xd3f\x92b\xee{s\x90\x1c\x1aDi\x8d^\xb0y<}R\xbf{w\xf4B\xceK\xedlC\xfd\xff\x00\xfc\xf9ע\xb7\x8a\xcd\xf3\x88#\t\xff\x0f\x00\x00\xff\xfft\x8f\x1aC\x8e\f\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xd4YMo\xf3\xb8\xf1\xbf\xfbS\f\xf2?<\xbb@\xac\xfc\x9f\xb6\x87·4O\x16\bv7\r\x92\xa7\xe9\xa1聖\xc66\x1b\x8a\xd4\xf2ʼn[\xf4\xbb\x173$\xf5bɲ\x17\xedb[]\fS\xc3\xe1̏\xf3\xae\xc5r\xb9\\\x88F\xbe\xa2u\xd2\xe8\x15\x88F\xe2\x87GM\xff\\\xf1\xf6{WHs\xb3\xff\xbcF/>/ޤ\xaeVp\x17\x9c7\xf53:\x13l\x89_p#\xb5\xf4\xd2\xe8E\x8d^T\u008b\xd5\x02@hm\xbc\xa0eG\u007f\x01J\xa3\xbd5J\xa1]nQ\x17oa\x8d\xeb U\x85\x96O\xc8\xe7\xef\xff\xbf\xf8M\xf1\xbb\x05@i\x91\xb7\u007f\x955:/\xeaf\x05:(\xb5\x00Т\xc6\x15Xt\xdeXt\xc5\x1e\x15ZSH\xb3p\r\x96t\xd8֚Ь\xa0{\x11\xf7$A\xa2\x12\xcfq;\xaf(\xe9\xfc\xf7\xfd\xd5\x1f\xa4\xf3\xfc\xa6Q\xc1\n\xd5\x1dƋN\xeamP¶\xcb\v\x80ƢC\xbb\xc7?\xe97m\xde\xf5w\x12U\xe5V\xb0\x11\xca\xd1kW\x9a\x06W\xf0HR4\xa2\xc4j\x01\xb0\x17JV\xacb\x94\xcb4\xa8o\x9f\x1e^\u007f\xfbR\xee\xb0\x16q\x11\xa0BWZ\xd90]\x96\x0f\xa4\x03\x01\xaf\xac\x1f\t\xc1\x17\x01~')\x0eX7\xfep\rB\xa9\xbe\x03\n\xdb\x01\xf8\xeb\x1aԅ\x8e\xf5pL}ƱN#46\x8e>F\x9d\xa5%\xba\x14Y\xff\v\x00Sb\x8d\xea\x05\x15\x96\xde\xd8Y\xb0~\xe8SF\xa0(/\xee?\x17\xc37\xde\xc0F*\x8f\x16ޥߍ\x14xߡN8QE\"u%\xf7\xb2\nB\r\xac\xac\x87R\a&\x18\vZ\xaa\xeb\x11O\xc28\xef\x1e`\n\u007fd\xe1\x85\xfaY>x*\xc5\xd2S\v_\xee\xee?\xa88r]\xf73\x03\xdb\xf1\x86\x88\\N_\f?\xb8\x8c\x1dUH\xd2b\xcdu\xd7\x04g\x80\xafle\x1d\x15\xeb{\xfb\xf8el@pڈFB\xde\xce\b\x92|\xa2\xbd^\xca.9\x11Or\x86T9_\x83\x807<\xc4\"\x9b\xea\xf8\x86Bifa\x91\xcbs\xbe\xe87<0Q\xaa\xb8'\xb9\xce]J|\xde\xf0p\xeaՑ\xbat^\xaa~\xa2\u07b4\xc0Rq=\x96U\xe5\xee\nO)I\x8f7\xd3\xc2¼\xa7\xe6'#r\xa1\xd8-\x80\xbd.\x90!\xfe\xe4\"\x9cd_;\x19\x1b\xbc\x19\xa9\x1d\xb2\xed\xe5\xfe\xe6\x95:Ֆy\xb4\xa8\a}\r\x8f\xc6\xd3\xcf\xfd\x87\xa4\x9a]\xe8q\x06\xed\x9e/\x06ݣ\xf1L\xfboA\x12\x85\xba\x10\x90H\xcc\x06\xaacl#\xbd\xfa\xed\x8f\xe3\xe8A\xb7\x9a\xf5\x9bQB:jA\x8c͚s\xdb\x1a\x8f\x88\xcc\xeb\xe0\xb8c\xd1F/9\"e\xee3L\xdbK\x93.Ci\xec\x00\xaf\x13\a\xcd\xf0\\#\xa4\xe3\xbfR#\x16\xf7\xc4VZ\x89\x12+\xa8\x02C\xc0\xad\xa0\xf0\xb8\x95%\xd4h\xb7sr6\x14\xa7N_\xddL$\x89\xcf\x05w{:\v\xe5'\x85\x9dj\xfa\xa0%\xd9\xfa\x897\xb3\xd7;٫]&\x15\x87oNp\x93ڋ\xaa\x921\xc3<\x9d\x89Og\xf0\x19\xe7\x8cxhJ\xb4\xa2!\xcb\xfe\a\x85S6\x94\u007fB#\xa4u\x05\xdc\xf2\xbcJM\xdfl\x9f>U\x1e}\xd6\xc4U: \xcc\xf7BQ\xa8\xa7\xc0\xa1\x01\x15\a\xfeI\x96f3\xcah\xd7\xf0\xbe3.F\xf1\x8dD\xc5S\x88\xab7<\\]\x0f<\x0f\xe4t(\xbdz\xd0W1I\x8c\xfc\xa0m\xf8\x8cV\a\xb8\xe2wW\xc5(\tN\xb2\x9dM\x8c3\x16q\xf2U[\xe9\xfe(\x9aF\xea\xed\xf1=_f\v3v0\xb0\x81ǣ\xd3\x06\x86\xd0/K\a%\xfc\xf8\xb88\xe6\x9b(\xf6m\x1e6jo\n\xb8Շ\x11WG\x1d\xe3D\xa9;\xec I\xa4w\xa9\x14E\xa5ijb\xa6}Fi\xb0\xe0D\x1d\xf9O\x0f\r&@O\x1c\x9f^\xe7+\xf9\xe7\x96l\xa2\x0f\xec)K\x95b\xab\xc0\xd3\xeb\xd8r\xb8\xf8tZ4ng<|\xb3\x97\"M\xb5L\xa8\x1ak\xf6\xd4\x0f~\xfb\x1f\xea\xe8\\\xb9\xc3*(<;\xb4y\xe9\x11\x9e\x1f\xdbd\xb6c[\xe8ph;\xb9\x8cV\x15=p8\x1eJ-L\xe2K\x97<\xd5D\xb7\f\xa39\x18\xc73Y\n'.\x94%:\xb7\t*w<<\xf6\xa7\xf6:\x92K\xd7J{\xe1\x18i*C,{ï\xb3\x13:/|pggtL\x05\xa5h|\xb0\xa9H-\x83\xb5\xacT|g6\xa31\xdd\x05S:\xb4\xd6\xd83\xd3\x1e&\x89\xee^\x9a\xa0\xb9V#\xbb\xe5\xbdP\xa3sb\x9b\xc7<\xefh\x11\xb6\xa8)\xc2N\x8c8R\x1d\x80\x1fX\x86\xf4\x91`اR$\x15\xa5\xa7\xf6+\x8a\xc6a\xb3u\xe2S9\x93\b\xc4\xf6ĝI\xedq\x8b\xc3L\xbc\x11R\x05\x8b\xcf(\xdc\xf0\xdb\xc1H\xfd\xef\xfa\x94\xa9\xb4\x8b\x9a\xc7\xceC\x04\x87U\x1a;{i\xf1\xe4l\x92\xfaP!G\x9d\xdf\xc9\xf8\xdb섛w\xc3'\xa2\xc8\xfe\xd77\x87\xd6\x03\x9f'eA\x1d\xeac\xc6Kx\xc4\xf7\xd1\x1a)\x8f\xd5k\xfb\xe9hD𠟬\xd9R\xe6\x1d\xbd\xba3u\xa3pl\x05Kx\x12\xd6K\xa1\xd4!\xb2?q\xea\xa58u\x1f\xb6\xee\xcf\x1b\xf3\xeb\x11\xf1р\x85̺\xe3\x97M\xf0\x1b9\x1e\xad\xa5/]k\x85\xdf\xfe:\x83\x92wa\xb5\xd4\xdbyu\xff\x9c\x88&\xbc7\xed\xff\xe5\xfc7\v8\xf4\xe0\x13\xb3\xbc\x9f\xeb\xc1\x13\xb1\xf4h\xa9\xfb\xc4\xfb\xb9\xfb\xc7h-\xd3']~Aݧ\xddc\xd5\xc3>\x89\x92V\xba\x00-\xca\x12\x1b\x9f&\x98\xfd\x8f\xbbWW\xfc'\u007f\xbd忥ѱ\xfar+\xf8\xcb_\x17\x90\x10x\xcdr\xd0\xe2\xbf\x02\x00\x00\xff\xffF\x9c\x18\xb7\x0e\x1f\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\x1c\xcbn#\xb9\xf1\xee\xaf(8\x87I\x00K\xce$9\x04\xba\xcdz\xbc\x88\x91\xc9\xec`\xc7\xf1%ȁ\xea.Y\x8c\xba\xc9\x0eɖ\xad\x04\xf9\xf7\xa0\x8a\xec\xa7\xfa\xc1\xf6z\x82\xcd\xc2<\xd9\x14Y,֛\xc5b_\xacV\xab\vQ\xc8\a4Vj\xb5\x01QH|v\xa8\xe8?\xbb>\xfcѮ\xa5\xbe>\xbeߢ\x13\xef/\x0eR\xa5\x1b\xb8)\xad\xd3\xf9\x8fhui\x12\xfc\x88;\xa9\xa4\x93Z]\xe4\xe8D*\x9c\xd8\\\x00\b\xa5\xb4\x13\xd4m\xe9_\x80D+gt\x96\xa1Y=\xa2Z\x1f\xca-nK\x99\xa5hx\x85j\xfd\xe3o\u05ff[\xff\xe1\x02 1\xc8\xd3\xefe\x8e։\xbc\u0600*\xb3\xec\x02@\x89\x1c7`\x93=\xa6e\x86v}\xc4\f\x8d^K}a\vLh\xb5G\xa3\xcbb\x03\xcd\x0f~R\xc0\xc4\xef\xe2k\x98\xcf]\x99\xb4\xeeϝ\xeeO\xd2:\xfe\xa9\xc8J#\xb2\xd6z\xdck\xa5z,3a\x9a\xfe\v\x80\u00a0EsĿ\xaa\x83\xd2O\xea{\x89Yj7\xb0\x13\x99\xa5\x9fm\xa2\v\xdc\xc0g¤\x10\t\xa6\x17\x00G\x91ɔ\xf7\xe9q\xd3\x05\xaa\x0f_\xee\x1e~Ox\xe4\xc2w\x02\xa4h\x13#\v\x1eW\xa3\b҂\x80\a\xde$\x98\xc0\x0ep{\xe1\xc0 \xe3\xa2\x1c\x8d(\f\xae*,S\xd0&\xc0\x04(\xd0H\x9d\xca\x04\xbe\x13ɡ,\xfcT\xbb\xd7e\x96\xc2\x16\xc1\x94j\x1d\xc6\x16F\x17h\x9c\xacHH\xad%5u_\x0f\xd3w\xb4\x15?\x06R\x92\x13\xb4\xe0\xf6\b\x81ۘ2\xf5r\x01z\an/m\x837\x93\xa4\x05\x16h\x88P\xa0\xb7\xff\xc0ĭ\xe1+\xd1\xd9\xd8\n\xdbD\xab#\x1a\xdaw\xa2\x1f\x95\xfcW\rقӼd&\x1c\x06\x8eVM*\x87F\x89\x8c\x98P\xe2\x15\b\x95B.N`\x90րR\xb5\xa0\xf1\x10\xbb\x86\xbfh\x83 \xd5No`\xef\\a7\xd7\u05cf\xd2Uz\x92\xe8\xae\x84I\xf6\xd2a\xe2J\x83ע\x90+F\\\xb1\x9a\xac\xf3\xf4W\x15\x17\xed\xbb\x16\xa6\xeeDbc\x9d\x91\xea\xb1\xeef!\x1e\xa5;ɲ\x17\x0f?\xcd\xe3ߐ\x97\xba\x88*?\xde~\xbdo\x8b\x8e\xb4]\x9a3\xb5[\xd2\xd4\x10\x9e\b%\xd5\x0e\x8dg\xdc\xce\xe8\x9c!\xa2J\v-\x95\xe3\u007f\x92L\xa2\xea\x12ݖ\xdb\\:\xe2\xf4?K\xb4\x8e\xf8\xb3\x86\x1b\xb6\x16$se\x91\n\x87\xe9\x1a\xee\x14܈\x1c\xb3\x1ba\U0005b4dd(lWD\xd2y·\x8d\\w\xa0\xa7V\xdd]\x19\xa3A\x0eU:\xfc\xb5\xc0\xa4\xa3\x1a4K\xeed\xc2\n\x00;m\x1a\x15\xb7m\x8b5\xae\x97\xbcv\x18\xda흴#7F+\xc0g\xe2t\xa3\xaf$'O{T\xa4E\xa6T\x84a\x0f\"\x04\xe3\xb1\xee\xf5\x0fҎ\u007f\xc0\xbc e\x9cD\xed>\f\"Ԉ*i\xedd\xbc\xa9\xc0\xdad\xe9`\xa9@\x0fcW\x18}\x94i\xb0\a=\xeaMQ\x90\x1a>'Y\x99bZ[\xec\x811=\xc4oϦ\xb0\xe3\x13R\x11\x8d\xc9\xcd\xd0\x06T\xf3+\xd9\xdc\x01\xa0\x00\xc2 \x90RH\xe5!\x82\xe4\r\xc2v\x90\xdcԤ\xc3|\x10\xc3\tn\xf8F\x8eUl3܀3\xe59\r\xab\xf9\xc2\x18q\x1a\xa5R\x15\x10\xc4\x13\xa9\x9e\x11LU&\x13$\xf2\xd4\x06\x89\xe9\xf4\v \xd1^\xeb\xc3\x1c\x03\xe27\x13\x02\xaa\xfa\x84\xe5\xb34W \xe0\x80'\x1f\x05\t\x05\xc4(AK\x8d\x1e$Ή\xc4\xe9\"6\x11\a<1\xa0\x90\x01\x8a\x98\x1f/\x1a\xbe\x1d\xf0\x147\xb0GJ\xc2,\x9c\x8f=M\xa9\x83\t\u0089\x84%d\x04\xce籆\x80\xd31\x9b\x84%\xe6\xa6j\x15'^\xb4ݚ\x8d\x9d\xe4\xe6\x01O\xef\xacg\x18i\xc7^\x16\xd1\x1b&\x03\f\x16Y\x8f\xaa\xfcރ\xc8dZ/\xe5\xf5\xe1NME\xdd\xdd\xf6Y\xbb;u\x05\xb7\xcf\xd2\x12z*\x85\x8f\x1a\xedg\xed\xb8\xe7\x9b\x11֣\xff\"\xb2\xfa\xa9\xaczʛy\xa2G;m\x18%\xf4\xbe\xdd\xf9\x13f\xcd*i\xe1N\xd1Y)Ѕ\x93\xbf\f3^,\x19\xa5\xbc\xb4\x9c\x1fTZ\xad\xd8Ѯ\a֊\x86\x19أM\x87;m\xf4Z\xcbFC\xa5\x03\x9dG\xed\x9e|\x8f\x87\xe0\x93ڙH0\x85\xb4d\xa2\x8ah\x88\xd6\x19\xe1\xf0Q&\x90\xa3yD(\xc8\x17\xc4r#\xda>\xfb\xb6X\xe6bC\x83\xaa\x05C\x9fƠ\xb4\"\xbd\x8e\x1aW\xb1?b\xf0`\x96vzp\xcc\xde\xd8As\x1c\x13Am\x91\xa6\x9c\xc6\x14ٗE^b\x11w\xce\xe3\a\x8f\x9e\xf7\xaf\xb9(H\xc3\xffM.\x92\x85\xfd?P\bi\xa2\xb4\xfc\x03_|eؙ\x1d\xb2n\xed\x85h\ri\x818~\x14Y\xff\x0e`d\x8b\x9a,\x10f>\x14л\xb3\xc8\xe7\n\x9e\xf6\xdaz\x8f\xbc\x93\x98\r'\xbe\xbaMZ\xb8<\xe0\xe9\xf2\xea\xcc.]ީK\x1f\"\xf4\xb5>\x02l\x1dqh\x95\x9d\xe0\x92g_\xfe\xb4p*Z:#\a\xf2-i| N'\xd9*\x9a\xa0\xa9\xf5\x95\x1c\x85\xd0\xd3\xd8G\xcaf\xa1\xad[\x80\xd0\x17m\x9dψv\x02\xdee\xf96\xf0r\x15\xf2l v\x0e\rX\xa7Mu\x01FF\xb2\x976&.ڹ\x03\a1\xb6\xce\xdey\xb0t\x98\xbbl\xf4\xdb\xdb\xdfK\u007f3F\u007f\xcfAL8\x80aȅ\xd1\tZ;'6Q\x16~&\xb1Y'5\x85?,\xf1\x8dӬ\xb0\xce%[\xab\xb6$\x14&r.>N\xdc>\xb7\xf2\xb2d>\xe8\xffy\x91]\x8e\x1d\xb0\xd6\xe7\xb9PQ\x0e\xec\f\xd1\x1b?\xb7R\xb1\x00\xca\x1fQ\xccc\xc9\xe6bI\xe4\x1a\x84\xef\xe7\x13\f\xe4R\xdd\xf1\"\xf0\xfe\x9b\x84\x0f\xb5\xd1ŗ\x1d\x1fn\xaa\xd9\r\v\xea\x8e\xe1\xabñVh\xbe\xaf0\xd8\xe1\xe4yV\u007fQج\xb4k\xa7>\br\xa1\xd3w\x16v\xd2X\xd7 \x1b\rSZ(g-HӖ\x9f\xe5ԭ1/<\xca\xfd\xe0綒q{\xfdT_s3!\x17D\xdd{qD\x90;\x90\x0eP%\xbaT\x9c4\"c\xc0\x8bxv\xc4\v2\xc4\xfa\xbd\xa6\xa1*\xf3XB\xacX\x12\xa5\x9a\xc9/\xb5'|/d\xf6\xad\xd8\xe8d\x8e\xba\x9cq\xccM\xeb\xde\xd2\xfb\xb9\x9d\xfa\x85\\<˼\xccA\xe4Ĉh\x92S\xb4!s\xec\xca\x00<\t\xe9\xd8#\x11dvONG\x83Lt^d\xe8\x10\xb6\xb8ӆ\xf5\xdd\xca\x14k\xd7\x1f\xe4B\x8f\xdf;\xf6\x9b\x80\x9d\x90Yi\xa2\xad\xeeBn,;!\x05\xc3\xf3\xba\a\x9fX\x14VL\xbe\xa8tttH;\xe7\t\n\xb3$\xa0\xfdb\xf0\xb5\xc3\xc7\xc2H\x92E=\x17A\xce@\xe4\xf8\xb2\x1bA\x06\x11\x15\xea4\x16B\xce\xc0d,\xdeBȷ\x102\x0e\xee[\b\xf9\x16Bη\xb7\x10\xf2-\x84\x8c\x99\xf0\x16B\xbe\x85\x90\xcbQ\xf8߇\x90\xf3\x98\xad8\xf79\xfas\x046Q%\x04\xd3\xc8N\xae\x12\xaaan\xb2\xd2:4\xf15\xb7w\xc3\xf3Z\xf6\xf3i\x8fn\x8f\x06\x12?d\xc5\xcfU\x86e\xa3)\xb7h\\V]\x84K\xcaV)\x8a\xafb\x9f\x8d\x8e#\x8bk\xb7Zg(Ε-\xae\x94k\xae\x80\xab[\x83\\\x17OUE\xc8\xc3V#,\x1d\xb8\xe5\xdfA\xb4\xab\x81\xbauX\x1c\x99W\xd8\xfe\x8cꓣJ\xacf\n\xab\xa6K\xb8u\xb5\xc6\b\x15;\t\xf8.\xf9L\xa7\x8c\xf8gH\xbd\xd9ڧ\xf1\x8a\xa7p#\x87N\x1c߯\xbb\xbf8\x1d\xea\x9f\xe0I\xba\xfd\xe0\xa6\xf8e\x06\x1d\x17\xd5c\xbb0\xba\x92\xc5\xf0\xf2\xa9OU\xd0\x06\x94̆k\x1a\x88\xe0\xd5\xfc\x0e\xb9\xe1\x87\xc2\x1fJ_\xa4\xbfsǤ\xd8\x1a\xa9\x17WFu\xeb\x9eF\r\xfc\xf2\v\xbc%e\xe5\xf1\xb5Os\xa5JK*\x9e\xda\xd5L\x13 c\xeb\x9c\xe2N\xbc\xb35M/\xa8d\x8a\xae\xab|\x95\xebʸZ\xa5oQ\xa1\xb4\xa0.\xa9[o4\x03wY5R$\x99b*\x8f\x16\xd7\x1b\x85ڞ\x99\xfdDT\x19\x8dV\x0f̀\x1e\xa8c\x9a\xaf\x19\x9a#\u007f\a\x95W\xa9\x14zA}\xd0\xeb\xd6\x11\xbfV\xd4=U\xed\x13Q\xe3\x13\x11\x97\xcfa\x1aQų\xacv'\x82\x86/\xacө\xabpF\xd7^Z\x9dӭ\xbd\x19\x05\x1bS\x933Rq3\ns\xb2\x12'\xb6\xcef\x14\xfa\xac\xfb\x9e\x91\x9cɟ\xad\x12\x85\xddk\xf7\xa0\xb32\x8f\x88\x99\xbfv\xc7\x0f\x1c\xbd(b\x13\a\x84$\xd3eZ\xc3\x1f\xde\x1e\xbf\x87?\xc1\x97\av.\xfc\xd0/i\x9e@\x06\xf7Q\x85r\xfd\x17\x92\xc3ov\xe1\xa7\x1fŬ\xd3F<\xe2'\x9d\xb4>j0E\x93\xee\xf8Λ\xf5\xc0\xfc*\xd9\x12\xaa\x92\x86\xc3ְ\xa3>\xb8&\xc7\xea\xa3\xe0\xd6y\x950\x1d\x96\x8bI\xcdu.\x9b\xdd\xd4\xfd\xfd'\xbf\x11's\\\u007f,\xfd1xU\bc\x91h[m\xd0Oڎ\x19\x88\xbd~\x82L\x87\xdd\u007f\xd7\xc7\xdf gs\xf9\xbc\xbdx\x17G\x16\xc1J +r͋\xf0\xc3\xf0\xbcV\xe4\xddb\x1a\x1f\xfd\xc6dw\f\x92\xb0V'R\xf0\x9bP\xe9\xc23\x8eo\xf1\xa0w\xdc!\x8c(\xfd\x90\x1f[\r=+_\xd5o\xdc/f\x80Z'\\i\xe7?\x12\xc0\xc3 \x11\x85+MH\x92&\xa5᧽\x04\x02\xfd\v\xd8\xe5\x9f\tȄu^\xb0&_\xe3\u007f\xaa\x875Q\xbau>\xdbZi\x1e<\t\xcb\xef\xf0}\xaeU\xda\xee\aV\xdam\xe4%\xfeN\x9b\\\xb8\r\xa4\xc2\xe1\x8a`\xf7~\x9f\xb4L\xa3\xcc\xe6\xa7ϓ\xbb\xfbB#\xea\x1b\x9d@V\x9eV=\x98\x1e\xd9\xc9P\xca~\x05\x9f\xf1\xe9\xac\xefV\x11\xe2\xfd\\\x9a\xcf\xcac\xfaP\u007f\x03&vS\xcdWc\xf8\x12\xe4L\x01\xba:\xdb\x1b\xdc\xcb\xd4Љ\xbf\x81\xe7/<,\xfcZ\x9e\x1bZ>~%\xb4\x93\xdf\xf4~\x1b\xd1\xc2\t\r\x1c־\x01%\xe9u5\xdf\tz\xdf\xfc\xc7K\xaf\xc2w\x81\x8e\xfe\xee\x90?ē\xb6d%x\xa6\xd0\xd3h\x9eH\x12,\\\xc8\x04\xb6?\x10ty\xc9\xffT\xdf\xff\xe1\u007f\x13\xad|\fh7\xf0\xb7\xbf_@\xf0\"\x0f\x15\x1e\xd4\xf9\xdf\x00\x00\x00\xff\xff\xcb\xe6҉SI\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\x1cMo,9\xf1\x9e_Q\n\x87\aRf\xc2\x03\x0ehno\xf3\xb2\"\xe2\xf1\xf6i\x13rA\x1c<\xdd5\x19\x93n\xbb\xb1ݓ\f\x88\xff\x8e\xaa\xec\xfe\x9c\xfepg\x13\xb4\xac\xe2S\xe2\xb1\xcb\xe5\xfav\xb9\xdcg\xab\xd5\xeaL\x14\xf2\x1e\x8d\x95Zm@\x14\x12\x9f\x1d*\xfaϮ\x1f\xffh\xd7R_\x1e>nщ\x8fg\x8fR\xa5\x1b\xb8*\xad\xd3\xf9\x8fhui\x12\xfc\x8c;\xa9\xa4\x93Z\x9d\xe5\xe8D*\x9c\u061c\x01\b\xa5\xb4\x13\xd4m\xe9_\x80D+gt\x96\xa1Y=\xa0Z?\x96[ܖ2K\xd1\xf0\n\xd5\xfa\x87߮\u007f\xb7\xfe\xc3\x19@b\x90\xa7\xdf\xc9\x1c\xad\x13y\xb1\x01Uf\xd9\x19\x80\x129n\xc0&{L\xcb\f\xed\xfa\x80\x19\x1a\xbd\x96\xfa\xcc\x16\x98\xd0j\x0fF\x97\xc5\x06\x9a\x1f\xfc\xa4\x80\x89\xdf\xc5m\x98\xcf]\x99\xb4\xeeϝ\xee/\xd2:\xfe\xa9\xc8J#\xb2\xd6z\xdck\xa5z(3a\x9a\xfe3\x80\u00a0Es\xc0\xbf\xaaG\xa5\x9f\xd4\xf7\x12\xb3\xd4n`'2K?\xdbD\x17\xb8\x81\xaf\x84I!\x12L\xcf\x00\x0e\"\x93)\xef\xd3\xe3\xa6\vT\x9f\xbe\xdd\xdc\xff\x9e\xf0ȅ\xef\x04H\xd1&F\x16<\xaeF\x11\xa4\x05\x01\xf7\xbcI0\x81\x1d\xe0\xf6\u0081A\xc6E9\x1aQ\x18\\UX\xa6\xa0M\x80\tP\xa0\x91:\x95\t|'\x92Dz\xf0S\xed^\x97Y\n[\x04S\xaau\x18[\x18]\xa0q\xb2\"!\xb5\x96\xd4\xd4}=L?\xd0V\xfc\x18HINЂ\xdb#\x04nc\xca\xd4\xcb\x05\xe8\x1d\xb8\xbd\xb4\r\xdeL\x92\x16X\xa0!B\x81\xde\xfe\x03\x13\xb7\x86[\xa2\xb3\xb1\x15\xb6\x89V\a4\xb4\xefD?(\xf9\xaf\x1a\xb2\x05\xa7y\xc9L8\f\x1c\xad\x9aT\x0e\x8d\x12\x191\xa1\xc4\v\x10*\x85\\\x1c\xc1 \xad\x01\xa5jA\xe3!v\r\u007f\xd1\x06A\xaa\x9d\xde\xc0\u07b9\xc2n./\x1f\xa4\xab\xf4$\xd1y^*鎗,\xedr[:m\xece\x8a\a\xcc.\xad|X\t\x93\xec\xa5\xc3ĕ\x06/E!W\x8c\xb8b5Y\xe7\xe9\xaf*.\xda\x0f-Lݑ\xc4\xc6:#\xd5C\xdd\xcdBh\xcb\xc4\x163\xb0\x98a\xe2\xb4\x19#\xcb<\xd3}\x8b\xb3\x85#\xf4\x1c\xb0\x8a\x8dݧ-7\x1b\x9c\x04\nd\xf2\x9f\xf62\xd9\xfb\xf8\x8ad\x8a!A\xaaѲ-\x10E\x91\x1d\xc77\v\xf3\x92\x10\x16\x9a6\aM\x9b5\f}\x98\xc3&\xa2iQ\xf6\xb4i3\x96\xb5K\xe7ZD\xde\xc9\\9\x8d\x17\t\xf4\xcd\xc9\xe4\xd7\x16h\"\xb0\xa4\xb3\xd5\xcd\x0e0/\xdc\xf1\x02\xa4\xabz\xe7aR\x18\xd4\xe0\xf0\x8b`\xd4K\xf4\xe1\xa6?\xf7\x95\xf5\xe1\x15\xb8T\xa3\xf0\u007f\xcd$v6\xb7\xc1\xd7,`З\xf6\xbc\v\x90\xbb\x9aA\xe9\x05\xecd\xe6\x90\xe3\x9ei\x14[\xaeo\x96S\xafE\x968\xafI-\x17.\xd9_\xd7G\xc8\xd9\xf1=\n\xf5\xa7\xfbX\xb9:It\x9d\xfc,d\xe0\f\x834\x98\xfb\xbc\xc5\x1d\xeb@\xd3Ñڧ\xaf\x9f1\x9d&\x14\xc4J\xe4\xc9v>\xf5Pn/\x1f\x8e\x01\xf1\x9b\t\x01U}\xc2\xf2\xf9\xa8\v\x10\xf0\x88G\x1f\x05\t\x05\xc4(AK\x8d\x1e$N\x89ĉ16\x11\x8fxd@!\xd7\x151?^4|{\xc4c\xdc\xc0\x1e)\t\xb3\x90\t\xf04\xa5\x0e&\b\xa7L\x96\x90\x118s\xc9\x1a\x02N\xc7l\x12\x96\x98\x9b\xaaU\x9cx\xd1vk6vҸ\x8fx\xfc`=\xc3H;\xf6\xb2\x88\xde0\x19`\xb0\xc8zTe2\xefE&\xd3z)\xaf\x0f7j*\xea\uedaf\xdaݨ\v\xb8~\x96\x96\xd0S)|\xd6h\xbfj\xc7=oFX\x8f\xfe\x8b\xc8꧲\xea)o\xe6\x89\x1e\xed\x04i\x94\xd0\xfbv\xe3O\x985\xab\xa4\x85\x1bEg\xa5@\x17Ns3\xccx\xb1d\x94\xf2\xd2r&Ti\xb5bG\xbb\x1eX+\x1af`\x8f6\x1d\xee\xb4\xd1k-\x1b\r\x95\x0et\x1e\xb5;\xf2=\x1e\x82O\xdfg\"\xc1\x14Ғ\x89*\xa2!Zg\x84\xc3\a\x99@\x8e\xe6\x01\xa1 _\x10ˍh\xfb\xec\xdbb\x99\x8b\r\r\xaa\x16\f}\x1a\x83Ҋ\xf4:j\\\xc5\xfe\x88\xc1\x83\xf9\xe8\xe9\xc11{c\a\xcdqL\x04\xb5E\x9ar\xc2Vd\xdf\x16y\x89E\xdc9\x8d\x1f#\xda\tx\x97\xe5\xdb\xc0\xcbUȳ\x81\xd894`\x9d6\xd5U\x1f\x19\xc9^ژ\xb8h\xe7\x0e\x1c\xc4\xd8:{\xe7\xc1\xd2a\xee\xbc\xd1oo\u007f\xcf\xfd\x1d \xfd=\a1\xe1\x00\x86!\x17F'h\xed\x9c\xd8DY\xf8\x99\xc4f\x9d\xd4\x14\xfe\xb0\xc4wk\xb3\xc2:\x97l\xadڒP\x98ȹ\xf88q\xfd\xdc\xca˒\xf9\xa0\xff\xe7Ev9v\xc0Z\x9f\xe7BE9\xb0\x13D\xaf\xfc\xdcJ\xc5\x02(\u007fD1\x0f%\x9b\x8b%\x91k\x10\xbe\x9fO0\x90KuË\xc0\xc77\t\x1fj\xa3\x8b/;>\\U\xb3\x1b\x16\xd4\x1d×\xa4c\xad\xd0|_a\xb0\xc3\xc9Ӭ\xfe\xa2\xb0Yi\xd7N}\x10\xe4B\xa7\x1f,줱\xaeA6\x1a\xa6\xb4|I\xfavg9um\xcc\v\x8fr?\xf8\xb9\xadd\xdc^?\xd5\x17\xfa\xe3\x17\xbfC\x8d\xaf\xc7\x10\xe4\x0e\xa4\x03T\x89.\x15'\x8d\xc8\x18\xf0\"\x9e\x1d\xf1\x82\f\xb1~\xafi\xa8\xca<\x96\x10+\x96D\xa9f\xf2K\xed\t\xdf\v\x99\xbd\x15\x1b\x9d\xccQ\x973\x8e\xb9i\xddz\x04?\xb7S\xa9\x91\x8bg\x99\x979\x88\x9c\x18\x11Mr\x8a6d\x8e]\x19\x80'!\x1d{$\x82\xcc\xee\xc9\xe9h\x90\x89\u038b\f\x1d\xc2\x16wڰ\xbe[\x99b\xed\xfa\x83\\\xe8\xf1{\xc7~\x13\xb0\x132+M\xb4\xd5]ȍe'\xa4`x^\xf7\xe0\x13\x8b\u008a\xc9\x17\x95\x8e\x8e\x0ei\xe7G\u007f\x8e\xc0&\xaa\x84`\x1a\xd9\xc9UB5\xccUVZ\x87&\xbe\xe6\xf6fx\xde@\xfdu⇬\xf8aΰl4\xe5\x16\x8d˪\x8bpI\xd9*E\xf1\xf5\xfa\xb3\xd1qdq\xedX\x9dv\\)\xd7\\\x01W\xb7\x06\xb9.\x9e\xaa\x8a\x90\x87\xadFX:p˿\xf8hW\x03u\xeb\xb082\xaf\xb0\xfd\x19\xd5'G\x95X\xcd\x14VM\x97p\xebj\x8d\x11*v\x12\xf0]\xf2\x99N\x19\xf1ϐz\xb3\xb5O\xe3\x15O\xe1F\x0e\x9d8|\\w\u007fq:\xd4?\xc1\x93t\xfb\xc1M\xf1\x1b\x14:.\xaa\x87vat%\x8b\xe1\x8dW\x9f\xaa\xa0\r(\x99\r\xd74\x10\xc1\xab\xf9\x1dr\xc3\x0f\x85?\x94\xbeH\u007f\xe7\x8eI\xb15R/\xae\x8c\xea\xd6=\x8d\x1a\xf8\xe5\x17xK\xca\xca\xe3k\x9f\xe6J\x95\x96T<\xb5\xab\x99&@\xc6\xd69ŝxgk\x9a^P\xc9\x14]W\xf9*וq\xb5JoQ\xa1\xb4\xa0.\xa9[o4\x03wY5R$\x99b*\x8f\x16\xd7\x1b\x85ڞ\x99\xfdDT\x19\x8dV\x0f̀\x1e\xa8c\x9a\xaf\x19\x9a#\u007f\a\x95W\xa9\x14zA}\xd0\xeb\xd6\x11\xbfV\xd4=U\xed\x13Q\xe3\x13\x11\x97\xcfa\x1aQų\xacv'\x82\x86/\xacө\xabpF\xd7^Z\x9dӭ\xbd\x19\x05\x1bS\x933Rq3\ns\xb2\x12'\xb6\xcef\x14\xfa\xac\xfb\x9e\x91\x9cɟ\xad\x12\x85\xdd\xeb\xeaI\xebl\xe4w\xdb\x1d?p\xf4\xaa\x1e\xb4&\x99.\xd3\x1a\xfe\xf0\xf6\xf8\xe5\xff\x11\xbeݳs\xe1\x87~I\xf3\x042\xb8\x8f*\x94뿐\x1c~\x9d\f?\xfd(f\x9d6\xe2\x01\xbf\xe8\xa4\xf5\xf9\x86)\x9at\xc7w^\xe7\a\xe6WɖP\x954\x1c\xb6\x86\x1d\xf5\xc159\xd6\xf0B\xb89\xaf\x12\xa6\xc3r1\xa9\xb9\xcee\xb3\x9b\xba\xbb\xfb\xe27\xe2d\x8e\xebϥ?\x06\xaf\na,\x12m\xab\r\xfaI\xdb1\x03\xb1\xd7O\x90\xe9\xb0\xfb\xef\xfa\xf8\x1b\xe4l.\x9f\xb7\x17\xef¿\x8e\xae\x04\xb2\"\u05fc\b\xdf\x0f\xcfkE\xde-\xa6\xf1\xd1oLv\xc7 \tku\"\x05\xbf\t\x95.<\xe3x\x8b\a\xbd\xe3\x0eaD\xe9\x87\xfc\xd8j\xe8\x01\xfd\xaa~\xcd\u007f6\x03\xd4:\xe1J;\xff9\x04\x1e\x06\x89(\\iB\x924)\r?\xed%\x10\xe8_\xc0.\xff B&\xac\xf3\x825\xf9݁/\xf5\xb0&J\xb7\xceg[+̓'a\xf9\x8b\x03>\xd7*m\xf7S2\xed6\xf2́\x9d6\xb9p\x1bH\x85\xc3\x15\xc1\xee\xfd>i\x99F\x99\xcdO\x9f'w\xf7\x8dF\xd47:\x81\xac<\xadz0=\xb2\x93\xa1\x94\xfd\n\xbe\xe2\xd3Iߵ\"\xc4\xfb\xb94\x9f\x95\xc7\xf4\xbe\xfe\xdaM즚\xef\xe3\xf0%ȉ\x02tu\xb67\xb8\x97\xa9\xa1\x13\u007f\x03\xcf_xX\xf8\xb5<5\xb4|\xfcJh'\xbf\xe9\xfd6\xa2\x85\x13\x1a8\xac}\x03J\xd2\xebj\xbe\x88\xf4\xb1\xf9\x8f\x97^\x85/ \x1d\xfc\xdd!\u007fr(m\xc9J\xf0L\xa1\xa7\xd1<\x91$X\xb8\x90\tl\u007f\n\xe9\xfc\x9c\xff\xa9\xbet\xc4\xff&Z\xf9\x18\xd0n\xe0o\u007f?\x83\xe0E\xee+<\xa8\xf3\xbf\x01\x00\x00\xff\xff|l\xf6\x1b=J\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VM\x93\xe34\x10\xbd\xfbWt-\x87\xbd`\x87\x01\x0e\x94o\xd4\x00US\xc0\xd4\xd4d\x99\v\xc5A\x91\xdaI3\xb2dԭ\f\xe1\xd7S\x92\xecI\xe28;\xbb\x87\xf5\xcd\xed\xfexz\xfd\xba\xad\xaa\xae\xebJ\r\xf4\x84\x81ɻ\x16\xd4@\xf8\xaf\xa0Ko\xdc<\xff\xc0\r\xf9\xd5\xfef\x83\xa2n\xaagr\xa6\x85\xdb\xc8\xe2\xfbGd\x1f\x83Ɵ\xb0#GB\xdeU=\x8a2JT[\x01(缨d\xe6\xf4\n\xa0\xbd\x93\xe0\xad\xc5Po\xd15\xcfq\x83\x9bH\xd6`\xc8\x15\xa6\xfa\xfbo\x9ao\x9b\xef+\x00\x1d0\x87\u007f\xa0\x1eYT?\xb4\u08b5\x15\x80S=\xb6\xc0\x18R\x90(\x89\x1c\xf0\x9f\x88,\xdc\xec\xd1b\xf0\r\xf9\x8a\aԩ\xf06\xf88\xb4p\xfcP\xe2GP\xe5@\xeb\x9cj\x9dS=\x96T\xf9\xab%\x96_\xafy\xfcF\xa3\xd7`cPv\x19Pv`r\xdbhUXt\xa9\x00\x86\x80\xf9\xc3\x1f\xee\xd9\xf9\x17\xf7\v\xa15\xdcB\xa7,c\x05\xc0\xda\x0f\xd8\xc2}B=(\x8d\xa6\x02\xd8+K&\xd3S\xce\xe1\at?>\xdc=}\xb7\xd6;\xecU1\x02\x18d\x1dh\xc8~Kg\x00bP0\"\x01\xf1\xa0\xb4Ff\xd01\x04t\x02\x05)\x90\xeb|\xe8s\xb911\x80\xda\xf8( ;\x84\xa7L\xedx\xb6ft\x18\x82\x1f0\bMD琣\xcc^m3\x8c\xef\xd3!\x8a\x0f\x98$,\xe4\\c\x94\a\x1a\xe0|@\xf0\x1dȎ\x18\x02f\xf6\x9c\x9c\xa3˜t\xa0\x1c\xf8\xcdߨ\xa5\x19O\xcf\xc0;\x1f\xadIj\xdcc\x10\b\xa8\xfd\xd6\xd1\u007f\xaf\x999ѐJZ%\x93\x0e\xa6\x87\x9c`p\xca&\xfa#~\r\xca\x19\xe8\xd5\x01\x02\xa6\x1a\x10\xddI\xb6\xec\xc2\r\xfc\xee\x03f\x02[؉\fܮV[\x92i\xb0\xb4\xef\xfb\xe8H\x0e\xab<\x1e\xb4\x89\xe2\x03\xaf\f\xeeѮ\x98\xb6\xb5\nzG\x82Zb\xc0\x95\x1a\xa8\xce\xc0]\x9e\xab\xa67_\x85q\n\xf9\xfd\tR9$\xc1\xb0\x04r\xdbWs\x96\xfaUޓ̋\x1aJX\xc1\u007f\xa47\x99\x12+\x8f?\xaf?\xc0T4\xb7\xe0\x9c\xf3\xcc\xf61\x8c\x8f\xc4'\xa2\xc8u\x18J\xe3\xba\xe0\xfb\x9c\x11\x9d\x19<\xb9\xa2%m\t\xdd9\xe9\x1c7=\tO*M\xfdi\xe06\xaf\x17\xd8 \xc4\xc1(A\xd3\xc0\x9d\x83[գ\xbdU\x8c_\x9c\xf6\xc40\u05c9ҷ\x89?݊玅\xadW\xf3\xb4\xb2\x16;\xb40\xbd\xeb\x01u\xeaY\".\xc5RG:\x8f\x01t>\x80Z\ni\xdeĐ\xbd?\vŸ#\n\x8e\xd9\xe6H3\xf8\x16\x8e\xa5U\x91\xed;\xc5xn\x9a\xa1yH\x1e\xf3ʖ:\xd4\am\xb1$(\x9b\x02\xdf\x02\x91\x1et\xb1\x9f\u05eb\xe1\x1e_.l\x0f\xc1\xa7=\x99W\xf1\xe9\xb3\xd8\u007f(\xff\x88-9\xfe\xf8i\x8aO\xfe뜮ܓU;\xa6\x81\x10\x9dK\x13\xe9]2ϒ\xc2\xf9F\x9e}%\xc1\xfe\x02\xc7\"\x92;\xd7\xf9\xfc\xd7V\xa9\xa4\x922'86u\xacQ\x10]\xa4\xbb\xd6\xd3\xf2\xccW\xd1'\x10X\x9e\xfc\xe7\xff\xfc\xc0\xb4:(\xe0B\xcd:cY0\xa7J\x17\xe6ʼn\x19\x91Ek\xd5\xc6b\v\x12\xe2<\xb2ĩ\x10\xd4\xe1\\\x15\x93\x8c\x8ew\x9c\x8f\n\xe4\xc2=i\xffe\x87\xee\x9a\xc2\xe1E\xf1RoJ\x1a\xd8\x1c\xae\x05\u07be^\xd6\xe6CRd\xd9Bں\xb5\xd0\x05K\x9f@\xc4B\x97\x8aT\x17n\a\x17$\xacO=\xa7\xd9?\x13\xfctY\x98#\xbfR|\xa1\xa93\xd3\xf1nzs|\xcb®ǻh\xfe0\x9e\u009c\x9c\x9c\xc5\a\xb5\x9d\xb88\xee\xd6t\xcd\x1a\x04\xcd\xfd\xfc&\xfa\xee\xddٕ2\xbfj\xef\f\x95\x8b4\xfc\xf9WU\xb2\xa2y\x9ap$\xe3\xff\x01\x00\x00\xff\xff\x8e\xadi\xa0\xc7\v\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4V\xc1\x8e\xdc6\f\xbd\xfb+\x88\xf4\x90K\xed\xe9\xb6=\x14\xbe\x05\xdb\x16\b\x9a\x06\x8bl2\x97\xa2\a\x8dDϰ+K\xaaH9\xdd~}!Y\xde\x19\xcf\xce$-\x8a\xf8&\x9a\"\x1f\x1f\x1f\t5m\xdb6*\xd0\x16#\x93w=\xa8@\xf8\x97\xa0\xcb'\xee\x1e~\xe0\x8e\xfcf\xba١\xa8\x9b恜\xe9\xe16\xb1\xf8\xf1\x1d\xb2OQ\xe3\x8f8\x90#!\xef\x9a\x11E\x19%\xaao\x00\x94s^T6s>\x02h\xef$zk1\xb6{t\xddC\xda\xe1.\x915\x18K\x86%\xff\xf4M\xf7m\xf7}\x03\xa0#\x96\xeb\xefiD\x165\x86\x1e\\\xb2\xb6\x01pj\xc4\x1e&oӈ\xecT\xe0\x83\x17\xeb\xf5\x9c\xac\x9b\xd0b\xf4\x1d\xf9\x86\x03\xea\x9c{\x1f}\n=\x1c\u007f\xcc!*\xae\xb9\xa6m\x89v_\xa3\xbd\xa9ъ\x83%\x96_>\xe1\xf4\x86X\x8ac\xb0)*{\x15Y\xf1ar\xfbdU\xbc\xe6\xd5\x00\x84\x88\x8cq\xc2\x0f\xee\xc1\xf9\x8f\xeegBk\xb8\x87AY\xc6\x06\x80\xb5\x0f\xd8\xc3\xdb\\AP\x1aM\x030)K\xa6ܟk\xf2\x01ݫ\xbb\xd7\xdb\xef\xee\xf5\x01G5\x1b\x01\f\xb2\x8e\x14\x8aߕb\x80\x18\x14,h\xe0\xe3\x01#¶0\a,>\"W\xe05$\xc0R\x01w\xd5\x14\xa2\x0f\x18\x85\x16\x82\xf3w\xa2\xb0'\xdb\x19\x9e\x97\x19\xf0\xec\x03&k\n\x19\xe4\x80P\x95\x81\x06\xb8\x14\x03~\x009\x10C\xc4\u0094\x93c\xab\x96\xcf\x0f\xa0\x1c\xf8\xdd\x1f\xa8\xa5\x83\xfb\xccfd\xe0\x83O\xd6d!N\x18\x05\"j\xbfw\xf4\xf7Sd\x06\xf1%\xa5U\x82\xb5\xa7\xcbGN0:e3\xd5\t\xbf\x06\xe5\f\x8c\xea\x11\"\xe6\x1c\x90\xdcI\xb4\xe2\xc2\x1d\xfc\xea#\x02\xb9\xc1\xf7p\x10\t\xdco6{\x92e\xa6\xb4\x1f\xc7\xe4H\x1e7e2h\x97\xc4G\xde\x18\x9c\xd0n\x98\xf6\xad\x8a\xfa@\x82ZRč\n\xd4\x16\xe0nV\xf9h\xbe\x8au\x00\xf9\xe5\tRy\xcc\xe2`\x89\xe4\xf6O\xe6\"\xf1\xab\xbcgm\xcfm\x9f\xaf\xcd\xf8\x8f\xf4fSf\xe5\xddO\xf7\xefaIZZ\xb0漰}\xbc\xc6G\xe23Q\xe4\x06\x8cs\xe3\x86\xe8\xc7\x12\x11\x9d\t\x9e\x9c\x94\x83\xb6\x84nM:\xa7\xddH\x92;\xfdgB\x96ܟ\x0en\xcbf\x81\x1dB\nF\t\x9a\x0e^;\xb8U#\xda[\xc5\xf8\xc5i\xcf\fs\x9b)\xfd<\xf1\xa7\vq\xed8\xb3u\x1c\xa2\xba\xaa.v\xe8\xf2\xa4\xde\aԫA\xc91h\xa0:\xb9\x83\x8f\xa0Vl\xd6)\xbe\x1c\xad;q\xbd4\xc00o\xf0\x81\xf6k\x1b\x802\xa6l\u007fe\xef\xaeܻJυZoK\x8e,\xc7\\@\x88~\"\x83\xb1]j\xab\x18R\xacE\x96\xdd\xd85\x97r\x9d1\\\v+\xe1\xce\xe1\xad\x10\xdcU\xa7\x8c!Ӻ\\\x9a\xf7\x0e\xd6\xf5W\x96\xa1\xda\xe3\xe5\xdc\xcf\xea\xcc\n\xa6\x88\xab)l\x9fB\u007fV\x1d\xa2$\xf1\u007f\xd5G\xb9T=wU#:ňNjD\xf0\xc3\n\xbe\xfa\xff\x1a\t\a\xc5\xf8I~/Ǿ\xcb\xf7\x16\xca-\r\xa8\x1f-\xce\xd1\xca6\u007f&\xa8\u007f\x8d4\u007f\xe8\xd2x\x0e\xaa\x85W\x93\"\xabv\x16\x9f\xfd\xf9\xe0ԕ\u007fW\xfa{\xa1mg\xa6\xe3\x03\xe7\xe6x*\xe4\xb5˃\xe6f~!\xe4\xa5iz\x90\x98\xe6\xe4Ui\xd5rԂ\xd2\x1a\x83\xa0y{\xfe\x96y\xf1b\xf5\x1c)G\xed\xdd<\xa6\xdc\xc3o\xbf7sT4\xdb\x05G6\xfe\x13\x00\x00\xff\xff\x9f\xfe\xa5\x85\f\n\x00\x00"), } diff --git a/pkg/generated/crds/manifests/velero.io_backups.yaml b/pkg/generated/crds/manifests/velero.io_backups.yaml index 7f68b2d1c..b527f7513 100644 --- a/pkg/generated/crds/manifests/velero.io_backups.yaml +++ b/pkg/generated/crds/manifests/velero.io_backups.yaml @@ -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. diff --git a/pkg/generated/crds/manifests/velero.io_schedules.yaml b/pkg/generated/crds/manifests/velero.io_schedules.yaml index dc05b1976..e612fc11d 100644 --- a/pkg/generated/crds/manifests/velero.io_schedules.yaml +++ b/pkg/generated/crds/manifests/velero.io_schedules.yaml @@ -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. diff --git a/pkg/install/deployment.go b/pkg/install/deployment.go index 32303cf57..758736a75 100644 --- a/pkg/install/deployment.go +++ b/pkg/install/deployment.go @@ -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" diff --git a/pkg/install/resources.go b/pkg/install/resources.go index 35d1bc4f2..b88dbb183 100644 --- a/pkg/install/resources.go +++ b/pkg/install/resources.go @@ -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) diff --git a/pkg/restic/command.go b/pkg/restic/command.go index ab6f0aedc..d06968018 100644 --- a/pkg/restic/command.go +++ b/pkg/restic/command.go @@ -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. diff --git a/pkg/restic/command_test.go b/pkg/restic/command_test.go index 349796976..dc8ff0f29 100644 --- a/pkg/restic/command_test.go +++ b/pkg/restic/command_test.go @@ -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. diff --git a/pkg/restic/common.go b/pkg/restic/common.go index 41f5c9f89..ffb2c1167 100644 --- a/pkg/restic/common.go +++ b/pkg/restic/common.go @@ -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 { diff --git a/pkg/restic/common_test.go b/pkg/restic/common_test.go index 2a73c6039..c69a1a44e 100644 --- a/pkg/restic/common_test.go +++ b/pkg/restic/common_test.go @@ -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) + }) + } +} diff --git a/site/docs/master/api-types/backup.md b/site/docs/master/api-types/backup.md index 70120a921..e0addb97a 100644 --- a/site/docs/master/api-types/backup.md +++ b/site/docs/master/api-types/backup.md @@ -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: diff --git a/site/docs/master/contributions/ibm-config.md b/site/docs/master/contributions/ibm-config.md index 69d34420c..140face6d 100644 --- a/site/docs/master/contributions/ibm-config.md +++ b/site/docs/master/contributions/ibm-config.md @@ -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: ` 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 diff --git a/site/docs/master/customize-installation.md b/site/docs/master/customize-installation.md index fcd17d774..59fca8cbc 100644 --- a/site/docs/master/customize-installation.md +++ b/site/docs/master/customize-installation.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]. diff --git a/site/docs/master/restic.md b/site/docs/master/restic.md index 762933f57..ac63ca993 100644 --- a/site/docs/master/restic.md +++ b/site/docs/master/restic.md @@ -158,7 +158,87 @@ kubectl patch storageclass/ \ 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 \ No newline at end of file +[11]: customize-installation.md#default-pod-volume-backup-to-restic diff --git a/site/docs/master/velero-install.md b/site/docs/master/velero-install.md index 58718d16c..d0793bf0c 100644 --- a/site/docs/master/velero-install.md +++ b/site/docs/master/velero-install.md @@ -19,6 +19,7 @@ velero install \ --velero-pod-cpu-limit \ --velero-pod-mem-limit \ [--use-restic] \ + [--default-volumes-to-restic] \ [--restic-pod-cpu-request ] \ [--restic-pod-mem-request ] \ [--restic-pod-cpu-limit ] \