set azure restic env vars based on default backup location's config
Signed-off-by: Steve Kriss <steve@heptio.com>pull/820/head
parent
ae373bdbfb
commit
d34994cb5f
|
@ -13,8 +13,9 @@ ark restic server [flags]
|
|||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for server
|
||||
--log-level the level at which to log. Valid values are debug, info, warning, error, fatal, panic. (default info)
|
||||
--default-backup-storage-location string name of the default backup storage location (default "default")
|
||||
-h, --help help for server
|
||||
--log-level the level at which to log. Valid values are debug, info, warning, error, fatal, panic. (default info)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
|
|
@ -61,15 +61,5 @@ spec:
|
|||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
- name: AZURE_ACCOUNT_NAME
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: cloud-credentials
|
||||
key: AZURE_STORAGE_ACCOUNT_ID
|
||||
- name: AZURE_ACCOUNT_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: cloud-credentials
|
||||
key: AZURE_STORAGE_KEY
|
||||
- name: ARK_SCRATCH_DIR
|
||||
value: /scratch
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package azure
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest/adal"
|
||||
|
@ -31,6 +32,21 @@ const (
|
|||
clientSecretEnvVar = "AZURE_CLIENT_SECRET"
|
||||
)
|
||||
|
||||
// SetResticEnvVars sets 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])
|
||||
|
||||
storageAccountKey, err := getStorageAccountKey(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
os.Setenv("AZURE_ACCOUNT_KEY", storageAccountKey)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func newServicePrincipalToken(tenantID, clientID, clientSecret, scope string) (*adal.ServicePrincipalToken, error) {
|
||||
oauthConfig, err := adal.NewOAuthConfig(azure.PublicCloud.ActiveDirectoryEndpoint, tenantID)
|
||||
if err != nil {
|
||||
|
|
|
@ -46,8 +46,30 @@ func NewObjectStore(logger logrus.FieldLogger) cloudprovider.ObjectStore {
|
|||
return &objectStore{log: logger}
|
||||
}
|
||||
|
||||
func getStorageAccountKey(client storagemgmt.AccountsClient, resourceGroup, storageAccount string) (string, error) {
|
||||
res, err := client.ListKeys(resourceGroup, storageAccount)
|
||||
func getStorageAccountKey(config map[string]string) (string, error) {
|
||||
// 1. we need AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_SUBSCRIPTION_ID
|
||||
envVars, err := getRequiredValues(os.Getenv, tenantIDEnvVar, clientIDEnvVar, clientSecretEnvVar, subscriptionIDEnvVar)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "unable to get all required environment variables")
|
||||
}
|
||||
|
||||
// 2. we need config["resourceGroup"], config["storageAccount"]
|
||||
if _, err := getRequiredValues(mapLookup(config), resourceGroupConfigKey, storageAccountConfigKey); err != nil {
|
||||
return "", errors.Wrap(err, "unable to get all required config values")
|
||||
}
|
||||
|
||||
// 3. get SPT
|
||||
spt, err := newServicePrincipalToken(envVars[tenantIDEnvVar], envVars[clientIDEnvVar], envVars[clientSecretEnvVar], azure.PublicCloud.ResourceManagerEndpoint)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "error getting service principal token")
|
||||
}
|
||||
|
||||
// 4. get storageAccountsClient
|
||||
storageAccountsClient := storagemgmt.NewAccountsClient(envVars[subscriptionIDEnvVar])
|
||||
storageAccountsClient.Authorizer = autorest.NewBearerAuthorizer(spt)
|
||||
|
||||
// 5. get storage key
|
||||
res, err := storageAccountsClient.ListKeys(config[resourceGroupConfigKey], config[storageAccountConfigKey])
|
||||
if err != nil {
|
||||
return "", errors.WithStack(err)
|
||||
}
|
||||
|
@ -56,7 +78,6 @@ func getStorageAccountKey(client storagemgmt.AccountsClient, resourceGroup, stor
|
|||
}
|
||||
|
||||
var storageKey string
|
||||
|
||||
for _, key := range *res.Keys {
|
||||
// uppercase both strings for comparison because the ListKeys call returns e.g. "FULL" but
|
||||
// the storagemgmt.Full constant in the SDK is defined as "Full".
|
||||
|
@ -80,31 +101,9 @@ func mapLookup(data map[string]string) func(string) string {
|
|||
}
|
||||
|
||||
func (o *objectStore) Init(config map[string]string) error {
|
||||
// 1. we need AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_SUBSCRIPTION_ID
|
||||
envVars, err := getRequiredValues(os.Getenv, tenantIDEnvVar, clientIDEnvVar, clientSecretEnvVar, subscriptionIDEnvVar)
|
||||
storageAccountKey, err := getStorageAccountKey(config)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to get all required environment variables")
|
||||
}
|
||||
|
||||
// 2. we need config["resourceGroup"], config["storageAccount"]
|
||||
if _, err := getRequiredValues(mapLookup(config), resourceGroupConfigKey, storageAccountConfigKey); err != nil {
|
||||
return errors.Wrap(err, "unable to get all required config values")
|
||||
}
|
||||
|
||||
// 3. get SPT
|
||||
spt, err := newServicePrincipalToken(envVars[tenantIDEnvVar], envVars[clientIDEnvVar], envVars[clientSecretEnvVar], azure.PublicCloud.ResourceManagerEndpoint)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error getting service principal token")
|
||||
}
|
||||
|
||||
// 4. get storageAccountsClient
|
||||
storageAccountsClient := storagemgmt.NewAccountsClient(envVars[subscriptionIDEnvVar])
|
||||
storageAccountsClient.Authorizer = autorest.NewBearerAuthorizer(spt)
|
||||
|
||||
// 5. get storage key
|
||||
storageAccountKey, err := getStorageAccountKey(storageAccountsClient, config[resourceGroupConfigKey], config[storageAccountConfigKey])
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error getting storage account key")
|
||||
return err
|
||||
}
|
||||
|
||||
// 6. get storageClient and blobClient
|
||||
|
|
|
@ -34,6 +34,7 @@ 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"
|
||||
|
@ -44,7 +45,10 @@ import (
|
|||
)
|
||||
|
||||
func NewServerCommand(f client.Factory) *cobra.Command {
|
||||
var logLevelFlag = logging.LogLevelFlag(logrus.InfoLevel)
|
||||
var (
|
||||
logLevelFlag = logging.LogLevelFlag(logrus.InfoLevel)
|
||||
location = "default"
|
||||
)
|
||||
|
||||
var command = &cobra.Command{
|
||||
Use: "server",
|
||||
|
@ -57,7 +61,7 @@ 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()))
|
||||
s, err := newResticServer(logger, fmt.Sprintf("%s-%s", c.Parent().Name(), c.Name()), location)
|
||||
cmd.CheckError(err)
|
||||
|
||||
s.run()
|
||||
|
@ -65,6 +69,7 @@ func NewServerCommand(f client.Factory) *cobra.Command {
|
|||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -81,7 +86,7 @@ type resticServer struct {
|
|||
cancelFunc context.CancelFunc
|
||||
}
|
||||
|
||||
func newResticServer(logger logrus.FieldLogger, baseName string) (*resticServer, error) {
|
||||
func newResticServer(logger logrus.FieldLogger, baseName, locationName string) (*resticServer, error) {
|
||||
clientConfig, err := client.Config("", "", baseName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -97,6 +102,17 @@ func newResticServer(logger logrus.FieldLogger, baseName string) (*resticServer,
|
|||
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(
|
||||
|
|
|
@ -54,6 +54,7 @@ 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"
|
||||
|
@ -285,7 +286,7 @@ func (s *server) run() error {
|
|||
}
|
||||
|
||||
if backupStorageLocation.Spec.Config[restic.ResticLocationConfigKey] != "" {
|
||||
if err := s.initRestic(backupStorageLocation.Spec.Provider); err != nil {
|
||||
if err := s.initRestic(backupStorageLocation); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -488,7 +489,7 @@ func getBlockStore(cloudConfig api.CloudProviderConfig, manager plugin.Manager)
|
|||
return blockStore, nil
|
||||
}
|
||||
|
||||
func (s *server) initRestic(providerName string) error {
|
||||
func (s *server) initRestic(location *api.BackupStorageLocation) error {
|
||||
// warn if restic daemonset does not exist
|
||||
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")
|
||||
|
@ -502,9 +503,10 @@ func (s *server) initRestic(providerName string) error {
|
|||
}
|
||||
|
||||
// set the env vars that restic uses for creds purposes
|
||||
if providerName == string(restic.AzureBackend) {
|
||||
os.Setenv("AZURE_ACCOUNT_NAME", os.Getenv("AZURE_STORAGE_ACCOUNT_ID"))
|
||||
os.Setenv("AZURE_ACCOUNT_KEY", os.Getenv("AZURE_STORAGE_KEY"))
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue