state/azure: support passing of lease ID when writing storage blob (#10115)
Also fixed tests failing auth caused by getStorageAccountAccessKey returning the key name rather than the value TF_ACC= go test ./state/remote -v -run=TestAz -timeout=10m -parallel=4 === RUN TestAzureClient_impl --- PASS: TestAzureClient_impl (0.00s) === RUN TestAzureClient 2016/11/18 13:57:34 [DEBUG] New state was assigned lineage "96037426-f95e-45c3-9183-6c39b49f590b" 2016/11/18 13:57:34 [TRACE] Preserving existing state lineage "96037426-f95e-45c3-9183-6c39b49f590b" --- PASS: TestAzureClient (130.60s) === RUN TestAzureClientEmptyLease 2016/11/18 13:59:44 [DEBUG] New state was assigned lineage "d9997445-1ebf-4b2c-b4df-15ae152f6417" 2016/11/18 13:59:44 [TRACE] Preserving existing state lineage "d9997445-1ebf-4b2c-b4df-15ae152f6417" --- PASS: TestAzureClientEmptyLease (128.15s) === RUN TestAzureClientLease 2016/11/18 14:01:55 [DEBUG] New state was assigned lineage "85912a12-2e0e-464c-9886-8add39ea3a87" 2016/11/18 14:01:55 [TRACE] Preserving existing state lineage "85912a12-2e0e-464c-9886-8add39ea3a87" --- PASS: TestAzureClientLease (138.09s) PASS ok github.com/hashicorp/terraform/state/remote 397.111s
This commit is contained in:
parent
9aba1b4238
commit
507efcb180
|
@ -46,11 +46,13 @@ func azureFactory(conf map[string]string) (Client, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
blobClient := storageClient.GetBlobService()
|
blobClient := storageClient.GetBlobService()
|
||||||
|
leaseID, _ := confOrEnv(conf, "lease_id", "ARM_LEASE_ID")
|
||||||
|
|
||||||
return &AzureClient{
|
return &AzureClient{
|
||||||
blobClient: &blobClient,
|
blobClient: &blobClient,
|
||||||
containerName: containerName,
|
containerName: containerName,
|
||||||
keyName: keyName,
|
keyName: keyName,
|
||||||
|
leaseID: leaseID,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +88,7 @@ func getStorageAccountAccessKey(conf map[string]string, resourceGroupName, stora
|
||||||
}
|
}
|
||||||
|
|
||||||
accessKeys := *keys.Keys
|
accessKeys := *keys.Keys
|
||||||
return *accessKeys[0].KeyName, nil
|
return *accessKeys[0].Value, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCredentialsFromConf(conf map[string]string) (*riviera.AzureResourceManagerCredentials, error) {
|
func getCredentialsFromConf(conf map[string]string) (*riviera.AzureResourceManagerCredentials, error) {
|
||||||
|
@ -130,6 +132,7 @@ type AzureClient struct {
|
||||||
blobClient *mainStorage.BlobStorageClient
|
blobClient *mainStorage.BlobStorageClient
|
||||||
containerName string
|
containerName string
|
||||||
keyName string
|
keyName string
|
||||||
|
leaseID string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AzureClient) Get() (*Payload, error) {
|
func (c *AzureClient) Get() (*Payload, error) {
|
||||||
|
@ -163,17 +166,28 @@ func (c *AzureClient) Get() (*Payload, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AzureClient) Put(data []byte) error {
|
func (c *AzureClient) Put(data []byte) error {
|
||||||
|
headers := map[string]string{
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.leaseID != "" {
|
||||||
|
headers["x-ms-lease-id"] = c.leaseID
|
||||||
|
}
|
||||||
|
|
||||||
return c.blobClient.CreateBlockBlobFromReader(
|
return c.blobClient.CreateBlockBlobFromReader(
|
||||||
c.containerName,
|
c.containerName,
|
||||||
c.keyName,
|
c.keyName,
|
||||||
uint64(len(data)),
|
uint64(len(data)),
|
||||||
bytes.NewReader(data),
|
bytes.NewReader(data),
|
||||||
map[string]string{
|
headers,
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AzureClient) Delete() error {
|
func (c *AzureClient) Delete() error {
|
||||||
return c.blobClient.DeleteBlob(c.containerName, c.keyName, nil)
|
headers := map[string]string{}
|
||||||
|
if c.leaseID != "" {
|
||||||
|
headers["x-ms-lease-id"] = c.leaseID
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.blobClient.DeleteBlob(c.containerName, c.keyName, headers)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,22 +5,84 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
mainStorage "github.com/Azure/azure-sdk-for-go/storage"
|
mainStorage "github.com/Azure/azure-sdk-for-go/storage"
|
||||||
|
"github.com/hashicorp/terraform/helper/acctest"
|
||||||
riviera "github.com/jen20/riviera/azure"
|
riviera "github.com/jen20/riviera/azure"
|
||||||
"github.com/jen20/riviera/storage"
|
"github.com/jen20/riviera/storage"
|
||||||
|
"github.com/satori/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAzureClient_impl(t *testing.T) {
|
func TestAzureClient_impl(t *testing.T) {
|
||||||
var _ Client = new(AzureClient)
|
var _ Client = new(AzureClient)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This test creates a bucket in Azure and populates it.
|
||||||
|
// It may incur costs, so it will only run if Azure credential environment
|
||||||
|
// variables are present.
|
||||||
func TestAzureClient(t *testing.T) {
|
func TestAzureClient(t *testing.T) {
|
||||||
// This test creates a bucket in Azure and populates it.
|
config := getAzureConfig(t)
|
||||||
// It may incur costs, so it will only run if Azure credential environment
|
|
||||||
// variables are present.
|
|
||||||
|
|
||||||
|
setup(t, config)
|
||||||
|
defer teardown(t, config)
|
||||||
|
|
||||||
|
client, err := azureFactory(config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error for valid config: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
testClient(t, client)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This test is the same as TestAzureClient with the addition of passing an
|
||||||
|
// empty string in the lease_id, we expect the client to pass tests
|
||||||
|
func TestAzureClientEmptyLease(t *testing.T) {
|
||||||
|
config := getAzureConfig(t)
|
||||||
|
config["lease_id"] = ""
|
||||||
|
|
||||||
|
setup(t, config)
|
||||||
|
defer teardown(t, config)
|
||||||
|
|
||||||
|
client, err := azureFactory(config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error for valid config: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
testClient(t, client)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This test is the same as TestAzureClient with the addition of using the
|
||||||
|
// lease_id config option
|
||||||
|
func TestAzureClientLease(t *testing.T) {
|
||||||
|
leaseID := uuid.NewV4().String()
|
||||||
|
config := getAzureConfig(t)
|
||||||
|
config["lease_id"] = leaseID
|
||||||
|
|
||||||
|
setup(t, config)
|
||||||
|
defer teardown(t, config)
|
||||||
|
|
||||||
|
client, err := azureFactory(config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error for valid config: %v", err)
|
||||||
|
}
|
||||||
|
azureClient := client.(*AzureClient)
|
||||||
|
|
||||||
|
// put empty blob so we can acquire lease against it
|
||||||
|
err = azureClient.blobClient.CreateBlockBlob(azureClient.containerName, azureClient.keyName)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error creating blob for leasing: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = azureClient.blobClient.AcquireLease(azureClient.containerName, azureClient.keyName, -1, leaseID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error acquiring lease: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// no need to release lease as blob is deleted in testing
|
||||||
|
testClient(t, client)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAzureConfig(t *testing.T) map[string]string {
|
||||||
config := map[string]string{
|
config := map[string]string{
|
||||||
"arm_subscription_id": os.Getenv("ARM_SUBSCRIPTION_ID"),
|
"arm_subscription_id": os.Getenv("ARM_SUBSCRIPTION_ID"),
|
||||||
"arm_client_id": os.Getenv("ARM_CLIENT_ID"),
|
"arm_client_id": os.Getenv("ARM_CLIENT_ID"),
|
||||||
|
@ -34,20 +96,14 @@ func TestAzureClient(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
config["resource_group_name"] = fmt.Sprintf("terraform-%x", time.Now().Unix())
|
rs := acctest.RandString(8)
|
||||||
config["storage_account_name"] = fmt.Sprintf("terraform%x", time.Now().Unix())
|
|
||||||
|
config["resource_group_name"] = fmt.Sprintf("terraform-%s", rs)
|
||||||
|
config["storage_account_name"] = fmt.Sprintf("terraform%s", rs)
|
||||||
config["container_name"] = "terraform"
|
config["container_name"] = "terraform"
|
||||||
config["key"] = "test.tfstate"
|
config["key"] = "test.tfstate"
|
||||||
|
|
||||||
setup(t, config)
|
return config
|
||||||
defer teardown(t, config)
|
|
||||||
|
|
||||||
client, err := azureFactory(config)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error for valid config: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testClient(t, client)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setup(t *testing.T, conf map[string]string) {
|
func setup(t *testing.T, conf map[string]string) {
|
||||||
|
|
|
@ -48,3 +48,4 @@ The following configuration options are supported:
|
||||||
* `container_name` - (Required) The name of the container to use within the storage account
|
* `container_name` - (Required) The name of the container to use within the storage account
|
||||||
* `key` - (Required) The key where to place/look for state file inside the container
|
* `key` - (Required) The key where to place/look for state file inside the container
|
||||||
* `access_key` / `ARM_ACCESS_KEY` - (Required) Storage account access key
|
* `access_key` / `ARM_ACCESS_KEY` - (Required) Storage account access key
|
||||||
|
* `lease_id` / `ARM_LEASE_ID` - (Optional) If set, will be used when writing to storage blob.
|
||||||
|
|
Loading…
Reference in New Issue