Skip to content

Commit

Permalink
add azure-specific code to support multi-location restic
Browse files Browse the repository at this point in the history
Signed-off-by: Steve Kriss <steve@heptio.com>
  • Loading branch information
skriss committed Oct 5, 2018
1 parent d009163 commit 3af43b4
Show file tree
Hide file tree
Showing 15 changed files with 159 additions and 89 deletions.
4 changes: 4 additions & 0 deletions pkg/apis/ark/v1/pod_volume_backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ type PodVolumeBackupSpec struct {
// up.
Volume string `json:"volume"`

// BackupStorageLocation is the name of the backup storage location
// where the restic repository is stored.
BackupStorageLocation string `json:"backupStorageLocation"`

// RepoIdentifier is the restic repository identifier.
RepoIdentifier string `json:"repoIdentifier"`

Expand Down
4 changes: 4 additions & 0 deletions pkg/apis/ark/v1/pod_volume_restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ type PodVolumeRestoreSpec struct {
// Volume is the name of the volume within the Pod to be restored.
Volume string `json:"volume"`

// BackupStorageLocation is the name of the backup storage location
// where the restic repository is stored.
BackupStorageLocation string `json:"backupStorageLocation"`

// RepoIdentifier is the restic repository identifier.
RepoIdentifier string `json:"repoIdentifier"`

Expand Down
15 changes: 7 additions & 8 deletions pkg/cloudprovider/azure/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ limitations under the License.
package azure

import (
"os"
"strings"

"github.com/Azure/go-autorest/autorest/adal"
Expand All @@ -32,19 +31,19 @@ const (
clientSecretEnvVar = "AZURE_CLIENT_SECRET"
)

// SetResticEnvVars sets the environment variables that restic
// GetResticEnvVars gets the environment variables that restic
// relies on (AZURE_ACCOUNT_NAME and AZURE_ACCOUNT_KEY) based
// on info in the provided object storage location config map.
func SetResticEnvVars(config map[string]string) error {
os.Setenv("AZURE_ACCOUNT_NAME", config[storageAccountConfigKey])

func GetResticEnvVars(config map[string]string) (map[string]string, error) {
storageAccountKey, err := getStorageAccountKey(config)
if err != nil {
return err
return nil, err
}
os.Setenv("AZURE_ACCOUNT_KEY", storageAccountKey)

return nil
return map[string]string{
"AZURE_ACCOUNT_NAME": config[storageAccountConfigKey],
"AZURE_ACCOUNT_KEY": storageAccountKey,
}, nil
}

func newServicePrincipalToken(tenantID, clientID, clientSecret, scope string) (*adal.ServicePrincipalToken, error) {
Expand Down
26 changes: 6 additions & 20 deletions pkg/cmd/cli/restic/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import (

"github.com/heptio/ark/pkg/buildinfo"
"github.com/heptio/ark/pkg/client"
"github.com/heptio/ark/pkg/cloudprovider/azure"
"github.com/heptio/ark/pkg/cmd"
"github.com/heptio/ark/pkg/cmd/util/signals"
"github.com/heptio/ark/pkg/controller"
Expand All @@ -45,12 +44,9 @@ import (
)

func NewServerCommand(f client.Factory) *cobra.Command {
var (
logLevelFlag = logging.LogLevelFlag(logrus.InfoLevel)
location = "default"
)
logLevelFlag := logging.LogLevelFlag(logrus.InfoLevel)

var command = &cobra.Command{
command := &cobra.Command{
Use: "server",
Short: "Run the ark restic server",
Long: "Run the ark restic server",
Expand All @@ -61,15 +57,14 @@ func NewServerCommand(f client.Factory) *cobra.Command {
logger := logging.DefaultLogger(logLevel)
logger.Infof("Starting Ark restic server %s", buildinfo.FormattedGitSHA())

s, err := newResticServer(logger, fmt.Sprintf("%s-%s", c.Parent().Name(), c.Name()), location)
s, err := newResticServer(logger, fmt.Sprintf("%s-%s", c.Parent().Name(), c.Name()))
cmd.CheckError(err)

s.run()
},
}

command.Flags().Var(logLevelFlag, "log-level", fmt.Sprintf("the level at which to log. Valid values are %s.", strings.Join(logLevelFlag.AllowedValues(), ", ")))
command.Flags().StringVar(&location, "default-backup-storage-location", location, "name of the default backup storage location")

return command
}
Expand All @@ -86,7 +81,7 @@ type resticServer struct {
cancelFunc context.CancelFunc
}

func newResticServer(logger logrus.FieldLogger, baseName, locationName string) (*resticServer, error) {
func newResticServer(logger logrus.FieldLogger, baseName string) (*resticServer, error) {
clientConfig, err := client.Config("", "", baseName)
if err != nil {
return nil, err
Expand All @@ -102,17 +97,6 @@ func newResticServer(logger logrus.FieldLogger, baseName, locationName string) (
return nil, errors.WithStack(err)
}

location, err := arkClient.ArkV1().BackupStorageLocations(os.Getenv("HEPTIO_ARK_NAMESPACE")).Get(locationName, metav1.GetOptions{})
if err != nil {
return nil, errors.WithStack(err)
}

if location.Spec.Provider == "azure" {
if err := azure.SetResticEnvVars(location.Spec.Config); err != nil {
return nil, err
}
}

// use a stand-alone pod informer because we want to use a field selector to
// filter to only pods scheduled on this node.
podInformer := corev1informers.NewFilteredPodInformer(
Expand Down Expand Up @@ -170,6 +154,7 @@ func (s *resticServer) run() {
s.podInformer,
s.secretInformer,
s.kubeInformerFactory.Core().V1().PersistentVolumeClaims(),
s.arkInformerFactory.Ark().V1().BackupStorageLocations(),
os.Getenv("NODE_NAME"),
)
wg.Add(1)
Expand All @@ -185,6 +170,7 @@ func (s *resticServer) run() {
s.podInformer,
s.secretInformer,
s.kubeInformerFactory.Core().V1().PersistentVolumeClaims(),
s.arkInformerFactory.Ark().V1().BackupStorageLocations(),
os.Getenv("NODE_NAME"),
)
wg.Add(1)
Expand Down
9 changes: 1 addition & 8 deletions pkg/cmd/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ import (
"github.com/heptio/ark/pkg/buildinfo"
"github.com/heptio/ark/pkg/client"
"github.com/heptio/ark/pkg/cloudprovider"
"github.com/heptio/ark/pkg/cloudprovider/azure"
"github.com/heptio/ark/pkg/cmd"
"github.com/heptio/ark/pkg/cmd/util/signals"
"github.com/heptio/ark/pkg/controller"
Expand Down Expand Up @@ -538,13 +537,6 @@ func (s *server) initRestic(location *api.BackupStorageLocation) error {
return err
}

// set the env vars that restic uses for creds purposes
if location.Spec.Provider == string(restic.AzureBackend) {
if err := azure.SetResticEnvVars(location.Spec.Config); err != nil {
return err
}
}

// use a stand-alone secrets informer so we can filter to only the restic credentials
// secret(s) within the heptio-ark namespace
//
Expand All @@ -569,6 +561,7 @@ func (s *server) initRestic(location *api.BackupStorageLocation) error {
secretsInformer,
s.sharedInformerFactory.Ark().V1().ResticRepositories(),
s.arkClient.ArkV1(),
s.sharedInformerFactory.Ark().V1().BackupStorageLocations(),
s.logger,
)
if err != nil {
Expand Down
16 changes: 15 additions & 1 deletion pkg/controller/pod_volume_backup_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"fmt"
"os"
"path/filepath"
"strings"

jsonpatch "github.com/evanphx/json-patch"
"github.com/pkg/errors"
Expand Down Expand Up @@ -50,6 +51,7 @@ type podVolumeBackupController struct {
secretLister corev1listers.SecretLister
podLister corev1listers.PodLister
pvcLister corev1listers.PersistentVolumeClaimLister
backupLocationLister listers.BackupStorageLocationLister
nodeName string

processBackupFunc func(*arkv1api.PodVolumeBackup) error
Expand All @@ -64,6 +66,7 @@ func NewPodVolumeBackupController(
podInformer cache.SharedIndexInformer,
secretInformer cache.SharedIndexInformer,
pvcInformer corev1informers.PersistentVolumeClaimInformer,
backupLocationInformer informers.BackupStorageLocationInformer,
nodeName string,
) Interface {
c := &podVolumeBackupController{
Expand All @@ -73,6 +76,7 @@ func NewPodVolumeBackupController(
podLister: corev1listers.NewPodLister(podInformer.GetIndexer()),
secretLister: corev1listers.NewSecretLister(secretInformer.GetIndexer()),
pvcLister: pvcInformer.Lister(),
backupLocationLister: backupLocationInformer.Lister(),
nodeName: nodeName,

fileSystem: filesystem.NewFileSystem(),
Expand All @@ -85,6 +89,7 @@ func NewPodVolumeBackupController(
podInformer.HasSynced,
secretInformer.HasSynced,
pvcInformer.Informer().HasSynced,
backupLocationInformer.Informer().HasSynced,
)
c.processBackupFunc = c.processBackup

Expand Down Expand Up @@ -213,6 +218,15 @@ func (c *podVolumeBackupController) processBackup(req *arkv1api.PodVolumeBackup)
req.Spec.Tags,
)

// if this is azure, set resticCmd.Env appropriately
var env []string
if strings.HasPrefix(req.Spec.RepoIdentifier, "azure") {
if env, err = restic.AzureCmdEnv(c.backupLocationLister, req.Namespace, req.Spec.BackupStorageLocation); err != nil {
return c.fail(req, errors.Wrap(err, "error setting restic cmd env").Error(), log)
}
resticCmd.Env = env
}

var stdout, stderr string

if stdout, stderr, err = arkexec.RunCommand(resticCmd.Cmd()); err != nil {
Expand All @@ -221,7 +235,7 @@ func (c *podVolumeBackupController) processBackup(req *arkv1api.PodVolumeBackup)
}
log.Debugf("Ran command=%s, stdout=%s, stderr=%s", resticCmd.String(), stdout, stderr)

snapshotID, err := restic.GetSnapshotID(req.Spec.RepoIdentifier, file, req.Spec.Tags)
snapshotID, err := restic.GetSnapshotID(req.Spec.RepoIdentifier, file, req.Spec.Tags, env)
if err != nil {
log.WithError(err).Error("Error getting SnapshotID")
return c.fail(req, errors.Wrap(err, "error getting snapshot id").Error(), log)
Expand Down
18 changes: 16 additions & 2 deletions pkg/controller/pod_volume_restore_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"strings"

jsonpatch "github.com/evanphx/json-patch"
"github.com/pkg/errors"
Expand Down Expand Up @@ -54,6 +55,7 @@ type podVolumeRestoreController struct {
podLister corev1listers.PodLister
secretLister corev1listers.SecretLister
pvcLister corev1listers.PersistentVolumeClaimLister
backupLocationLister listers.BackupStorageLocationLister
nodeName string

processRestoreFunc func(*arkv1api.PodVolumeRestore) error
Expand All @@ -68,6 +70,7 @@ func NewPodVolumeRestoreController(
podInformer cache.SharedIndexInformer,
secretInformer cache.SharedIndexInformer,
pvcInformer corev1informers.PersistentVolumeClaimInformer,
backupLocationInformer informers.BackupStorageLocationInformer,
nodeName string,
) Interface {
c := &podVolumeRestoreController{
Expand All @@ -77,6 +80,7 @@ func NewPodVolumeRestoreController(
podLister: corev1listers.NewPodLister(podInformer.GetIndexer()),
secretLister: corev1listers.NewSecretLister(secretInformer.GetIndexer()),
pvcLister: pvcInformer.Lister(),
backupLocationLister: backupLocationInformer.Lister(),
nodeName: nodeName,

fileSystem: filesystem.NewFileSystem(),
Expand All @@ -89,6 +93,7 @@ func NewPodVolumeRestoreController(
podInformer.HasSynced,
secretInformer.HasSynced,
pvcInformer.Informer().HasSynced,
backupLocationInformer.Informer().HasSynced,
)
c.processRestoreFunc = c.processRestore

Expand Down Expand Up @@ -281,7 +286,7 @@ func (c *podVolumeRestoreController) processRestore(req *arkv1api.PodVolumeResto
defer os.Remove(credsFile)

// execute the restore process
if err := restorePodVolume(req, credsFile, volumeDir, log); err != nil {
if err := c.restorePodVolume(req, credsFile, volumeDir, log); err != nil {
log.WithError(err).Error("Error restoring volume")
return c.failRestore(req, errors.Wrap(err, "error restoring volume").Error(), log)
}
Expand All @@ -297,7 +302,7 @@ func (c *podVolumeRestoreController) processRestore(req *arkv1api.PodVolumeResto
return nil
}

func restorePodVolume(req *arkv1api.PodVolumeRestore, credsFile, volumeDir string, log logrus.FieldLogger) error {
func (c *podVolumeRestoreController) restorePodVolume(req *arkv1api.PodVolumeRestore, credsFile, volumeDir string, log logrus.FieldLogger) error {
// Get the full path of the new volume's directory as mounted in the daemonset pod, which
// will look like: /host_pods/<new-pod-uid>/volumes/<volume-plugin-name>/<volume-dir>
volumePath, err := singlePathMatch(fmt.Sprintf("/host_pods/%s/volumes/*/%s", string(req.Spec.Pod.UID), volumeDir))
Expand All @@ -312,6 +317,15 @@ func restorePodVolume(req *arkv1api.PodVolumeRestore, credsFile, volumeDir strin
volumePath,
)

// if this is azure, set resticCmd.Env appropriately
if strings.HasPrefix(req.Spec.RepoIdentifier, "azure") {
env, err := restic.AzureCmdEnv(c.backupLocationLister, req.Namespace, req.Spec.BackupStorageLocation)
if err != nil {
return c.failRestore(req, errors.Wrap(err, "error setting restic cmd env").Error(), log)
}
resticCmd.Env = env
}

var stdout, stderr string

if stdout, stderr, err = arkexec.RunCommand(resticCmd.Cmd()); err != nil {
Expand Down
16 changes: 8 additions & 8 deletions pkg/controller/restic_repository_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ func (c *resticRepositoryController) initializeRepo(req *v1.ResticRepository, lo
return err
}

if err := ensureRepo(req.Name, req.Spec.ResticIdentifier, c.repositoryManager); err != nil {
if err := ensureRepo(req, c.repositoryManager); err != nil {
return c.patchResticRepository(req, repoNotReady(err.Error()))
}

Expand All @@ -165,12 +165,12 @@ func (c *resticRepositoryController) initializeRepo(req *v1.ResticRepository, lo

// ensureRepo first checks the repo, and returns if check passes. If it fails,
// attempts to init the repo, and returns the result.
func ensureRepo(name, identifier string, repoManager restic.RepositoryManager) error {
if repoManager.CheckRepo(name, identifier) == nil {
func ensureRepo(repo *v1.ResticRepository, repoManager restic.RepositoryManager) error {
if repoManager.CheckRepo(repo) == nil {
return nil
}

return repoManager.InitRepo(name, identifier)
return repoManager.InitRepo(repo)
}

func (c *resticRepositoryController) runMaintenanceIfDue(req *v1.ResticRepository, log logrus.FieldLogger) error {
Expand All @@ -186,14 +186,14 @@ func (c *resticRepositoryController) runMaintenanceIfDue(req *v1.ResticRepositor
log.Info("Running maintenance on restic repository")

log.Debug("Checking repo before prune")
if err := c.repositoryManager.CheckRepo(req.Name, req.Spec.ResticIdentifier); err != nil {
if err := c.repositoryManager.CheckRepo(req); err != nil {
return c.patchResticRepository(req, repoNotReady(err.Error()))
}

// prune failures should be displayed in the `.status.message` field but
// should not cause the repo to move to `NotReady`.
log.Debug("Pruning repo")
if err := c.repositoryManager.PruneRepo(req.Name, req.Spec.ResticIdentifier); err != nil {
if err := c.repositoryManager.PruneRepo(req); err != nil {
log.WithError(err).Warn("error pruning repository")
if patchErr := c.patchResticRepository(req, func(r *v1.ResticRepository) {
r.Status.Message = err.Error()
Expand All @@ -203,7 +203,7 @@ func (c *resticRepositoryController) runMaintenanceIfDue(req *v1.ResticRepositor
}

log.Debug("Checking repo after prune")
if err := c.repositoryManager.CheckRepo(req.Name, req.Spec.ResticIdentifier); err != nil {
if err := c.repositoryManager.CheckRepo(req); err != nil {
return c.patchResticRepository(req, repoNotReady(err.Error()))
}

Expand All @@ -221,7 +221,7 @@ func (c *resticRepositoryController) checkNotReadyRepo(req *v1.ResticRepository,

// we need to ensure it (first check, if check fails, attempt to init)
// because we don't know if it's been successfully initialized yet.
if err := ensureRepo(req.Name, req.Spec.ResticIdentifier, c.repositoryManager); err != nil {
if err := ensureRepo(req, c.repositoryManager); err != nil {
return c.patchResticRepository(req, repoNotReady(err.Error()))
}

Expand Down
3 changes: 2 additions & 1 deletion pkg/restic/backupper.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,8 @@ func newPodVolumeBackup(backup *arkv1api.Backup, pod *corev1api.Pod, volumeName,
"ns": pod.Namespace,
"volume": volumeName,
},
RepoIdentifier: repoIdentifier,
BackupStorageLocation: backup.Spec.StorageLocation,
RepoIdentifier: repoIdentifier,
},
}
}
Expand Down
5 changes: 5 additions & 0 deletions pkg/restic/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type Command struct {
Dir string
Args []string
ExtraFlags []string
Env []string
}

func (c *Command) RepoName() string {
Expand Down Expand Up @@ -75,6 +76,10 @@ func (c *Command) Cmd() *exec.Cmd {
cmd := exec.Command(parts[0], parts[1:]...)
cmd.Dir = c.Dir

if len(c.Env) > 0 {
cmd.Env = c.Env
}

return cmd
}

Expand Down
Loading

0 comments on commit 3af43b4

Please sign in to comment.