Obtaining the current metadata before setting it

This commit is contained in:
tombuildsstuff 2017-09-04 12:04:43 +01:00 committed by Martin Atkins
parent a10d23dd95
commit d074b0da29
4 changed files with 141 additions and 27 deletions

View File

@ -101,6 +101,17 @@ type Backend struct {
leaseID string leaseID string
} }
type BackendConfig struct {
AccessKey string
Environment string
ClientID string
ClientSecret string
ResourceGroupName string
StorageAccountName string
SubscriptionID string
TenantID string
}
func (b *Backend) configure(ctx context.Context) error { func (b *Backend) configure(ctx context.Context) error {
if b.containerName != "" { if b.containerName != "" {
return nil return nil
@ -112,7 +123,18 @@ func (b *Backend) configure(ctx context.Context) error {
b.containerName = data.Get("container_name").(string) b.containerName = data.Get("container_name").(string)
b.keyName = data.Get("key").(string) b.keyName = data.Get("key").(string)
blobClient, err := getBlobClient(data) config := BackendConfig{
AccessKey: data.Get("access_key").(string),
ClientID: data.Get("arm_client_id").(string),
ClientSecret: data.Get("arm_client_secret").(string),
Environment: data.Get("environment").(string),
ResourceGroupName: data.Get("resource_group_name").(string),
StorageAccountName: data.Get("storage_account_name").(string),
SubscriptionID: data.Get("arm_subscription_id").(string),
TenantID: data.Get("arm_tenant_id").(string),
}
blobClient, err := getBlobClient(config)
if err != nil { if err != nil {
return err return err
} }
@ -121,65 +143,63 @@ func (b *Backend) configure(ctx context.Context) error {
return nil return nil
} }
func getBlobClient(d *schema.ResourceData) (storage.BlobStorageClient, error) { func getBlobClient(config BackendConfig) (storage.BlobStorageClient, error) {
var client storage.BlobStorageClient var client storage.BlobStorageClient
env, err := getAzureEnvironment(d.Get("environment").(string)) env, err := getAzureEnvironment(config.Environment)
if err != nil { if err != nil {
return client, err return client, err
} }
storageAccountName := d.Get("storage_account_name").(string) accessKey, err := getAccessKey(config, env)
accessKey, err := getAccessKey(d, storageAccountName, env)
if err != nil { if err != nil {
return client, err return client, err
} }
storageClient, err := storage.NewClient(storageAccountName, accessKey, env.StorageEndpointSuffix, storageClient, err := storage.NewClient(config.StorageAccountName, accessKey, env.StorageEndpointSuffix,
storage.DefaultAPIVersion, true) storage.DefaultAPIVersion, true)
if err != nil { if err != nil {
return client, fmt.Errorf("Error creating storage client for storage account %q: %s", storageAccountName, err) return client, fmt.Errorf("Error creating storage client for storage account %q: %s", config.StorageAccountName, err)
} }
client = storageClient.GetBlobService() client = storageClient.GetBlobService()
return client, nil return client, nil
} }
func getAccessKey(d *schema.ResourceData, storageAccountName string, env azure.Environment) (string, error) { func getAccessKey(config BackendConfig, env azure.Environment) (string, error) {
if key, ok := d.GetOk("access_key"); ok { if config.AccessKey != "" {
return key.(string), nil return config.AccessKey, nil
} }
resourceGroupName, rgOk := d.GetOk("resource_group_name") rgOk := config.ResourceGroupName != ""
subscriptionID, subOk := d.GetOk("arm_subscription_id") subOk := config.SubscriptionID != ""
clientID, clientIDOk := d.GetOk("arm_client_id") clientIDOk := config.ClientID != ""
clientSecret, clientSecretOK := d.GetOk("arm_client_secret") clientSecretOK := config.ClientSecret != ""
tenantID, tenantIDOk := d.GetOk("arm_tenant_id") tenantIDOk := config.TenantID != ""
if !rgOk || !subOk || !clientIDOk || !clientSecretOK || !tenantIDOk { if !rgOk || !subOk || !clientIDOk || !clientSecretOK || !tenantIDOk {
return "", fmt.Errorf("resource_group_name and credentials must be provided when access_key is absent") return "", fmt.Errorf("resource_group_name and credentials must be provided when access_key is absent")
} }
oauthConfig, err := adal.NewOAuthConfig(env.ActiveDirectoryEndpoint, tenantID.(string)) oauthConfig, err := adal.NewOAuthConfig(env.ActiveDirectoryEndpoint, config.TenantID)
if err != nil { if err != nil {
return "", err return "", err
} }
spt, err := adal.NewServicePrincipalToken(*oauthConfig, clientID.(string), clientSecret.(string), env.ResourceManagerEndpoint) spt, err := adal.NewServicePrincipalToken(*oauthConfig, config.ClientID, config.ClientSecret, env.ResourceManagerEndpoint)
if err != nil { if err != nil {
return "", err return "", err
} }
accountsClient := armStorage.NewAccountsClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID.(string)) accountsClient := armStorage.NewAccountsClientWithBaseURI(env.ResourceManagerEndpoint, config.SubscriptionID)
accountsClient.Authorizer = autorest.NewBearerAuthorizer(spt) accountsClient.Authorizer = autorest.NewBearerAuthorizer(spt)
keys, err := accountsClient.ListKeys(resourceGroupName.(string), storageAccountName) keys, err := accountsClient.ListKeys(config.ResourceGroupName, config.StorageAccountName)
if err != nil { if err != nil {
return "", fmt.Errorf("Error retrieving keys for storage account %q: %s", storageAccountName, err) return "", fmt.Errorf("Error retrieving keys for storage account %q: %s", config.StorageAccountName, err)
} }
if keys.Keys == nil { if keys.Keys == nil {
return "", fmt.Errorf("Nil key returned for storage account %q", storageAccountName) return "", fmt.Errorf("Nil key returned for storage account %q", config.StorageAccountName)
} }
accessKeys := *keys.Keys accessKeys := *keys.Keys

View File

@ -33,6 +33,11 @@ func (c *RemoteClient) Get() (*remote.Payload, error) {
containerReference := c.blobClient.GetContainerReference(c.containerName) containerReference := c.blobClient.GetContainerReference(c.containerName)
blobReference := containerReference.GetBlobReference(c.keyName) blobReference := containerReference.GetBlobReference(c.keyName)
options := &storage.GetBlobOptions{} options := &storage.GetBlobOptions{}
if c.leaseID != "" {
options.LeaseID = c.leaseID
}
blob, err := blobReference.Get(options) blob, err := blobReference.Get(options)
if err != nil { if err != nil {
if storErr, ok := err.(storage.AzureStorageServiceError); ok { if storErr, ok := err.(storage.AzureStorageServiceError); ok {
@ -63,6 +68,7 @@ func (c *RemoteClient) Get() (*remote.Payload, error) {
} }
func (c *RemoteClient) Put(data []byte) error { func (c *RemoteClient) Put(data []byte) error {
getOptions := &storage.GetBlobMetadataOptions{}
setOptions := &storage.SetBlobPropertiesOptions{} setOptions := &storage.SetBlobPropertiesOptions{}
putOptions := &storage.PutBlobOptions{} putOptions := &storage.PutBlobOptions{}
@ -73,13 +79,26 @@ func (c *RemoteClient) Put(data []byte) error {
blobReference.Properties.ContentLength = int64(len(data)) blobReference.Properties.ContentLength = int64(len(data))
if c.leaseID != "" { if c.leaseID != "" {
getOptions.LeaseID = c.leaseID
setOptions.LeaseID = c.leaseID setOptions.LeaseID = c.leaseID
putOptions.LeaseID = c.leaseID putOptions.LeaseID = c.leaseID
} }
exists, err := blobReference.Exists()
if err != nil {
return err
}
if exists {
err = blobReference.GetMetadata(getOptions)
if err != nil {
return err
}
}
reader := bytes.NewReader(data) reader := bytes.NewReader(data)
err := blobReference.CreateBlockBlobFromReader(reader, putOptions) err = blobReference.CreateBlockBlobFromReader(reader, putOptions)
if err != nil { if err != nil {
return err return err
} }
@ -177,7 +196,7 @@ func (c *RemoteClient) getLockInfo() (*state.LockInfo, error) {
raw := blobReference.Metadata[lockInfoMetaKey] raw := blobReference.Metadata[lockInfoMetaKey]
if raw == "" { if raw == "" {
return nil, fmt.Errorf("blob metadata %s was empty", lockInfoMetaKey) return nil, fmt.Errorf("blob metadata %q was empty", lockInfoMetaKey)
} }
data, err := base64.StdEncoding.DecodeString(raw) data, err := base64.StdEncoding.DecodeString(raw)
@ -198,7 +217,9 @@ func (c *RemoteClient) getLockInfo() (*state.LockInfo, error) {
func (c *RemoteClient) writeLockInfo(info *state.LockInfo) error { func (c *RemoteClient) writeLockInfo(info *state.LockInfo) error {
containerReference := c.blobClient.GetContainerReference(c.containerName) containerReference := c.blobClient.GetContainerReference(c.containerName)
blobReference := containerReference.GetBlobReference(c.keyName) blobReference := containerReference.GetBlobReference(c.keyName)
err := blobReference.GetMetadata(&storage.GetBlobMetadataOptions{}) err := blobReference.GetMetadata(&storage.GetBlobMetadataOptions{
LeaseID: c.leaseID,
})
if err != nil { if err != nil {
return err return err
} }
@ -214,7 +235,6 @@ func (c *RemoteClient) writeLockInfo(info *state.LockInfo) error {
LeaseID: c.leaseID, LeaseID: c.leaseID,
} }
return blobReference.SetMetadata(opts) return blobReference.SetMetadata(opts)
} }
func (c *RemoteClient) Unlock(id string) error { func (c *RemoteClient) Unlock(id string) error {

View File

@ -3,7 +3,9 @@ package azure
import ( import (
"testing" "testing"
"github.com/Azure/azure-sdk-for-go/storage"
"github.com/hashicorp/terraform/backend" "github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/state/remote" "github.com/hashicorp/terraform/state/remote"
) )
@ -67,3 +69,75 @@ func TestRemoteClientLocks(t *testing.T) {
remote.TestRemoteLocks(t, s1.(*remote.State).Client, s2.(*remote.State).Client) remote.TestRemoteLocks(t, s1.(*remote.State).Client, s2.(*remote.State).Client)
} }
func TestPutMaintainsMetaData(t *testing.T) {
testACC(t)
keyName := "testState"
headerName := "acceptancetest"
expectedValue := "f3b56bad-33ad-4b93-a600-7a66e9cbd1eb"
res := setupResources(t, keyName)
defer destroyResources(t, res.resourceGroupName)
config := getBackendConfig(t, res)
blobClient, err := getBlobClient(config)
if err != nil {
t.Fatalf("Error getting Blob Client: %+v", err)
}
containerReference := blobClient.GetContainerReference(res.containerName)
blobReference := containerReference.GetBlobReference(keyName)
err = blobReference.CreateBlockBlob(&storage.PutBlobOptions{})
if err != nil {
t.Fatalf("Error Creating Block Blob: %+v", err)
}
err = blobReference.GetMetadata(&storage.GetBlobMetadataOptions{})
if err != nil {
t.Fatalf("Error loading MetaData: %+v", err)
}
blobReference.Metadata[headerName] = expectedValue
err = blobReference.SetMetadata(&storage.SetBlobMetadataOptions{})
if err != nil {
t.Fatalf("Error setting MetaData: %+v", err)
}
// update the metadata using the Backend
remoteClient := RemoteClient{
keyName: res.keyName,
containerName: res.containerName,
blobClient: blobClient,
}
bytes := []byte(acctest.RandString(20))
err = remoteClient.Put(bytes)
if err != nil {
t.Fatalf("Error putting data: %+v", err)
}
// Verify it still exists
err = blobReference.GetMetadata(&storage.GetBlobMetadataOptions{})
if err != nil {
t.Fatalf("Error loading MetaData: %+v", err)
}
if blobReference.Metadata[headerName] != expectedValue {
t.Fatalf("%q was not set to %q in the MetaData: %+v", headerName, expectedValue, blobReference.Metadata)
}
}
func getBackendConfig(t *testing.T, res testResources) BackendConfig {
clients := getTestClient(t)
return BackendConfig{
ClientID: clients.clientID,
ClientSecret: clients.clientSecret,
Environment: clients.environment.Name,
SubscriptionID: clients.subscriptionID,
TenantID: clients.tenantID,
ResourceGroupName: res.resourceGroupName,
StorageAccountName: res.storageAccountName,
}
}

View File

@ -45,7 +45,7 @@ func TestBackendConfig(t *testing.T, b Backend, c map[string]interface{}) Backen
// error ErrNamedStatesNotSupported, then it will not test that. // error ErrNamedStatesNotSupported, then it will not test that.
// //
// If you want to test locking, two backends must be given. If b2 is nil, // If you want to test locking, two backends must be given. If b2 is nil,
// then state lockign won't be tested. // then state locking won't be tested.
func TestBackend(t *testing.T, b1, b2 Backend) { func TestBackend(t *testing.T, b1, b2 Backend) {
t.Helper() t.Helper()