Skip to content

Commit

Permalink
feat: support configure BSL CR to indicate which one is the default (v…
Browse files Browse the repository at this point in the history
…mware-tanzu#3092)

* Add default field to BSL CRD

Signed-off-by: JenTing Hsiao <jenting.hsiao@suse.com>

* Add a new flag `--default` under `velero backup-location create`

add a new flag `--default` under `velero backup-location create`
to specify this new location to be the new default BSL.

Signed-off-by: JenTing Hsiao <jenting.hsiao@suse.com>

* Add a new default field under `velero backup-location get`

add a new default field under `velero backup-location get` to indicate
which BSL is the default one.

Signed-off-by: JenTing Hsiao <jenting.hsiao@suse.com>

* Add a new sub-command and flag under `velero backup-location`

Add a new sub-command called `velero backup-location set` sub-command
and a new flag `velero backup-cation set --default` to configure which
BSL is the default one.

Signed-off-by: JenTing Hsiao <jenting.hsiao@suse.com>

* Add new flag to get the default backup-location

Add a new flag `--default` under `velero backup-location get`
to displays the current default BSL.

Signed-off-by: JenTing Hsiao <jenting.hsiao@suse.com>

* Configures default BSL in BSL controller

When upgrade the BSL CRDs, none of the BSL has been labeled as default.
Sets the BSL default field to true if the BSL name matches to the default BSL setting.

Signed-off-by: JenTing Hsiao <jenting.hsiao@suse.com>

* Configures the default BSL in BSL controller for velero upgrade

When upgrade the BSL CRDs, none of the BSL be marked as the default.
Sets the BSL `.spec.default: true` if the BSL name matches against the
`velero server --default-backup-storage-location`.

Signed-off-by: JenTing Hsiao <jenting.hsiao@suse.com>

* Add unit test to test default BSL behavior

Signed-off-by: JenTing Hsiao <jenting.hsiao@suse.com>

* Update check which one is the default BSL in backup/backup_sync/restore controller

Signed-off-by: JenTing Hsiao <jenting.hsiao@suse.com>

* Add changelog

Signed-off-by: JenTing Hsiao <jenting.hsiao@suse.com>

* Update docs locations.md and upgrade-to-1.6.md

Signed-off-by: JenTing Hsiao <jenting.hsiao@suse.com>
  • Loading branch information
JenTing Hsiao authored and georgettica committed Jan 26, 2021
1 parent 10edc04 commit 2c1a346
Show file tree
Hide file tree
Showing 21 changed files with 440 additions and 64 deletions.
1 change: 1 addition & 0 deletions changelogs/unreleased/3092-jenting
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
feat: support configures BackupStorageLocation custom resources to indicate which one is the default
8 changes: 8 additions & 0 deletions config/crd/bases/velero.io_backupstoragelocations.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ spec:
- JSONPath: .metadata.creationTimestamp
name: Age
type: date
- JSONPath: .spec.default
description: Default backup storage location
name: Default
type: boolean
group: velero.io
names:
kind: BackupStorageLocation
Expand Down Expand Up @@ -83,6 +87,10 @@ spec:
type: string
description: Config is for provider-specific configuration fields.
type: object
default:
description: Default indicates this location is the default backup storage
location.
type: boolean
objectStorage:
description: ObjectStorageLocation specifies the settings necessary
to connect to a provider's object storage.
Expand Down
2 changes: 1 addition & 1 deletion config/crd/crds/crds.go

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions pkg/apis/velero/v1/backupstoragelocation_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ type BackupStorageLocationSpec struct {

StorageType `json:",inline"`

// Default indicates this location is the default backup storage location.
// +optional
Default bool `json:"default,omitempty"`

// AccessMode defines the permissions for the backup storage location.
// +optional
AccessMode BackupStorageLocationAccessMode `json:"accessMode,omitempty"`
Expand Down Expand Up @@ -96,6 +100,7 @@ type BackupStorageLocationStatus struct {
// +kubebuilder:printcolumn:name="Last Validated",type="date",JSONPath=".status.lastValidationTime",description="LastValidationTime is the last time the backup store location was validated"
// +kubebuilder:printcolumn:name="Access Mode",type="string",JSONPath=".spec.accessMode",description="Permissions for the backup storage location"
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
// +kubebuilder:printcolumn:name="Default",type="boolean",JSONPath=".spec.default",description="Default backup storage location"

// BackupStorageLocation is a location where Velero stores backup objects
type BackupStorageLocation struct {
Expand Down
8 changes: 7 additions & 1 deletion pkg/builder/backup_storage_location_builder.go
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -83,6 +83,12 @@ func (b *BackupStorageLocationBuilder) Prefix(val string) *BackupStorageLocation
return b
}

// Default sets the BackupStorageLocation's is default or not
func (b *BackupStorageLocationBuilder) Default(isDefault bool) *BackupStorageLocationBuilder {
b.object.Spec.Default = isDefault
return b
}

// AccessMode sets the BackupStorageLocation's access mode.
func (b *BackupStorageLocationBuilder) AccessMode(accessMode velerov1api.BackupStorageLocationAccessMode) *BackupStorageLocationBuilder {
b.object.Spec.AccessMode = accessMode
Expand Down
1 change: 1 addition & 0 deletions pkg/cmd/cli/backuplocation/backup_location.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func NewCommand(f client.Factory) *cobra.Command {
NewCreateCommand(f, "create"),
NewDeleteCommand(f, "delete"),
NewGetCommand(f, "get"),
NewSetCommand(f, "set"),
)

return c
Expand Down
22 changes: 22 additions & 0 deletions pkg/cmd/cli/backuplocation/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ type CreateOptions struct {
Name string
Provider string
Bucket string
DefaultBackupStorageLocation bool
Prefix string
BackupSyncPeriod, ValidationFrequency time.Duration
Config flag.Map
Expand All @@ -85,6 +86,7 @@ func NewCreateOptions() *CreateOptions {
func (o *CreateOptions) BindFlags(flags *pflag.FlagSet) {
flags.StringVar(&o.Provider, "provider", o.Provider, "Name of the backup storage provider (e.g. aws, azure, gcp).")
flags.StringVar(&o.Bucket, "bucket", o.Bucket, "Name of the object storage bucket where backups should be stored.")
flags.BoolVar(&o.DefaultBackupStorageLocation, "default", o.DefaultBackupStorageLocation, "Sets this new location to be the new default backup storage location. Optional.")
flags.StringVar(&o.Prefix, "prefix", o.Prefix, "Prefix under which all Velero data should be stored within the bucket. Optional.")
flags.DurationVar(&o.BackupSyncPeriod, "backup-sync-period", o.BackupSyncPeriod, "How often to ensure all Velero backups in object storage exist as Backup API objects in the cluster. Optional. Set this to `0s` to disable sync. Default: 1 minute.")
flags.DurationVar(&o.ValidationFrequency, "validation-frequency", o.ValidationFrequency, "How often to verify if the backup storage location is valid. Optional. Set this to `0s` to disable sync. Default 1 minute.")
Expand Down Expand Up @@ -162,6 +164,7 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error {
},
},
Config: o.Config.Data(),
Default: o.DefaultBackupStorageLocation,
AccessMode: velerov1api.BackupStorageLocationAccessMode(o.AccessMode.String()),
BackupSyncPeriod: backupSyncPeriod,
ValidationFrequency: validationFrequency,
Expand All @@ -177,6 +180,25 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error {
return err
}

if o.DefaultBackupStorageLocation {
// There is one and only one default backup storage location.
// Disable the origin default backup storage location.
locations := new(velerov1api.BackupStorageLocationList)
if err := kbClient.List(context.Background(), locations, &kbclient.ListOptions{Namespace: f.Namespace()}); err != nil {
return errors.WithStack(err)
}
for _, location := range locations.Items {
if !location.Spec.Default {
continue
}
location.Spec.Default = false
if err := kbClient.Update(context.Background(), &location, &kbclient.UpdateOptions{}); err != nil {
return errors.WithStack(err)
}
break
}
}

if err := kbClient.Create(context.Background(), backupStorageLocation, &kbclient.CreateOptions{}); err != nil {
return errors.WithStack(err)
}
Expand Down
26 changes: 24 additions & 2 deletions pkg/cmd/cli/backuplocation/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (

func NewGetCommand(f client.Factory, use string) *cobra.Command {
var listOptions metav1.ListOptions
var showDefaultOnly bool

c := &cobra.Command{
Use: use,
Expand All @@ -45,28 +46,49 @@ func NewGetCommand(f client.Factory, use string) *cobra.Command {

locations := new(velerov1api.BackupStorageLocationList)
if len(args) > 0 {
location := &velerov1api.BackupStorageLocation{}
for _, name := range args {
location := &velerov1api.BackupStorageLocation{}
err = kbClient.Get(context.Background(), kbclient.ObjectKey{
Namespace: f.Namespace(),
Name: name,
}, location)
cmd.CheckError(err)
locations.Items = append(locations.Items, *location)

if showDefaultOnly {
if location.Spec.Default {
locations.Items = append(locations.Items, *location)
}
} else {
locations.Items = append(locations.Items, *location)
}
}
} else {
err := kbClient.List(context.Background(), locations, &kbclient.ListOptions{
Namespace: f.Namespace(),
Raw: &listOptions,
})
cmd.CheckError(err)

if showDefaultOnly {
for i := 0; i < len(locations.Items); i++ {
if locations.Items[i].Spec.Default {
continue
}
if i != len(locations.Items)-1 {
copy(locations.Items[i:], locations.Items[i+1:])
i = i - 1
}
locations.Items = locations.Items[:len(locations.Items)-1]
}
}
}

_, err = output.PrintWithFormat(c, locations)
cmd.CheckError(err)
},
}

c.Flags().BoolVar(&showDefaultOnly, "default", false, "Displays the current default backup storage location.")
c.Flags().StringVarP(&listOptions.LabelSelector, "selector", "l", listOptions.LabelSelector, "Only show items matching this label selector.")

output.BindFlags(c.Flags())
Expand Down
115 changes: 115 additions & 0 deletions pkg/cmd/cli/backuplocation/set.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
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.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package backuplocation

import (
"context"
"fmt"

"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"

kbclient "sigs.k8s.io/controller-runtime/pkg/client"

velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/client"
"github.com/vmware-tanzu/velero/pkg/cmd"
)

func NewSetCommand(f client.Factory, use string) *cobra.Command {
o := NewSetOptions()

c := &cobra.Command{
Use: use + " NAME",
Short: "Set a backup storage location",
Args: cobra.ExactArgs(1),
Run: func(c *cobra.Command, args []string) {
cmd.CheckError(o.Complete(args, f))
cmd.CheckError(o.Run(c, f))
},
}

o.BindFlags(c.Flags())

return c
}

type SetOptions struct {
Name string
DefaultBackupStorageLocation bool
}

func NewSetOptions() *SetOptions {
return &SetOptions{}
}

func (o *SetOptions) BindFlags(flags *pflag.FlagSet) {
flags.BoolVar(&o.DefaultBackupStorageLocation, "default", o.DefaultBackupStorageLocation, "Sets this new location to be the new default backup storage location. Optional.")
}

func (o *SetOptions) Complete(args []string, f client.Factory) error {
o.Name = args[0]
return nil
}

func (o *SetOptions) Run(c *cobra.Command, f client.Factory) error {
kbClient, err := f.KubebuilderClient()
if err != nil {
return err
}

location := &velerov1api.BackupStorageLocation{}
err = kbClient.Get(context.Background(), kbclient.ObjectKey{
Namespace: f.Namespace(),
Name: o.Name,
}, location)
if err != nil {
return errors.WithStack(err)
}

if o.DefaultBackupStorageLocation {
// There is one and only one default backup storage location.
// Disable the origin default backup storage location.
locations := new(velerov1api.BackupStorageLocationList)
if err := kbClient.List(context.Background(), locations, &kbclient.ListOptions{Namespace: f.Namespace()}); err != nil {
return errors.WithStack(err)
}
for _, location := range locations.Items {
if !location.Spec.Default {
continue
}
if location.Name == o.Name {
// Do not update if the origin default BSL is the current one.
break
}
location.Spec.Default = false
if err := kbClient.Update(context.Background(), &location, &kbclient.UpdateOptions{}); err != nil {
return errors.WithStack(err)
}
break
}
}

location.Spec.Default = o.DefaultBackupStorageLocation
if err := kbClient.Update(context.Background(), location, &kbclient.UpdateOptions{}); err != nil {
return errors.WithStack(err)
}

fmt.Printf("Backup storage location %q configured successfully.\n", o.Name)
return nil
}
3 changes: 1 addition & 2 deletions pkg/cmd/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ func NewCommand(f client.Factory) *cobra.Command {
command.Flags().BoolVar(&config.restoreOnly, "restore-only", config.restoreOnly, "Run in a mode where only restores are allowed; backups, schedules, and garbage-collection are all disabled. DEPRECATED: this flag will be removed in v2.0. Use read-only backup storage locations instead.")
command.Flags().StringSliceVar(&config.disabledControllers, "disable-controllers", config.disabledControllers, fmt.Sprintf("List of controllers to disable on startup. Valid values are %s", strings.Join(controller.DisableableControllers, ",")))
command.Flags().StringSliceVar(&config.restoreResourcePriorities, "restore-resource-priorities", config.restoreResourcePriorities, "Desired order of resource restores; any resource not in the list will be restored alphabetically after the prioritized resources.")
command.Flags().StringVar(&config.defaultBackupLocation, "default-backup-storage-location", config.defaultBackupLocation, "Name of the default backup storage location.")
command.Flags().StringVar(&config.defaultBackupLocation, "default-backup-storage-location", config.defaultBackupLocation, "Name of the default backup storage location. DEPRECATED: this flag will be removed in v2.0. Use \"velero backup-location set --default\" instead.")
command.Flags().DurationVar(&config.storeValidationFrequency, "store-validation-frequency", config.storeValidationFrequency, "How often to verify if the storage is valid. Optional. Set this to `0s` to disable sync. Default 1 minute.")
command.Flags().Var(&volumeSnapshotLocations, "default-volume-snapshot-locations", "List of unique volume providers and default volume snapshot location (provider1:location-01,provider2:location-02,...)")
command.Flags().Float32Var(&config.clientQPS, "client-qps", config.clientQPS, "Maximum number of requests per second by the server to the Kubernetes API once the burst limit has been reached.")
Expand Down Expand Up @@ -713,7 +713,6 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string
s.logger,
s.logLevel,
newPluginManager,
s.config.defaultBackupLocation,
s.metrics,
s.config.formatFlag.Parse(),
)
Expand Down
7 changes: 7 additions & 0 deletions pkg/cmd/util/output/backup_storage_location_printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ var (
{Name: "Phase"},
{Name: "Last Validated"},
{Name: "Access Mode"},
{Name: "Default"},
}
)

Expand All @@ -50,6 +51,11 @@ func printBackupStorageLocation(location *velerov1api.BackupStorageLocation) []m
Object: runtime.RawExtension{Object: location},
}

isDefault := ""
if location.Spec.Default {
isDefault = "true"
}

bucketAndPrefix := location.Spec.ObjectStorage.Bucket
if location.Spec.ObjectStorage.Prefix != "" {
bucketAndPrefix += "/" + location.Spec.ObjectStorage.Prefix
Expand Down Expand Up @@ -78,6 +84,7 @@ func printBackupStorageLocation(location *velerov1api.BackupStorageLocation) []m
status,
LastValidatedStr,
accessMode,
isDefault,
)

return []metav1.TableRow{row}
Expand Down
11 changes: 11 additions & 0 deletions pkg/controller/backup_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import (
snapshotv1beta1api "github.com/kubernetes-csi/external-snapshotter/v2/pkg/apis/volumesnapshot/v1beta1"
snapshotv1beta1listers "github.com/kubernetes-csi/external-snapshotter/v2/pkg/client/listers/volumesnapshot/v1beta1"

"github.com/vmware-tanzu/velero/internal/storage"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
pkgbackup "github.com/vmware-tanzu/velero/pkg/backup"
"github.com/vmware-tanzu/velero/pkg/discovery"
Expand Down Expand Up @@ -345,6 +346,16 @@ func (c *backupController) prepareBackupRequest(backup *velerov1api.Backup) *pkg
// default storage location if not specified
if request.Spec.StorageLocation == "" {
request.Spec.StorageLocation = c.defaultBackupLocation

locationList, err := storage.ListBackupStorageLocations(context.Background(), c.kbClient, request.Namespace)
if err == nil {
for _, location := range locationList.Items {
if location.Spec.Default {
request.Spec.StorageLocation = location.Name
break
}
}
}
}

if request.Spec.DefaultVolumesToRestic == nil {
Expand Down
2 changes: 1 addition & 1 deletion pkg/controller/backup_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ func TestDefaultBackupTTL(t *testing.T) {
}

func TestProcessBackupCompletions(t *testing.T) {
defaultBackupLocation := builder.ForBackupStorageLocation("velero", "loc-1").Bucket("store-1").Result()
defaultBackupLocation := builder.ForBackupStorageLocation("velero", "loc-1").Default(true).Bucket("store-1").Result()

now, err := time.Parse(time.RFC1123Z, time.RFC1123Z)
require.NoError(t, err)
Expand Down
Loading

0 comments on commit 2c1a346

Please sign in to comment.