Obtaining the current metadata before setting it
This commit is contained in:
parent
a10d23dd95
commit
d074b0da29
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue