Add a BSL controller to handle validation + update BSL status phase (#2674)

* Add BSL controller

Signed-off-by: Carlisia <carlisia@vmware.com>

* Add changelog

Signed-off-by: Carlisia <carlisia@vmware.com>

* Make update

Signed-off-by: Carlisia <carlisia@vmware.com>

* Update docs

Signed-off-by: Carlisia <carlisia@vmware.com>

* Add kubebuilder dependency

Signed-off-by: Carlisia <carlisia@vmware.com>

* Add export

Signed-off-by: Carlisia <carlisia@vmware.com>

* add kubebuilder binaries into velero builder image

Signed-off-by: Ashish Amarnath <ashisham@vmware.com>

* Reset velero dockerfile

Signed-off-by: Carlisia <carlisia@vmware.com>

* Consolidate all logic

Signed-off-by: Carlisia <carlisia@vmware.com>

* Add copyright header

Signed-off-by: Carlisia <carlisia@vmware.com>

* Clean up + add "last validated" column

Signed-off-by: Carlisia <carlisia@vmware.com>

* Better tests

Signed-off-by: Carlisia <carlisia@vmware.com>

* Add more tests

Signed-off-by: Carlisia <carlisia@vmware.com>

* Better logging

Signed-off-by: Carlisia <carlisia@vmware.com>

* Format

Signed-off-by: Carlisia <carlisia@vmware.com>

* Code reviews

Signed-off-by: Carlisia <carlisia@vmware.com>

* Address code review

Signed-off-by: Carlisia <carlisia@vmware.com>

* Remove redundant logic

Signed-off-by: Carlisia <carlisia@vmware.com>

Co-authored-by: Ashish Amarnath <ashisham@vmware.com>
This commit is contained in:
Carlisia Campos
2020-07-14 14:47:00 -07:00
committed by GitHub
parent 3d3b9e312a
commit dbd0aa4915
19 changed files with 1015 additions and 92 deletions

View File

@@ -58,14 +58,14 @@ func NewCreateCommand(f client.Factory, use string) *cobra.Command {
}
type CreateOptions struct {
Name string
Provider string
Bucket string
Prefix string
BackupSyncPeriod time.Duration
Config flag.Map
Labels flag.Map
AccessMode *flag.Enum
Name string
Provider string
Bucket string
Prefix string
BackupSyncPeriod, ValidationFrequency time.Duration
Config flag.Map
Labels flag.Map
AccessMode *flag.Enum
}
func NewCreateOptions() *CreateOptions {
@@ -83,7 +83,8 @@ 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.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")
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.")
flags.Var(&o.Config, "config", "configuration key-value pairs")
flags.Var(&o.Labels, "labels", "labels to apply to the backup storage location")
flags.Var(
@@ -119,12 +120,16 @@ func (o *CreateOptions) Complete(args []string, f client.Factory) error {
}
func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error {
var backupSyncPeriod *metav1.Duration
var backupSyncPeriod, validationFrequency *metav1.Duration
if c.Flags().Changed("backup-sync-period") {
backupSyncPeriod = &metav1.Duration{Duration: o.BackupSyncPeriod}
}
if c.Flags().Changed("validation-frequency") {
validationFrequency = &metav1.Duration{Duration: o.ValidationFrequency}
}
backupStorageLocation := &velerov1api.BackupStorageLocation{
ObjectMeta: metav1.ObjectMeta{
Namespace: f.Namespace(),
@@ -139,9 +144,10 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error {
Prefix: o.Prefix,
},
},
Config: o.Config.Data(),
AccessMode: velerov1api.BackupStorageLocationAccessMode(o.AccessMode.String()),
BackupSyncPeriod: backupSyncPeriod,
Config: o.Config.Data(),
AccessMode: velerov1api.BackupStorageLocationAccessMode(o.AccessMode.String()),
BackupSyncPeriod: backupSyncPeriod,
ValidationFrequency: validationFrequency,
},
}

View File

@@ -49,7 +49,6 @@ import (
snapshotv1beta1informers "github.com/kubernetes-csi/external-snapshotter/v2/pkg/client/informers/externalversions"
snapshotv1beta1listers "github.com/kubernetes-csi/external-snapshotter/v2/pkg/client/listers/volumesnapshot/v1beta1"
api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/backup"
"github.com/vmware-tanzu/velero/pkg/buildinfo"
"github.com/vmware-tanzu/velero/pkg/client"
@@ -72,10 +71,10 @@ import (
ctrl "sigs.k8s.io/controller-runtime"
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/manager"
"github.com/vmware-tanzu/velero/internal/util/managercontroller"
"github.com/vmware-tanzu/velero/internal/velero"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
)
@@ -84,6 +83,7 @@ const (
defaultMetricsAddress = ":8085"
defaultBackupSyncPeriod = time.Minute
defaultStoreValidationFrequency = time.Minute
defaultPodVolumeOperationTimeout = 240 * time.Minute
defaultResourceTerminatingTimeout = 10 * time.Minute
@@ -125,7 +125,7 @@ var disableControllerList = []string{
type serverConfig struct {
pluginDir, metricsAddress, defaultBackupLocation string
backupSyncPeriod, podVolumeOperationTimeout, resourceTerminatingTimeout time.Duration
defaultBackupTTL time.Duration
defaultBackupTTL, storeValidationFrequency time.Duration
restoreResourcePriorities []string
defaultVolumeSnapshotLocations map[string]string
restoreOnly bool
@@ -154,6 +154,7 @@ func NewCommand(f client.Factory) *cobra.Command {
defaultVolumeSnapshotLocations: make(map[string]string),
backupSyncPeriod: defaultBackupSyncPeriod,
defaultBackupTTL: defaultBackupTTL,
storeValidationFrequency: defaultStoreValidationFrequency,
podVolumeOperationTimeout: defaultPodVolumeOperationTimeout,
restoreResourcePriorities: defaultRestorePriorities,
clientQPS: defaultClientQPS,
@@ -217,6 +218,7 @@ func NewCommand(f client.Factory) *cobra.Command {
command.Flags().StringSliceVar(&config.disabledControllers, "disable-controllers", config.disabledControllers, fmt.Sprintf("list of controllers to disable on startup. Valid values are %s", strings.Join(disableControllerList, ",")))
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().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")
command.Flags().IntVar(&config.clientBurst, "client-burst", config.clientBurst, "maximum number of requests by the server to the Kubernetes API in a short period of time")
@@ -296,7 +298,7 @@ func newServer(f client.Factory, config serverConfig, logger *logrus.Logger) (*s
}
var csiSnapClient *snapshotv1beta1client.Clientset
if features.IsEnabled(api.CSIFeatureFlag) {
if features.IsEnabled(velerov1api.CSIFeatureFlag) {
csiSnapClient, err = snapshotv1beta1client.NewForConfig(clientConfig)
if err != nil {
cancelFunc()
@@ -359,21 +361,6 @@ func (s *server) run() error {
return err
}
if err := s.validateBackupStorageLocations(); err != nil {
return err
}
// Fetching from the server directly since at this point
// the cache has not yet started
bsl := &velerov1api.BackupStorageLocation{}
if err := s.mgr.GetAPIReader().Get(context.Background(), kbclient.ObjectKey{
Namespace: s.namespace,
Name: s.config.defaultBackupLocation,
}, bsl); err != nil {
s.logger.WithError(errors.WithStack(err)).
Warnf("A backup storage location named %s has been specified for the server to use by default, but no corresponding backup storage location exists. Backups with a location not matching the default will need to explicitly specify an existing location", s.config.defaultBackupLocation)
}
if err := s.initRestic(); err != nil {
return err
}
@@ -427,14 +414,14 @@ func (s *server) veleroResourcesExist() error {
var veleroGroupVersion *metav1.APIResourceList
for _, gv := range s.discoveryHelper.Resources() {
if gv.GroupVersion == api.SchemeGroupVersion.String() {
if gv.GroupVersion == velerov1api.SchemeGroupVersion.String() {
veleroGroupVersion = gv
break
}
}
if veleroGroupVersion == nil {
return errors.Errorf("Velero API group %s not found. Apply examples/common/00-prereqs.yaml to create it.", api.SchemeGroupVersion)
return errors.Errorf("Velero API group %s not found. Apply examples/common/00-prereqs.yaml to create it.", velerov1api.SchemeGroupVersion)
}
foundResources := sets.NewString()
@@ -443,13 +430,13 @@ func (s *server) veleroResourcesExist() error {
}
var errs []error
for kind := range api.CustomResources() {
for kind := range velerov1api.CustomResources() {
if foundResources.Has(kind) {
s.logger.WithField("kind", kind).Debug("Found custom resource")
continue
}
errs = append(errs, errors.Errorf("custom resource %s not found in Velero API group %s", kind, api.SchemeGroupVersion))
errs = append(errs, errors.Errorf("custom resource %s not found in Velero API group %s", kind, velerov1api.SchemeGroupVersion))
}
if len(errs) > 0 {
@@ -461,43 +448,6 @@ func (s *server) veleroResourcesExist() error {
return nil
}
// validateBackupStorageLocations checks to ensure all backup storage locations exist
// and have a compatible layout, and returns an error if not.
func (s *server) validateBackupStorageLocations() error {
s.logger.Info("Checking that all backup storage locations are valid")
pluginManager := clientmgmt.NewManager(s.logger, s.logLevel, s.pluginRegistry)
defer pluginManager.CleanupClients()
// Fetching from the server directly since at this point
// the cache has not yet started
locations := &velerov1api.BackupStorageLocationList{}
if err := s.mgr.GetAPIReader().List(context.Background(), locations, &kbclient.ListOptions{
Namespace: s.namespace,
}); err != nil {
return errors.WithStack(err)
}
var invalid []string
for _, location := range locations.Items {
backupStore, err := persistence.NewObjectBackupStore(&location, pluginManager, s.logger)
if err != nil {
invalid = append(invalid, errors.Wrapf(err, "error getting backup store for location %q", location.Name).Error())
continue
}
if err := backupStore.IsValid(); err != nil {
invalid = append(invalid, errors.Wrapf(err, "backup store for location %q is invalid", location.Name).Error())
}
}
if len(invalid) > 0 {
return errors.Errorf("some backup storage locations are invalid: %s", strings.Join(invalid, "; "))
}
return nil
}
// - Custom Resource Definitions come before Custom Resource so that they can be
// restored with their corresponding CRD.
// - Namespaces go second because all namespaced resources depend on them.
@@ -595,12 +545,12 @@ func (s *server) getCSISnapshotListers() (snapshotv1beta1listers.VolumeSnapshotL
// If CSI is enabled, check for the CSI groups and generate the listers
// If CSI isn't enabled, return empty listers.
if features.IsEnabled(api.CSIFeatureFlag) {
if features.IsEnabled(velerov1api.CSIFeatureFlag) {
_, err = s.discoveryClient.ServerResourcesForGroupVersion(snapshotv1beta1api.SchemeGroupVersion.String())
switch {
case apierrors.IsNotFound(err):
// CSI is enabled, but the required CRDs aren't installed, so halt.
s.logger.Fatalf("The '%s' feature flag was specified, but CSI API group [%s] was not found.", api.CSIFeatureFlag, snapshotv1beta1api.SchemeGroupVersion.String())
s.logger.Fatalf("The '%s' feature flag was specified, but CSI API group [%s] was not found.", velerov1api.CSIFeatureFlag, snapshotv1beta1api.SchemeGroupVersion.String())
case err == nil:
// CSI is enabled, and the resources were found.
// Instantiate the listers fully
@@ -901,6 +851,22 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string
s.logger.WithField("informer", informer).Info("Informer cache synced")
}
storageLocationInfo := velero.StorageLocation{
Client: s.mgr.GetClient(),
Ctx: s.ctx,
DefaultStorageLocation: s.config.defaultBackupLocation,
DefaultStoreValidationFrequency: s.config.storeValidationFrequency,
NewPluginManager: newPluginManager,
NewBackupStore: persistence.NewObjectBackupStore,
}
if err := (&controller.BackupStorageLocationReconciler{
Scheme: s.mgr.GetScheme(),
StorageLocation: storageLocationInfo,
Log: s.logger,
}).SetupWithManager(s.mgr); err != nil {
s.logger.Fatal(err, "unable to create controller", "controller", "BackupStorageLocation")
}
// TODO(2.0): presuming all controllers and resources are converted to runtime-controller
// by v2.0, the block from this line and including the `s.mgr.Start() will be
// deprecated, since the manager auto-starts all the caches. Until then, we need to start the
@@ -944,7 +910,7 @@ func NewCSIInformerFactoryWrapper(c snapshotv1beta1client.Interface) *CSIInforme
// This is desirable for VolumeSnapshots, as we want to query for all VolumeSnapshots across all namespaces using this informer
w := &CSIInformerFactoryWrapper{}
if features.IsEnabled(api.CSIFeatureFlag) {
if features.IsEnabled(velerov1api.CSIFeatureFlag) {
w.factory = snapshotv1beta1informers.NewSharedInformerFactoryWithOptions(c, 0)
}
return w
@@ -952,14 +918,14 @@ func NewCSIInformerFactoryWrapper(c snapshotv1beta1client.Interface) *CSIInforme
// Start proxies the Start call to the CSI SharedInformerFactory.
func (w *CSIInformerFactoryWrapper) Start(stopCh <-chan struct{}) {
if features.IsEnabled(api.CSIFeatureFlag) {
if features.IsEnabled(velerov1api.CSIFeatureFlag) {
w.factory.Start(stopCh)
}
}
// WaitForCacheSync proxies the WaitForCacheSync call to the CSI SharedInformerFactory.
func (w *CSIInformerFactoryWrapper) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool {
if features.IsEnabled(api.CSIFeatureFlag) {
if features.IsEnabled(velerov1api.CSIFeatureFlag) {
return w.factory.WaitForCacheSync(stopCh)
}
return nil

View File

@@ -31,6 +31,7 @@ var (
{Name: "Provider"},
{Name: "Bucket/Prefix"},
{Name: "Phase"},
{Name: "Last Validated"},
{Name: "Access Mode"},
}
)
@@ -64,11 +65,18 @@ func printBackupStorageLocation(location *velerov1api.BackupStorageLocation) []m
status = "Unknown"
}
lastValidated := location.Status.LastValidationTime
LastValidatedStr := "Unknown"
if lastValidated != nil {
LastValidatedStr = lastValidated.String()
}
row.Cells = append(row.Cells,
location.Name,
location.Spec.Provider,
bucketAndPrefix,
status,
LastValidatedStr,
accessMode,
)