use the default backup storage location for restic
Signed-off-by: Steve Kriss <steve@heptio.com>pull/799/head
parent
833a6307a9
commit
20f89fbcef
|
@ -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)
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue