use the default backup storage location for restic

Signed-off-by: Steve Kriss <steve@heptio.com>
pull/799/head
Steve Kriss 2018-08-09 13:28:57 -07:00
parent 833a6307a9
commit 20f89fbcef
5 changed files with 69 additions and 49 deletions

View File

@ -271,6 +271,11 @@ func (s *server) run() error {
s.watchConfig(originalConfig) s.watchConfig(originalConfig)
backupStorageLocation, err := s.arkClient.ArkV1().BackupStorageLocations(s.namespace).Get(s.defaultBackupLocation, metav1.GetOptions{})
if err != nil {
return errors.WithStack(err)
}
objectStore, err := getObjectStore(config.BackupStorageProvider.CloudProviderConfig, s.pluginManager) objectStore, err := getObjectStore(config.BackupStorageProvider.CloudProviderConfig, s.pluginManager)
if err != nil { if err != nil {
return err return err
@ -288,13 +293,13 @@ func (s *server) run() error {
s.blockStore = blockStore s.blockStore = blockStore
} }
if config.BackupStorageProvider.ResticLocation != "" { if backupStorageLocation.Spec.Config[restic.ResticLocationConfigKey] != "" {
if err := s.initRestic(config.BackupStorageProvider); err != nil { if err := s.initRestic(backupStorageLocation.Spec.Provider); err != nil {
return err return err
} }
} }
if err := s.runControllers(config); err != nil { if err := s.runControllers(config, backupStorageLocation); err != nil {
return err return err
} }
@ -525,7 +530,7 @@ func durationMin(a, b time.Duration) time.Duration {
return b return b
} }
func (s *server) initRestic(config api.ObjectStorageProviderConfig) error { func (s *server) initRestic(providerName string) error {
// warn if restic daemonset does not exist // warn if restic daemonset does not exist
if _, err := s.kubeClient.AppsV1().DaemonSets(s.namespace).Get(restic.DaemonSet, metav1.GetOptions{}); apierrors.IsNotFound(err) { if _, err := s.kubeClient.AppsV1().DaemonSets(s.namespace).Get(restic.DaemonSet, metav1.GetOptions{}); apierrors.IsNotFound(err) {
s.logger.Warn("Ark restic daemonset not found; restic backups/restores will not work until it's created") s.logger.Warn("Ark restic daemonset not found; restic backups/restores will not work until it's created")
@ -539,7 +544,7 @@ func (s *server) initRestic(config api.ObjectStorageProviderConfig) error {
} }
// set the env vars that restic uses for creds purposes // set the env vars that restic uses for creds purposes
if config.Name == string(restic.AzureBackend) { if providerName == string(restic.AzureBackend) {
os.Setenv("AZURE_ACCOUNT_NAME", os.Getenv("AZURE_STORAGE_ACCOUNT_ID")) os.Setenv("AZURE_ACCOUNT_NAME", os.Getenv("AZURE_STORAGE_ACCOUNT_ID"))
os.Setenv("AZURE_ACCOUNT_KEY", os.Getenv("AZURE_STORAGE_KEY")) os.Setenv("AZURE_ACCOUNT_KEY", os.Getenv("AZURE_STORAGE_KEY"))
} }
@ -578,7 +583,7 @@ func (s *server) initRestic(config api.ObjectStorageProviderConfig) error {
return nil return nil
} }
func (s *server) runControllers(config *api.Config) error { func (s *server) runControllers(config *api.Config, defaultBackupLocation *api.BackupStorageLocation) error {
s.logger.Info("Starting controllers") s.logger.Info("Starting controllers")
ctx := s.ctx ctx := s.ctx
@ -755,7 +760,7 @@ func (s *server) runControllers(config *api.Config) error {
s.logger, s.logger,
s.sharedInformerFactory.Ark().V1().ResticRepositories(), s.sharedInformerFactory.Ark().V1().ResticRepositories(),
s.arkClient.ArkV1(), s.arkClient.ArkV1(),
config.BackupStorageProvider, defaultBackupLocation,
s.resticManager, s.resticManager,
) )
wg.Add(1) wg.Add(1)

View File

@ -44,7 +44,7 @@ type resticRepositoryController struct {
resticRepositoryClient arkv1client.ResticRepositoriesGetter resticRepositoryClient arkv1client.ResticRepositoriesGetter
resticRepositoryLister listers.ResticRepositoryLister resticRepositoryLister listers.ResticRepositoryLister
objectStorageConfig arkv1api.ObjectStorageProviderConfig storageLocation *arkv1api.BackupStorageLocation
repositoryManager restic.RepositoryManager repositoryManager restic.RepositoryManager
clock clock.Clock clock clock.Clock
@ -55,14 +55,14 @@ func NewResticRepositoryController(
logger logrus.FieldLogger, logger logrus.FieldLogger,
resticRepositoryInformer informers.ResticRepositoryInformer, resticRepositoryInformer informers.ResticRepositoryInformer,
resticRepositoryClient arkv1client.ResticRepositoriesGetter, resticRepositoryClient arkv1client.ResticRepositoriesGetter,
objectStorageConfig arkv1api.ObjectStorageProviderConfig, storageLocation *arkv1api.BackupStorageLocation,
repositoryManager restic.RepositoryManager, repositoryManager restic.RepositoryManager,
) Interface { ) Interface {
c := &resticRepositoryController{ c := &resticRepositoryController{
genericController: newGenericController("restic-repository", logger), genericController: newGenericController("restic-repository", logger),
resticRepositoryClient: resticRepositoryClient, resticRepositoryClient: resticRepositoryClient,
resticRepositoryLister: resticRepositoryInformer.Lister(), resticRepositoryLister: resticRepositoryInformer.Lister(),
objectStorageConfig: objectStorageConfig, storageLocation: storageLocation,
repositoryManager: repositoryManager, repositoryManager: repositoryManager,
clock: &clock.RealClock{}, clock: &clock.RealClock{},
} }
@ -139,7 +139,7 @@ func (c *resticRepositoryController) initializeRepo(req *v1.ResticRepository, lo
// defaulting - if the patch fails, return an error so the item is returned to the queue // defaulting - if the patch fails, return an error so the item is returned to the queue
if err := c.patchResticRepository(req, func(r *v1.ResticRepository) { if err := c.patchResticRepository(req, func(r *v1.ResticRepository) {
r.Spec.ResticIdentifier = restic.GetRepoIdentifier(c.objectStorageConfig, r.Name) r.Spec.ResticIdentifier = restic.GetRepoIdentifier(c.storageLocation, r.Name)
if r.Spec.MaintenanceFrequency.Duration <= 0 { if r.Spec.MaintenanceFrequency.Duration <= 0 {
r.Spec.MaintenanceFrequency = metav1.Duration{Duration: restic.DefaultMaintenanceFrequency} r.Spec.MaintenanceFrequency = metav1.Duration{Duration: restic.DefaultMaintenanceFrequency}

View File

@ -36,6 +36,7 @@ const (
DaemonSet = "restic" DaemonSet = "restic"
InitContainer = "restic-wait" InitContainer = "restic-wait"
DefaultMaintenanceFrequency = 24 * time.Hour DefaultMaintenanceFrequency = 24 * time.Hour
ResticLocationConfigKey = "restic-location"
podAnnotationPrefix = "snapshot.ark.heptio.com/" podAnnotationPrefix = "snapshot.ark.heptio.com/"
volumesToBackupAnnotation = "backup.ark.heptio.com/backup-volumes" volumesToBackupAnnotation = "backup.ark.heptio.com/backup-volumes"

View File

@ -38,9 +38,10 @@ var getAWSBucketRegion = aws.GetBucketRegion
// getRepoPrefix returns the prefix of the value of the --repo flag for // getRepoPrefix returns the prefix of the value of the --repo flag for
// restic commands, i.e. everything except the "/<repo-name>". // restic commands, i.e. everything except the "/<repo-name>".
func getRepoPrefix(config arkv1api.ObjectStorageProviderConfig) string { func getRepoPrefix(location *arkv1api.BackupStorageLocation) string {
var ( var (
parts = strings.SplitN(config.ResticLocation, "/", 2) resticLocation = location.Spec.Config[ResticLocationConfigKey]
parts = strings.SplitN(resticLocation, "/", 2)
bucket, path, prefix string bucket, path, prefix string
) )
@ -51,13 +52,13 @@ func getRepoPrefix(config arkv1api.ObjectStorageProviderConfig) string {
path = parts[1] path = parts[1]
} }
switch BackendType(config.Name) { switch BackendType(location.Spec.Provider) {
case AWSBackend: case AWSBackend:
var url string var url string
switch { switch {
// non-AWS, S3-compatible object store // non-AWS, S3-compatible object store
case config.Config["s3Url"] != "": case location.Spec.Config["s3Url"] != "":
url = config.Config["s3Url"] url = location.Spec.Config["s3Url"]
default: default:
region, err := getAWSBucketRegion(bucket) region, err := getAWSBucketRegion(bucket)
if err != nil { if err != nil {
@ -68,7 +69,7 @@ func getRepoPrefix(config arkv1api.ObjectStorageProviderConfig) string {
url = fmt.Sprintf("s3-%s.amazonaws.com", region) url = fmt.Sprintf("s3-%s.amazonaws.com", region)
} }
return fmt.Sprintf("s3:%s/%s", url, config.ResticLocation) return fmt.Sprintf("s3:%s/%s", url, resticLocation)
case AzureBackend: case AzureBackend:
prefix = "azure" prefix = "azure"
case GCPBackend: case GCPBackend:
@ -80,8 +81,8 @@ func getRepoPrefix(config arkv1api.ObjectStorageProviderConfig) string {
// GetRepoIdentifier returns the string to be used as the value of the --repo flag in // GetRepoIdentifier returns the string to be used as the value of the --repo flag in
// restic commands for the given repository. // restic commands for the given repository.
func GetRepoIdentifier(config arkv1api.ObjectStorageProviderConfig, name string) string { func GetRepoIdentifier(location *arkv1api.BackupStorageLocation, name string) string {
prefix := getRepoPrefix(config) prefix := getRepoPrefix(location)
return fmt.Sprintf("%s/%s", strings.TrimSuffix(prefix, "/"), name) return fmt.Sprintf("%s/%s", strings.TrimSuffix(prefix, "/"), name)
} }

View File

@ -30,47 +30,60 @@ func TestGetRepoIdentifier(t *testing.T) {
getAWSBucketRegion = func(string) (string, error) { getAWSBucketRegion = func(string) (string, error) {
return "", errors.New("no region found") return "", errors.New("no region found")
} }
config := arkv1api.ObjectStorageProviderConfig{
CloudProviderConfig: arkv1api.CloudProviderConfig{Name: "aws"}, backupLocation := &arkv1api.BackupStorageLocation{
ResticLocation: "bucket/prefix", Spec: arkv1api.BackupStorageLocationSpec{
Provider: "aws",
Config: map[string]string{ResticLocationConfigKey: "bucket/prefix"},
},
} }
assert.Equal(t, "s3:s3.amazonaws.com/bucket/prefix/repo-1", GetRepoIdentifier(config, "repo-1")) assert.Equal(t, "s3:s3.amazonaws.com/bucket/prefix/repo-1", GetRepoIdentifier(backupLocation, "repo-1"))
// stub implementation of getAWSBucketRegion // stub implementation of getAWSBucketRegion
getAWSBucketRegion = func(string) (string, error) { getAWSBucketRegion = func(string) (string, error) {
return "us-west-2", nil return "us-west-2", nil
} }
config = arkv1api.ObjectStorageProviderConfig{ backupLocation = &arkv1api.BackupStorageLocation{
CloudProviderConfig: arkv1api.CloudProviderConfig{Name: "aws"}, Spec: arkv1api.BackupStorageLocationSpec{
ResticLocation: "bucket", Provider: "aws",
} Config: map[string]string{ResticLocationConfigKey: "bucket"},
assert.Equal(t, "s3:s3-us-west-2.amazonaws.com/bucket/repo-1", GetRepoIdentifier(config, "repo-1"))
config = arkv1api.ObjectStorageProviderConfig{
CloudProviderConfig: arkv1api.CloudProviderConfig{Name: "aws"},
ResticLocation: "bucket/prefix",
}
assert.Equal(t, "s3:s3-us-west-2.amazonaws.com/bucket/prefix/repo-1", GetRepoIdentifier(config, "repo-1"))
config = arkv1api.ObjectStorageProviderConfig{
CloudProviderConfig: arkv1api.CloudProviderConfig{
Name: "aws",
Config: map[string]string{"s3Url": "alternate-url"},
}, },
ResticLocation: "bucket/prefix",
} }
assert.Equal(t, "s3:alternate-url/bucket/prefix/repo-1", GetRepoIdentifier(config, "repo-1")) assert.Equal(t, "s3:s3-us-west-2.amazonaws.com/bucket/repo-1", GetRepoIdentifier(backupLocation, "repo-1"))
config = arkv1api.ObjectStorageProviderConfig{ backupLocation = &arkv1api.BackupStorageLocation{
CloudProviderConfig: arkv1api.CloudProviderConfig{Name: "azure"}, Spec: arkv1api.BackupStorageLocationSpec{
ResticLocation: "bucket/prefix", Provider: "aws",
Config: map[string]string{ResticLocationConfigKey: "bucket/prefix"},
},
} }
assert.Equal(t, "azure:bucket:/prefix/repo-1", GetRepoIdentifier(config, "repo-1")) assert.Equal(t, "s3:s3-us-west-2.amazonaws.com/bucket/prefix/repo-1", GetRepoIdentifier(backupLocation, "repo-1"))
config = arkv1api.ObjectStorageProviderConfig{ backupLocation = &arkv1api.BackupStorageLocation{
CloudProviderConfig: arkv1api.CloudProviderConfig{Name: "gcp"}, Spec: arkv1api.BackupStorageLocationSpec{
ResticLocation: "bucket-2/prefix-2", Provider: "aws",
Config: map[string]string{
ResticLocationConfigKey: "bucket/prefix",
"s3Url": "alternate-url",
},
},
} }
assert.Equal(t, "gs:bucket-2:/prefix-2/repo-2", GetRepoIdentifier(config, "repo-2")) assert.Equal(t, "s3:alternate-url/bucket/prefix/repo-1", GetRepoIdentifier(backupLocation, "repo-1"))
backupLocation = &arkv1api.BackupStorageLocation{
Spec: arkv1api.BackupStorageLocationSpec{
Provider: "azure",
Config: map[string]string{ResticLocationConfigKey: "bucket/prefix"},
},
}
assert.Equal(t, "azure:bucket:/prefix/repo-1", GetRepoIdentifier(backupLocation, "repo-1"))
backupLocation = &arkv1api.BackupStorageLocation{
Spec: arkv1api.BackupStorageLocationSpec{
Provider: "gcp",
Config: map[string]string{ResticLocationConfigKey: "bucket-2/prefix-2"},
},
}
assert.Equal(t, "gs:bucket-2:/prefix-2/repo-2", GetRepoIdentifier(backupLocation, "repo-2"))
} }