Azure china/germany support (#1938)

* feat: add azure china support

Signed-off-by: andyzhangx <xiazhang@microsoft.com>

* remove AZURE_CLOUD_NAME from required env var fetching

Signed-off-by: Steve Kriss <krisss@vmware.com>

* minor simplification of parseAzureEnvironment

Signed-off-by: Steve Kriss <krisss@vmware.com>

* changelog

Signed-off-by: Steve Kriss <krisss@vmware.com>

* remove cloudNameEnvVar from getRequiredValues call

Signed-off-by: Steve Kriss <krisss@vmware.com>

* just check for err != nil

Signed-off-by: Steve Kriss <krisss@vmware.com>
This commit is contained in:
Steve Kriss
2019-10-08 17:13:51 -04:00
committed by Nolan Brubaker
parent 92a3797460
commit d3e30dd58b
5 changed files with 58 additions and 29 deletions
+1
View File
@@ -0,0 +1 @@
Azure: add support for Azure China/German clouds
+16 -4
View File
@@ -31,6 +31,7 @@ const (
subscriptionIDEnvVar = "AZURE_SUBSCRIPTION_ID"
clientIDEnvVar = "AZURE_CLIENT_ID"
clientSecretEnvVar = "AZURE_CLIENT_SECRET"
cloudNameEnvVar = "AZURE_CLOUD_NAME"
resourceGroupConfigKey = "resourceGroup"
)
@@ -39,7 +40,7 @@ const (
// relies on (AZURE_ACCOUNT_NAME and AZURE_ACCOUNT_KEY) based
// on info in the provided object storage location config map.
func GetResticEnvVars(config map[string]string) (map[string]string, error) {
storageAccountKey, err := getStorageAccountKey(config)
storageAccountKey, _, err := getStorageAccountKey(config)
if err != nil {
return nil, err
}
@@ -63,13 +64,24 @@ func loadEnv() error {
return nil
}
func newServicePrincipalToken(tenantID, clientID, clientSecret, scope string) (*adal.ServicePrincipalToken, error) {
oauthConfig, err := adal.NewOAuthConfig(azure.PublicCloud.ActiveDirectoryEndpoint, tenantID)
// ParseAzureEnvironment returns an azure.Environment for the given cloud
// name, or azure.PublicCloud if cloudName is empty.
func parseAzureEnvironment(cloudName string) (*azure.Environment, error) {
if cloudName == "" {
return &azure.PublicCloud, nil
}
env, err := azure.EnvironmentFromName(cloudName)
return &env, errors.WithStack(err)
}
func newServicePrincipalToken(tenantID, clientID, clientSecret string, env *azure.Environment) (*adal.ServicePrincipalToken, error) {
oauthConfig, err := adal.NewOAuthConfig(env.ActiveDirectoryEndpoint, tenantID)
if err != nil {
return nil, errors.Wrap(err, "error getting OAuthConfig")
}
return adal.NewServicePrincipalToken(*oauthConfig, clientID, clientSecret, scope)
return adal.NewServicePrincipalToken(*oauthConfig, clientID, clientSecret, env.ResourceManagerEndpoint)
}
func getRequiredValues(getValue func(string) string, keys ...string) (map[string]string, error) {
+25 -18
View File
@@ -135,46 +135,53 @@ func NewObjectStore(logger logrus.FieldLogger) *ObjectStore {
return &ObjectStore{log: logger}
}
func getStorageAccountKey(config map[string]string) (string, error) {
func getStorageAccountKey(config map[string]string) (string, *azure.Environment, error) {
// load environment vars from $AZURE_CREDENTIALS_FILE, if it exists
if err := loadEnv(); err != nil {
return "", err
return "", nil, err
}
// 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")
return "", nil, errors.Wrap(err, "unable to get all required environment variables")
}
// 2. check whether a different subscription ID was set for backups in config["subscriptionId"]
// 2. Get Azure cloud from AZURE_CLOUD_NAME, if it exists. If the env var does not
// exist, parseAzureEnvironment will return azure.PublicCloud.
env, err := parseAzureEnvironment(os.Getenv(cloudNameEnvVar))
if err != nil {
return "", nil, errors.Wrap(err, "unable to parse azure cloud name environment variable")
}
// 3. check whether a different subscription ID was set for backups in config["subscriptionId"]
subscriptionId := envVars[subscriptionIDEnvVar]
if val := config[subscriptionIdConfigKey]; val != "" {
subscriptionId = val
}
// 3. we need config["resourceGroup"], config["storageAccount"]
// 4. 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")
return "", env, errors.Wrap(err, "unable to get all required config values")
}
// 4. get SPT
spt, err := newServicePrincipalToken(envVars[tenantIDEnvVar], envVars[clientIDEnvVar], envVars[clientSecretEnvVar], azure.PublicCloud.ResourceManagerEndpoint)
// 5. get SPT
spt, err := newServicePrincipalToken(envVars[tenantIDEnvVar], envVars[clientIDEnvVar], envVars[clientSecretEnvVar], env)
if err != nil {
return "", errors.Wrap(err, "error getting service principal token")
return "", env, errors.Wrap(err, "error getting service principal token")
}
// 5. get storageAccountsClient
storageAccountsClient := storagemgmt.NewAccountsClient(subscriptionId)
// 6. get storageAccountsClient
storageAccountsClient := storagemgmt.NewAccountsClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionId)
storageAccountsClient.Authorizer = autorest.NewBearerAuthorizer(spt)
// 6. get storage key
// 7. get storage key
res, err := storageAccountsClient.ListKeys(context.TODO(), config[resourceGroupConfigKey], config[storageAccountConfigKey])
if err != nil {
return "", errors.WithStack(err)
return "", env, errors.WithStack(err)
}
if res.Keys == nil || len(*res.Keys) == 0 {
return "", errors.New("No storage keys found")
return "", env, errors.New("No storage keys found")
}
var storageKey string
@@ -188,10 +195,10 @@ func getStorageAccountKey(config map[string]string) (string, error) {
}
if storageKey == "" {
return "", errors.New("No storage key with Full permissions found")
return "", env, errors.New("No storage key with Full permissions found")
}
return storageKey, nil
return storageKey, env, nil
}
func mapLookup(data map[string]string) func(string) string {
@@ -209,13 +216,13 @@ func (o *ObjectStore) Init(config map[string]string) error {
return err
}
storageAccountKey, err := getStorageAccountKey(config)
storageAccountKey, env, err := getStorageAccountKey(config)
if err != nil {
return err
}
// 6. get storageClient and blobClient
storageClient, err := storage.NewBasicClient(config[storageAccountConfigKey], storageAccountKey)
storageClient, err := storage.NewBasicClientOnSovereignCloud(config[storageAccountConfigKey], storageAccountKey, *env)
if err != nil {
return errors.Wrap(err, "error getting storage client")
}
+13 -7
View File
@@ -27,7 +27,6 @@ import (
disk "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute"
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/azure"
"github.com/pkg/errors"
uuid "github.com/satori/go.uuid"
"github.com/sirupsen/logrus"
@@ -98,7 +97,14 @@ func (b *VolumeSnapshotter) Init(config map[string]string) error {
snapshotsSubscriptionId = val
}
// 3. if config["apiTimeout"] is empty, default to 2m; otherwise, parse it
// 3. Get Azure cloud from AZURE_CLOUD_NAME, if it exists. If the env var does not
// exist, parseAzureEnvironment will return azure.PublicCloud.
env, err := parseAzureEnvironment(os.Getenv(cloudNameEnvVar))
if err != nil {
return errors.Wrap(err, "unable to parse azure cloud name environment variable")
}
// 4. if config["apiTimeout"] is empty, default to 2m; otherwise, parse it
var apiTimeout time.Duration
if val := config[apiTimeoutConfigKey]; val == "" {
apiTimeout = 2 * time.Minute
@@ -109,15 +115,15 @@ func (b *VolumeSnapshotter) Init(config map[string]string) error {
}
}
// 4. get SPT
spt, err := newServicePrincipalToken(envVars[tenantIDEnvVar], envVars[clientIDEnvVar], envVars[clientSecretEnvVar], azure.PublicCloud.ResourceManagerEndpoint)
// 5. get SPT
spt, err := newServicePrincipalToken(envVars[tenantIDEnvVar], envVars[clientIDEnvVar], envVars[clientSecretEnvVar], env)
if err != nil {
return errors.Wrap(err, "error getting service principal token")
}
// 5. set up clients
disksClient := disk.NewDisksClient(envVars[subscriptionIDEnvVar])
snapsClient := disk.NewSnapshotsClient(snapshotsSubscriptionId)
// 6. set up clients
disksClient := disk.NewDisksClientWithBaseURI(env.ResourceManagerEndpoint, envVars[subscriptionIDEnvVar])
snapsClient := disk.NewSnapshotsClientWithBaseURI(env.ResourceManagerEndpoint, snapshotsSubscriptionId)
disksClient.PollingDelay = 5 * time.Second
snapsClient.PollingDelay = 5 * time.Second
+3
View File
@@ -165,9 +165,12 @@ To integrate Velero with Azure, you must create a Velero-specific [service princ
AZURE_CLIENT_ID=${AZURE_CLIENT_ID}
AZURE_CLIENT_SECRET=${AZURE_CLIENT_SECRET}
AZURE_RESOURCE_GROUP=${AZURE_RESOURCE_GROUP}
AZURE_CLOUD_NAME=AzurePublicCloud
EOF
```
> available `AZURE_CLOUD_NAME` values: `AzurePublicCloud`, `AzureUSGovernmentCloud`, `AzureChinaCloud`, `AzureGermanCloud`
## Install and start Velero
Install Velero, including all prerequisites, into the cluster and start the deployment. This will create a namespace called `velero`, and place a deployment named `velero` in it.