From d51bdd177270d8236139cee501caf80a328ad792 Mon Sep 17 00:00:00 2001 From: James Nugent Date: Thu, 18 Feb 2016 14:50:43 -0800 Subject: [PATCH] provider/azurerm: Use Riviera for Resource Groups --- Godeps/Godeps.json | 6 +- .../azurerm/resource_arm_resource_group.go | 146 +++++++++--------- .../resource_arm_resource_group_test.go | 4 +- .../jen20/riviera/azure/get_resource_group.go | 24 +++ .../github.com/jen20/riviera/azure/request.go | 59 +++---- .../riviera/azure/update_resource_group.go | 25 +++ 6 files changed, 142 insertions(+), 122 deletions(-) create mode 100644 vendor/github.com/jen20/riviera/azure/get_resource_group.go create mode 100644 vendor/github.com/jen20/riviera/azure/update_resource_group.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 77cb4bbdf..903e8c5f7 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -536,15 +536,15 @@ }, { "ImportPath": "github.com/jen20/riviera/azure", - "Rev": "5bae671a2903c37b4d580f24d9ab74ada633813f" + "Rev": "31f4644de1f4931e43271240069bf2b896f47005" }, { "ImportPath": "github.com/jen20/riviera/dns", - "Rev": "5bae671a2903c37b4d580f24d9ab74ada633813f" + "Rev": "31f4644de1f4931e43271240069bf2b896f47005" }, { "ImportPath": "github.com/jen20/riviera/sql", - "Rev": "5bae671a2903c37b4d580f24d9ab74ada633813f" + "Rev": "31f4644de1f4931e43271240069bf2b896f47005" }, { "ImportPath": "github.com/jmespath/go-jmespath", diff --git a/builtin/providers/azurerm/resource_arm_resource_group.go b/builtin/providers/azurerm/resource_arm_resource_group.go index 58fcb3bdb..9770bf5f5 100644 --- a/builtin/providers/azurerm/resource_arm_resource_group.go +++ b/builtin/providers/azurerm/resource_arm_resource_group.go @@ -3,14 +3,11 @@ package azurerm import ( "fmt" "log" - "net/http" "regexp" "strings" - "time" - "github.com/Azure/azure-sdk-for-go/arm/resources/resources" - "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" + "github.com/jen20/riviera/azure" ) func resourceArmResourceGroup() *schema.Resource { @@ -60,20 +57,27 @@ func validateArmResourceGroupName(v interface{}, k string) (ws []string, es []er func resourceArmResourceGroupUpdate(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient) - resGroupClient := client.resourceGroupClient + rivieraClient := client.rivieraClient if !d.HasChange("tags") { return nil } name := d.Get("name").(string) - newTags := d.Get("tags").(map[string]interface{}) - _, err := resGroupClient.Patch(name, resources.ResourceGroup{ - Tags: expandTags(newTags), - }) + + updateRequest := rivieraClient.NewRequestForURI(d.Id()) + updateRequest.Command = &azure.UpdateResourceGroup{ + Name: name, + Tags: *expandTags(newTags), + } + + updateResponse, err := updateRequest.Execute() if err != nil { - return fmt.Errorf("Error issuing Azure ARM create request to update resource group %q: %s", name, err) + return fmt.Errorf("Error updating resource group: %s", err) + } + if !updateResponse.IsSuccessful() { + return fmt.Errorf("Error updating resource group: %s", updateResponse.Error) } return resourceArmResourceGroupRead(d, meta) @@ -81,110 +85,100 @@ func resourceArmResourceGroupUpdate(d *schema.ResourceData, meta interface{}) er func resourceArmResourceGroupCreate(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient) - resGroupClient := client.resourceGroupClient + rivieraClient := client.rivieraClient - name := d.Get("name").(string) - location := d.Get("location").(string) - tags := d.Get("tags").(map[string]interface{}) - - rg := resources.ResourceGroup{ - Name: &name, - Location: &location, - Tags: expandTags(tags), + createRequest := rivieraClient.NewRequest() + createRequest.Command = &azure.CreateResourceGroup{ + Name: d.Get("name").(string), + Location: d.Get("location").(string), + Tags: *expandTags(d.Get("tags").(map[string]interface{})), } - resp, err := resGroupClient.CreateOrUpdate(name, rg) + createResponse, err := createRequest.Execute() if err != nil { - return fmt.Errorf("Error issuing Azure ARM create request for resource group '%s': %s", name, err) + return fmt.Errorf("Error creating resource group: %s", err) + } + if !createResponse.IsSuccessful() { + return fmt.Errorf("Error creating resource group: %s", createResponse.Error) } + resp := createResponse.Parsed.(*azure.CreateResourceGroupResponse) d.SetId(*resp.ID) - log.Printf("[DEBUG] Waiting for Resource Group (%s) to become available", name) - stateConf := &resource.StateChangeConf{ - Pending: []string{"Accepted"}, - Target: []string{"Succeeded"}, - Refresh: resourceGroupStateRefreshFunc(client, name), - Timeout: 10 * time.Minute, - } - if _, err := stateConf.WaitForState(); err != nil { - return fmt.Errorf("Error waiting for Resource Group (%s) to become available: %s", name, err) - } + // TODO(jen20): Decide whether we need this or not and migrate to use @stack72's work if so + // log.Printf("[DEBUG] Waiting for Resource Group (%s) to become available", name) + // stateConf := &resource.StateChangeConf{ + // Pending: []string{"Accepted"}, + // Target: []string{"Succeeded"}, + // Refresh: resourceGroupStateRefreshFunc(client, name), + // Timeout: 10 * time.Minute, + // } + // if _, err := stateConf.WaitForState(); err != nil { + // return fmt.Errorf("Error waiting for Resource Group (%s) to become available: %s", name, err) + // } return resourceArmResourceGroupRead(d, meta) } func resourceArmResourceGroupRead(d *schema.ResourceData, meta interface{}) error { - resGroupClient := meta.(*ArmClient).resourceGroupClient + client := meta.(*ArmClient) + rivieraClient := client.rivieraClient - id, err := parseAzureResourceID(d.Id()) + readRequest := rivieraClient.NewRequestForURI(d.Id()) + readRequest.Command = &azure.GetResourceGroup{} + + readResponse, err := readRequest.Execute() if err != nil { - return err + return fmt.Errorf("Error reading resource group: %s", err) } - name := id.ResourceGroup - - res, err := resGroupClient.Get(name) - if err != nil { - if res.StatusCode == http.StatusNotFound { - d.SetId("") - return nil - } - return fmt.Errorf("Error issuing read request to Azure ARM for resource group '%s': %s", name, err) + if !readResponse.IsSuccessful() { + log.Printf("[INFO] Error reading resource group %q - removing from state", d.Id()) + d.SetId("") + return fmt.Errorf("Error reading resource group: %s", readResponse.Error) } - d.Set("name", res.Name) - d.Set("location", res.Location) + resp := readResponse.Parsed.(*azure.GetResourceGroupResponse) - flattenAndSetTags(d, res.Tags) + d.Set("name", resp.Name) + d.Set("location", resp.Location) + flattenAndSetTags(d, resp.Tags) return nil } func resourceArmResourceGroupExists(d *schema.ResourceData, meta interface{}) (bool, error) { - resGroupClient := meta.(*ArmClient).resourceGroupClient + client := meta.(*ArmClient) + rivieraClient := client.rivieraClient - id, err := parseAzureResourceID(d.Id()) + readRequest := rivieraClient.NewRequestForURI(d.Id()) + readRequest.Command = &azure.GetResourceGroup{} + + readResponse, err := readRequest.Execute() if err != nil { - return false, err + return false, fmt.Errorf("Error reading resource group: %s", err) } - name := id.ResourceGroup - - resp, err := resGroupClient.CheckExistence(name) - if err != nil { - if resp.StatusCode != 200 { - return false, err - } - + if readResponse.IsSuccessful() { return true, nil } - return true, nil + return false, nil } func resourceArmResourceGroupDelete(d *schema.ResourceData, meta interface{}) error { - resGroupClient := meta.(*ArmClient).resourceGroupClient + client := meta.(*ArmClient) + rivieraClient := client.rivieraClient - id, err := parseAzureResourceID(d.Id()) + deleteRequest := rivieraClient.NewRequestForURI(d.Id()) + deleteRequest.Command = &azure.DeleteResourceGroup{} + + deleteResponse, err := deleteRequest.Execute() if err != nil { - return err + return fmt.Errorf("Error deleting resource group: %s", err) } - name := id.ResourceGroup - - _, err = resGroupClient.Delete(name) - if err != nil { - return err + if !deleteResponse.IsSuccessful() { + return fmt.Errorf("Error deleting resource group: %s", deleteResponse.Error) } return nil -} -func resourceGroupStateRefreshFunc(client *ArmClient, id string) resource.StateRefreshFunc { - return func() (interface{}, string, error) { - res, err := client.resourceGroupClient.Get(id) - if err != nil { - return nil, "", fmt.Errorf("Error issuing read request in resourceGroupStateRefreshFunc to Azure ARM for resource group '%s': %s", id, err) - } - - return res, *res.Properties.ProvisioningState, nil - } } diff --git a/builtin/providers/azurerm/resource_arm_resource_group_test.go b/builtin/providers/azurerm/resource_arm_resource_group_test.go index 8433e83f0..ed7e6a0a8 100644 --- a/builtin/providers/azurerm/resource_arm_resource_group_test.go +++ b/builtin/providers/azurerm/resource_arm_resource_group_test.go @@ -128,8 +128,8 @@ resource "azurerm_resource_group" "test" { location = "West US" tags { - environment = "Production" - cost_center = "MSFT" + environment = "Production" + cost_center = "MSFT" } } ` diff --git a/vendor/github.com/jen20/riviera/azure/get_resource_group.go b/vendor/github.com/jen20/riviera/azure/get_resource_group.go new file mode 100644 index 000000000..aa8a118b1 --- /dev/null +++ b/vendor/github.com/jen20/riviera/azure/get_resource_group.go @@ -0,0 +1,24 @@ +package azure + +type GetResourceGroupResponse struct { + ID *string `mapstructure:"id"` + Name *string `mapstructure:"name"` + Location *string `mapstructure:"location"` + ProvisioningState *string `mapstructure:"provisioningState"` + Tags *map[string]*string `mapstructure:"tags"` +} + +type GetResourceGroup struct { + Name string `json:"-"` +} + +func (command GetResourceGroup) APIInfo() APIInfo { + return APIInfo{ + APIVersion: resourceGroupAPIVersion, + Method: "GET", + URLPathFunc: resourceGroupDefaultURLFunc(command.Name), + ResponseTypeFunc: func() interface{} { + return &GetResourceGroupResponse{} + }, + } +} diff --git a/vendor/github.com/jen20/riviera/azure/request.go b/vendor/github.com/jen20/riviera/azure/request.go index f420f0b98..a7546a751 100644 --- a/vendor/github.com/jen20/riviera/azure/request.go +++ b/vendor/github.com/jen20/riviera/azure/request.go @@ -26,39 +26,24 @@ type Request struct { client *Client } -func readLocation(req interface{}) (string, bool) { +func readTaggedFields(command interface{}) map[string]interface{} { var value reflect.Value - if reflect.ValueOf(req).Kind() == reflect.Ptr { - value = reflect.ValueOf(req).Elem() + if reflect.ValueOf(command).Kind() == reflect.Ptr { + value = reflect.ValueOf(command).Elem() } else { - value = reflect.ValueOf(req) + value = reflect.ValueOf(command) } + result := make(map[string]interface{}) + for i := 0; i < value.NumField(); i++ { // iterates through every struct type field tag := value.Type().Field(i).Tag // returns the tag string - if tag.Get("riviera") == "location" { - return value.Field(i).String(), true + tagValue := tag.Get("riviera") + if tagValue != "" { + result[tagValue] = value.Field(i).Interface() } } - return "", false -} - -func readTags(req interface{}) (map[string]*string, bool) { - var value reflect.Value - if reflect.ValueOf(req).Kind() == reflect.Ptr { - value = reflect.ValueOf(req).Elem() - } else { - value = reflect.ValueOf(req) - } - - for i := 0; i < value.NumField(); i++ { // iterates through every struct type field - tag := value.Type().Field(i).Tag // returns the tag string - if tag.Get("riviera") == "tags" { - tags := value.Field(i) - return tags.Interface().(map[string]*string), true - } - } - return make(map[string]*string), false + return result } func (request *Request) pollForAsynchronousResponse(acceptedResponse *http.Response) (*http.Response, error) { @@ -108,24 +93,15 @@ func (request *Request) pollForAsynchronousResponse(acceptedResponse *http.Respo } func defaultARMRequestStruct(request *Request, properties interface{}) interface{} { - bodyStruct := struct { - Location *string `json:"location,omitempty"` - Tags *map[string]*string `json:"tags,omitempty"` - Properties interface{} `json:"properties"` - }{ - Properties: properties, + body := make(map[string]interface{}) + + envelopeFields := readTaggedFields(properties) + for k, v := range envelopeFields { + body[k] = v } - if location, hasLocation := readLocation(request.Command); hasLocation { - bodyStruct.Location = &location - } - if tags, hasTags := readTags(request.Command); hasTags { - if len(tags) > 0 { - bodyStruct.Tags = &tags - } - } - - return bodyStruct + body["properties"] = properties + return body } func defaultARMRequestSerialize(body interface{}) (io.ReadSeeker, error) { @@ -162,6 +138,7 @@ func (request *Request) Execute() (*Response, error) { } else { bodyStruct = defaultARMRequestStruct(request, request.Command) } + serialized, err := defaultARMRequestSerialize(bodyStruct) if err != nil { return nil, err diff --git a/vendor/github.com/jen20/riviera/azure/update_resource_group.go b/vendor/github.com/jen20/riviera/azure/update_resource_group.go new file mode 100644 index 000000000..9c3ab6706 --- /dev/null +++ b/vendor/github.com/jen20/riviera/azure/update_resource_group.go @@ -0,0 +1,25 @@ +package azure + +type UpdateResourceGroupResponse struct { + ID *string `mapstructure:"id"` + Name *string `mapstructure:"name"` + Location *string `mapstructure:"location"` + ProvisioningState *string `mapstructure:"provisioningState"` + Tags *map[string]*string `mapstructure:"tags"` +} + +type UpdateResourceGroup struct { + Name string `json:"-"` + Tags map[string]*string `json:"-" riviera:"tags"` +} + +func (command UpdateResourceGroup) APIInfo() APIInfo { + return APIInfo{ + APIVersion: resourceGroupAPIVersion, + Method: "PATCH", + URLPathFunc: resourceGroupDefaultURLFunc(command.Name), + ResponseTypeFunc: func() interface{} { + return &UpdateResourceGroupResponse{} + }, + } +}