Skip to content

Commit

Permalink
feat: add azure china support
Browse files Browse the repository at this point in the history
Signed-off-by: andyzhangx <[email protected]>
  • Loading branch information
andyzhangx committed Sep 4, 2019
1 parent fd2609e commit be93f4d
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 27 deletions.
23 changes: 18 additions & 5 deletions pkg/cloudprovider/azure/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const (
subscriptionIDEnvVar = "AZURE_SUBSCRIPTION_ID"
clientIDEnvVar = "AZURE_CLIENT_ID"
clientSecretEnvVar = "AZURE_CLIENT_SECRET"
cloudNameEnvVar = "AZURE_CLOUD_NAME"

resourceGroupConfigKey = "resourceGroup"
)
Expand All @@ -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
}
Expand All @@ -63,21 +64,33 @@ 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 azure environment by name
func parseAzureEnvironment(cloudName string) (*azure.Environment, error) {
var env azure.Environment
var err error
if cloudName == "" {
env = azure.PublicCloud
} else {
env, err = azure.EnvironmentFromName(cloudName)
}
return &env, 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) {
missing := []string{}
results := map[string]string{}

for _, key := range keys {
if val := getValue(key); val == "" {
if val := getValue(key); val == "" && key != cloudNameEnvVar {
missing = append(missing, key)
} else {
results[key] = val
Expand Down
37 changes: 21 additions & 16 deletions pkg/cloudprovider/azure/object_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,40 +134,45 @@ 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)
// 1. we need AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_SUBSCRIPTION_ID, AZURE_CLOUD_NAME
envVars, err := getRequiredValues(os.Getenv, tenantIDEnvVar, clientIDEnvVar, clientSecretEnvVar, subscriptionIDEnvVar, cloudNameEnvVar)
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")
}

env, err := parseAzureEnvironment(envVars[cloudNameEnvVar])
if err != nil || env == nil {
return "", nil, errors.Wrap(err, "unable to parse azure cloud name environment variable")
}

// 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")
return "", env, 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)
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")
}

// 4. get storageAccountsClient
storageAccountsClient := storagemgmt.NewAccountsClient(envVars[subscriptionIDEnvVar])
storageAccountsClient := storagemgmt.NewAccountsClientWithBaseURI(env.ResourceManagerEndpoint, envVars[subscriptionIDEnvVar])
storageAccountsClient.Authorizer = autorest.NewBearerAuthorizer(spt)

// 5. 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
Expand All @@ -181,10 +186,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 {
Expand All @@ -198,13 +203,13 @@ func (o *ObjectStore) Init(config map[string]string) error {
return err
}

storageAccountKey, err := getStorageAccountKey(config)
if err != nil {
storageAccountKey, env, err := getStorageAccountKey(config)
if err != nil || env == 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")
}
Expand Down
16 changes: 10 additions & 6 deletions pkg/cloudprovider/azure/volume_snapshotter.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -81,12 +80,17 @@ func (b *VolumeSnapshotter) Init(config map[string]string) error {
return err
}

// 1. we need AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_SUBSCRIPTION_ID, AZURE_RESOURCE_GROUP
envVars, err := getRequiredValues(os.Getenv, tenantIDEnvVar, clientIDEnvVar, clientSecretEnvVar, subscriptionIDEnvVar, resourceGroupEnvVar)
// 1. we need AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_SUBSCRIPTION_ID, AZURE_RESOURCE_GROUP, AZURE_CLOUD_NAME
envVars, err := getRequiredValues(os.Getenv, tenantIDEnvVar, clientIDEnvVar, clientSecretEnvVar, subscriptionIDEnvVar, resourceGroupEnvVar, cloudNameEnvVar)
if err != nil {
return errors.Wrap(err, "unable to get all required environment variables")
}

env, err := parseAzureEnvironment(envVars[cloudNameEnvVar])
if err != nil || env == nil {
return errors.Wrap(err, "unable to parse azure cloud name environment variable")
}

// 2. if config["apiTimeout"] is empty, default to 2m; otherwise, parse it
var apiTimeout time.Duration
if val := config[apiTimeoutConfigKey]; val == "" {
Expand All @@ -99,14 +103,14 @@ func (b *VolumeSnapshotter) Init(config map[string]string) error {
}

// 3. get SPT
spt, err := newServicePrincipalToken(envVars[tenantIDEnvVar], envVars[clientIDEnvVar], envVars[clientSecretEnvVar], azure.PublicCloud.ResourceManagerEndpoint)
spt, err := newServicePrincipalToken(envVars[tenantIDEnvVar], envVars[clientIDEnvVar], envVars[clientSecretEnvVar], env)
if err != nil {
return errors.Wrap(err, "error getting service principal token")
}

// 4. set up clients
disksClient := disk.NewDisksClient(envVars[subscriptionIDEnvVar])
snapsClient := disk.NewSnapshotsClient(envVars[subscriptionIDEnvVar])
disksClient := disk.NewDisksClientWithBaseURI(env.ResourceManagerEndpoint, envVars[subscriptionIDEnvVar])
snapsClient := disk.NewSnapshotsClientWithBaseURI(env.ResourceManagerEndpoint, envVars[subscriptionIDEnvVar])

disksClient.PollingDelay = 5 * time.Second
snapsClient.PollingDelay = 5 * time.Second
Expand Down
3 changes: 3 additions & 0 deletions site/docs/master/azure-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,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.
Expand Down
2 changes: 2 additions & 0 deletions site/docs/master/build-from-source.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ Azure:

7. AZURE_RESOURCE_GROUP

8. AZURE_CLOUD_NAME

#### 2. Create required Velero resources in the cluster

You can use the `velero install` command to install velero into your cluster, then remove the deployment from the cluster, leaving you
Expand Down

0 comments on commit be93f4d

Please sign in to comment.