Skip to content

Commit

Permalink
State Migrations for the Container Registry
Browse files Browse the repository at this point in the history
  • Loading branch information
tombuildsstuff committed Oct 5, 2017
1 parent 16563ec commit d8aa8ce
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 20 deletions.
20 changes: 13 additions & 7 deletions azurerm/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,16 +84,14 @@ func testArmEnvironment() (*azure.Environment, error) {
return &env, nil
}

func TestAccAzureRMResourceProviderRegistration(t *testing.T) {
environment := testArmEnvironmentName()

func testGetAzureConfig(t *testing.T) *Config {
if os.Getenv(resource.TestEnvVar) == "" {
t.Skip(fmt.Sprintf(
"Integration test skipped unless env '%s' set",
resource.TestEnvVar))
return
t.Skip(fmt.Sprintf("Integration test skipped unless env '%s' set", resource.TestEnvVar))
return nil
}

environment := testArmEnvironmentName()

// we deliberately don't use the main config - since we care about
config := Config{
SubscriptionID: os.Getenv("ARM_SUBSCRIPTION_ID"),
Expand All @@ -103,6 +101,14 @@ func TestAccAzureRMResourceProviderRegistration(t *testing.T) {
Environment: environment,
SkipProviderRegistration: false,
}
return &config
}

func TestAccAzureRMResourceProviderRegistration(t *testing.T) {
config := testGetAzureConfig(t)
if config == nil {
return
}

armClient, err := config.getArmClient()
if err != nil {
Expand Down
12 changes: 5 additions & 7 deletions azurerm/resource_arm_container_registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func resourceArmContainerRegistry() *schema.Resource {
State: schema.ImportStatePassthrough,
},
MigrateState: resourceAzureRMContainerRegistryMigrateState,
SchemaVersion: 1,
SchemaVersion: 2,

Schema: map[string]*schema.Schema{
"name": {
Expand All @@ -36,10 +36,9 @@ func resourceArmContainerRegistry() *schema.Resource {
"location": locationSchema(),

"sku": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
// TODO: a state migration to handle this
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Default: string(containerregistry.Classic),
DiffSuppressFunc: ignoreCaseDiffSuppressFunc,
ValidateFunc: validation.StringInSlice([]string{
Expand All @@ -56,14 +55,13 @@ func resourceArmContainerRegistry() *schema.Resource {
Default: false,
},

// TODO: add a State Migration to move over to this
"storage_account_id": {
Type: schema.TypeString,
Required: true,
},

"storage_account": {
Type: schema.TypeSet,
Type: schema.TypeList,
Optional: true,
Deprecated: "`storage_account` has been replaced by `storage_account_id`.",
MaxItems: 1,
Expand Down
69 changes: 69 additions & 0 deletions azurerm/resource_arm_container_registry_migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import (
"fmt"
"log"

"strings"

"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform"
)

Expand All @@ -13,6 +16,9 @@ func resourceAzureRMContainerRegistryMigrateState(
case 0:
log.Println("[INFO] Found AzureRM Container Registry State v0; migrating to v1")
return migrateAzureRMContainerRegistryStateV0toV1(is)
case 1:
log.Println("[INFO] Found AzureRM Container Registry State v1; migrating to v2")
return migrateAzureRMContainerRegistryStateV1toV2(is, meta)
default:
return is, fmt.Errorf("Unexpected schema version: %d", v)
}
Expand All @@ -32,3 +38,66 @@ func migrateAzureRMContainerRegistryStateV0toV1(is *terraform.InstanceState) (*t

return is, nil
}

func migrateAzureRMContainerRegistryStateV1toV2(is *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) {
if is.Empty() {
log.Println("[DEBUG] Empty InstanceState; nothing to migrate.")
return is, nil
}

log.Printf("[DEBUG] ARM Container Registry Attributes before Migration: %#v", is.Attributes)

// Basic's been renamed Classic to allow for "ManagedBasic" ¯\_(ツ)_/¯
is.Attributes["sku"] = "Classic"

updateV1ToV2StorageAccountName(is, meta)

// we have to look this up, since we don't have the resource group name

log.Printf("[DEBUG] ARM Container Registry Attributes after State Migration: %#v", is.Attributes)

return is, nil
}

func updateV1ToV2StorageAccountName(is *terraform.InstanceState, meta interface{}) error {
reader := &schema.MapFieldReader{
Schema: resourceArmContainerRegistry().Schema,
Map: schema.BasicMapReader(is.Attributes),
}

result, err := reader.ReadField([]string{"storage_account"})
if err != nil {
return err
}

if result.Value == nil {
return nil
}

inputAccounts := result.Value.([]interface{})
inputAccount := inputAccounts[0].(map[string]interface{})
name := inputAccount["name"].(string)
storageAccountId, err := findAzureStorageAccountIdFromName(name, meta)
if err != nil {
return err
}

is.Attributes["storage_account_id"] = storageAccountId
return nil
}

func findAzureStorageAccountIdFromName(name string, meta interface{}) (string, error) {
client := meta.(*ArmClient).storageServiceClient
accounts, err := client.List()
if err != nil {
return "", err
}

for _, account := range *accounts.Value {
if strings.ToLower(*account.Name) == strings.ToLower(name) {
return *account.ID, nil
}
}

return "", fmt.Errorf("Unable to find ID for Storage Account %q", name)
}
105 changes: 99 additions & 6 deletions azurerm/resource_arm_container_registry_migrate_test.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,78 @@
package azurerm

import (
"fmt"
"reflect"
"testing"

"github.com/Azure/azure-sdk-for-go/arm/resources/resources"
"github.com/Azure/azure-sdk-for-go/arm/storage"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/terraform"
)

func TestAzureRMContainerRegistryMigrateState(t *testing.T) {
func TestAccAzureRMContainerRegistryMigrateState(t *testing.T) {
config := testGetAzureConfig(t)
if config == nil {
t.SkipNow()
return
}
client, err := config.getArmClient()
if err != nil {
t.Fatal(fmt.Errorf("Error building ARM Client: %+v", err))
return
}

rs := acctest.RandString(4)
resourceGroupName := fmt.Sprintf("acctesrg%s", rs)
storageAccountName := fmt.Sprintf("acctestsa%s", rs)
location := azureRMNormalizeLocation(testLocation())

err = createResourceGroup(client, resourceGroupName, location)
if err != nil {
t.Fatal(err)
return
}

storageAccount, err := createStorageAccount(client, resourceGroupName, storageAccountName, location)
if err != nil {
t.Fatal(err)
return
}

defer destroyStorageAccountAndResourceGroup(client, resourceGroupName, storageAccountName)

cases := map[string]struct {
StateVersion int
ID string
Attributes map[string]string
Expected string
Expected map[string]string
Meta interface{}
}{
"v0_1_without_value": {
StateVersion: 0,
ID: "some_id",
Attributes: map[string]string{},
Expected: "Basic",
Expected: map[string]string{
"sku": "Basic",
},
},
"v1_2_with_value": {
StateVersion: 1,
ID: "some_id",
Attributes: map[string]string{
// TODO: storage_account also needs to become a List
"sku": "Basic",
"storage_account.#": "1",
"storage_account.0.name": storageAccountName,
},
Expected: map[string]string{
"sku": "Classic",
"storage_account.#": "1",
"storage_account.0.name": storageAccountName,
"storage_account_id": *storageAccount.ID,
},
Meta: client,
},
}

Expand All @@ -30,11 +84,50 @@ func TestAzureRMContainerRegistryMigrateState(t *testing.T) {
is, err := resourceAzureRMContainerRegistryMigrateState(tc.StateVersion, is, tc.Meta)

if err != nil {
t.Fatalf("bad: %s, err: %#v", tn, err)
t.Fatalf("bad: %q, err: %#v", tn, err)
}

if is.Attributes["sku"] != tc.Expected {
t.Fatalf("bad Container Registry Migrate: %s\n\n expected: %s", is.Attributes["sku"], tc.Expected)
if !reflect.DeepEqual(tc.Expected, is.Attributes) {
t.Fatalf("Bad Container Registry Migrate\n\n. Got: %+v\n\n expected: %+v", is.Attributes, tc.Expected)
}
}
}

func createResourceGroup(client *ArmClient, resourceGroupName string, location string) error {
group := resources.Group{
Location: &location,
}
_, err := client.resourceGroupClient.CreateOrUpdate(resourceGroupName, group)
if err != nil {
return fmt.Errorf("Error creating Resource Group %q: %+v", resourceGroupName, err)
}
return nil
}

func createStorageAccount(client *ArmClient, resourceGroupName, storageAccountName, location string) (*storage.Account, error) {
createParams := storage.AccountCreateParameters{
Location: &location,
Kind: storage.Storage,
Sku: &storage.Sku{
Name: storage.StandardLRS,
Tier: storage.Standard,
},
}
_, createErr := client.storageServiceClient.Create(resourceGroupName, storageAccountName, createParams, make(chan struct{}))
err := <-createErr
if err != nil {
return nil, fmt.Errorf("Error creating Storage Account %q: %+v", resourceGroupName, err)
}

account, err := client.storageServiceClient.GetProperties(resourceGroupName, storageAccountName)
if err != nil {
return nil, fmt.Errorf("Error retrieving Storage Account %q: %+v", resourceGroupName, err)
}

return &account, nil
}

func destroyStorageAccountAndResourceGroup(client *ArmClient, resourceGroupName, storageAccountName string) {
client.storageServiceClient.Delete(resourceGroupName, storageAccountName)
client.resourceGroupClient.Delete(resourceGroupName, make(chan struct{}))
}

0 comments on commit d8aa8ce

Please sign in to comment.