Skip to content

Commit

Permalink
azure: refactor to not use helpers/ pkg, validate all env/config inputs
Browse files Browse the repository at this point in the history
Signed-off-by: Steve Kriss <steve@heptio.com>
  • Loading branch information
skriss committed Aug 28, 2018
1 parent 9d7ea74 commit cb321db
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 129 deletions.
3 changes: 1 addition & 2 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

66 changes: 24 additions & 42 deletions pkg/cloudprovider/azure/block_store.go
Expand Up @@ -26,7 +26,6 @@ import (
"time"

"github.com/Azure/azure-sdk-for-go/arm/disk"
"github.com/Azure/azure-sdk-for-go/arm/examples/helpers"
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/azure"
"github.com/pkg/errors"
Expand All @@ -40,14 +39,10 @@ import (
)

const (
azureTenantIDKey = "AZURE_TENANT_ID"
azureSubscriptionIDKey = "AZURE_SUBSCRIPTION_ID"
azureClientIDKey = "AZURE_CLIENT_ID"
azureClientSecretKey = "AZURE_CLIENT_SECRET"
azureResourceGroupKey = "AZURE_RESOURCE_GROUP"
apiTimeoutKey = "apiTimeout"
snapshotsResource = "snapshots"
disksResource = "disks"
resourceGroupEnvVar = "AZURE_RESOURCE_GROUP"
apiTimeoutConfigKey = "apiTimeout"
snapshotsResource = "snapshots"
disksResource = "disks"
)

type blockStore struct {
Expand All @@ -69,50 +64,37 @@ func (si *snapshotIdentifier) String() string {
return getComputeResourceName(si.subscription, si.resourceGroup, snapshotsResource, si.name)
}

func getAzureEnvVars() map[string]string {
cfg := map[string]string{
azureTenantIDKey: "",
azureSubscriptionIDKey: "",
azureClientIDKey: "",
azureClientSecretKey: "",
azureResourceGroupKey: "",
}

for key := range cfg {
cfg[key] = os.Getenv(key)
}

return cfg
}

func NewBlockStore(logger logrus.FieldLogger) cloudprovider.BlockStore {
return &blockStore{log: logger}
}

func (b *blockStore) Init(config map[string]string) error {
var (
apiTimeoutVal = config[apiTimeoutKey]
apiTimeout time.Duration
err error
)

if apiTimeout, err = time.ParseDuration(apiTimeoutVal); err != nil {
return errors.Wrapf(err, "could not parse %s (expected time.Duration)", apiTimeoutKey)
// 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)
if err != nil {
return errors.Wrap(err, "unable to get all required environment variables")
}

if apiTimeout == 0 {
// 2. if config["apiTimeout"] is empty, default to 2m; otherwise, parse it
var apiTimeout time.Duration
if val := config[apiTimeoutConfigKey]; val == "" {
apiTimeout = 2 * time.Minute
} else {
apiTimeout, err = time.ParseDuration(val)
if err != nil {
return errors.Wrapf(err, "unable to parse value %q for config key %q (expected a duration string)", val, apiTimeoutConfigKey)
}
}

cfg := getAzureEnvVars()

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

disksClient := disk.NewDisksClient(cfg[azureSubscriptionIDKey])
snapsClient := disk.NewSnapshotsClient(cfg[azureSubscriptionIDKey])
// 4. set up clients
disksClient := disk.NewDisksClient(envVars[subscriptionIDEnvVar])
snapsClient := disk.NewSnapshotsClient(envVars[subscriptionIDEnvVar])

disksClient.PollingDelay = 5 * time.Second
snapsClient.PollingDelay = 5 * time.Second
Expand All @@ -123,8 +105,8 @@ func (b *blockStore) Init(config map[string]string) error {

b.disks = &disksClient
b.snaps = &snapsClient
b.subscription = cfg[azureSubscriptionIDKey]
b.resourceGroup = cfg[azureResourceGroupKey]
b.subscription = envVars[subscriptionIDEnvVar]
b.resourceGroup = envVars[resourceGroupEnvVar]
b.apiTimeout = apiTimeout

return nil
Expand Down
60 changes: 60 additions & 0 deletions pkg/cloudprovider/azure/common.go
@@ -0,0 +1,60 @@
/*
Copyright 2018 the Heptio Ark contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package azure

import (
"strings"

"github.com/Azure/go-autorest/autorest/adal"
"github.com/Azure/go-autorest/autorest/azure"
"github.com/pkg/errors"
)

const (
tenantIDEnvVar = "AZURE_TENANT_ID"
subscriptionIDEnvVar = "AZURE_SUBSCRIPTION_ID"
clientIDEnvVar = "AZURE_CLIENT_ID"
clientSecretEnvVar = "AZURE_CLIENT_SECRET"
)

func newServicePrincipalToken(tenantID, clientID, clientSecret, scope string) (*adal.ServicePrincipalToken, error) {
oauthConfig, err := adal.NewOAuthConfig(azure.PublicCloud.ActiveDirectoryEndpoint, tenantID)
if err != nil {
return nil, errors.Wrap(err, "error getting OAuthConfig")
}

return adal.NewServicePrincipalToken(*oauthConfig, clientID, clientSecret, scope)
}

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 == "" {
missing = append(missing, key)
} else {
results[key] = val
}
}

if len(missing) > 0 {
return nil, errors.Errorf("the following keys do not have values: %s", strings.Join(missing, ", "))
}

return results, nil
}
58 changes: 37 additions & 21 deletions pkg/cloudprovider/azure/object_store.go
Expand Up @@ -18,10 +18,10 @@ package azure

import (
"io"
"os"
"strings"
"time"

"github.com/Azure/azure-sdk-for-go/arm/examples/helpers"
storagemgmt "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2017-10-01/storage"
"github.com/Azure/azure-sdk-for-go/storage"
"github.com/Azure/go-autorest/autorest"
Expand All @@ -32,6 +32,11 @@ import (
"github.com/heptio/ark/pkg/cloudprovider"
)

const (
resourceGroupConfigKey = "resourceGroup"
storageAccountConfigKey = "storageAccount"
)

type objectStore struct {
blobClient *storage.BlobStorageClient
log logrus.FieldLogger
Expand All @@ -41,19 +46,7 @@ func NewObjectStore(logger logrus.FieldLogger) cloudprovider.ObjectStore {
return &objectStore{log: logger}
}

func getStorageAccountsClient(envVars map[string]string) (*storagemgmt.AccountsClient, error) {
spt, err := helpers.NewServicePrincipalTokenFromCredentials(envVars, azure.PublicCloud.ResourceManagerEndpoint)
if err != nil {
return nil, errors.Wrap(err, "error creating new service principal token")
}

accountsClient := storagemgmt.NewAccountsClient(envVars[azureSubscriptionIDKey])
accountsClient.Authorizer = autorest.NewBearerAuthorizer(spt)

return &accountsClient, nil
}

func getStorageAccountKey(client *storagemgmt.AccountsClient, resourceGroup, storageAccount string) (string, error) {
func getStorageAccountKey(client storagemgmt.AccountsClient, resourceGroup, storageAccount string) (string, error) {
res, err := client.ListKeys(resourceGroup, storageAccount)
if err != nil {
return "", errors.WithStack(err)
Expand All @@ -80,24 +73,47 @@ func getStorageAccountKey(client *storagemgmt.AccountsClient, resourceGroup, sto
return storageKey, nil
}

func mapLookup(data map[string]string) func(string) string {
return func(key string) string {
return data[key]
}
}

func (o *objectStore) Init(config map[string]string) error {
storageAccountsClient, err := getStorageAccountsClient(getAzureEnvVars())
// 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 err
return errors.Wrap(err, "unable to get all required environment variables")
}

storageAccountKey, err := getStorageAccountKey(storageAccountsClient, config["resourceGroup"], config["storageAccount"])
// 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 err
return errors.Wrap(err, "error getting service principal token")
}

storageClient, err := storage.NewBasicClient(config["storageAccount"], storageAccountKey)
// 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.WithStack(err)
return errors.Wrap(err, "error getting storage account key")
}

blobClient := storageClient.GetBlobService()
// 6. get storageClient and blobClient
storageClient, err := storage.NewBasicClient(config[storageAccountConfigKey], storageAccountKey)
if err != nil {
return errors.Wrap(err, "error getting storage client")
}

blobClient := storageClient.GetBlobService()
o.blobClient = &blobClient

return nil
Expand Down

This file was deleted.

0 comments on commit cb321db

Please sign in to comment.