diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index ef62c5331..2ed64424f 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -55,6 +55,7 @@ import ( "github.com/heptio/ark/pkg/client" "github.com/heptio/ark/pkg/cloudprovider" "github.com/heptio/ark/pkg/cmd" + "github.com/heptio/ark/pkg/cmd/util/flag" "github.com/heptio/ark/pkg/cmd/util/signals" "github.com/heptio/ark/pkg/controller" arkdiscovery "github.com/heptio/ark/pkg/discovery" @@ -80,6 +81,7 @@ type serverConfig struct { pluginDir, metricsAddress, defaultBackupLocation string backupSyncPeriod, podVolumeOperationTimeout time.Duration restoreResourcePriorities []string + defaultVolumeSnapshotLocations map[string]string restoreOnly bool } @@ -87,12 +89,13 @@ func NewCommand() *cobra.Command { var ( logLevelFlag = logging.LogLevelFlag(logrus.InfoLevel) config = serverConfig{ - pluginDir: "/plugins", - metricsAddress: defaultMetricsAddress, - defaultBackupLocation: "default", - backupSyncPeriod: defaultBackupSyncPeriod, - podVolumeOperationTimeout: defaultPodVolumeOperationTimeout, - restoreResourcePriorities: defaultRestorePriorities, + pluginDir: "/plugins", + metricsAddress: defaultMetricsAddress, + defaultBackupLocation: "default", + defaultVolumeSnapshotLocations: make(map[string]string), + backupSyncPeriod: defaultBackupSyncPeriod, + podVolumeOperationTimeout: defaultPodVolumeOperationTimeout, + restoreResourcePriorities: defaultRestorePriorities, } ) @@ -144,6 +147,12 @@ func NewCommand() *cobra.Command { 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") + volumeSnapshotLocations := flag.NewMap().WithKeyValueDelimiter(":") + command.Flags().Var(&volumeSnapshotLocations, "default-volume-snapshot-locations", "list of unique volume providers and default volume snapshot location (provider1:location-01, provider2:location-02, ...)") + if volumeSnapshotLocations.Data() != nil { + config.defaultVolumeSnapshotLocations = volumeSnapshotLocations.Data() + } + return command } @@ -277,6 +286,11 @@ func (s *server) run() error { Warnf("Default backup storage location %q not found; backups must explicitly specify a location", s.config.defaultBackupLocation) } + defaultVolumeSnapshotLocations, err := s.getDefaultVolumeSnapshotLocations() + if err != nil { + return err + } + if config.PersistentVolumeProvider == nil { s.logger.Info("PersistentVolumeProvider config not provided, volume snapshots and restores are disabled") } else { @@ -292,13 +306,50 @@ func (s *server) run() error { return err } - if err := s.runControllers(config); err != nil { + if err := s.runControllers(config, defaultVolumeSnapshotLocations); err != nil { return err } return nil } +func (s *server) getDefaultVolumeSnapshotLocations() (map[string]*api.VolumeSnapshotLocation, error) { + providerDefaults := make(map[string]*api.VolumeSnapshotLocation) + if len(s.config.defaultVolumeSnapshotLocations) == 0 { + return providerDefaults, nil + } + + volumeSnapshotLocations, err := s.arkClient.ArkV1().VolumeSnapshotLocations(s.namespace).List(metav1.ListOptions{}) + if err != nil { + return providerDefaults, errors.WithStack(err) + } + + providerLocations := make(map[string][]*api.VolumeSnapshotLocation) + for _, vsl := range volumeSnapshotLocations.Items { + providerLocations[vsl.Spec.Provider] = append(providerLocations[vsl.Spec.Provider], &vsl) + } + + for provider, locations := range providerLocations { + defaultLocation, ok := s.config.defaultVolumeSnapshotLocations[provider] + if !ok { + return providerDefaults, errors.Errorf("missing provider %s. When using default volume snapshot locations, one must exist for every known provider.", provider) + } + + for _, location := range locations { + if location.ObjectMeta.Name == defaultLocation { + providerDefaults[provider] = location + break + } + } + + if _, ok := providerDefaults[provider]; !ok { + return providerDefaults, errors.Errorf("%s is not a valid volume snapshot location for %s", defaultLocation, provider) + } + } + + return providerDefaults, nil +} + func (s *server) applyConfigDefaults(c *api.Config) { if s.config.backupSyncPeriod == 0 { s.config.backupSyncPeriod = defaultBackupSyncPeriod @@ -572,7 +623,7 @@ func (s *server) initRestic() error { return nil } -func (s *server) runControllers(config *api.Config) error { +func (s *server) runControllers(config *api.Config, defaultVolumeSnapshotLocations map[string]*api.VolumeSnapshotLocation) error { s.logger.Info("Starting controllers") ctx := s.ctx @@ -637,7 +688,7 @@ func (s *server) runControllers(config *api.Config) error { s.sharedInformerFactory.Ark().V1().BackupStorageLocations(), s.config.defaultBackupLocation, s.sharedInformerFactory.Ark().V1().VolumeSnapshotLocations(), - nil, + s.config.defaultVolumeSnapshotLocations, s.metrics, ) wg.Add(1)