From f615474fed06e1825f6c2c2ed8819a1781afc030 Mon Sep 17 00:00:00 2001 From: "Pedro J. Molina" Date: Mon, 9 Oct 2017 18:18:54 +0200 Subject: [PATCH 01/55] Provision sample for ASP.NET on azure_rm_app_service --- examples/app-service-asp-net/README.md | 23 +++++++ examples/app-service-asp-net/app.tf | 65 +++++++++++++++++++ examples/app-service-asp-net/variables.tf | 45 +++++++++++++ examples/app-service-asp-net/web-package.zip | Bin 0 -> 378 bytes 4 files changed, 133 insertions(+) create mode 100644 examples/app-service-asp-net/README.md create mode 100644 examples/app-service-asp-net/app.tf create mode 100644 examples/app-service-asp-net/variables.tf create mode 100644 examples/app-service-asp-net/web-package.zip diff --git a/examples/app-service-asp-net/README.md b/examples/app-service-asp-net/README.md new file mode 100644 index 000000000000..d0190b8b7a23 --- /dev/null +++ b/examples/app-service-asp-net/README.md @@ -0,0 +1,23 @@ +# ASP.NET App-Service Sample + +Sample to deploy an ASP.NET application into Azure App-Services. + +## Creates + +1. A Resource Group +2. An App Service Plan +3. An App Service for usage with .NET +4. Deploy a simple app into (3). + +## Usage + +- Provide values to all variables (credentials and names). +- Create with `terraform apply` +- Destroy all with `terraform destroy --force` + + +## Prerequisites + +- Uses `curl` for application publication. Preinstall curl in your system and add it to the system path. + +**Note:** The Sample uses a local provisioner prepared for a DOS shell (trivial changes needed for a bash shell). \ No newline at end of file diff --git a/examples/app-service-asp-net/app.tf b/examples/app-service-asp-net/app.tf new file mode 100644 index 000000000000..f22515c6dcd2 --- /dev/null +++ b/examples/app-service-asp-net/app.tf @@ -0,0 +1,65 @@ +# Configure the Microsoft Azure Provider +provider "azurerm" { + subscription_id = "${var.subscription_id}" + client_id = "${var.client_id}" + client_secret = "${var.client_secret}" + tenant_id = "${var.tenant_id}" +} + +resource "azurerm_resource_group" "g1" { + name = "{$var.groupName}" + location = "${var.location}" +} + +resource "azurerm_app_service_plan" "plan0" { + name = "service-plan0" + location = "${var.location}" + resource_group_name = "${azurerm_resource_group.g1.name}" + + sku { + tier = "Basic" # Basic | Standard | ... + size = "B1" # B1 | S1 | ... + } +} + +# underscores not supported as app_service name -> if not: you will receive error 400 + +resource "azurerm_app_service" "common_service" { + name = "${var.webName}" + location = "${var.location}" + resource_group_name = "${azurerm_resource_group.g1.name}" + app_service_plan_id = "${azurerm_app_service_plan.plan0.id}" + + site_config { + dotnet_framework_version = "v4.0" + remote_debugging_enabled = true + remote_debugging_version = "VS2015" + } + + # app_settings { + # "SOME_KEY" = "some-value" + # } + # connection_string { + # name = "Database" + # type = "SQLServer" + # value = "Server=some-server.mydomain.com;Integrated Security=SSPI" + # } + + provisioner "local-exec" { + command = "curl -k -u ${var.deploy_user}:${var.deploy_pass} -X PUT --data-binary @${var.deployZipFile} https://${azurerm_app_service.common_service.name}.scm.azurewebsites.net/api/zip/site/wwwroot/" + + # interpreter = ["cmd"] + } +} + +output "service" { + value = "${azurerm_app_service.common_service.name}" +} + +output "serviceUrl" { + value = "https://${azurerm_app_service.common_service.name}.azurewebsites.net" +} + +output "adminUrl" { + value = "https://${azurerm_app_service.common_service.name}.scm.azurewebsites.net" +} diff --git a/examples/app-service-asp-net/variables.tf b/examples/app-service-asp-net/variables.tf new file mode 100644 index 000000000000..be7ddb061782 --- /dev/null +++ b/examples/app-service-asp-net/variables.tf @@ -0,0 +1,45 @@ +variable "groupName" { + type = "string" + default = "yourGroupName" +} + +variable "location" { + type = "string" + default = "westeurope" +} + +variable "webName" { + type = "string" + + # will create -> mySite123456.azurewebsites.net #should be unique + # default = "mySite123456" +} + +variable "subscription_id" { + type = "string" +} + +variable "client_id" { + type = "string" +} + +variable "client_secret" { + type = "string" +} + +variable "tenant_id" { + type = "string" +} + +variable "deploy_user" { + type = "string" +} + +variable "deploy_pass" { + type = "string" +} + +variable "deployZipFile" { + type = "string" + default = "web-package.zip" +} diff --git a/examples/app-service-asp-net/web-package.zip b/examples/app-service-asp-net/web-package.zip new file mode 100644 index 0000000000000000000000000000000000000000..2291a451f720284b27924254f1867e79bd2e5e68 GIT binary patch literal 378 zcmWIWW@Zs#U|`^2_%XrL`|CZ29CIMg4T!mbI5RILwL&kWBsXXCNsW_dH$Hy)Xp845 z|CKC;A|g3PJa27+iiGYQ-f2^X8b Date: Mon, 30 Oct 2017 13:05:53 -0700 Subject: [PATCH 02/55] Added vnet datasource --- azurerm/data_source_vnet.go | 104 ++++++++++++++++++++++++++++++ azurerm/data_source_vnet_test.go | 105 +++++++++++++++++++++++++++++++ azurerm/provider.go | 1 + 3 files changed, 210 insertions(+) create mode 100644 azurerm/data_source_vnet.go create mode 100644 azurerm/data_source_vnet_test.go diff --git a/azurerm/data_source_vnet.go b/azurerm/data_source_vnet.go new file mode 100644 index 000000000000..4b480d407491 --- /dev/null +++ b/azurerm/data_source_vnet.go @@ -0,0 +1,104 @@ +package azurerm + +import ( + "fmt" + "net/http" + + "github.com/Azure/azure-sdk-for-go/arm/network" + "github.com/hashicorp/terraform/helper/schema" + //"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func dataSourceArmVnet() *schema.Resource { + return &schema.Resource{ + Read: dataSourceArmVnetRead, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + + "resource_group_name": resourceGroupNameForDataSourceSchema(), + + "address_spaces": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + + "dns_servers": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + + "subnets": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + } +} + +func dataSourceArmVnetRead(d *schema.ResourceData, meta interface{}) error { + vnetClient := meta.(*ArmClient).vnetClient + + resGroup := d.Get("resource_group_name").(string) + name := d.Get("name").(string) + + resp, err := vnetClient.Get(resGroup, name, "") + if err != nil { + if resp.StatusCode == http.StatusNotFound { + d.SetId("") + } + return fmt.Errorf("Error making Read request on Azure public ip %s: %s", name, err) + } + + d.SetId(*resp.ID) + + if props := resp.VirtualNetworkPropertiesFormat; props != nil { + address_spaces := flattenVnetAddressPrefixes(props.AddressSpace.AddressPrefixes) + if err := d.Set("address_spaces", address_spaces); err != nil { + return err + } + + dns_servers := flattenVnetAddressPrefixes(props.DhcpOptions.DNSServers) + if err := d.Set("dns_servers", dns_servers); err != nil { + return err + } + + subnets := flattenVnetSubnetsAddressSpace(props.Subnets) + if err := d.Set("subnets", subnets); err != nil { + return err + } + } + + return nil +} + +func flattenVnetAddressPrefixes(input *[]string) []interface{} { + prefixes := make([]interface{}, 0) + + for _, prefix := range *input { + prefixes = append(prefixes, prefix) + } + + return prefixes +} + +func flattenVnetSubnetsAddressSpace(input *[]network.Subnet) []interface{} { + subnets := make([]interface{}, 0) + + for _, subnet := range *input { + subnets = append(subnets, *subnet.Name) + } + + return subnets +} diff --git a/azurerm/data_source_vnet_test.go b/azurerm/data_source_vnet_test.go new file mode 100644 index 000000000000..f4a7406b02a9 --- /dev/null +++ b/azurerm/data_source_vnet_test.go @@ -0,0 +1,105 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccDataSourceAzureRMVnet_basic(t *testing.T) { + dataSourceName := "data.azurerm_vnet.test" + ri := acctest.RandInt() + + name := fmt.Sprintf("acctestvnet-%d", ri) + config := testAccDataSourceAzureRMVnet_basic(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMVirtualNetworkDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dataSourceName, "name", name), + resource.TestCheckResourceAttr(dataSourceName, "dns_servers.0", "10.0.0.10"), + resource.TestCheckResourceAttr(dataSourceName, "address_spaces.0", "10.0.0.0/16"), + ), + }, + }, + }) +} + +// func TestAccDataSourceAzureRMVnet_Subnets(t *testing.T) { +// dataSourceName := "data.azurerm_vnet.test" +// ri := acctest.RandInt() + +// name := fmt.Sprintf("acctestvnet-%d", ri) +// //resourceGroupName := fmt.Sprintf("acctestRG-%d", ri) + +// config := testAccDataSourceAzureRMVnet_basic(ri, testLocation()) + +// resource.Test(t, resource.TestCase{ +// PreCheck: func() { testAccPreCheck(t) }, +// Providers: testAccProviders, +// CheckDestroy: testCheckAzureRMPublicIpDestroy, +// Steps: []resource.TestStep{ +// { +// Config: config, +// Check: resource.ComposeTestCheckFunc( +// resource.TestCheckResourceAttr(dataSourceName, "name", name), +// resource.TestCheckResourceAttr(dataSourceName, "dns_servers", `["10.0.0.10","10.0.0.11"]`), +// ), +// }, +// }, +// }) +// } + +func testAccDataSourceAzureRMVnet_basic(rInt int, location string) string { + return fmt.Sprintf(` + resource "azurerm_resource_group" "test" { + name = "acctest%d-rg" + location = "%s" + } + + resource "azurerm_virtual_network" "test" { + name = "acctestvnet-%d" + address_space = ["10.0.0.0/16"] + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + dns_servers = ["10.0.0.10"] + } + + data "azurerm_vnet" "test" { + resource_group_name = "${azurerm_resource_group.test.name}" + name = "${azurerm_virtual_network.test.name}" + } + + `, rInt, location, rInt) +} + +// func testAccDataSourceAzureRMVnet_Subnets(rInt int, location string) string { +// return fmt.Sprintf(` +// resource "azurerm_resource_group" "test" { +// name = "acctest%d-rg" +// location = "%s" +// } + +// resource "azurerm_virtual_network" "test" { +// name = "acctestvnet-%d" +// address_space = ["10.0.0.0/16"] +// location = "${azurerm_resource_group.test.location}" +// resource_group_name = "${azurerm_resource_group.test.name}" +// dns_servers = ["10.0.0.10","10.0.0.11"] +// } + +// data "azurerm_vnet" "test" { +// resource_group_name = "${azurerm_resource_group.test.name}" +// name = "${azurerm_virtual_network.test.name}" +// dns_servers = "${azurerm_virtual_network.test.dns_servers}" +// } + +// `, rInt, location, rInt) +// } diff --git a/azurerm/provider.go b/azurerm/provider.go index 8bf452d849a3..6ef55769d068 100644 --- a/azurerm/provider.go +++ b/azurerm/provider.go @@ -80,6 +80,7 @@ func Provider() terraform.ResourceProvider { "azurerm_snapshot": dataSourceArmSnapshot(), "azurerm_subnet": dataSourceArmSubnet(), "azurerm_subscription": dataSourceArmSubscription(), + "azurerm_vnet": dataSourceArmVnet(), }, ResourcesMap: map[string]*schema.Resource{ From d3072dd81dc77a2b0aad17609792bbbb834b9624 Mon Sep 17 00:00:00 2001 From: Henry Buckle Date: Wed, 1 Nov 2017 09:15:14 +0000 Subject: [PATCH 03/55] add identity property to vm --- azurerm/resource_arm_virtual_machine.go | 43 +++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/azurerm/resource_arm_virtual_machine.go b/azurerm/resource_arm_virtual_machine.go index 3cb883856a92..574ae4132a7d 100644 --- a/azurerm/resource_arm_virtual_machine.go +++ b/azurerm/resource_arm_virtual_machine.go @@ -71,6 +71,25 @@ func resourceArmVirtualMachine() *schema.Resource { }, }, + "identity": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Optional: true, + }, + "principal_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "license_type": { Type: schema.TypeString, Optional: true, @@ -589,6 +608,16 @@ func resourceArmVirtualMachineCreate(d *schema.ResourceData, meta interface{}) e Tags: expandedTags, } + if v, ok := d.GetOk("identity"); ok { + identities := v.(*schema.Set).List() + identity := identities[0].(map[string]interface{}) + identityType := identity["type"].(string) + vmIdentity := compute.VirtualMachineIdentity{ + Type: compute.ResourceIdentityType(identityType), + } + vm.Identity = &vmIdentity + } + if _, ok := d.GetOk("plan"); ok { plan, err := expandAzureRmVirtualMachinePlan(d) if err != nil { @@ -647,6 +676,10 @@ func resourceArmVirtualMachineRead(d *schema.ResourceData, meta interface{}) err } } + if resp.Identity != nil { + d.Set("identity", flattenAzureRmVirtualMachineIdentity(resp.Identity)) + } + if resp.VirtualMachineProperties.AvailabilitySet != nil { d.Set("availability_set_id", strings.ToLower(*resp.VirtualMachineProperties.AvailabilitySet.ID)) } @@ -875,6 +908,16 @@ func flattenAzureRmVirtualMachineImageReference(image *compute.ImageReference) [ return []interface{}{result} } +func flattenAzureRmVirtualMachineIdentity(identity *compute.VirtualMachineIdentity) []interface{} { + result := make(map[string]interface{}) + result["type"] = string(identity.Type) + if identity.PrincipalID != nil { + result["principal_id"] = *identity.PrincipalID + } + + return []interface{}{result} +} + func flattenAzureRmVirtualMachineDiagnosticsProfile(profile *compute.BootDiagnostics) []interface{} { result := make(map[string]interface{}) From e363abd11e14753cdb4a8df79005e21b2a2bb6c7 Mon Sep 17 00:00:00 2001 From: Henry Buckle Date: Wed, 1 Nov 2017 21:15:26 +0000 Subject: [PATCH 04/55] refactor, tests and docs --- azurerm/resource_arm_virtual_machine.go | 25 +++-- ..._arm_virtual_machine_managed_disks_test.go | 104 ++++++++++++++++++ website/docs/r/virtual_machine.html.markdown | 31 ++++++ 3 files changed, 150 insertions(+), 10 deletions(-) diff --git a/azurerm/resource_arm_virtual_machine.go b/azurerm/resource_arm_virtual_machine.go index 574ae4132a7d..59f68fd8603a 100644 --- a/azurerm/resource_arm_virtual_machine.go +++ b/azurerm/resource_arm_virtual_machine.go @@ -72,7 +72,7 @@ func resourceArmVirtualMachine() *schema.Resource { }, "identity": { - Type: schema.TypeSet, + Type: schema.TypeList, Optional: true, Computed: true, MaxItems: 1, @@ -80,7 +80,7 @@ func resourceArmVirtualMachine() *schema.Resource { Schema: map[string]*schema.Schema{ "type": { Type: schema.TypeString, - Optional: true, + Required: true, }, "principal_id": { Type: schema.TypeString, @@ -608,14 +608,9 @@ func resourceArmVirtualMachineCreate(d *schema.ResourceData, meta interface{}) e Tags: expandedTags, } - if v, ok := d.GetOk("identity"); ok { - identities := v.(*schema.Set).List() - identity := identities[0].(map[string]interface{}) - identityType := identity["type"].(string) - vmIdentity := compute.VirtualMachineIdentity{ - Type: compute.ResourceIdentityType(identityType), - } - vm.Identity = &vmIdentity + if _, ok := d.GetOk("identity"); ok { + vmIdentity := expandAzureRmVirtualMachineIdentity(d) + vm.Identity = vmIdentity } if _, ok := d.GetOk("plan"); ok { @@ -1110,6 +1105,16 @@ func expandAzureRmVirtualMachinePlan(d *schema.ResourceData) (*compute.Plan, err }, nil } +func expandAzureRmVirtualMachineIdentity(d *schema.ResourceData) *compute.VirtualMachineIdentity { + v := d.Get("identity") + identities := v.([]interface{}) + identity := identities[0].(map[string]interface{}) + identityType := identity["type"].(string) + return &compute.VirtualMachineIdentity{ + Type: compute.ResourceIdentityType(identityType), + } +} + func expandAzureRmVirtualMachineOsProfile(d *schema.ResourceData) (*compute.OSProfile, error) { osProfiles := d.Get("os_profile").(*schema.Set).List() diff --git a/azurerm/resource_arm_virtual_machine_managed_disks_test.go b/azurerm/resource_arm_virtual_machine_managed_disks_test.go index b9cbb0679ca2..04e76c25a218 100644 --- a/azurerm/resource_arm_virtual_machine_managed_disks_test.go +++ b/azurerm/resource_arm_virtual_machine_managed_disks_test.go @@ -317,6 +317,110 @@ func TestAccAzureRMVirtualMachine_multipleNICs(t *testing.T) { }) } +func TestAccAzureRMVirtualMachine_managedServiceIdentity(t *testing.T) { + var vm compute.VirtualMachine + match := regexp.MustCompile("^(\\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\\}{0,1})$") + resourceName := "azurerm_virtual_machine.test" + ri := acctest.RandInt() + config := testAccAzureRMVirtualMachine_withManagedServiceIdentity(ri, testLocation()) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMVirtualMachineDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMVirtualMachineExists(resourceName, &vm), + resource.TestCheckResourceAttr(resourceName, "identity.0.type", "SystemAssigned"), + resource.TestMatchResourceAttr(resourceName, "identity.0.principal_id", match), + resource.TestMatchOutput("principal_id", match), + ), + }, + }, + }) +} + +func testAccAzureRMVirtualMachine_withManagedServiceIdentity(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_virtual_network" "test" { + name = "acctvn-%d" + address_space = ["10.0.0.0/16"] + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_subnet" "test" { + name = "acctsub-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.2.0/24" +} + +resource "azurerm_network_interface" "test" { + name = "acctni-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + ip_configuration { + name = "testconfiguration1" + subnet_id = "${azurerm_subnet.test.id}" + private_ip_address_allocation = "dynamic" + } +} + +resource "azurerm_virtual_machine" "test" { + name = "acctvm-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + network_interface_ids = ["${azurerm_network_interface.test.id}"] + vm_size = "Standard_D1_v2" + + identity = { + type = "SystemAssigned" + } + + storage_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } + + storage_os_disk { + name = "osd-%d" + caching = "ReadWrite" + create_option = "FromImage" + disk_size_gb = "50" + managed_disk_type = "Standard_LRS" + } + + os_profile { + computer_name = "hn%d" + admin_username = "testadmin" + admin_password = "Password1234!" + } + + os_profile_linux_config { + disable_password_authentication = false + } + + tags { + environment = "Production" + cost-center = "Ops" + } +} +output "principal_id" { + value = "${lookup(azurerm_virtual_machine.test.identity[0], "principal_id")}" +} +`, rInt, location, rInt, rInt, rInt, rInt, rInt, rInt) +} + func testAccAzureRMVirtualMachine_basicLinuxMachine_managedDisk_explicit(rInt int, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { diff --git a/website/docs/r/virtual_machine.html.markdown b/website/docs/r/virtual_machine.html.markdown index fa0104dfc2e5..fe6c21546092 100644 --- a/website/docs/r/virtual_machine.html.markdown +++ b/website/docs/r/virtual_machine.html.markdown @@ -237,6 +237,7 @@ The following arguments are supported: * `storage_data_disk` - (Optional) A list of Storage Data disk blocks as referenced below. * `delete_data_disks_on_termination` - (Optional) Flag to enable deletion of storage data disk VHD blobs or managed disks when the VM is deleted, defaults to `false` * `os_profile` - (Optional) An OS Profile block as documented below. Required when `create_option` in the `storage_os_disk` block is set to `FromImage`. +* `identity` - (Optional) An identity block as documented below. * `license_type` - (Optional, when a Windows machine) Specifies the Windows OS license type. The only allowable value, if supplied, is `Windows_Server`. * `os_profile_windows_config` - (Required, when a Windows machine) A Windows config block as documented below. @@ -323,6 +324,36 @@ resource "azurerm_virtual_machine" "test" { 3. Contains a numeric digit 4. Contains a special character +`identity` supports the following: + +* `type` - (Required) Specifies the identity type of the virtual machine. The only allowable value is `SystemAssigned`. To enable Managed Service Identity the ManagedIdentityExtension must also be added to the virtual machine. The Principal ID can be retrieved after the virtual machine has been created, e.g. + +```hcl +resource "azurerm_virtual_machine" "test" { + name = "test" + + identity = { + type = "SystemAssigned" + } +} +resource "azurerm_virtual_machine_extension" "test" { + name = "test" + virtual_machine_name = "${azurerm_virtual_machine.test.name}" + publisher = "Microsoft.ManagedIdentity" + type = "ManagedIdentityExtensionForWindows" + type_handler_version = "1.0" + + settings = < Date: Wed, 1 Nov 2017 14:52:43 -0700 Subject: [PATCH 05/55] added vnet_peering --- azurerm/data_source_vnet.go | 34 +++++++-- azurerm/data_source_vnet_test.go | 120 +++++++++++++++++-------------- 2 files changed, 96 insertions(+), 58 deletions(-) diff --git a/azurerm/data_source_vnet.go b/azurerm/data_source_vnet.go index 4b480d407491..bdbf358f2dbf 100644 --- a/azurerm/data_source_vnet.go +++ b/azurerm/data_source_vnet.go @@ -43,6 +43,17 @@ func dataSourceArmVnet() *schema.Resource { Type: schema.TypeString, }, }, + + "vnet_peerings": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, }, } } @@ -58,7 +69,7 @@ func dataSourceArmVnetRead(d *schema.ResourceData, meta interface{}) error { if resp.StatusCode == http.StatusNotFound { d.SetId("") } - return fmt.Errorf("Error making Read request on Azure public ip %s: %s", name, err) + return fmt.Errorf("Error making Read request on Azure virtual network %s: %s", name, err) } d.SetId(*resp.ID) @@ -74,12 +85,16 @@ func dataSourceArmVnetRead(d *schema.ResourceData, meta interface{}) error { return err } - subnets := flattenVnetSubnetsAddressSpace(props.Subnets) + subnets := flattenVnetSubnetsNames(props.Subnets) if err := d.Set("subnets", subnets); err != nil { return err } - } + vnet_peerings := flattenVnetPeerings(props.VirtualNetworkPeerings) + if err := d.Set("vnet_peerings", vnet_peerings); err != nil { + return err + } + } return nil } @@ -89,16 +104,23 @@ func flattenVnetAddressPrefixes(input *[]string) []interface{} { for _, prefix := range *input { prefixes = append(prefixes, prefix) } - return prefixes } -func flattenVnetSubnetsAddressSpace(input *[]network.Subnet) []interface{} { +func flattenVnetSubnetsNames(input *[]network.Subnet) []interface{} { subnets := make([]interface{}, 0) for _, subnet := range *input { subnets = append(subnets, *subnet.Name) } - return subnets } + +func flattenVnetPeerings(input *[]network.VirtualNetworkPeering) []interface{} { + vnetpeerings := make([]interface{}, 0) + + for _, vnetpeering := range *input { + vnetpeerings = append(vnetpeerings, []string{*vnetpeering.Name, *vnetpeering.RemoteVirtualNetwork.ID}) + } + return vnetpeerings +} diff --git a/azurerm/data_source_vnet_test.go b/azurerm/data_source_vnet_test.go index f4a7406b02a9..afaef34e675a 100644 --- a/azurerm/data_source_vnet_test.go +++ b/azurerm/data_source_vnet_test.go @@ -16,46 +16,46 @@ func TestAccDataSourceAzureRMVnet_basic(t *testing.T) { config := testAccDataSourceAzureRMVnet_basic(ri, testLocation()) resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testCheckAzureRMVirtualNetworkDestroy, + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, Steps: []resource.TestStep{ { Config: config, Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(dataSourceName, "name", name), - resource.TestCheckResourceAttr(dataSourceName, "dns_servers.0", "10.0.0.10"), + resource.TestCheckResourceAttr(dataSourceName, "dns_servers.0", "10.0.0.4"), resource.TestCheckResourceAttr(dataSourceName, "address_spaces.0", "10.0.0.0/16"), + resource.TestCheckResourceAttr(dataSourceName, "subnets.0", "subnet1"), ), }, }, }) } -// func TestAccDataSourceAzureRMVnet_Subnets(t *testing.T) { -// dataSourceName := "data.azurerm_vnet.test" -// ri := acctest.RandInt() - -// name := fmt.Sprintf("acctestvnet-%d", ri) -// //resourceGroupName := fmt.Sprintf("acctestRG-%d", ri) - -// config := testAccDataSourceAzureRMVnet_basic(ri, testLocation()) - -// resource.Test(t, resource.TestCase{ -// PreCheck: func() { testAccPreCheck(t) }, -// Providers: testAccProviders, -// CheckDestroy: testCheckAzureRMPublicIpDestroy, -// Steps: []resource.TestStep{ -// { -// Config: config, -// Check: resource.ComposeTestCheckFunc( -// resource.TestCheckResourceAttr(dataSourceName, "name", name), -// resource.TestCheckResourceAttr(dataSourceName, "dns_servers", `["10.0.0.10","10.0.0.11"]`), -// ), -// }, -// }, -// }) -// } +func TestAccDataSourceAzureRMVnet_peering(t *testing.T) { + firstDataSourceName := "data.azurerm_vnet.test" + ri := acctest.RandInt() + + name_vnet_1 := fmt.Sprintf("acctestvnet-1-%d", ri) + //name_peer := fmt.Sprintf("acctestpeer-1-%d", ri) + config := testAccDataSourceAzureRMVnet_peering(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(firstDataSourceName, "name", name_vnet_1), + resource.TestCheckResourceAttr(firstDataSourceName, "address_spaces.0", "10.0.1.0/24"), + resource.TestCheckResourceAttr(firstDataSourceName, "vnet_peerings.#", "1"), + //resource.TestCheckResourceAttr(firstDataSourceName, "vnet_peerings.0.0", "peer-1to2"), + ), + }, + }, + }) +} func testAccDataSourceAzureRMVnet_basic(rInt int, location string) string { return fmt.Sprintf(` @@ -69,7 +69,12 @@ func testAccDataSourceAzureRMVnet_basic(rInt int, location string) string { address_space = ["10.0.0.0/16"] location = "${azurerm_resource_group.test.location}" resource_group_name = "${azurerm_resource_group.test.name}" - dns_servers = ["10.0.0.10"] + dns_servers = ["10.0.0.4"] + + subnet { + name = "subnet1" + address_prefix = "10.0.1.0/24" + } } data "azurerm_vnet" "test" { @@ -80,26 +85,37 @@ func testAccDataSourceAzureRMVnet_basic(rInt int, location string) string { `, rInt, location, rInt) } -// func testAccDataSourceAzureRMVnet_Subnets(rInt int, location string) string { -// return fmt.Sprintf(` -// resource "azurerm_resource_group" "test" { -// name = "acctest%d-rg" -// location = "%s" -// } - -// resource "azurerm_virtual_network" "test" { -// name = "acctestvnet-%d" -// address_space = ["10.0.0.0/16"] -// location = "${azurerm_resource_group.test.location}" -// resource_group_name = "${azurerm_resource_group.test.name}" -// dns_servers = ["10.0.0.10","10.0.0.11"] -// } - -// data "azurerm_vnet" "test" { -// resource_group_name = "${azurerm_resource_group.test.name}" -// name = "${azurerm_virtual_network.test.name}" -// dns_servers = "${azurerm_virtual_network.test.dns_servers}" -// } - -// `, rInt, location, rInt) -// } +func testAccDataSourceAzureRMVnet_peering(rInt int, location string) string { + return fmt.Sprintf(` + resource "azurerm_resource_group" "test" { + name = "acctest%d-rg" + location = "%s" + } + + resource "azurerm_virtual_network" "test1" { + name = "acctestvnet-1-%d" + address_space = ["10.0.1.0/24"] + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + } + + resource "azurerm_virtual_network" "test2" { + name = "acctestvnet-2-%d" + address_space = ["10.0.2.0/24"] + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + } + + resource "azurerm_virtual_network_peering" "test1" { + name = "peer-1to2" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test1.name}" + remote_virtual_network_id = "${azurerm_virtual_network.test2.id}" + } + + data "azurerm_vnet" "test" { + resource_group_name = "${azurerm_resource_group.test.name}" + name = "${azurerm_virtual_network.test1.name}" + } + `, rInt, location, rInt, rInt) +} From 054102aa26f04a9fcb72c1fc9c04a32870ea8acc Mon Sep 17 00:00:00 2001 From: Damien Caro Date: Thu, 2 Nov 2017 16:42:19 -0700 Subject: [PATCH 06/55] changing to TypeMap --- azurerm/data_source_vnet.go | 25 +++++++++++++------------ azurerm/data_source_vnet_test.go | 10 ++++------ 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/azurerm/data_source_vnet.go b/azurerm/data_source_vnet.go index bdbf358f2dbf..2e94ce5a52c8 100644 --- a/azurerm/data_source_vnet.go +++ b/azurerm/data_source_vnet.go @@ -18,7 +18,10 @@ func dataSourceArmVnet() *schema.Resource { Required: true, }, - "resource_group_name": resourceGroupNameForDataSourceSchema(), + "resource_group_name": { + Type: schema.TypeString, + Required: true, + }, "address_spaces": { Type: schema.TypeList, @@ -45,14 +48,8 @@ func dataSourceArmVnet() *schema.Resource { }, "vnet_peerings": { - Type: schema.TypeList, + Type: schema.TypeMap, Computed: true, - Elem: &schema.Schema{ - Type: schema.TypeList, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, }, }, } @@ -116,11 +113,15 @@ func flattenVnetSubnetsNames(input *[]network.Subnet) []interface{} { return subnets } -func flattenVnetPeerings(input *[]network.VirtualNetworkPeering) []interface{} { - vnetpeerings := make([]interface{}, 0) +func flattenVnetPeerings(input *[]network.VirtualNetworkPeering) map[string]interface{} { + output := make(map[string]interface{}, 0) for _, vnetpeering := range *input { - vnetpeerings = append(vnetpeerings, []string{*vnetpeering.Name, *vnetpeering.RemoteVirtualNetwork.ID}) + key := *vnetpeering.Name + value := *vnetpeering.RemoteVirtualNetwork.ID + + output[key] = value + } - return vnetpeerings + return output } diff --git a/azurerm/data_source_vnet_test.go b/azurerm/data_source_vnet_test.go index afaef34e675a..2c568b93814e 100644 --- a/azurerm/data_source_vnet_test.go +++ b/azurerm/data_source_vnet_test.go @@ -33,11 +33,10 @@ func TestAccDataSourceAzureRMVnet_basic(t *testing.T) { } func TestAccDataSourceAzureRMVnet_peering(t *testing.T) { - firstDataSourceName := "data.azurerm_vnet.test" + dataSourceName := "data.azurerm_vnet.test" ri := acctest.RandInt() name_vnet_1 := fmt.Sprintf("acctestvnet-1-%d", ri) - //name_peer := fmt.Sprintf("acctestpeer-1-%d", ri) config := testAccDataSourceAzureRMVnet_peering(ri, testLocation()) resource.Test(t, resource.TestCase{ @@ -47,10 +46,9 @@ func TestAccDataSourceAzureRMVnet_peering(t *testing.T) { { Config: config, Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr(firstDataSourceName, "name", name_vnet_1), - resource.TestCheckResourceAttr(firstDataSourceName, "address_spaces.0", "10.0.1.0/24"), - resource.TestCheckResourceAttr(firstDataSourceName, "vnet_peerings.#", "1"), - //resource.TestCheckResourceAttr(firstDataSourceName, "vnet_peerings.0.0", "peer-1to2"), + resource.TestCheckResourceAttr(dataSourceName, "name", name_vnet_1), + resource.TestCheckResourceAttr(dataSourceName, "address_spaces.0", "10.0.1.0/24"), + resource.TestCheckResourceAttr(dataSourceName, "vnet_peerings.%", "1"), ), }, }, From 5c16837a8df65cc7a18937fcebab6c1ef01cb66b Mon Sep 17 00:00:00 2001 From: Tom Harvey Date: Sat, 4 Nov 2017 13:49:39 +0000 Subject: [PATCH 07/55] Updating the Provider block --- examples/app-service-asp-net/app.tf | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/examples/app-service-asp-net/app.tf b/examples/app-service-asp-net/app.tf index f22515c6dcd2..1ba51ae510fd 100644 --- a/examples/app-service-asp-net/app.tf +++ b/examples/app-service-asp-net/app.tf @@ -1,13 +1,14 @@ # Configure the Microsoft Azure Provider provider "azurerm" { - subscription_id = "${var.subscription_id}" - client_id = "${var.client_id}" - client_secret = "${var.client_secret}" - tenant_id = "${var.tenant_id}" + # if you're using a Service Principal (shared account) then either set the environment variables, or fill these in: + # subscription_id = "..." + # client_id = "..." + # client_secret = "..." + # tenant_id = "..." } resource "azurerm_resource_group" "g1" { - name = "{$var.groupName}" + name = "${var.resource_group_name}" location = "${var.location}" } @@ -25,7 +26,7 @@ resource "azurerm_app_service_plan" "plan0" { # underscores not supported as app_service name -> if not: you will receive error 400 resource "azurerm_app_service" "common_service" { - name = "${var.webName}" + name = "${var.app_service_name}" location = "${var.location}" resource_group_name = "${azurerm_resource_group.g1.name}" app_service_plan_id = "${azurerm_app_service_plan.plan0.id}" @@ -56,10 +57,10 @@ output "service" { value = "${azurerm_app_service.common_service.name}" } -output "serviceUrl" { +output "service_url" { value = "https://${azurerm_app_service.common_service.name}.azurewebsites.net" } -output "adminUrl" { +output "admin_url" { value = "https://${azurerm_app_service.common_service.name}.scm.azurewebsites.net" } From 65805ab297a5057f70eb30891be12bc097c9a50f Mon Sep 17 00:00:00 2001 From: Tom Harvey Date: Sat, 4 Nov 2017 13:50:21 +0000 Subject: [PATCH 08/55] Variable consistency and removing unused variables --- examples/app-service-asp-net/variables.tf | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/examples/app-service-asp-net/variables.tf b/examples/app-service-asp-net/variables.tf index be7ddb061782..78abf98e6a59 100644 --- a/examples/app-service-asp-net/variables.tf +++ b/examples/app-service-asp-net/variables.tf @@ -1,4 +1,4 @@ -variable "groupName" { +variable "resource_group_name" { type = "string" default = "yourGroupName" } @@ -8,29 +8,13 @@ variable "location" { default = "westeurope" } -variable "webName" { +variable "app_service_name" { type = "string" # will create -> mySite123456.azurewebsites.net #should be unique # default = "mySite123456" } -variable "subscription_id" { - type = "string" -} - -variable "client_id" { - type = "string" -} - -variable "client_secret" { - type = "string" -} - -variable "tenant_id" { - type = "string" -} - variable "deploy_user" { type = "string" } From c32a560ca8d20a18b68d792fcea7eb67616d0491 Mon Sep 17 00:00:00 2001 From: Damien Caro Date: Wed, 15 Nov 2017 00:25:06 -0600 Subject: [PATCH 09/55] Changed to azure_virtual_network, added crash control and added documentation. --- ...vnet.go => data_source_virtual_network.go} | 33 ++++++++++------- ...go => data_source_virtual_network_test.go} | 20 +++++----- azurerm/provider.go | 2 +- website/docs/d/virtual_network.html.markdown | 37 +++++++++++++++++++ 4 files changed, 67 insertions(+), 25 deletions(-) rename azurerm/{data_source_vnet.go => data_source_virtual_network.go} (75%) rename azurerm/{data_source_vnet_test.go => data_source_virtual_network_test.go} (82%) create mode 100644 website/docs/d/virtual_network.html.markdown diff --git a/azurerm/data_source_vnet.go b/azurerm/data_source_virtual_network.go similarity index 75% rename from azurerm/data_source_vnet.go rename to azurerm/data_source_virtual_network.go index 2e94ce5a52c8..8fa68d5061b9 100644 --- a/azurerm/data_source_vnet.go +++ b/azurerm/data_source_virtual_network.go @@ -2,14 +2,13 @@ package azurerm import ( "fmt" - "net/http" "github.com/Azure/azure-sdk-for-go/arm/network" "github.com/hashicorp/terraform/helper/schema" - //"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) -func dataSourceArmVnet() *schema.Resource { +func dataSourceArmVirtualNetwork() *schema.Resource { return &schema.Resource{ Read: dataSourceArmVnetRead, Schema: map[string]*schema.Schema{ @@ -63,10 +62,10 @@ func dataSourceArmVnetRead(d *schema.ResourceData, meta interface{}) error { resp, err := vnetClient.Get(resGroup, name, "") if err != nil { - if resp.StatusCode == http.StatusNotFound { - d.SetId("") + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Error making Read request on Azure virtual network %s: %+v", name, err) } - return fmt.Errorf("Error making Read request on Azure virtual network %s: %s", name, err) + return err } d.SetId(*resp.ID) @@ -98,8 +97,10 @@ func dataSourceArmVnetRead(d *schema.ResourceData, meta interface{}) error { func flattenVnetAddressPrefixes(input *[]string) []interface{} { prefixes := make([]interface{}, 0) - for _, prefix := range *input { - prefixes = append(prefixes, prefix) + if myprefixes := input; myprefixes != nil { + for _, prefix := range *myprefixes { + prefixes = append(prefixes, prefix) + } } return prefixes } @@ -107,8 +108,10 @@ func flattenVnetAddressPrefixes(input *[]string) []interface{} { func flattenVnetSubnetsNames(input *[]network.Subnet) []interface{} { subnets := make([]interface{}, 0) - for _, subnet := range *input { - subnets = append(subnets, *subnet.Name) + if mysubnets := input; mysubnets != nil { + for _, subnet := range *mysubnets { + subnets = append(subnets, *subnet.Name) + } } return subnets } @@ -116,12 +119,14 @@ func flattenVnetSubnetsNames(input *[]network.Subnet) []interface{} { func flattenVnetPeerings(input *[]network.VirtualNetworkPeering) map[string]interface{} { output := make(map[string]interface{}, 0) - for _, vnetpeering := range *input { - key := *vnetpeering.Name - value := *vnetpeering.RemoteVirtualNetwork.ID + if peerings := input; peerings != nil { + for _, vnetpeering := range *peerings { + key := *vnetpeering.Name + value := *vnetpeering.RemoteVirtualNetwork.ID - output[key] = value + output[key] = value + } } return output } diff --git a/azurerm/data_source_vnet_test.go b/azurerm/data_source_virtual_network_test.go similarity index 82% rename from azurerm/data_source_vnet_test.go rename to azurerm/data_source_virtual_network_test.go index 2c568b93814e..e218579f87f3 100644 --- a/azurerm/data_source_vnet_test.go +++ b/azurerm/data_source_virtual_network_test.go @@ -8,12 +8,12 @@ import ( "github.com/hashicorp/terraform/helper/resource" ) -func TestAccDataSourceAzureRMVnet_basic(t *testing.T) { - dataSourceName := "data.azurerm_vnet.test" +func TestAccDataSourceArmVirtualNetwork_basic(t *testing.T) { + dataSourceName := "data.azurerm_virtual_network.test" ri := acctest.RandInt() name := fmt.Sprintf("acctestvnet-%d", ri) - config := testAccDataSourceAzureRMVnet_basic(ri, testLocation()) + config := testAccDataSourceArmVirtualNetwork_basic(ri, testLocation()) resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -32,12 +32,12 @@ func TestAccDataSourceAzureRMVnet_basic(t *testing.T) { }) } -func TestAccDataSourceAzureRMVnet_peering(t *testing.T) { - dataSourceName := "data.azurerm_vnet.test" +func TestAccDataSourceArmVirtualNetwork_peering(t *testing.T) { + dataSourceName := "data.azurerm_virtual_network.test" ri := acctest.RandInt() name_vnet_1 := fmt.Sprintf("acctestvnet-1-%d", ri) - config := testAccDataSourceAzureRMVnet_peering(ri, testLocation()) + config := testAccDataSourceArmVirtualNetwork_peering(ri, testLocation()) resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -55,7 +55,7 @@ func TestAccDataSourceAzureRMVnet_peering(t *testing.T) { }) } -func testAccDataSourceAzureRMVnet_basic(rInt int, location string) string { +func testAccDataSourceArmVirtualNetwork_basic(rInt int, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { name = "acctest%d-rg" @@ -75,7 +75,7 @@ func testAccDataSourceAzureRMVnet_basic(rInt int, location string) string { } } - data "azurerm_vnet" "test" { + data "azurerm_virtual_network" "test" { resource_group_name = "${azurerm_resource_group.test.name}" name = "${azurerm_virtual_network.test.name}" } @@ -83,7 +83,7 @@ func testAccDataSourceAzureRMVnet_basic(rInt int, location string) string { `, rInt, location, rInt) } -func testAccDataSourceAzureRMVnet_peering(rInt int, location string) string { +func testAccDataSourceArmVirtualNetwork_peering(rInt int, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { name = "acctest%d-rg" @@ -111,7 +111,7 @@ func testAccDataSourceAzureRMVnet_peering(rInt int, location string) string { remote_virtual_network_id = "${azurerm_virtual_network.test2.id}" } - data "azurerm_vnet" "test" { + data "azurerm_virtual_network" "test" { resource_group_name = "${azurerm_resource_group.test.name}" name = "${azurerm_virtual_network.test1.name}" } diff --git a/azurerm/provider.go b/azurerm/provider.go index 6ef55769d068..a81a0ae5b763 100644 --- a/azurerm/provider.go +++ b/azurerm/provider.go @@ -80,7 +80,7 @@ func Provider() terraform.ResourceProvider { "azurerm_snapshot": dataSourceArmSnapshot(), "azurerm_subnet": dataSourceArmSubnet(), "azurerm_subscription": dataSourceArmSubscription(), - "azurerm_vnet": dataSourceArmVnet(), + "azurerm_virtual_network": dataSourceArmVirtualNetwork(), }, ResourcesMap: map[string]*schema.Resource{ diff --git a/website/docs/d/virtual_network.html.markdown b/website/docs/d/virtual_network.html.markdown new file mode 100644 index 000000000000..72a0d147df31 --- /dev/null +++ b/website/docs/d/virtual_network.html.markdown @@ -0,0 +1,37 @@ +--- +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_virtual_network" +sidebar_current: "docs-azurerm-datasource-virtual-network" +description: |- + Get information about the specified Virtual Network. +--- + +# azurerm_virtual_network + +Use this data source to access the properties of an Azure Virtual Network. + +## Example Usage + +```hcl +data "azurerm_virtual_network" "test" { + name = "production" + resource_group_name = "networking" +} + +output "virtual_network_id" { + value = "${data.azurerm_virtual_network.test.id}" +} +``` + +## Argument Reference + +* `name` - (Required) Specifies the name of the Virtual Network. +* `resource_group_name` - (Required) Specifies the name of the resource group the Virtual Network is located in. + +## Attributes Reference + +* `id` - The ID of the virtual network. +* `address_spaces` - The list of address spaces used by the virtual network. +* `dns_servers` - The list of DNS servers used by the virtual network. +* `subnets` - The list of name of the subnets that are attached to this virtual network. +* `vnet_peerings` - A mapping of name - virtual network id of the virtual network peerings. From 8a6346ce5e122fdcd18ab4e47415d27d21912c86 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Tue, 14 Nov 2017 16:09:31 +0000 Subject: [PATCH 10/55] vmss: Support for updating the customData field Fixes #61 Fixes #490 --- ...port_arm_virtual_machine_scale_set_test.go | 43 ++++-- azurerm/provider.go | 5 + .../resource_arm_virtual_machine_scale_set.go | 45 +++--- ...urce_arm_virtual_machine_scale_set_test.go | 128 ++++++++++++++++++ .../r/virtual_machine_scale_set.html.markdown | 2 +- 5 files changed, 189 insertions(+), 34 deletions(-) diff --git a/azurerm/import_arm_virtual_machine_scale_set_test.go b/azurerm/import_arm_virtual_machine_scale_set_test.go index 71faf5758c8c..edc88a94b915 100644 --- a/azurerm/import_arm_virtual_machine_scale_set_test.go +++ b/azurerm/import_arm_virtual_machine_scale_set_test.go @@ -22,9 +22,10 @@ func TestAccAzureRMVirtualMachineScaleSet_importBasic(t *testing.T) { Config: config, }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"os_profile.0.admin_password"}, }, }, }) @@ -45,9 +46,10 @@ func TestAccAzureRMVirtualMachineScaleSet_importBasic_managedDisk(t *testing.T) Config: config, }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"os_profile.0.admin_password"}, }, }, }) @@ -71,6 +73,10 @@ func TestAccAzureRMVirtualMachineScaleSet_importLinux(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "os_profile.0.admin_password", + "os_profile.0.custom_data", + }, }, }, }) @@ -91,9 +97,10 @@ func TestAccAzureRMVirtualMachineScaleSet_importLoadBalancer(t *testing.T) { Config: config, }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"os_profile.0.admin_password"}, }, }, }) @@ -116,6 +123,12 @@ func TestAccAzureRMVirtualMachineScaleSet_importOverProvision(t *testing.T) { testCheckAzureRMVirtualMachineScaleSetOverprovision(resourceName), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"os_profile.0.admin_password"}, + }, }, }) } @@ -137,6 +150,12 @@ func TestAccAzureRMVirtualMachineScaleSet_importExtension(t *testing.T) { testCheckAzureRMVirtualMachineScaleSetExtension(resourceName), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"os_profile.0.admin_password"}, + }, }, }) } @@ -158,6 +177,12 @@ func TestAccAzureRMVirtualMachineScaleSet_importMultipleExtensions(t *testing.T) testCheckAzureRMVirtualMachineScaleSetExtension(resourceName), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"os_profile.0.admin_password"}, + }, }, }) } diff --git a/azurerm/provider.go b/azurerm/provider.go index eb73ea196a4e..5f7dfea31866 100644 --- a/azurerm/provider.go +++ b/azurerm/provider.go @@ -494,6 +494,11 @@ func ignoreCaseStateFunc(val interface{}) string { return strings.ToLower(val.(string)) } +func userDataDiffSuppressFunc(k, old, new string, d *schema.ResourceData) bool { + oldValue := userDataStateFunc(old) + return oldValue == new +} + func userDataStateFunc(v interface{}) string { switch s := v.(type) { case string: diff --git a/azurerm/resource_arm_virtual_machine_scale_set.go b/azurerm/resource_arm_virtual_machine_scale_set.go index daf71d642c19..e4c7a35a98c1 100644 --- a/azurerm/resource_arm_virtual_machine_scale_set.go +++ b/azurerm/resource_arm_virtual_machine_scale_set.go @@ -80,7 +80,7 @@ func resourceArmVirtualMachineScaleSet() *schema.Resource { }, "os_profile": { - Type: schema.TypeSet, + Type: schema.TypeList, Required: true, MaxItems: 1, Elem: &schema.Resource{ @@ -102,14 +102,13 @@ func resourceArmVirtualMachineScaleSet() *schema.Resource { }, "custom_data": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - StateFunc: userDataStateFunc, + Type: schema.TypeString, + Optional: true, + StateFunc: userDataStateFunc, + DiffSuppressFunc: userDataDiffSuppressFunc, }, }, }, - Set: resourceArmVirtualMachineScaleSetsOsProfileHash, }, "os_profile_secrets": { @@ -697,7 +696,7 @@ func resourceArmVirtualMachineScaleSetRead(d *schema.ResourceData, meta interfac d.Set("overprovision", properties.Overprovision) d.Set("single_placement_group", properties.SinglePlacementGroup) - osProfile, err := flattenAzureRMVirtualMachineScaleSetOsProfile(properties.VirtualMachineProfile.OsProfile) + osProfile, err := flattenAzureRMVirtualMachineScaleSetOsProfile(d, properties.VirtualMachineProfile.OsProfile) if err != nil { return fmt.Errorf("[DEBUG] Error flattening Virtual Machine Scale Set OS Profile. Error: %#v", err) } @@ -964,14 +963,27 @@ func flattenAzureRmVirtualMachineScaleSetNetworkProfile(profile *compute.Virtual return result } -func flattenAzureRMVirtualMachineScaleSetOsProfile(profile *compute.VirtualMachineScaleSetOSProfile) ([]interface{}, error) { +func flattenAzureRMVirtualMachineScaleSetOsProfile(d *schema.ResourceData, profile *compute.VirtualMachineScaleSetOSProfile) ([]interface{}, error) { result := make(map[string]interface{}) result["computer_name_prefix"] = *profile.ComputerNamePrefix result["admin_username"] = *profile.AdminUsername + // admin password isn't returned, so let's look it up + if v, ok := d.GetOk("os_profile.0.admin_password"); ok { + password := v.(string) + result["admin_password"] = password + } + if profile.CustomData != nil { result["custom_data"] = *profile.CustomData + } else { + // look up the current custom data + value := d.Get("os_profile.0.custom_data").(string) + if !isBase64Encoded(value) { + value = base64Encode(value) + } + result["custom_data"] = value } return []interface{}{result}, nil @@ -1146,21 +1158,6 @@ func resourceArmVirtualMachineScaleSetNetworkConfigurationHash(v interface{}) in return hashcode.String(buf.String()) } -func resourceArmVirtualMachineScaleSetsOsProfileHash(v interface{}) int { - var buf bytes.Buffer - m := v.(map[string]interface{}) - buf.WriteString(fmt.Sprintf("%s-", m["computer_name_prefix"].(string))) - buf.WriteString(fmt.Sprintf("%s-", m["admin_username"].(string))) - if m["custom_data"] != nil { - customData := m["custom_data"].(string) - if !isBase64Encoded(customData) { - customData = base64Encode(customData) - } - buf.WriteString(fmt.Sprintf("%s-", customData)) - } - return hashcode.String(buf.String()) -} - func resourceArmVirtualMachineScaleSetOsProfileLinuxConfigHash(v interface{}) int { var buf bytes.Buffer m := v.(map[string]interface{}) @@ -1325,7 +1322,7 @@ func expandAzureRmVirtualMachineScaleSetNetworkProfile(d *schema.ResourceData) * } func expandAzureRMVirtualMachineScaleSetsOsProfile(d *schema.ResourceData) (*compute.VirtualMachineScaleSetOSProfile, error) { - osProfileConfigs := d.Get("os_profile").(*schema.Set).List() + osProfileConfigs := d.Get("os_profile").([]interface{}) osProfileConfig := osProfileConfigs[0].(map[string]interface{}) namePrefix := osProfileConfig["computer_name_prefix"].(string) diff --git a/azurerm/resource_arm_virtual_machine_scale_set_test.go b/azurerm/resource_arm_virtual_machine_scale_set_test.go index 1d0761aff5f7..ffdea5290223 100644 --- a/azurerm/resource_arm_virtual_machine_scale_set_test.go +++ b/azurerm/resource_arm_virtual_machine_scale_set_test.go @@ -181,6 +181,34 @@ func TestAccAzureRMVirtualMachineScaleSet_linuxUpdated(t *testing.T) { }) } +func TestAccAzureRMVirtualMachineScaleSet_customDataUpdated(t *testing.T) { + resourceName := "azurerm_virtual_machine_scale_set.test" + ri := acctest.RandInt() + location := testLocation() + config := testAccAzureRMVirtualMachineScaleSet_linux(ri, location) + updatedConfig := testAccAzureRMVirtualMachineScaleSet_linuxCustomDataUpdated(ri, location) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMVirtualMachineScaleSetExists(resourceName), + ), + }, + { + Config: updatedConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMVirtualMachineScaleSetExists(resourceName), + ), + }, + }, + }) +} + func TestAccAzureRMVirtualMachineScaleSet_basicLinux_managedDisk(t *testing.T) { ri := acctest.RandInt() config := testAccAzureRMVirtualMachineScaleSet_basicLinux_managedDisk(ri, testLocation()) @@ -1659,6 +1687,106 @@ resource "azurerm_virtual_machine_scale_set" "test" { `, rInt, location, rInt, rInt, rInt, rInt, rInt, rInt, rInt, rInt) } +func testAccAzureRMVirtualMachineScaleSet_linuxCustomDataUpdated(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestrg-%d" + location = "%s" +} +resource "azurerm_virtual_network" "test" { + name = "acctestvn-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + address_space = ["10.0.0.0/8"] +} +resource "azurerm_subnet" "test" { + name = "acctestsn-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.1.0/24" +} +resource "azurerm_storage_account" "test" { + name = "accsa%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + account_tier = "Standard" + account_replication_type = "LRS" +} +resource "azurerm_storage_container" "test" { + name = "acctestsc-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + container_access_type = "private" +} +resource "azurerm_public_ip" "test" { + name = "acctestpip-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + public_ip_address_allocation = "static" +} +resource "azurerm_lb" "test" { + name = "acctestlb-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + frontend_ip_configuration { + name = "ip-address" + public_ip_address_id = "${azurerm_public_ip.test.id}" + } +} +resource "azurerm_lb_backend_address_pool" "test" { + name = "acctestbap-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + loadbalancer_id = "${azurerm_lb.test.id}" +} +resource "azurerm_virtual_machine_scale_set" "test" { + name = "acctestvmss-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + upgrade_policy_mode = "Automatic" + sku { + name = "Standard_A0" + tier = "Standard" + capacity = "1" + } + os_profile { + computer_name_prefix = "prefix" + admin_username = "ubuntu" + admin_password = "password" + custom_data = "updated custom data!" + } + os_profile_linux_config { + disable_password_authentication = true + ssh_keys { + path = "/home/ubuntu/.ssh/authorized_keys" + key_data = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDCsTcryUl51Q2VSEHqDRNmceUFo55ZtcIwxl2QITbN1RREti5ml/VTytC0yeBOvnZA4x4CFpdw/lCDPk0yrH9Ei5vVkXmOrExdTlT3qI7YaAzj1tUVlBd4S6LX1F7y6VLActvdHuDDuXZXzCDd/97420jrDfWZqJMlUK/EmCE5ParCeHIRIvmBxcEnGfFIsw8xQZl0HphxWOtJil8qsUWSdMyCiJYYQpMoMliO99X40AUc4/AlsyPyT5ddbKk08YrZ+rKDVHF7o29rh4vi5MmHkVgVQHKiKybWlHq+b71gIAUQk9wrJxD+dqt4igrmDSpIjfjwnd+l5UIn5fJSO5DYV4YT/4hwK7OKmuo7OFHD0WyY5YnkYEMtFgzemnRBdE8ulcT60DQpVgRMXFWHvhyCWy0L6sgj1QWDZlLpvsIvNfHsyhKFMG1frLnMt/nP0+YCcfg+v1JYeCKjeoJxB8DWcRBsjzItY0CGmzP8UYZiYKl/2u+2TgFS5r7NWH11bxoUzjKdaa1NLw+ieA8GlBFfCbfWe6YVB9ggUte4VtYFMZGxOjS2bAiYtfgTKFJv+XqORAwExG6+G2eDxIDyo80/OA9IG7Xv/jwQr7D6KDjDuULFcN/iTxuttoKrHeYz1hf5ZQlBdllwJHYx6fK2g8kha6r2JIQKocvsAXiiONqSfw== hello@world.com" + } + } + network_profile { + name = "TestNetworkProfile" + primary = true + ip_configuration { + name = "TestIPConfiguration" + subnet_id = "${azurerm_subnet.test.id}" + load_balancer_backend_address_pool_ids = ["${azurerm_lb_backend_address_pool.test.id}"] + } + } + storage_profile_os_disk { + name = "osDiskProfile" + caching = "ReadWrite" + create_option = "FromImage" + os_type = "linux" + vhd_containers = ["${azurerm_storage_account.test.primary_blob_endpoint}${azurerm_storage_container.test.name}"] + } + storage_profile_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } +} +`, rInt, location, rInt, rInt, rInt, rInt, rInt, rInt, rInt, rInt) +} + func testAccAzureRMVirtualMachineScaleSet_basicLinux_managedDisk(rInt int, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { diff --git a/website/docs/r/virtual_machine_scale_set.html.markdown b/website/docs/r/virtual_machine_scale_set.html.markdown index f92704ccaefb..5505451466eb 100644 --- a/website/docs/r/virtual_machine_scale_set.html.markdown +++ b/website/docs/r/virtual_machine_scale_set.html.markdown @@ -273,7 +273,7 @@ The following arguments are supported: * `computer_name_prefix` - (Required) Specifies the computer name prefix for all of the virtual machines in the scale set. Computer name prefixes must be 1 to 15 characters long. * `admin_username` - (Required) Specifies the administrator account name to use for all the instances of virtual machines in the scale set. * `admin_password` - (Required) Specifies the administrator password to use for all the instances of virtual machines in a scale set.. -* `custom_data` - (Optional) Specifies custom data to supply to the machine. On linux-based systems, this can be used as a cloud-init script. On other systems, this will be copied as a file on disk. Internally, Terraform will base64 encode this value before sending it to the API. The maximum length of the binary array is 65535 bytes. Changing this forces a new resource to be created. +* `custom_data` - (Optional) Specifies custom data to supply to the machine. On linux-based systems, this can be used as a cloud-init script. On other systems, this will be copied as a file on disk. Internally, Terraform will base64 encode this value before sending it to the API. The maximum length of the binary array is 65535 bytes. `os_profile_secrets` supports the following: From 5907ba4be1a066e7c3e15381670affd10474efb3 Mon Sep 17 00:00:00 2001 From: Tom Harvey Date: Thu, 16 Nov 2017 11:53:34 +0000 Subject: [PATCH 11/55] Updating to include #559 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 580d3948a8d4..b37c562c213d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ IMPROVEMENTS: * `azurerm_eventhub_namespace`: capacity can now be configured up to 20 [GH-556] +* `azurerm_virtual_machine_scale_set`: Support for updating the customData field [GH-559] ## 0.3.3 (November 14, 2017) From 8ae6169e4c6d0c01cd4508cbfbdef603258154c0 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Thu, 16 Nov 2017 12:21:32 +0000 Subject: [PATCH 12/55] Support for Auto Inflating ``` $ acctests azurerm TestAccAzureRMEventHubNamespace_maximumThroughputUnits === RUN TestAccAzureRMEventHubNamespace_maximumThroughputUnits --- PASS: TestAccAzureRMEventHubNamespace_maximumThroughputUnits (202.41s) PASS ok github.com/terraform-providers/terraform-provider-azurerm/azurerm 202.432s ``` --- azurerm/resource_arm_eventhub_namespace.go | 29 ++++++++++++++ .../resource_arm_eventhub_namespace_test.go | 40 +++++++++++++++++++ .../docs/r/eventhub_namespace.html.markdown | 16 ++++---- 3 files changed, 78 insertions(+), 7 deletions(-) diff --git a/azurerm/resource_arm_eventhub_namespace.go b/azurerm/resource_arm_eventhub_namespace.go index 76171f7ef3b8..3c6097400120 100644 --- a/azurerm/resource_arm_eventhub_namespace.go +++ b/azurerm/resource_arm_eventhub_namespace.go @@ -9,6 +9,7 @@ import ( "github.com/Azure/azure-sdk-for-go/arm/eventhub" "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -51,6 +52,19 @@ func resourceArmEventHubNamespace() *schema.Resource { ValidateFunc: validateEventHubNamespaceCapacity, }, + "auto_inflate_enabled": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + + "maximum_throughput_units": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + ValidateFunc: validation.IntBetween(1, 20), + }, + "default_primary_connection_string": { Type: schema.TypeString, Computed: true, @@ -88,6 +102,8 @@ func resourceArmEventHubNamespaceCreate(d *schema.ResourceData, meta interface{} capacity := int32(d.Get("capacity").(int)) tags := d.Get("tags").(map[string]interface{}) + autoInflateEnabled := d.Get("auto_inflate_enabled").(bool) + parameters := eventhub.EHNamespace{ Location: &location, Sku: &eventhub.Sku{ @@ -95,9 +111,17 @@ func resourceArmEventHubNamespaceCreate(d *schema.ResourceData, meta interface{} Tier: eventhub.SkuTier(sku), Capacity: &capacity, }, + EHNamespaceProperties: &eventhub.EHNamespaceProperties{ + IsAutoInflateEnabled: utils.Bool(autoInflateEnabled), + }, Tags: expandTags(tags), } + if v, ok := d.GetOk("maximum_throughput_units"); ok { + maximumThroughputUnits := v.(int) + parameters.EHNamespaceProperties.MaximumThroughputUnits = utils.Int32(int32(maximumThroughputUnits)) + } + _, error := namespaceClient.CreateOrUpdate(resGroup, name, parameters, make(chan struct{})) err := <-error if err != nil { @@ -153,6 +177,11 @@ func resourceArmEventHubNamespaceRead(d *schema.ResourceData, meta interface{}) d.Set("default_secondary_key", keys.SecondaryKey) } + if props := resp.EHNamespaceProperties; props != nil { + d.Set("auto_inflate_enabled", props.IsAutoInflateEnabled) + d.Set("maximum_throughput_units", int(*props.MaximumThroughputUnits)) + } + flattenAndSetTags(d, resp.Tags) return nil diff --git a/azurerm/resource_arm_eventhub_namespace_test.go b/azurerm/resource_arm_eventhub_namespace_test.go index 96641e7d1ef9..369ff20f7f89 100644 --- a/azurerm/resource_arm_eventhub_namespace_test.go +++ b/azurerm/resource_arm_eventhub_namespace_test.go @@ -147,6 +147,26 @@ func TestAccAzureRMEventHubNamespace_readDefaultKeys(t *testing.T) { }) } +func TestAccAzureRMEventHubNamespace_maximumThroughputUnits(t *testing.T) { + resourceName := "azurerm_eventhub_namespace.test" + ri := acctest.RandInt() + config := testAccAzureRMEventHubNamespace_maximumThroughputUnits(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMEventHubNamespaceDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMEventHubNamespaceExists(resourceName), + ), + }, + }, + }) +} + func TestAccAzureRMEventHubNamespace_NonStandardCasing(t *testing.T) { ri := acctest.RandInt() @@ -273,3 +293,23 @@ resource "azurerm_eventhub_namespace" "test" { } `, rInt, location, rInt) } + +func testAccAzureRMEventHubNamespace_maximumThroughputUnits(rInt int, location string) string { + return fmt.Sprintf(` + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_eventhub_namespace" "test" { + name = "acctesteventhubnamespace-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" + capacity = "2" + auto_inflate_enabled = true + maximum_throughput_units = 20 +} +`, rInt, location, rInt) +} diff --git a/website/docs/r/eventhub_namespace.html.markdown b/website/docs/r/eventhub_namespace.html.markdown index 0f6ddf9ce797..8d7b959eb3e1 100644 --- a/website/docs/r/eventhub_namespace.html.markdown +++ b/website/docs/r/eventhub_namespace.html.markdown @@ -20,7 +20,7 @@ resource "azurerm_resource_group" "test" { resource "azurerm_eventhub_namespace" "test" { name = "acceptanceTestEventHubNamespace" - location = "West US" + location = "${azurerm_resource_group.test.location}" resource_group_name = "${azurerm_resource_group.test.name}" sku = "Standard" capacity = 2 @@ -35,17 +35,19 @@ resource "azurerm_eventhub_namespace" "test" { The following arguments are supported: -* `name` - (Required) Specifies the name of the EventHub Namespace resource . Changing this forces a - new resource to be created. +* `name` - (Required) Specifies the name of the EventHub Namespace resource. Changing this forces a new resource to be created. -* `resource_group_name` - (Required) The name of the resource group in which to - create the namespace. Changing this forces a new resource to be created. +* `resource_group_name` - (Required) The name of the resource group in which to create the namespace. Changing this forces a new resource to be created. * `location` - (Required) Specifies the supported Azure location where the resource exists. Changing this forces a new resource to be created. -* `sku` - (Required) Defines which tier to use. Options are Basic or Standard. +* `sku` - (Required) Defines which tier to use. Valid options are `Basic` and `Standard`. -* `capacity` - (Optional) Specifies the capacity of a Standard namespace. Can be 1, 2 or 4 +* `capacity` - (Optional) Specifies the Capacity / Throughput Units for a `Standard` SKU namespace. Valid values range from 1 - 20. + +* `auto_inflate_enabled` - (Optional) Is Auto Inflate enabled for the EventHub Namespace? + +* `maximum_throughput_units` - (Optional) Specifies the maximum number of throughput units when Auto Inflate is Enabled. Valid values range from 1 - 20. * `tags` - (Optional) A mapping of tags to assign to the resource. From c8141c7c4289de10c85c77adc202da270438b106 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Fri, 17 Nov 2017 15:21:51 +0000 Subject: [PATCH 13/55] New Resource: `azurerm_network_watcher` ``` $ acctests azurerm TestAccAzureRMNetworkWatcher_ === RUN TestAccAzureRMNetworkWatcher_importBasic --- PASS: TestAccAzureRMNetworkWatcher_importBasic (75.79s) === RUN TestAccAzureRMNetworkWatcher_importComplete --- PASS: TestAccAzureRMNetworkWatcher_importComplete (69.85s) === RUN TestAccAzureRMNetworkWatcher_basic --- PASS: TestAccAzureRMNetworkWatcher_basic (69.62s) === RUN TestAccAzureRMNetworkWatcher_complete --- PASS: TestAccAzureRMNetworkWatcher_complete (72.16s) === RUN TestAccAzureRMNetworkWatcher_update --- PASS: TestAccAzureRMNetworkWatcher_update (81.75s) === RUN TestAccAzureRMNetworkWatcher_disappears --- PASS: TestAccAzureRMNetworkWatcher_disappears (94.38s) PASS ok ``` --- azurerm/config.go | 13 ++ azurerm/import_arm_network_watcher_test.go | 50 +++++ azurerm/provider.go | 1 + azurerm/resource_arm_network_watcher.go | 116 +++++++++++ azurerm/resource_arm_network_watcher_test.go | 204 +++++++++++++++++++ website/azurerm.erb | 4 + website/docs/r/network_watcher.html.markdown | 53 +++++ 7 files changed, 441 insertions(+) create mode 100644 azurerm/import_arm_network_watcher_test.go create mode 100644 azurerm/resource_arm_network_watcher.go create mode 100644 azurerm/resource_arm_network_watcher_test.go create mode 100644 website/docs/r/network_watcher.html.markdown diff --git a/azurerm/config.go b/azurerm/config.go index 159a54726a8d..d17e403f99d8 100644 --- a/azurerm/config.go +++ b/azurerm/config.go @@ -160,6 +160,9 @@ type ArmClient struct { sqlElasticPoolsClient sql.ElasticPoolsClient sqlFirewallRulesClient sql.FirewallRulesClient sqlServersClient sql.ServersClient + + // Networking + watcherClient network.WatchersClient } func withRequestLogging() autorest.SendDecorator { @@ -655,11 +658,21 @@ func (c *Config) getArmClient() (*ArmClient, error) { client.registerDatabases(endpoint, c.SubscriptionID, auth, sender) client.registerDisks(endpoint, c.SubscriptionID, auth, sender) client.registerKeyVaultClients(endpoint, c.SubscriptionID, auth, keyVaultAuth, sender) + client.registerNetworkingClients(endpoint, c.SubscriptionID, auth, sender) client.registerRedisClients(endpoint, c.SubscriptionID, auth, sender) return &client, nil } +func (c *ArmClient) registerNetworkingClients(endpoint, subscriptionId string, auth autorest.Authorizer, sender autorest.Sender) { + // TODO: move the other networking stuff in here, gradually + watchersClient := network.NewWatchersClientWithBaseURI(endpoint, subscriptionId) + setUserAgent(&watchersClient.Client) + watchersClient.Authorizer = auth + watchersClient.Sender = sender + c.watcherClient = watchersClient +} + func (c *ArmClient) registerAuthentication(endpoint, graphEndpoint, subscriptionId, tenantId string, auth, graphAuth autorest.Authorizer, sender autorest.Sender) { spc := graphrbac.NewServicePrincipalsClientWithBaseURI(graphEndpoint, tenantId) setUserAgent(&spc.Client) diff --git a/azurerm/import_arm_network_watcher_test.go b/azurerm/import_arm_network_watcher_test.go new file mode 100644 index 000000000000..32e4868d7e31 --- /dev/null +++ b/azurerm/import_arm_network_watcher_test.go @@ -0,0 +1,50 @@ +package azurerm + +import ( + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccAzureRMNetworkWatcher_importBasic(t *testing.T) { + rInt := acctest.RandInt() + resourceName := "azurerm_network_watcher.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMNetworkWatcherDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMNetworkWatcher_basic(rInt, testLocation()), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMNetworkWatcher_importComplete(t *testing.T) { + rInt := acctest.RandInt() + resourceName := "azurerm_network_watcher.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMNetworkWatcherDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMNetworkWatcher_complete(rInt, testLocation()), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} diff --git a/azurerm/provider.go b/azurerm/provider.go index 5f7dfea31866..c085d8709899 100644 --- a/azurerm/provider.go +++ b/azurerm/provider.go @@ -134,6 +134,7 @@ func Provider() terraform.ResourceProvider { "azurerm_network_interface": resourceArmNetworkInterface(), "azurerm_network_security_group": resourceArmNetworkSecurityGroup(), "azurerm_network_security_rule": resourceArmNetworkSecurityRule(), + "azurerm_network_watcher": resourceArmNetworkWatcher(), "azurerm_postgresql_configuration": resourceArmPostgreSQLConfiguration(), "azurerm_postgresql_database": resourceArmPostgreSQLDatabase(), "azurerm_postgresql_firewall_rule": resourceArmPostgreSQLFirewallRule(), diff --git a/azurerm/resource_arm_network_watcher.go b/azurerm/resource_arm_network_watcher.go new file mode 100644 index 000000000000..e81f49b1ae6f --- /dev/null +++ b/azurerm/resource_arm_network_watcher.go @@ -0,0 +1,116 @@ +package azurerm + +import ( + "fmt" + + "github.com/Azure/azure-sdk-for-go/arm/network" + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmNetworkWatcher() *schema.Resource { + return &schema.Resource{ + Create: resourceArmNetworkWatcherCreateUpdate, + Read: resourceArmNetworkWatcherRead, + Update: resourceArmNetworkWatcherCreateUpdate, + Delete: resourceArmNetworkWatcherDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "resource_group_name": resourceGroupNameSchema(), + + "location": locationSchema(), + + "tags": tagsSchema(), + }, + } +} + +func resourceArmNetworkWatcherCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).watcherClient + + name := d.Get("name").(string) + resourceGroup := d.Get("resource_group_name").(string) + location := d.Get("location").(string) + tags := d.Get("tags").(map[string]interface{}) + + watcher := network.Watcher{ + Location: utils.String(location), + Tags: expandTags(tags), + } + _, err := client.CreateOrUpdate(resourceGroup, name, watcher) + if err != nil { + return err + } + + read, err := client.Get(resourceGroup, name) + if err != nil { + return err + } + if read.ID == nil { + return fmt.Errorf("Cannot read Network Watcher %q (Resource Group %q) ID", name, resourceGroup) + } + + d.SetId(*read.ID) + + return resourceArmNetworkWatcherRead(d, meta) +} + +func resourceArmNetworkWatcherRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).watcherClient + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + resourceGroup := id.ResourceGroup + name := id.Path["networkWatchers"] + + resp, err := client.Get(resourceGroup, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("Error making Read request on Network Watcher %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + d.Set("name", resp.Name) + d.Set("resource_group_name", resourceGroup) + d.Set("location", azureRMNormalizeLocation(*resp.Location)) + + flattenAndSetTags(d, resp.Tags) + + return nil +} + +func resourceArmNetworkWatcherDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).watcherClient + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + resourceGroup := id.ResourceGroup + name := id.Path["networkWatchers"] + + deleteResp, deleteErr := client.Delete(resourceGroup, name, make(chan struct{})) + resp := <-deleteResp + err = <-deleteErr + + if err != nil { + if !utils.ResponseWasNotFound(resp) { + return fmt.Errorf("Error deleting Network Watcher %q (Resource Group %q): %+v", name, resourceGroup, err) + } + } + + return nil +} diff --git a/azurerm/resource_arm_network_watcher_test.go b/azurerm/resource_arm_network_watcher_test.go new file mode 100644 index 000000000000..73d963550f65 --- /dev/null +++ b/azurerm/resource_arm_network_watcher_test.go @@ -0,0 +1,204 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestAccAzureRMNetworkWatcher_basic(t *testing.T) { + resourceGroup := "azurerm_network_watcher.test" + rInt := acctest.RandInt() + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMNetworkWatcherDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMNetworkWatcher_basic(rInt, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMNetworkWatcherExists(resourceGroup), + ), + }, + }, + }) +} + +func TestAccAzureRMNetworkWatcher_complete(t *testing.T) { + resourceGroup := "azurerm_network_watcher.test" + rInt := acctest.RandInt() + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMNetworkWatcherDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMNetworkWatcher_complete(rInt, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMNetworkWatcherExists(resourceGroup), + ), + }, + }, + }) +} + +func TestAccAzureRMNetworkWatcher_update(t *testing.T) { + resourceGroup := "azurerm_network_watcher.test" + rInt := acctest.RandInt() + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMNetworkWatcherDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMNetworkWatcher_basic(rInt, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMNetworkWatcherExists(resourceGroup), + ), + }, + { + Config: testAccAzureRMNetworkWatcher_complete(rInt, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMNetworkWatcherExists(resourceGroup), + ), + }, + }, + }) +} + +func TestAccAzureRMNetworkWatcher_disappears(t *testing.T) { + resourceGroup := "azurerm_network_watcher.test" + rInt := acctest.RandInt() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMNetworkWatcherDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMNetworkWatcher_basic(rInt, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMNetworkWatcherExists(resourceGroup), + testCheckAzureRMNetworkWatcherDisappears(resourceGroup), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testCheckAzureRMNetworkWatcherExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + name := rs.Primary.Attributes["name"] + resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] + if !hasResourceGroup { + return fmt.Errorf("Bad: no resource group found in state for Network Watcher: %q", name) + } + + conn := testAccProvider.Meta().(*ArmClient).watcherClient + + resp, err := conn.Get(resourceGroup, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Network Watcher %q (resource group: %q) does not exist", name, resourceGroup) + } + return fmt.Errorf("Bad: Get on watcherClient: %+v", err) + } + + return nil + } +} + +func testCheckAzureRMNetworkWatcherDisappears(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %q", name) + } + + name := rs.Primary.Attributes["name"] + resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] + if !hasResourceGroup { + return fmt.Errorf("Bad: no resource group found in state for Network Watcher: %q", name) + } + + client := testAccProvider.Meta().(*ArmClient).watcherClient + deleteResp, deleteErr := client.Delete(resourceGroup, name, make(chan struct{})) + resp := <-deleteResp + err := <-deleteErr + if err != nil { + if !utils.ResponseWasNotFound(resp) { + return fmt.Errorf("Bad: Delete on watcherClient: %+v", err) + } + } + + return nil + } +} + +func testCheckAzureRMNetworkWatcherDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + + if rs.Type != "azurerm_network_watcher" { + continue + } + + name := rs.Primary.Attributes["name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + client := testAccProvider.Meta().(*ArmClient).watcherClient + resp, err := client.Get(resourceGroup, name) + + if err != nil { + if !utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Network Watcher still exists:\n%#v", resp) + } + } + } + + return nil +} + +func testAccAzureRMNetworkWatcher_basic(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_network_watcher" "test" { + name = "acctestnw-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} +`, rInt, location, rInt) +} + +func testAccAzureRMNetworkWatcher_complete(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_network_watcher" "test" { + name = "acctestnw-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + tags { + "Source" = "AccTests" + } +} +`, rInt, location, rInt) +} diff --git a/website/azurerm.erb b/website/azurerm.erb index c01de2a7752d..8436799e6489 100644 --- a/website/azurerm.erb +++ b/website/azurerm.erb @@ -439,6 +439,10 @@ azurerm_network_security_rule + > + azurerm_network_watcher + + > azurerm_public_ip diff --git a/website/docs/r/network_watcher.html.markdown b/website/docs/r/network_watcher.html.markdown new file mode 100644 index 000000000000..6bdf22bcbca5 --- /dev/null +++ b/website/docs/r/network_watcher.html.markdown @@ -0,0 +1,53 @@ +--- +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_network_watcher" +sidebar_current: "docs-azurerm-resource-network-watcher" +description: |- + Manages a Network Watcher. + +--- + +# azurerm_network_watcher + +Manages a Network Watcher. + +## Example Usage + +```hcl +resource "azurerm_resource_group" "test" { + name = "production-nwwatcher" + location = "West US" +} + +resource "azurerm_network_watcher" "test" { + name = "production-nwwatcher" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the Network Watcher. Changing this forces a new resource to be created. + +* `resource_group_name` - (Required) The name of the resource group in which to create the Network Watcher. Changing this forces a new resource to be created. + +* `location` - (Required) Specifies the supported Azure location where the resource exists. Changing this forces a new resource to be created. + +* `tags` - (Optional) A mapping of tags to assign to the resource. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The Network Watcher ID. + +## Import + +Network Watchers can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_network_watcher.watcher1 /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mygroup1/providers/Microsoft.Network/networkWatchers/watcher1 +``` From 2c3e9a23fd66abe33309b7a8ffbe833a42edec8c Mon Sep 17 00:00:00 2001 From: Tom Harvey Date: Fri, 17 Nov 2017 15:35:53 +0000 Subject: [PATCH 14/55] Updating to include #569 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b37c562c213d..6302ef72864a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ IMPROVEMENTS: * `azurerm_eventhub_namespace`: capacity can now be configured up to 20 [GH-556] +* `azurerm_eventhub_namespace` - support for AutoInflating/MaximumThroughputCapacity [GH-569] * `azurerm_virtual_machine_scale_set`: Support for updating the customData field [GH-559] ## 0.3.3 (November 14, 2017) From b8b4aa74cce8636cdee3e2a9d96b027be7b65262 Mon Sep 17 00:00:00 2001 From: Su Shi <1684739+metacpp@users.noreply.github.com> Date: Fri, 17 Nov 2017 14:26:54 -0800 Subject: [PATCH 15/55] Hotfix: upgrade packages under go-autorest to be v9.4.1. Intergrate with latest version of go-autorest to read access tokens through new way customized through environment variable. The old behavior on local shell will be kept. Notice: for Azure Cloud Shell user, please make sure that they're using latest patched provider. --- .../Azure/go-autorest/autorest/adal/config.go | 14 + .../go-autorest/autorest/adal/devicetoken.go | 14 + .../Azure/go-autorest/autorest/adal/msi.go | 14 + .../go-autorest/autorest/adal/msi_windows.go | 14 + .../go-autorest/autorest/adal/persist.go | 14 + .../Azure/go-autorest/autorest/adal/sender.go | 14 + .../Azure/go-autorest/autorest/adal/token.go | 14 + .../go-autorest/autorest/authorization.go | 14 + .../Azure/go-autorest/autorest/autorest.go | 17 ++ .../Azure/go-autorest/autorest/azure/async.go | 270 ++++++++++++++---- .../Azure/go-autorest/autorest/azure/azure.go | 22 +- .../go-autorest/autorest/azure/cli/profile.go | 14 + .../go-autorest/autorest/azure/cli/token.go | 27 +- .../autorest/azure/environments.go | 48 +++- .../Azure/go-autorest/autorest/azure/rp.go | 203 +++++++++++++ .../Azure/go-autorest/autorest/client.go | 30 +- .../Azure/go-autorest/autorest/date/date.go | 14 + .../Azure/go-autorest/autorest/date/time.go | 14 + .../go-autorest/autorest/date/timerfc1123.go | 14 + .../go-autorest/autorest/date/unixtime.go | 14 + .../go-autorest/autorest/date/utility.go | 14 + .../Azure/go-autorest/autorest/error.go | 14 + .../Azure/go-autorest/autorest/preparer.go | 14 + .../Azure/go-autorest/autorest/responder.go | 14 + .../go-autorest/autorest/retriablerequest.go | 14 + .../autorest/retriablerequest_1.7.go | 36 ++- .../autorest/retriablerequest_1.8.go | 42 +-- .../Azure/go-autorest/autorest/sender.go | 20 +- .../Azure/go-autorest/autorest/to/convert.go | 14 + .../Azure/go-autorest/autorest/utility.go | 26 ++ .../autorest/validation/validation.go | 21 +- .../Azure/go-autorest/autorest/version.go | 14 + vendor/vendor.json | 84 +++--- 33 files changed, 984 insertions(+), 142 deletions(-) create mode 100644 vendor/github.com/Azure/go-autorest/autorest/azure/rp.go diff --git a/vendor/github.com/Azure/go-autorest/autorest/adal/config.go b/vendor/github.com/Azure/go-autorest/autorest/adal/config.go index 12375e0e4bb8..49e9214d598a 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/adal/config.go +++ b/vendor/github.com/Azure/go-autorest/autorest/adal/config.go @@ -1,5 +1,19 @@ package adal +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import ( "fmt" "net/url" diff --git a/vendor/github.com/Azure/go-autorest/autorest/adal/devicetoken.go b/vendor/github.com/Azure/go-autorest/autorest/adal/devicetoken.go index 6c511f8c8779..b38f4c245897 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/adal/devicetoken.go +++ b/vendor/github.com/Azure/go-autorest/autorest/adal/devicetoken.go @@ -1,5 +1,19 @@ package adal +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* This file is largely based on rjw57/oauth2device's code, with the follow differences: * scope -> resource, and only allow a single one diff --git a/vendor/github.com/Azure/go-autorest/autorest/adal/msi.go b/vendor/github.com/Azure/go-autorest/autorest/adal/msi.go index e87911e835d9..5e02d52ac27c 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/adal/msi.go +++ b/vendor/github.com/Azure/go-autorest/autorest/adal/msi.go @@ -2,5 +2,19 @@ package adal +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + // msiPath is the path to the MSI Extension settings file (to discover the endpoint) var msiPath = "/var/lib/waagent/ManagedIdentity-Settings" diff --git a/vendor/github.com/Azure/go-autorest/autorest/adal/msi_windows.go b/vendor/github.com/Azure/go-autorest/autorest/adal/msi_windows.go index 80f8004327f1..261b568829c8 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/adal/msi_windows.go +++ b/vendor/github.com/Azure/go-autorest/autorest/adal/msi_windows.go @@ -2,6 +2,20 @@ package adal +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import ( "os" "strings" diff --git a/vendor/github.com/Azure/go-autorest/autorest/adal/persist.go b/vendor/github.com/Azure/go-autorest/autorest/adal/persist.go index 73711c6674ea..9e15f2751f27 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/adal/persist.go +++ b/vendor/github.com/Azure/go-autorest/autorest/adal/persist.go @@ -1,5 +1,19 @@ package adal +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import ( "encoding/json" "fmt" diff --git a/vendor/github.com/Azure/go-autorest/autorest/adal/sender.go b/vendor/github.com/Azure/go-autorest/autorest/adal/sender.go index 7928c971abbe..0e5ad14d3962 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/adal/sender.go +++ b/vendor/github.com/Azure/go-autorest/autorest/adal/sender.go @@ -1,5 +1,19 @@ package adal +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import ( "net/http" ) diff --git a/vendor/github.com/Azure/go-autorest/autorest/adal/token.go b/vendor/github.com/Azure/go-autorest/autorest/adal/token.go index 2ac8c3c22040..67dd97a18c18 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/adal/token.go +++ b/vendor/github.com/Azure/go-autorest/autorest/adal/token.go @@ -1,5 +1,19 @@ package adal +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import ( "crypto/rand" "crypto/rsa" diff --git a/vendor/github.com/Azure/go-autorest/autorest/authorization.go b/vendor/github.com/Azure/go-autorest/autorest/authorization.go index 314ed7876bcc..71e3ced2d6a6 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/authorization.go +++ b/vendor/github.com/Azure/go-autorest/autorest/authorization.go @@ -1,5 +1,19 @@ package autorest +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import ( "fmt" "net/http" diff --git a/vendor/github.com/Azure/go-autorest/autorest/autorest.go b/vendor/github.com/Azure/go-autorest/autorest/autorest.go index 51f1c4bbcac2..f86b66a410b1 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/autorest.go +++ b/vendor/github.com/Azure/go-autorest/autorest/autorest.go @@ -57,6 +57,20 @@ generated clients, see the Client described below. */ package autorest +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import ( "net/http" "time" @@ -73,6 +87,9 @@ const ( // ResponseHasStatusCode returns true if the status code in the HTTP Response is in the passed set // and false otherwise. func ResponseHasStatusCode(resp *http.Response, codes ...int) bool { + if resp == nil { + return false + } return containsInt(codes, resp.StatusCode) } diff --git a/vendor/github.com/Azure/go-autorest/autorest/azure/async.go b/vendor/github.com/Azure/go-autorest/autorest/azure/async.go index 332a8909d1a9..f90699f6cc38 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/azure/async.go +++ b/vendor/github.com/Azure/go-autorest/autorest/azure/async.go @@ -1,7 +1,23 @@ package azure +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import ( "bytes" + "context" + "encoding/json" "fmt" "io/ioutil" "net/http" @@ -23,6 +39,152 @@ const ( operationSucceeded string = "Succeeded" ) +var pollingCodes = [...]int{http.StatusAccepted, http.StatusCreated, http.StatusOK} + +// Future provides a mechanism to access the status and results of an asynchronous request. +// Since futures are stateful they should be passed by value to avoid race conditions. +type Future struct { + req *http.Request + resp *http.Response + ps pollingState +} + +// NewFuture returns a new Future object initialized with the specified request. +func NewFuture(req *http.Request) Future { + return Future{req: req} +} + +// Response returns the last HTTP response or nil if there isn't one. +func (f Future) Response() *http.Response { + return f.resp +} + +// Status returns the last status message of the operation. +func (f Future) Status() string { + if f.ps.State == "" { + return "Unknown" + } + return f.ps.State +} + +// PollingMethod returns the method used to monitor the status of the asynchronous operation. +func (f Future) PollingMethod() PollingMethodType { + return f.ps.PollingMethod +} + +// Done queries the service to see if the operation has completed. +func (f *Future) Done(sender autorest.Sender) (bool, error) { + // exit early if this future has terminated + if f.ps.hasTerminated() { + return true, f.errorInfo() + } + + resp, err := sender.Do(f.req) + f.resp = resp + if err != nil || !autorest.ResponseHasStatusCode(resp, pollingCodes[:]...) { + return false, err + } + + err = updatePollingState(resp, &f.ps) + if err != nil { + return false, err + } + + if f.ps.hasTerminated() { + return true, f.errorInfo() + } + + f.req, err = newPollingRequest(f.ps) + return false, err +} + +// GetPollingDelay returns a duration the application should wait before checking +// the status of the asynchronous request and true; this value is returned from +// the service via the Retry-After response header. If the header wasn't returned +// then the function returns the zero-value time.Duration and false. +func (f Future) GetPollingDelay() (time.Duration, bool) { + if f.resp == nil { + return 0, false + } + + retry := f.resp.Header.Get(autorest.HeaderRetryAfter) + if retry == "" { + return 0, false + } + + d, err := time.ParseDuration(retry + "s") + if err != nil { + panic(err) + } + + return d, true +} + +// WaitForCompletion will return when one of the following conditions is met: the long +// running operation has completed, the provided context is cancelled, or the client's +// polling duration has been exceeded. It will retry failed polling attempts based on +// the retry value defined in the client up to the maximum retry attempts. +func (f Future) WaitForCompletion(ctx context.Context, client autorest.Client) error { + ctx, cancel := context.WithTimeout(ctx, client.PollingDuration) + defer cancel() + + done, err := f.Done(client) + for attempts := 0; !done; done, err = f.Done(client) { + if attempts >= client.RetryAttempts { + return autorest.NewErrorWithError(err, "azure", "WaitForCompletion", f.resp, "the number of retries has been exceeded") + } + // we want delayAttempt to be zero in the non-error case so + // that DelayForBackoff doesn't perform exponential back-off + var delayAttempt int + var delay time.Duration + if err == nil { + // check for Retry-After delay, if not present use the client's polling delay + var ok bool + delay, ok = f.GetPollingDelay() + if !ok { + delay = client.PollingDelay + } + } else { + // there was an error polling for status so perform exponential + // back-off based on the number of attempts using the client's retry + // duration. update attempts after delayAttempt to avoid off-by-one. + delayAttempt = attempts + delay = client.RetryDuration + attempts++ + } + // wait until the delay elapses or the context is cancelled + delayElapsed := autorest.DelayForBackoff(delay, delayAttempt, ctx.Done()) + if !delayElapsed { + return autorest.NewErrorWithError(ctx.Err(), "azure", "WaitForCompletion", f.resp, "context has been cancelled") + } + } + return err +} + +// if the operation failed the polling state will contain +// error information and implements the error interface +func (f *Future) errorInfo() error { + if !f.ps.hasSucceeded() { + return f.ps + } + return nil +} + +// MarshalJSON implements the json.Marshaler interface. +func (f Future) MarshalJSON() ([]byte, error) { + return json.Marshal(&f.ps) +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (f *Future) UnmarshalJSON(data []byte) error { + err := json.Unmarshal(data, &f.ps) + if err != nil { + return err + } + f.req, err = newPollingRequest(f.ps) + return err +} + // DoPollForAsynchronous returns a SendDecorator that polls if the http.Response is for an Azure // long-running operation. It will delay between requests for the duration specified in the // RetryAfter header or, if the header is absent, the passed delay. Polling may be canceled by @@ -34,8 +196,7 @@ func DoPollForAsynchronous(delay time.Duration) autorest.SendDecorator { if err != nil { return resp, err } - pollingCodes := []int{http.StatusAccepted, http.StatusCreated, http.StatusOK} - if !autorest.ResponseHasStatusCode(resp, pollingCodes...) { + if !autorest.ResponseHasStatusCode(resp, pollingCodes[:]...) { return resp, nil } @@ -52,10 +213,11 @@ func DoPollForAsynchronous(delay time.Duration) autorest.SendDecorator { break } - r, err = newPollingRequest(resp, ps) + r, err = newPollingRequest(ps) if err != nil { return resp, err } + r.Cancel = resp.Request.Cancel delay = autorest.GetRetryAfter(resp, delay) resp, err = autorest.SendWithSender(s, r, @@ -72,20 +234,15 @@ func getAsyncOperation(resp *http.Response) string { } func hasSucceeded(state string) bool { - return state == operationSucceeded + return strings.EqualFold(state, operationSucceeded) } func hasTerminated(state string) bool { - switch state { - case operationCanceled, operationFailed, operationSucceeded: - return true - default: - return false - } + return strings.EqualFold(state, operationCanceled) || strings.EqualFold(state, operationFailed) || strings.EqualFold(state, operationSucceeded) } func hasFailed(state string) bool { - return state == operationFailed + return strings.EqualFold(state, operationFailed) } type provisioningTracker interface { @@ -146,36 +303,42 @@ func (ps provisioningStatus) hasProvisioningError() bool { return ps.ProvisioningError != ServiceError{} } -type pollingResponseFormat string +// PollingMethodType defines a type used for enumerating polling mechanisms. +type PollingMethodType string const ( - usesOperationResponse pollingResponseFormat = "OperationResponse" - usesProvisioningStatus pollingResponseFormat = "ProvisioningStatus" - formatIsUnknown pollingResponseFormat = "" + // PollingAsyncOperation indicates the polling method uses the Azure-AsyncOperation header. + PollingAsyncOperation PollingMethodType = "AsyncOperation" + + // PollingLocation indicates the polling method uses the Location header. + PollingLocation PollingMethodType = "Location" + + // PollingUnknown indicates an unknown polling method and is the default value. + PollingUnknown PollingMethodType = "" ) type pollingState struct { - responseFormat pollingResponseFormat - uri string - state string - code string - message string + PollingMethod PollingMethodType `json:"pollingMethod"` + URI string `json:"uri"` + State string `json:"state"` + Code string `json:"code"` + Message string `json:"message"` } func (ps pollingState) hasSucceeded() bool { - return hasSucceeded(ps.state) + return hasSucceeded(ps.State) } func (ps pollingState) hasTerminated() bool { - return hasTerminated(ps.state) + return hasTerminated(ps.State) } func (ps pollingState) hasFailed() bool { - return hasFailed(ps.state) + return hasFailed(ps.State) } func (ps pollingState) Error() string { - return fmt.Sprintf("Long running operation terminated with status '%s': Code=%q Message=%q", ps.state, ps.code, ps.message) + return fmt.Sprintf("Long running operation terminated with status '%s': Code=%q Message=%q", ps.State, ps.Code, ps.Message) } // updatePollingState maps the operation status -- retrieved from either a provisioningState @@ -190,7 +353,7 @@ func updatePollingState(resp *http.Response, ps *pollingState) error { // -- The first response will always be a provisioningStatus response; only the polling requests, // depending on the header returned, may be something otherwise. var pt provisioningTracker - if ps.responseFormat == usesOperationResponse { + if ps.PollingMethod == PollingAsyncOperation { pt = &operationResource{} } else { pt = &provisioningStatus{} @@ -198,30 +361,30 @@ func updatePollingState(resp *http.Response, ps *pollingState) error { // If this is the first request (that is, the polling response shape is unknown), determine how // to poll and what to expect - if ps.responseFormat == formatIsUnknown { + if ps.PollingMethod == PollingUnknown { req := resp.Request if req == nil { return autorest.NewError("azure", "updatePollingState", "Azure Polling Error - Original HTTP request is missing") } // Prefer the Azure-AsyncOperation header - ps.uri = getAsyncOperation(resp) - if ps.uri != "" { - ps.responseFormat = usesOperationResponse + ps.URI = getAsyncOperation(resp) + if ps.URI != "" { + ps.PollingMethod = PollingAsyncOperation } else { - ps.responseFormat = usesProvisioningStatus + ps.PollingMethod = PollingLocation } // Else, use the Location header - if ps.uri == "" { - ps.uri = autorest.GetLocation(resp) + if ps.URI == "" { + ps.URI = autorest.GetLocation(resp) } // Lastly, requests against an existing resource, use the last request URI - if ps.uri == "" { + if ps.URI == "" { m := strings.ToUpper(req.Method) if m == http.MethodPatch || m == http.MethodPut || m == http.MethodGet { - ps.uri = req.URL.String() + ps.URI = req.URL.String() } } } @@ -242,23 +405,23 @@ func updatePollingState(resp *http.Response, ps *pollingState) error { // -- Unknown states are per-service inprogress states // -- Otherwise, infer state from HTTP status code if pt.hasTerminated() { - ps.state = pt.state() + ps.State = pt.state() } else if pt.state() != "" { - ps.state = operationInProgress + ps.State = operationInProgress } else { switch resp.StatusCode { case http.StatusAccepted: - ps.state = operationInProgress + ps.State = operationInProgress case http.StatusNoContent, http.StatusCreated, http.StatusOK: - ps.state = operationSucceeded + ps.State = operationSucceeded default: - ps.state = operationFailed + ps.State = operationFailed } } - if ps.state == operationInProgress && ps.uri == "" { + if strings.EqualFold(ps.State, operationInProgress) && ps.URI == "" { return autorest.NewError("azure", "updatePollingState", "Azure Polling Error - Unable to obtain polling URI for %s %s", resp.Request.Method, resp.Request.URL) } @@ -267,35 +430,30 @@ func updatePollingState(resp *http.Response, ps *pollingState) error { // -- Response // -- Otherwise, Unknown if ps.hasFailed() { - if ps.responseFormat == usesOperationResponse { + if ps.PollingMethod == PollingAsyncOperation { or := pt.(*operationResource) - ps.code = or.OperationError.Code - ps.message = or.OperationError.Message + ps.Code = or.OperationError.Code + ps.Message = or.OperationError.Message } else { p := pt.(*provisioningStatus) if p.hasProvisioningError() { - ps.code = p.ProvisioningError.Code - ps.message = p.ProvisioningError.Message + ps.Code = p.ProvisioningError.Code + ps.Message = p.ProvisioningError.Message } else { - ps.code = "Unknown" - ps.message = "None" + ps.Code = "Unknown" + ps.Message = "None" } } } return nil } -func newPollingRequest(resp *http.Response, ps pollingState) (*http.Request, error) { - req := resp.Request - if req == nil { - return nil, autorest.NewError("azure", "newPollingRequest", "Azure Polling Error - Original HTTP request is missing") - } - - reqPoll, err := autorest.Prepare(&http.Request{Cancel: req.Cancel}, +func newPollingRequest(ps pollingState) (*http.Request, error) { + reqPoll, err := autorest.Prepare(&http.Request{}, autorest.AsGet(), - autorest.WithBaseURL(ps.uri)) + autorest.WithBaseURL(ps.URI)) if err != nil { - return nil, autorest.NewErrorWithError(err, "azure", "newPollingRequest", nil, "Failure creating poll request to %s", ps.uri) + return nil, autorest.NewErrorWithError(err, "azure", "newPollingRequest", nil, "Failure creating poll request to %s", ps.URI) } return reqPoll, nil diff --git a/vendor/github.com/Azure/go-autorest/autorest/azure/azure.go b/vendor/github.com/Azure/go-autorest/autorest/azure/azure.go index 3f4d13421aac..fa18356476b9 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/azure/azure.go +++ b/vendor/github.com/Azure/go-autorest/autorest/azure/azure.go @@ -5,6 +5,20 @@ See the included examples for more detail. */ package azure +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import ( "encoding/json" "fmt" @@ -165,7 +179,13 @@ func WithErrorUnlessStatusCode(codes ...int) autorest.RespondDecorator { if decodeErr != nil { return fmt.Errorf("autorest/azure: error response cannot be parsed: %q error: %v", b.String(), decodeErr) } else if e.ServiceError == nil { - e.ServiceError = &ServiceError{Code: "Unknown", Message: "Unknown service error"} + // Check if error is unwrapped ServiceError + if err := json.Unmarshal(b.Bytes(), &e.ServiceError); err != nil || e.ServiceError.Message == "" { + e.ServiceError = &ServiceError{ + Code: "Unknown", + Message: "Unknown service error", + } + } } e.RequestID = ExtractRequestID(resp) diff --git a/vendor/github.com/Azure/go-autorest/autorest/azure/cli/profile.go b/vendor/github.com/Azure/go-autorest/autorest/azure/cli/profile.go index b5b897c7df9e..3e226fe9b265 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/azure/cli/profile.go +++ b/vendor/github.com/Azure/go-autorest/autorest/azure/cli/profile.go @@ -1,5 +1,19 @@ package cli +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import ( "bytes" "encoding/json" diff --git a/vendor/github.com/Azure/go-autorest/autorest/azure/cli/token.go b/vendor/github.com/Azure/go-autorest/autorest/azure/cli/token.go index a1f3af1517f0..83b81c34bf8d 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/azure/cli/token.go +++ b/vendor/github.com/Azure/go-autorest/autorest/azure/cli/token.go @@ -1,5 +1,19 @@ package cli +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import ( "encoding/json" "fmt" @@ -48,8 +62,19 @@ func (t Token) ToADALToken() (converted adal.Token, err error) { } // AccessTokensPath returns the path where access tokens are stored from the Azure CLI +// TODO(#199): add unit test. func AccessTokensPath() (string, error) { - return homedir.Expand("~/.azure/accessTokens.json") + // Azure-CLI allows user to customize the path of access tokens thorugh environment variable. + var accessTokenPath = os.Getenv("AZURE_ACCESS_TOKEN_FILE") + var err error + + // Fallback logic to default path on non-cloud-shell environment. + // TODO(#200): remove the dependency on hard-coding path. + if accessTokenPath == "" { + accessTokenPath, err = homedir.Expand("~/.azure/accessTokens.json") + } + + return accessTokenPath, err } // ParseExpirationDate parses either a Azure CLI or CloudShell date into a time object diff --git a/vendor/github.com/Azure/go-autorest/autorest/azure/environments.go b/vendor/github.com/Azure/go-autorest/autorest/azure/environments.go index 1cf55651f2a4..efdab6a110cf 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/azure/environments.go +++ b/vendor/github.com/Azure/go-autorest/autorest/azure/environments.go @@ -1,10 +1,31 @@ package azure +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import ( + "encoding/json" "fmt" + "io/ioutil" + "os" "strings" ) +// EnvironmentFilepathName captures the name of the environment variable containing the path to the file +// to be used while populating the Azure Environment. +const EnvironmentFilepathName = "AZURE_ENVIRONMENT_FILEPATH" + var environments = map[string]Environment{ "AZURECHINACLOUD": ChinaCloud, "AZUREGERMANCLOUD": GermanCloud, @@ -119,12 +140,37 @@ var ( } ) -// EnvironmentFromName returns an Environment based on the common name specified +// EnvironmentFromName returns an Environment based on the common name specified. func EnvironmentFromName(name string) (Environment, error) { + // IMPORTANT + // As per @radhikagupta5: + // This is technical debt, fundamentally here because Kubernetes is not currently accepting + // contributions to the providers. Once that is an option, the provider should be updated to + // directly call `EnvironmentFromFile`. Until then, we rely on dispatching Azure Stack environment creation + // from this method based on the name that is provided to us. + if strings.EqualFold(name, "AZURESTACKCLOUD") { + return EnvironmentFromFile(os.Getenv(EnvironmentFilepathName)) + } + name = strings.ToUpper(name) env, ok := environments[name] if !ok { return env, fmt.Errorf("autorest/azure: There is no cloud environment matching the name %q", name) } + return env, nil } + +// EnvironmentFromFile loads an Environment from a configuration file available on disk. +// This function is particularly useful in the Hybrid Cloud model, where one must define their own +// endpoints. +func EnvironmentFromFile(location string) (unmarshaled Environment, err error) { + fileContents, err := ioutil.ReadFile(location) + if err != nil { + return + } + + err = json.Unmarshal(fileContents, &unmarshaled) + + return +} diff --git a/vendor/github.com/Azure/go-autorest/autorest/azure/rp.go b/vendor/github.com/Azure/go-autorest/autorest/azure/rp.go new file mode 100644 index 000000000000..66d1c8c2b3b9 --- /dev/null +++ b/vendor/github.com/Azure/go-autorest/autorest/azure/rp.go @@ -0,0 +1,203 @@ +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package azure + +import ( + "errors" + "fmt" + "net/http" + "net/url" + "strings" + "time" + + "github.com/Azure/go-autorest/autorest" +) + +// DoRetryWithRegistration tries to register the resource provider in case it is unregistered. +// It also handles request retries +func DoRetryWithRegistration(client autorest.Client) autorest.SendDecorator { + return func(s autorest.Sender) autorest.Sender { + return autorest.SenderFunc(func(r *http.Request) (resp *http.Response, err error) { + rr := autorest.NewRetriableRequest(r) + for currentAttempt := 0; currentAttempt < client.RetryAttempts; currentAttempt++ { + err = rr.Prepare() + if err != nil { + return resp, err + } + + resp, err = autorest.SendWithSender(s, rr.Request(), + autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...), + ) + if err != nil { + return resp, err + } + + if resp.StatusCode != http.StatusConflict { + return resp, err + } + var re RequestError + err = autorest.Respond( + resp, + autorest.ByUnmarshallingJSON(&re), + ) + if err != nil { + return resp, err + } + err = re + + if re.ServiceError != nil && re.ServiceError.Code == "MissingSubscriptionRegistration" { + regErr := register(client, r, re) + if regErr != nil { + return resp, fmt.Errorf("failed auto registering Resource Provider: %s. Original error: %s", regErr, err) + } + } + } + return resp, fmt.Errorf("failed request: %s", err) + }) + } +} + +func getProvider(re RequestError) (string, error) { + if re.ServiceError != nil { + if re.ServiceError.Details != nil && len(*re.ServiceError.Details) > 0 { + detail := (*re.ServiceError.Details)[0].(map[string]interface{}) + return detail["target"].(string), nil + } + } + return "", errors.New("provider was not found in the response") +} + +func register(client autorest.Client, originalReq *http.Request, re RequestError) error { + subID := getSubscription(originalReq.URL.Path) + if subID == "" { + return errors.New("missing parameter subscriptionID to register resource provider") + } + providerName, err := getProvider(re) + if err != nil { + return fmt.Errorf("missing parameter provider to register resource provider: %s", err) + } + newURL := url.URL{ + Scheme: originalReq.URL.Scheme, + Host: originalReq.URL.Host, + } + + // taken from the resources SDK + // with almost identical code, this sections are easier to mantain + // It is also not a good idea to import the SDK here + // https://github.com/Azure/azure-sdk-for-go/blob/9f366792afa3e0ddaecdc860e793ba9d75e76c27/arm/resources/resources/providers.go#L252 + pathParameters := map[string]interface{}{ + "resourceProviderNamespace": autorest.Encode("path", providerName), + "subscriptionId": autorest.Encode("path", subID), + } + + const APIVersion = "2016-09-01" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsPost(), + autorest.WithBaseURL(newURL.String()), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/providers/{resourceProviderNamespace}/register", pathParameters), + autorest.WithQueryParameters(queryParameters), + ) + + req, err := preparer.Prepare(&http.Request{}) + if err != nil { + return err + } + req.Cancel = originalReq.Cancel + + resp, err := autorest.SendWithSender(client, req, + autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...), + ) + if err != nil { + return err + } + + type Provider struct { + RegistrationState *string `json:"registrationState,omitempty"` + } + var provider Provider + + err = autorest.Respond( + resp, + WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&provider), + autorest.ByClosing(), + ) + if err != nil { + return err + } + + // poll for registered provisioning state + now := time.Now() + for err == nil && time.Since(now) < client.PollingDuration { + // taken from the resources SDK + // https://github.com/Azure/azure-sdk-for-go/blob/9f366792afa3e0ddaecdc860e793ba9d75e76c27/arm/resources/resources/providers.go#L45 + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(newURL.String()), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/providers/{resourceProviderNamespace}", pathParameters), + autorest.WithQueryParameters(queryParameters), + ) + req, err = preparer.Prepare(&http.Request{}) + if err != nil { + return err + } + req.Cancel = originalReq.Cancel + + resp, err := autorest.SendWithSender(client.Sender, req, + autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...), + ) + if err != nil { + return err + } + + err = autorest.Respond( + resp, + WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&provider), + autorest.ByClosing(), + ) + if err != nil { + return err + } + + if provider.RegistrationState != nil && + *provider.RegistrationState == "Registered" { + break + } + + delayed := autorest.DelayWithRetryAfter(resp, originalReq.Cancel) + if !delayed { + autorest.DelayForBackoff(client.PollingDelay, 0, originalReq.Cancel) + } + } + if !(time.Since(now) < client.PollingDuration) { + return errors.New("polling for resource provider registration has exceeded the polling duration") + } + return err +} + +func getSubscription(path string) string { + parts := strings.Split(path, "/") + for i, v := range parts { + if v == "subscriptions" && (i+1) < len(parts) { + return parts[i+1] + } + } + return "" +} diff --git a/vendor/github.com/Azure/go-autorest/autorest/client.go b/vendor/github.com/Azure/go-autorest/autorest/client.go index 5f1e72fbe4df..9eebd83bf84c 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/client.go +++ b/vendor/github.com/Azure/go-autorest/autorest/client.go @@ -1,5 +1,19 @@ package autorest +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import ( "bytes" "fmt" @@ -21,6 +35,9 @@ const ( // DefaultRetryAttempts is number of attempts for retry status codes (5xx). DefaultRetryAttempts = 3 + + // DefaultRetryDuration is the duration to wait between retries. + DefaultRetryDuration = 30 * time.Second ) var ( @@ -33,7 +50,8 @@ var ( Version(), ) - statusCodesForRetry = []int{ + // StatusCodesForRetry are a defined group of status code for which the client will retry + StatusCodesForRetry = []int{ http.StatusRequestTimeout, // 408 http.StatusTooManyRequests, // 429 http.StatusInternalServerError, // 500 @@ -157,9 +175,10 @@ func NewClientWithUserAgent(ua string) Client { PollingDelay: DefaultPollingDelay, PollingDuration: DefaultPollingDuration, RetryAttempts: DefaultRetryAttempts, - RetryDuration: 30 * time.Second, + RetryDuration: DefaultRetryDuration, UserAgent: defaultUserAgent, } + c.Sender = c.sender() c.AddToUserAgent(ua) return c } @@ -187,10 +206,9 @@ func (c Client) Do(r *http.Request) (*http.Response, error) { if err != nil { return nil, NewErrorWithError(err, "autorest/Client", "Do", nil, "Preparing request failed") } - resp, err := SendWithSender(c.sender(), r, - DoRetryForStatusCodes(c.RetryAttempts, c.RetryDuration, statusCodesForRetry...)) - Respond(resp, - c.ByInspecting()) + + resp, err := SendWithSender(c.sender(), r) + Respond(resp, c.ByInspecting()) return resp, err } diff --git a/vendor/github.com/Azure/go-autorest/autorest/date/date.go b/vendor/github.com/Azure/go-autorest/autorest/date/date.go index 80ca60e9b08a..c4571065685a 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/date/date.go +++ b/vendor/github.com/Azure/go-autorest/autorest/date/date.go @@ -5,6 +5,20 @@ time.Time types. And both convert to time.Time through a ToTime method. */ package date +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import ( "fmt" "time" diff --git a/vendor/github.com/Azure/go-autorest/autorest/date/time.go b/vendor/github.com/Azure/go-autorest/autorest/date/time.go index c1af6296348d..b453fad0491e 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/date/time.go +++ b/vendor/github.com/Azure/go-autorest/autorest/date/time.go @@ -1,5 +1,19 @@ package date +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import ( "regexp" "time" diff --git a/vendor/github.com/Azure/go-autorest/autorest/date/timerfc1123.go b/vendor/github.com/Azure/go-autorest/autorest/date/timerfc1123.go index 11995fb9f2c5..48fb39ba9b9a 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/date/timerfc1123.go +++ b/vendor/github.com/Azure/go-autorest/autorest/date/timerfc1123.go @@ -1,5 +1,19 @@ package date +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import ( "errors" "time" diff --git a/vendor/github.com/Azure/go-autorest/autorest/date/unixtime.go b/vendor/github.com/Azure/go-autorest/autorest/date/unixtime.go index e085c77eea50..7073959b2a9c 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/date/unixtime.go +++ b/vendor/github.com/Azure/go-autorest/autorest/date/unixtime.go @@ -1,5 +1,19 @@ package date +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import ( "bytes" "encoding/binary" diff --git a/vendor/github.com/Azure/go-autorest/autorest/date/utility.go b/vendor/github.com/Azure/go-autorest/autorest/date/utility.go index 207b1a240a3a..12addf0ebb4b 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/date/utility.go +++ b/vendor/github.com/Azure/go-autorest/autorest/date/utility.go @@ -1,5 +1,19 @@ package date +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import ( "strings" "time" diff --git a/vendor/github.com/Azure/go-autorest/autorest/error.go b/vendor/github.com/Azure/go-autorest/autorest/error.go index aaef2ac8ec04..f724f33327ed 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/error.go +++ b/vendor/github.com/Azure/go-autorest/autorest/error.go @@ -1,5 +1,19 @@ package autorest +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import ( "fmt" "net/http" diff --git a/vendor/github.com/Azure/go-autorest/autorest/preparer.go b/vendor/github.com/Azure/go-autorest/autorest/preparer.go index afd114821bc7..2290c4010032 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/preparer.go +++ b/vendor/github.com/Azure/go-autorest/autorest/preparer.go @@ -1,5 +1,19 @@ package autorest +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import ( "bytes" "encoding/json" diff --git a/vendor/github.com/Azure/go-autorest/autorest/responder.go b/vendor/github.com/Azure/go-autorest/autorest/responder.go index 87f71e5854b5..a908a0adb708 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/responder.go +++ b/vendor/github.com/Azure/go-autorest/autorest/responder.go @@ -1,5 +1,19 @@ package autorest +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import ( "bytes" "encoding/json" diff --git a/vendor/github.com/Azure/go-autorest/autorest/retriablerequest.go b/vendor/github.com/Azure/go-autorest/autorest/retriablerequest.go index 0ab1eb3003c9..fa11dbed79b1 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/retriablerequest.go +++ b/vendor/github.com/Azure/go-autorest/autorest/retriablerequest.go @@ -1,5 +1,19 @@ package autorest +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import ( "bytes" "io" diff --git a/vendor/github.com/Azure/go-autorest/autorest/retriablerequest_1.7.go b/vendor/github.com/Azure/go-autorest/autorest/retriablerequest_1.7.go index e28eb2cbdbf9..7143cc61b58b 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/retriablerequest_1.7.go +++ b/vendor/github.com/Azure/go-autorest/autorest/retriablerequest_1.7.go @@ -1,17 +1,31 @@ // +build !go1.8 +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package autorest import ( "bytes" + "io/ioutil" "net/http" ) // RetriableRequest provides facilities for retrying an HTTP request. type RetriableRequest struct { - req *http.Request - br *bytes.Reader - reset bool + req *http.Request + br *bytes.Reader } // Prepare signals that the request is about to be sent. @@ -19,21 +33,17 @@ func (rr *RetriableRequest) Prepare() (err error) { // preserve the request body; this is to support retry logic as // the underlying transport will always close the reqeust body if rr.req.Body != nil { - if rr.reset { - if rr.br != nil { - _, err = rr.br.Seek(0, 0 /*io.SeekStart*/) - } - rr.reset = false - if err != nil { - return err - } + if rr.br != nil { + _, err = rr.br.Seek(0, 0 /*io.SeekStart*/) + rr.req.Body = ioutil.NopCloser(rr.br) + } + if err != nil { + return err } if rr.br == nil { // fall back to making a copy (only do this once) err = rr.prepareFromByteReader() } - // indicates that the request body needs to be reset - rr.reset = true } return err } diff --git a/vendor/github.com/Azure/go-autorest/autorest/retriablerequest_1.8.go b/vendor/github.com/Azure/go-autorest/autorest/retriablerequest_1.8.go index 8c1d1aec8d94..ae15c6bf9629 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/retriablerequest_1.8.go +++ b/vendor/github.com/Azure/go-autorest/autorest/retriablerequest_1.8.go @@ -1,19 +1,33 @@ // +build go1.8 +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package autorest import ( "bytes" "io" + "io/ioutil" "net/http" ) // RetriableRequest provides facilities for retrying an HTTP request. type RetriableRequest struct { - req *http.Request - rc io.ReadCloser - br *bytes.Reader - reset bool + req *http.Request + rc io.ReadCloser + br *bytes.Reader } // Prepare signals that the request is about to be sent. @@ -21,16 +35,14 @@ func (rr *RetriableRequest) Prepare() (err error) { // preserve the request body; this is to support retry logic as // the underlying transport will always close the reqeust body if rr.req.Body != nil { - if rr.reset { - if rr.rc != nil { - rr.req.Body = rr.rc - } else if rr.br != nil { - _, err = rr.br.Seek(0, io.SeekStart) - } - rr.reset = false - if err != nil { - return err - } + if rr.rc != nil { + rr.req.Body = rr.rc + } else if rr.br != nil { + _, err = rr.br.Seek(0, io.SeekStart) + rr.req.Body = ioutil.NopCloser(rr.br) + } + if err != nil { + return err } if rr.req.GetBody != nil { // this will allow us to preserve the body without having to @@ -43,8 +55,6 @@ func (rr *RetriableRequest) Prepare() (err error) { // fall back to making a copy (only do this once) err = rr.prepareFromByteReader() } - // indicates that the request body needs to be reset - rr.reset = true } return err } diff --git a/vendor/github.com/Azure/go-autorest/autorest/sender.go b/vendor/github.com/Azure/go-autorest/autorest/sender.go index 94b02984792e..e1ec49573ff8 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/sender.go +++ b/vendor/github.com/Azure/go-autorest/autorest/sender.go @@ -1,5 +1,19 @@ package autorest +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import ( "fmt" "log" @@ -207,7 +221,8 @@ func DoRetryForStatusCodes(attempts int, backoff time.Duration, codes ...int) Se return resp, err } resp, err = s.Do(rr.Request()) - if err != nil || !ResponseHasStatusCode(resp, codes...) { + // we want to retry if err is not nil (e.g. transient network failure) + if err == nil && !ResponseHasStatusCode(resp, codes...) { return resp, err } delayed := DelayWithRetryAfter(resp, r.Cancel) @@ -223,6 +238,9 @@ func DoRetryForStatusCodes(attempts int, backoff time.Duration, codes ...int) Se // DelayWithRetryAfter invokes time.After for the duration specified in the "Retry-After" header in // responses with status code 429 func DelayWithRetryAfter(resp *http.Response, cancel <-chan struct{}) bool { + if resp == nil { + return false + } retryAfter, _ := strconv.Atoi(resp.Header.Get("Retry-After")) if resp.StatusCode == http.StatusTooManyRequests && retryAfter > 0 { select { diff --git a/vendor/github.com/Azure/go-autorest/autorest/to/convert.go b/vendor/github.com/Azure/go-autorest/autorest/to/convert.go index 7b180b866b90..fdda2ce1aa8e 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/to/convert.go +++ b/vendor/github.com/Azure/go-autorest/autorest/to/convert.go @@ -3,6 +3,20 @@ Package to provides helpers to ease working with pointer values of marshalled st */ package to +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + // String returns a string value for the passed string pointer. It returns the empty string if the // pointer is nil. func String(s *string) string { diff --git a/vendor/github.com/Azure/go-autorest/autorest/utility.go b/vendor/github.com/Azure/go-autorest/autorest/utility.go index 78067148b28d..1ef4575fa0bf 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/utility.go +++ b/vendor/github.com/Azure/go-autorest/autorest/utility.go @@ -1,11 +1,26 @@ package autorest +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import ( "bytes" "encoding/json" "encoding/xml" "fmt" "io" + "net/http" "net/url" "reflect" "sort" @@ -176,3 +191,14 @@ func createQuery(v url.Values) string { } return buf.String() } + +// ChangeToGet turns the specified http.Request into a GET (it assumes it wasn't). +// This is mainly useful for long-running operations that use the Azure-AsyncOperation +// header, so we change the initial PUT into a GET to retrieve the final result. +func ChangeToGet(req *http.Request) *http.Request { + req.Method = "GET" + req.Body = nil + req.ContentLength = 0 + req.Header.Del("Content-Length") + return req +} diff --git a/vendor/github.com/Azure/go-autorest/autorest/validation/validation.go b/vendor/github.com/Azure/go-autorest/autorest/validation/validation.go index 38f0074d0abc..3fe62c93056d 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/validation/validation.go +++ b/vendor/github.com/Azure/go-autorest/autorest/validation/validation.go @@ -3,6 +3,20 @@ Package validation provides methods for validating parameter value using reflect */ package validation +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import ( "fmt" "reflect" @@ -91,15 +105,12 @@ func validateStruct(x reflect.Value, v Constraint, name ...string) error { return createError(x, v, fmt.Sprintf("field %q doesn't exist", v.Target)) } - if err := Validate([]Validation{ + return Validate([]Validation{ { TargetValue: getInterfaceValue(f), Constraints: []Constraint{v}, }, - }); err != nil { - return err - } - return nil + }) } func validatePtr(x reflect.Value, v Constraint) error { diff --git a/vendor/github.com/Azure/go-autorest/autorest/version.go b/vendor/github.com/Azure/go-autorest/autorest/version.go index a222e8efaaf9..f588807dbb9c 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/version.go +++ b/vendor/github.com/Azure/go-autorest/autorest/version.go @@ -1,5 +1,19 @@ package autorest +// Copyright 2017 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import ( "bytes" "fmt" diff --git a/vendor/vendor.json b/vendor/vendor.json index c713a5e3d24f..280f34ba691b 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -251,67 +251,67 @@ "versionExact": "v11.1.0-beta" }, { - "checksumSHA1": "+4d+Y67AMKKuyR1EO33Zdt+RVx0=", - "comment": "v8.4.0", + "checksumSHA1": "tXSzwlsAPETo3wSlIX6o8GqGZLY=", + "comment": "v9.4.1", "path": "github.com/Azure/go-autorest/autorest", - "revision": "f6be1abbb5abd0517522f850dd785990d373da7e", - "revisionTime": "2017-09-13T23:19:17Z", - "version": "v8.4.0", - "versionExact": "v8.4.0" + "revision": "c67b24a8e30d876542a85022ebbdecf0e5a935e8", + "revisionTime": "2017-11-17T21:15:24Z", + "version": "v9.4.1", + "versionExact": "v9.4.1" }, { - "checksumSHA1": "7G4HgRaIT25bgz/hPtXG6Kv8Fho=", - "comment": "v8.4.0", + "checksumSHA1": "Ktj3H1WpOqxnC9kdAA+F7Ol7/RQ=", + "comment": "v9.4.1", "path": "github.com/Azure/go-autorest/autorest/adal", - "revision": "f6be1abbb5abd0517522f850dd785990d373da7e", - "revisionTime": "2017-09-13T23:19:17Z", - "version": "v8.4.0", - "versionExact": "v8.4.0" + "revision": "c67b24a8e30d876542a85022ebbdecf0e5a935e8", + "revisionTime": "2017-11-17T21:15:24Z", + "version": "v9.4.1", + "versionExact": "v9.4.1" }, { - "checksumSHA1": "2KdBFgT4qY+fMOkBTa5vA9V0AiM=", - "comment": "v8.4.0", + "checksumSHA1": "zXyLmDVpkYkIsL0yinNLoW82IZc=", + "comment": "v9.4.1", "path": "github.com/Azure/go-autorest/autorest/azure", - "revision": "f6be1abbb5abd0517522f850dd785990d373da7e", - "revisionTime": "2017-09-13T23:19:17Z", - "version": "v8.4.0", - "versionExact": "v8.4.0" + "revision": "c67b24a8e30d876542a85022ebbdecf0e5a935e8", + "revisionTime": "2017-11-17T21:15:24Z", + "version": "v9.4.1", + "versionExact": "v9.4.1" }, { - "checksumSHA1": "apxw17Dm1naEXMbVDCRnEDkQDQ8=", - "comment": "v8.4.0", + "checksumSHA1": "+nXRwVB/JVEGe+oLsFhCmSkKPuI=", + "comment": "v9.4.1", "path": "github.com/Azure/go-autorest/autorest/azure/cli", - "revision": "f6be1abbb5abd0517522f850dd785990d373da7e", - "revisionTime": "2017-09-13T23:19:17Z", - "version": "v8.4.0", - "versionExact": "v8.4.0" + "revision": "c67b24a8e30d876542a85022ebbdecf0e5a935e8", + "revisionTime": "2017-11-17T21:15:24Z", + "version": "v9.4.1", + "versionExact": "v9.4.1" }, { - "checksumSHA1": "LSF/pNrjhIxl6jiS6bKooBFCOxI=", - "comment": "v8.4.0", + "checksumSHA1": "9nXCi9qQsYjxCeajJKWttxgEt0I=", + "comment": "v9.4.1", "path": "github.com/Azure/go-autorest/autorest/date", - "revision": "f6be1abbb5abd0517522f850dd785990d373da7e", - "revisionTime": "2017-09-13T23:19:17Z", - "version": "v8.4.0", - "versionExact": "v8.4.0" + "revision": "c67b24a8e30d876542a85022ebbdecf0e5a935e8", + "revisionTime": "2017-11-17T21:15:24Z", + "version": "v9.4.1", + "versionExact": "v9.4.1" }, { - "checksumSHA1": "Ev8qCsbFjDlMlX0N2tYAhYQFpUc=", - "comment": "v8.4.0", + "checksumSHA1": "SbBb2GcJNm5GjuPKGL2777QywR4=", + "comment": "v9.4.1", "path": "github.com/Azure/go-autorest/autorest/to", - "revision": "f6be1abbb5abd0517522f850dd785990d373da7e", - "revisionTime": "2017-09-13T23:19:17Z", - "version": "v8.4.0", - "versionExact": "v8.4.0" + "revision": "c67b24a8e30d876542a85022ebbdecf0e5a935e8", + "revisionTime": "2017-11-17T21:15:24Z", + "version": "v9.4.1", + "versionExact": "v9.4.1" }, { - "checksumSHA1": "rGkTfIycpeix5TAbZS74ceGAPHI=", - "comment": "v8.4.0", + "checksumSHA1": "HfqZyKllcHQDvTwgCaYL1jUPmW0=", + "comment": "v9.4.1", "path": "github.com/Azure/go-autorest/autorest/validation", - "revision": "f6be1abbb5abd0517522f850dd785990d373da7e", - "revisionTime": "2017-09-13T23:19:17Z", - "version": "v8.4.0", - "versionExact": "v8.4.0" + "revision": "c67b24a8e30d876542a85022ebbdecf0e5a935e8", + "revisionTime": "2017-11-17T21:15:24Z", + "version": "v9.4.1", + "versionExact": "v9.4.1" }, { "checksumSHA1": "FIL83loX9V9APvGQIjJpbxq53F0=", From 5b802d493c6db5358d2a5f1a65caf9ae7bc79b31 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Sat, 18 Nov 2017 08:06:13 +0000 Subject: [PATCH 16/55] Vendoring the Locks SDK --- .../arm/resources/locks/client.go | 51 + .../arm/resources/locks/managementlocks.go | 1356 +++++++++++++++++ .../arm/resources/locks/models.go | 76 + .../arm/resources/locks/version.go | 28 + vendor/vendor.json | 8 + 5 files changed, 1519 insertions(+) create mode 100755 vendor/github.com/Azure/azure-sdk-for-go/arm/resources/locks/client.go create mode 100755 vendor/github.com/Azure/azure-sdk-for-go/arm/resources/locks/managementlocks.go create mode 100755 vendor/github.com/Azure/azure-sdk-for-go/arm/resources/locks/models.go create mode 100755 vendor/github.com/Azure/azure-sdk-for-go/arm/resources/locks/version.go diff --git a/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/locks/client.go b/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/locks/client.go new file mode 100755 index 000000000000..0b70ea01fc31 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/locks/client.go @@ -0,0 +1,51 @@ +// Package locks implements the Azure ARM Locks service API version 2016-09-01. +// +// Azure resources can be locked to prevent other users in your organization from deleting or modifying resources. +package locks + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/Azure/go-autorest/autorest" +) + +const ( + // DefaultBaseURI is the default URI used for the service Locks + DefaultBaseURI = "https://management.azure.com" +) + +// ManagementClient is the base client for Locks. +type ManagementClient struct { + autorest.Client + BaseURI string + SubscriptionID string +} + +// New creates an instance of the ManagementClient client. +func New(subscriptionID string) ManagementClient { + return NewWithBaseURI(DefaultBaseURI, subscriptionID) +} + +// NewWithBaseURI creates an instance of the ManagementClient client. +func NewWithBaseURI(baseURI string, subscriptionID string) ManagementClient { + return ManagementClient{ + Client: autorest.NewClientWithUserAgent(UserAgent()), + BaseURI: baseURI, + SubscriptionID: subscriptionID, + } +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/locks/managementlocks.go b/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/locks/managementlocks.go new file mode 100755 index 000000000000..6f4699dc5375 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/locks/managementlocks.go @@ -0,0 +1,1356 @@ +package locks + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/azure" + "github.com/Azure/go-autorest/autorest/validation" + "net/http" +) + +// ManagementLocksClient is the azure resources can be locked to prevent other users in your organization from deleting +// or modifying resources. +type ManagementLocksClient struct { + ManagementClient +} + +// NewManagementLocksClient creates an instance of the ManagementLocksClient client. +func NewManagementLocksClient(subscriptionID string) ManagementLocksClient { + return NewManagementLocksClientWithBaseURI(DefaultBaseURI, subscriptionID) +} + +// NewManagementLocksClientWithBaseURI creates an instance of the ManagementLocksClient client. +func NewManagementLocksClientWithBaseURI(baseURI string, subscriptionID string) ManagementLocksClient { + return ManagementLocksClient{NewWithBaseURI(baseURI, subscriptionID)} +} + +// CreateOrUpdateAtResourceGroupLevel when you apply a lock at a parent scope, all child resources inherit the same +// lock. To create management locks, you must have access to Microsoft.Authorization/* or +// Microsoft.Authorization/locks/* actions. Of the built-in roles, only Owner and User Access Administrator are granted +// those actions. +// +// resourceGroupName is the name of the resource group to lock. lockName is the lock name. The lock name can be a +// maximum of 260 characters. It cannot contain <, > %, &, :, \, ?, /, or any control characters. parameters is the +// management lock parameters. +func (client ManagementLocksClient) CreateOrUpdateAtResourceGroupLevel(resourceGroupName string, lockName string, parameters ManagementLockObject) (result ManagementLockObject, err error) { + if err := validation.Validate([]validation.Validation{ + {TargetValue: resourceGroupName, + Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}, + {TargetValue: parameters, + Constraints: []validation.Constraint{{Target: "parameters.ManagementLockProperties", Name: validation.Null, Rule: true, Chain: nil}}}}); err != nil { + return result, validation.NewErrorWithValidationError(err, "locks.ManagementLocksClient", "CreateOrUpdateAtResourceGroupLevel") + } + + req, err := client.CreateOrUpdateAtResourceGroupLevelPreparer(resourceGroupName, lockName, parameters) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "CreateOrUpdateAtResourceGroupLevel", nil, "Failure preparing request") + return + } + + resp, err := client.CreateOrUpdateAtResourceGroupLevelSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "CreateOrUpdateAtResourceGroupLevel", resp, "Failure sending request") + return + } + + result, err = client.CreateOrUpdateAtResourceGroupLevelResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "CreateOrUpdateAtResourceGroupLevel", resp, "Failure responding to request") + } + + return +} + +// CreateOrUpdateAtResourceGroupLevelPreparer prepares the CreateOrUpdateAtResourceGroupLevel request. +func (client ManagementLocksClient) CreateOrUpdateAtResourceGroupLevelPreparer(resourceGroupName string, lockName string, parameters ManagementLockObject) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "lockName": autorest.Encode("path", lockName), + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2016-09-01" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsJSON(), + autorest.AsPut(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Authorization/locks/{lockName}", pathParameters), + autorest.WithJSON(parameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// CreateOrUpdateAtResourceGroupLevelSender sends the CreateOrUpdateAtResourceGroupLevel request. The method will close the +// http.Response Body if it receives an error. +func (client ManagementLocksClient) CreateOrUpdateAtResourceGroupLevelSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// CreateOrUpdateAtResourceGroupLevelResponder handles the response to the CreateOrUpdateAtResourceGroupLevel request. The method always +// closes the http.Response Body. +func (client ManagementLocksClient) CreateOrUpdateAtResourceGroupLevelResponder(resp *http.Response) (result ManagementLockObject, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusCreated), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// CreateOrUpdateAtResourceLevel when you apply a lock at a parent scope, all child resources inherit the same lock. To +// create management locks, you must have access to Microsoft.Authorization/* or Microsoft.Authorization/locks/* +// actions. Of the built-in roles, only Owner and User Access Administrator are granted those actions. +// +// resourceGroupName is the name of the resource group containing the resource to lock. resourceProviderNamespace is +// the resource provider namespace of the resource to lock. parentResourcePath is the parent resource identity. +// resourceType is the resource type of the resource to lock. resourceName is the name of the resource to lock. +// lockName is the name of lock. The lock name can be a maximum of 260 characters. It cannot contain <, > %, &, :, \, +// ?, /, or any control characters. parameters is parameters for creating or updating a management lock. +func (client ManagementLocksClient) CreateOrUpdateAtResourceLevel(resourceGroupName string, resourceProviderNamespace string, parentResourcePath string, resourceType string, resourceName string, lockName string, parameters ManagementLockObject) (result ManagementLockObject, err error) { + if err := validation.Validate([]validation.Validation{ + {TargetValue: resourceGroupName, + Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}, + {TargetValue: parameters, + Constraints: []validation.Constraint{{Target: "parameters.ManagementLockProperties", Name: validation.Null, Rule: true, Chain: nil}}}}); err != nil { + return result, validation.NewErrorWithValidationError(err, "locks.ManagementLocksClient", "CreateOrUpdateAtResourceLevel") + } + + req, err := client.CreateOrUpdateAtResourceLevelPreparer(resourceGroupName, resourceProviderNamespace, parentResourcePath, resourceType, resourceName, lockName, parameters) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "CreateOrUpdateAtResourceLevel", nil, "Failure preparing request") + return + } + + resp, err := client.CreateOrUpdateAtResourceLevelSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "CreateOrUpdateAtResourceLevel", resp, "Failure sending request") + return + } + + result, err = client.CreateOrUpdateAtResourceLevelResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "CreateOrUpdateAtResourceLevel", resp, "Failure responding to request") + } + + return +} + +// CreateOrUpdateAtResourceLevelPreparer prepares the CreateOrUpdateAtResourceLevel request. +func (client ManagementLocksClient) CreateOrUpdateAtResourceLevelPreparer(resourceGroupName string, resourceProviderNamespace string, parentResourcePath string, resourceType string, resourceName string, lockName string, parameters ManagementLockObject) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "lockName": autorest.Encode("path", lockName), + "parentResourcePath": parentResourcePath, + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "resourceName": autorest.Encode("path", resourceName), + "resourceProviderNamespace": autorest.Encode("path", resourceProviderNamespace), + "resourceType": resourceType, + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2016-09-01" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsJSON(), + autorest.AsPut(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{parentResourcePath}/{resourceType}/{resourceName}/providers/Microsoft.Authorization/locks/{lockName}", pathParameters), + autorest.WithJSON(parameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// CreateOrUpdateAtResourceLevelSender sends the CreateOrUpdateAtResourceLevel request. The method will close the +// http.Response Body if it receives an error. +func (client ManagementLocksClient) CreateOrUpdateAtResourceLevelSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// CreateOrUpdateAtResourceLevelResponder handles the response to the CreateOrUpdateAtResourceLevel request. The method always +// closes the http.Response Body. +func (client ManagementLocksClient) CreateOrUpdateAtResourceLevelResponder(resp *http.Response) (result ManagementLockObject, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusCreated), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// CreateOrUpdateAtSubscriptionLevel when you apply a lock at a parent scope, all child resources inherit the same +// lock. To create management locks, you must have access to Microsoft.Authorization/* or +// Microsoft.Authorization/locks/* actions. Of the built-in roles, only Owner and User Access Administrator are granted +// those actions. +// +// lockName is the name of lock. The lock name can be a maximum of 260 characters. It cannot contain <, > %, &, :, \, +// ?, /, or any control characters. parameters is the management lock parameters. +func (client ManagementLocksClient) CreateOrUpdateAtSubscriptionLevel(lockName string, parameters ManagementLockObject) (result ManagementLockObject, err error) { + if err := validation.Validate([]validation.Validation{ + {TargetValue: parameters, + Constraints: []validation.Constraint{{Target: "parameters.ManagementLockProperties", Name: validation.Null, Rule: true, Chain: nil}}}}); err != nil { + return result, validation.NewErrorWithValidationError(err, "locks.ManagementLocksClient", "CreateOrUpdateAtSubscriptionLevel") + } + + req, err := client.CreateOrUpdateAtSubscriptionLevelPreparer(lockName, parameters) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "CreateOrUpdateAtSubscriptionLevel", nil, "Failure preparing request") + return + } + + resp, err := client.CreateOrUpdateAtSubscriptionLevelSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "CreateOrUpdateAtSubscriptionLevel", resp, "Failure sending request") + return + } + + result, err = client.CreateOrUpdateAtSubscriptionLevelResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "CreateOrUpdateAtSubscriptionLevel", resp, "Failure responding to request") + } + + return +} + +// CreateOrUpdateAtSubscriptionLevelPreparer prepares the CreateOrUpdateAtSubscriptionLevel request. +func (client ManagementLocksClient) CreateOrUpdateAtSubscriptionLevelPreparer(lockName string, parameters ManagementLockObject) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "lockName": autorest.Encode("path", lockName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2016-09-01" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsJSON(), + autorest.AsPut(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/providers/Microsoft.Authorization/locks/{lockName}", pathParameters), + autorest.WithJSON(parameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// CreateOrUpdateAtSubscriptionLevelSender sends the CreateOrUpdateAtSubscriptionLevel request. The method will close the +// http.Response Body if it receives an error. +func (client ManagementLocksClient) CreateOrUpdateAtSubscriptionLevelSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// CreateOrUpdateAtSubscriptionLevelResponder handles the response to the CreateOrUpdateAtSubscriptionLevel request. The method always +// closes the http.Response Body. +func (client ManagementLocksClient) CreateOrUpdateAtSubscriptionLevelResponder(resp *http.Response) (result ManagementLockObject, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusCreated, http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// CreateOrUpdateByScope create or update a management lock by scope. +// +// scope is the scope for the lock. When providing a scope for the assignment, use '/subscriptions/{subscriptionId}' +// for subscriptions, '/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}' for resource groups, and +// '/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{parentResourcePathIfPresent}/{resourceType}/{resourceName}' +// for resources. lockName is the name of lock. parameters is create or update management lock parameters. +func (client ManagementLocksClient) CreateOrUpdateByScope(scope string, lockName string, parameters ManagementLockObject) (result ManagementLockObject, err error) { + if err := validation.Validate([]validation.Validation{ + {TargetValue: parameters, + Constraints: []validation.Constraint{{Target: "parameters.ManagementLockProperties", Name: validation.Null, Rule: true, Chain: nil}}}}); err != nil { + return result, validation.NewErrorWithValidationError(err, "locks.ManagementLocksClient", "CreateOrUpdateByScope") + } + + req, err := client.CreateOrUpdateByScopePreparer(scope, lockName, parameters) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "CreateOrUpdateByScope", nil, "Failure preparing request") + return + } + + resp, err := client.CreateOrUpdateByScopeSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "CreateOrUpdateByScope", resp, "Failure sending request") + return + } + + result, err = client.CreateOrUpdateByScopeResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "CreateOrUpdateByScope", resp, "Failure responding to request") + } + + return +} + +// CreateOrUpdateByScopePreparer prepares the CreateOrUpdateByScope request. +func (client ManagementLocksClient) CreateOrUpdateByScopePreparer(scope string, lockName string, parameters ManagementLockObject) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "lockName": autorest.Encode("path", lockName), + "scope": autorest.Encode("path", scope), + } + + const APIVersion = "2016-09-01" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsJSON(), + autorest.AsPut(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/{scope}/providers/Microsoft.Authorization/locks/{lockName}", pathParameters), + autorest.WithJSON(parameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// CreateOrUpdateByScopeSender sends the CreateOrUpdateByScope request. The method will close the +// http.Response Body if it receives an error. +func (client ManagementLocksClient) CreateOrUpdateByScopeSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// CreateOrUpdateByScopeResponder handles the response to the CreateOrUpdateByScope request. The method always +// closes the http.Response Body. +func (client ManagementLocksClient) CreateOrUpdateByScopeResponder(resp *http.Response) (result ManagementLockObject, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusCreated), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// DeleteAtResourceGroupLevel to delete management locks, you must have access to Microsoft.Authorization/* or +// Microsoft.Authorization/locks/* actions. Of the built-in roles, only Owner and User Access Administrator are granted +// those actions. +// +// resourceGroupName is the name of the resource group containing the lock. lockName is the name of lock to delete. +func (client ManagementLocksClient) DeleteAtResourceGroupLevel(resourceGroupName string, lockName string) (result autorest.Response, err error) { + if err := validation.Validate([]validation.Validation{ + {TargetValue: resourceGroupName, + Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil { + return result, validation.NewErrorWithValidationError(err, "locks.ManagementLocksClient", "DeleteAtResourceGroupLevel") + } + + req, err := client.DeleteAtResourceGroupLevelPreparer(resourceGroupName, lockName) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "DeleteAtResourceGroupLevel", nil, "Failure preparing request") + return + } + + resp, err := client.DeleteAtResourceGroupLevelSender(req) + if err != nil { + result.Response = resp + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "DeleteAtResourceGroupLevel", resp, "Failure sending request") + return + } + + result, err = client.DeleteAtResourceGroupLevelResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "DeleteAtResourceGroupLevel", resp, "Failure responding to request") + } + + return +} + +// DeleteAtResourceGroupLevelPreparer prepares the DeleteAtResourceGroupLevel request. +func (client ManagementLocksClient) DeleteAtResourceGroupLevelPreparer(resourceGroupName string, lockName string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "lockName": autorest.Encode("path", lockName), + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2016-09-01" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsDelete(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Authorization/locks/{lockName}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// DeleteAtResourceGroupLevelSender sends the DeleteAtResourceGroupLevel request. The method will close the +// http.Response Body if it receives an error. +func (client ManagementLocksClient) DeleteAtResourceGroupLevelSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// DeleteAtResourceGroupLevelResponder handles the response to the DeleteAtResourceGroupLevel request. The method always +// closes the http.Response Body. +func (client ManagementLocksClient) DeleteAtResourceGroupLevelResponder(resp *http.Response) (result autorest.Response, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusNoContent, http.StatusOK), + autorest.ByClosing()) + result.Response = resp + return +} + +// DeleteAtResourceLevel to delete management locks, you must have access to Microsoft.Authorization/* or +// Microsoft.Authorization/locks/* actions. Of the built-in roles, only Owner and User Access Administrator are granted +// those actions. +// +// resourceGroupName is the name of the resource group containing the resource with the lock to delete. +// resourceProviderNamespace is the resource provider namespace of the resource with the lock to delete. +// parentResourcePath is the parent resource identity. resourceType is the resource type of the resource with the lock +// to delete. resourceName is the name of the resource with the lock to delete. lockName is the name of the lock to +// delete. +func (client ManagementLocksClient) DeleteAtResourceLevel(resourceGroupName string, resourceProviderNamespace string, parentResourcePath string, resourceType string, resourceName string, lockName string) (result autorest.Response, err error) { + if err := validation.Validate([]validation.Validation{ + {TargetValue: resourceGroupName, + Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil { + return result, validation.NewErrorWithValidationError(err, "locks.ManagementLocksClient", "DeleteAtResourceLevel") + } + + req, err := client.DeleteAtResourceLevelPreparer(resourceGroupName, resourceProviderNamespace, parentResourcePath, resourceType, resourceName, lockName) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "DeleteAtResourceLevel", nil, "Failure preparing request") + return + } + + resp, err := client.DeleteAtResourceLevelSender(req) + if err != nil { + result.Response = resp + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "DeleteAtResourceLevel", resp, "Failure sending request") + return + } + + result, err = client.DeleteAtResourceLevelResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "DeleteAtResourceLevel", resp, "Failure responding to request") + } + + return +} + +// DeleteAtResourceLevelPreparer prepares the DeleteAtResourceLevel request. +func (client ManagementLocksClient) DeleteAtResourceLevelPreparer(resourceGroupName string, resourceProviderNamespace string, parentResourcePath string, resourceType string, resourceName string, lockName string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "lockName": autorest.Encode("path", lockName), + "parentResourcePath": parentResourcePath, + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "resourceName": autorest.Encode("path", resourceName), + "resourceProviderNamespace": autorest.Encode("path", resourceProviderNamespace), + "resourceType": resourceType, + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2016-09-01" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsDelete(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{parentResourcePath}/{resourceType}/{resourceName}/providers/Microsoft.Authorization/locks/{lockName}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// DeleteAtResourceLevelSender sends the DeleteAtResourceLevel request. The method will close the +// http.Response Body if it receives an error. +func (client ManagementLocksClient) DeleteAtResourceLevelSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// DeleteAtResourceLevelResponder handles the response to the DeleteAtResourceLevel request. The method always +// closes the http.Response Body. +func (client ManagementLocksClient) DeleteAtResourceLevelResponder(resp *http.Response) (result autorest.Response, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusNoContent, http.StatusOK), + autorest.ByClosing()) + result.Response = resp + return +} + +// DeleteAtSubscriptionLevel to delete management locks, you must have access to Microsoft.Authorization/* or +// Microsoft.Authorization/locks/* actions. Of the built-in roles, only Owner and User Access Administrator are granted +// those actions. +// +// lockName is the name of lock to delete. +func (client ManagementLocksClient) DeleteAtSubscriptionLevel(lockName string) (result autorest.Response, err error) { + req, err := client.DeleteAtSubscriptionLevelPreparer(lockName) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "DeleteAtSubscriptionLevel", nil, "Failure preparing request") + return + } + + resp, err := client.DeleteAtSubscriptionLevelSender(req) + if err != nil { + result.Response = resp + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "DeleteAtSubscriptionLevel", resp, "Failure sending request") + return + } + + result, err = client.DeleteAtSubscriptionLevelResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "DeleteAtSubscriptionLevel", resp, "Failure responding to request") + } + + return +} + +// DeleteAtSubscriptionLevelPreparer prepares the DeleteAtSubscriptionLevel request. +func (client ManagementLocksClient) DeleteAtSubscriptionLevelPreparer(lockName string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "lockName": autorest.Encode("path", lockName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2016-09-01" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsDelete(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/providers/Microsoft.Authorization/locks/{lockName}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// DeleteAtSubscriptionLevelSender sends the DeleteAtSubscriptionLevel request. The method will close the +// http.Response Body if it receives an error. +func (client ManagementLocksClient) DeleteAtSubscriptionLevelSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// DeleteAtSubscriptionLevelResponder handles the response to the DeleteAtSubscriptionLevel request. The method always +// closes the http.Response Body. +func (client ManagementLocksClient) DeleteAtSubscriptionLevelResponder(resp *http.Response) (result autorest.Response, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusNoContent, http.StatusOK), + autorest.ByClosing()) + result.Response = resp + return +} + +// DeleteByScope delete a management lock by scope. +// +// scope is the scope for the lock. lockName is the name of lock. +func (client ManagementLocksClient) DeleteByScope(scope string, lockName string) (result autorest.Response, err error) { + req, err := client.DeleteByScopePreparer(scope, lockName) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "DeleteByScope", nil, "Failure preparing request") + return + } + + resp, err := client.DeleteByScopeSender(req) + if err != nil { + result.Response = resp + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "DeleteByScope", resp, "Failure sending request") + return + } + + result, err = client.DeleteByScopeResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "DeleteByScope", resp, "Failure responding to request") + } + + return +} + +// DeleteByScopePreparer prepares the DeleteByScope request. +func (client ManagementLocksClient) DeleteByScopePreparer(scope string, lockName string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "lockName": autorest.Encode("path", lockName), + "scope": autorest.Encode("path", scope), + } + + const APIVersion = "2016-09-01" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsDelete(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/{scope}/providers/Microsoft.Authorization/locks/{lockName}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// DeleteByScopeSender sends the DeleteByScope request. The method will close the +// http.Response Body if it receives an error. +func (client ManagementLocksClient) DeleteByScopeSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// DeleteByScopeResponder handles the response to the DeleteByScope request. The method always +// closes the http.Response Body. +func (client ManagementLocksClient) DeleteByScopeResponder(resp *http.Response) (result autorest.Response, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusNoContent, http.StatusOK), + autorest.ByClosing()) + result.Response = resp + return +} + +// GetAtResourceGroupLevel gets a management lock at the resource group level. +// +// resourceGroupName is the name of the locked resource group. lockName is the name of the lock to get. +func (client ManagementLocksClient) GetAtResourceGroupLevel(resourceGroupName string, lockName string) (result ManagementLockObject, err error) { + if err := validation.Validate([]validation.Validation{ + {TargetValue: resourceGroupName, + Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil { + return result, validation.NewErrorWithValidationError(err, "locks.ManagementLocksClient", "GetAtResourceGroupLevel") + } + + req, err := client.GetAtResourceGroupLevelPreparer(resourceGroupName, lockName) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "GetAtResourceGroupLevel", nil, "Failure preparing request") + return + } + + resp, err := client.GetAtResourceGroupLevelSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "GetAtResourceGroupLevel", resp, "Failure sending request") + return + } + + result, err = client.GetAtResourceGroupLevelResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "GetAtResourceGroupLevel", resp, "Failure responding to request") + } + + return +} + +// GetAtResourceGroupLevelPreparer prepares the GetAtResourceGroupLevel request. +func (client ManagementLocksClient) GetAtResourceGroupLevelPreparer(resourceGroupName string, lockName string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "lockName": autorest.Encode("path", lockName), + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2016-09-01" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Authorization/locks/{lockName}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// GetAtResourceGroupLevelSender sends the GetAtResourceGroupLevel request. The method will close the +// http.Response Body if it receives an error. +func (client ManagementLocksClient) GetAtResourceGroupLevelSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// GetAtResourceGroupLevelResponder handles the response to the GetAtResourceGroupLevel request. The method always +// closes the http.Response Body. +func (client ManagementLocksClient) GetAtResourceGroupLevelResponder(resp *http.Response) (result ManagementLockObject, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// GetAtResourceLevel get the management lock of a resource or any level below resource. +// +// resourceGroupName is the name of the resource group. resourceProviderNamespace is the namespace of the resource +// provider. parentResourcePath is an extra path parameter needed in some services, like SQL Databases. resourceType is +// the type of the resource. resourceName is the name of the resource. lockName is the name of lock. +func (client ManagementLocksClient) GetAtResourceLevel(resourceGroupName string, resourceProviderNamespace string, parentResourcePath string, resourceType string, resourceName string, lockName string) (result ManagementLockObject, err error) { + if err := validation.Validate([]validation.Validation{ + {TargetValue: resourceGroupName, + Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil { + return result, validation.NewErrorWithValidationError(err, "locks.ManagementLocksClient", "GetAtResourceLevel") + } + + req, err := client.GetAtResourceLevelPreparer(resourceGroupName, resourceProviderNamespace, parentResourcePath, resourceType, resourceName, lockName) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "GetAtResourceLevel", nil, "Failure preparing request") + return + } + + resp, err := client.GetAtResourceLevelSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "GetAtResourceLevel", resp, "Failure sending request") + return + } + + result, err = client.GetAtResourceLevelResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "GetAtResourceLevel", resp, "Failure responding to request") + } + + return +} + +// GetAtResourceLevelPreparer prepares the GetAtResourceLevel request. +func (client ManagementLocksClient) GetAtResourceLevelPreparer(resourceGroupName string, resourceProviderNamespace string, parentResourcePath string, resourceType string, resourceName string, lockName string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "lockName": autorest.Encode("path", lockName), + "parentResourcePath": parentResourcePath, + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "resourceName": autorest.Encode("path", resourceName), + "resourceProviderNamespace": autorest.Encode("path", resourceProviderNamespace), + "resourceType": resourceType, + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2016-09-01" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{parentResourcePath}/{resourceType}/{resourceName}/providers/Microsoft.Authorization/locks/{lockName}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// GetAtResourceLevelSender sends the GetAtResourceLevel request. The method will close the +// http.Response Body if it receives an error. +func (client ManagementLocksClient) GetAtResourceLevelSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// GetAtResourceLevelResponder handles the response to the GetAtResourceLevel request. The method always +// closes the http.Response Body. +func (client ManagementLocksClient) GetAtResourceLevelResponder(resp *http.Response) (result ManagementLockObject, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// GetAtSubscriptionLevel gets a management lock at the subscription level. +// +// lockName is the name of the lock to get. +func (client ManagementLocksClient) GetAtSubscriptionLevel(lockName string) (result ManagementLockObject, err error) { + req, err := client.GetAtSubscriptionLevelPreparer(lockName) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "GetAtSubscriptionLevel", nil, "Failure preparing request") + return + } + + resp, err := client.GetAtSubscriptionLevelSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "GetAtSubscriptionLevel", resp, "Failure sending request") + return + } + + result, err = client.GetAtSubscriptionLevelResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "GetAtSubscriptionLevel", resp, "Failure responding to request") + } + + return +} + +// GetAtSubscriptionLevelPreparer prepares the GetAtSubscriptionLevel request. +func (client ManagementLocksClient) GetAtSubscriptionLevelPreparer(lockName string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "lockName": autorest.Encode("path", lockName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2016-09-01" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/providers/Microsoft.Authorization/locks/{lockName}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// GetAtSubscriptionLevelSender sends the GetAtSubscriptionLevel request. The method will close the +// http.Response Body if it receives an error. +func (client ManagementLocksClient) GetAtSubscriptionLevelSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// GetAtSubscriptionLevelResponder handles the response to the GetAtSubscriptionLevel request. The method always +// closes the http.Response Body. +func (client ManagementLocksClient) GetAtSubscriptionLevelResponder(resp *http.Response) (result ManagementLockObject, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// GetByScope get a management lock by scope. +// +// scope is the scope for the lock. lockName is the name of lock. +func (client ManagementLocksClient) GetByScope(scope string, lockName string) (result ManagementLockObject, err error) { + req, err := client.GetByScopePreparer(scope, lockName) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "GetByScope", nil, "Failure preparing request") + return + } + + resp, err := client.GetByScopeSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "GetByScope", resp, "Failure sending request") + return + } + + result, err = client.GetByScopeResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "GetByScope", resp, "Failure responding to request") + } + + return +} + +// GetByScopePreparer prepares the GetByScope request. +func (client ManagementLocksClient) GetByScopePreparer(scope string, lockName string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "lockName": autorest.Encode("path", lockName), + "scope": autorest.Encode("path", scope), + } + + const APIVersion = "2016-09-01" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/{scope}/providers/Microsoft.Authorization/locks/{lockName}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// GetByScopeSender sends the GetByScope request. The method will close the +// http.Response Body if it receives an error. +func (client ManagementLocksClient) GetByScopeSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// GetByScopeResponder handles the response to the GetByScope request. The method always +// closes the http.Response Body. +func (client ManagementLocksClient) GetByScopeResponder(resp *http.Response) (result ManagementLockObject, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// ListAtResourceGroupLevel gets all the management locks for a resource group. +// +// resourceGroupName is the name of the resource group containing the locks to get. filter is the filter to apply on +// the operation. +func (client ManagementLocksClient) ListAtResourceGroupLevel(resourceGroupName string, filter string) (result ManagementLockListResult, err error) { + if err := validation.Validate([]validation.Validation{ + {TargetValue: resourceGroupName, + Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil { + return result, validation.NewErrorWithValidationError(err, "locks.ManagementLocksClient", "ListAtResourceGroupLevel") + } + + req, err := client.ListAtResourceGroupLevelPreparer(resourceGroupName, filter) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "ListAtResourceGroupLevel", nil, "Failure preparing request") + return + } + + resp, err := client.ListAtResourceGroupLevelSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "ListAtResourceGroupLevel", resp, "Failure sending request") + return + } + + result, err = client.ListAtResourceGroupLevelResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "ListAtResourceGroupLevel", resp, "Failure responding to request") + } + + return +} + +// ListAtResourceGroupLevelPreparer prepares the ListAtResourceGroupLevel request. +func (client ManagementLocksClient) ListAtResourceGroupLevelPreparer(resourceGroupName string, filter string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2016-09-01" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + if len(filter) > 0 { + queryParameters["$filter"] = autorest.Encode("query", filter) + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Authorization/locks", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// ListAtResourceGroupLevelSender sends the ListAtResourceGroupLevel request. The method will close the +// http.Response Body if it receives an error. +func (client ManagementLocksClient) ListAtResourceGroupLevelSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// ListAtResourceGroupLevelResponder handles the response to the ListAtResourceGroupLevel request. The method always +// closes the http.Response Body. +func (client ManagementLocksClient) ListAtResourceGroupLevelResponder(resp *http.Response) (result ManagementLockListResult, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// ListAtResourceGroupLevelNextResults retrieves the next set of results, if any. +func (client ManagementLocksClient) ListAtResourceGroupLevelNextResults(lastResults ManagementLockListResult) (result ManagementLockListResult, err error) { + req, err := lastResults.ManagementLockListResultPreparer() + if err != nil { + return result, autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "ListAtResourceGroupLevel", nil, "Failure preparing next results request") + } + if req == nil { + return + } + + resp, err := client.ListAtResourceGroupLevelSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + return result, autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "ListAtResourceGroupLevel", resp, "Failure sending next results request") + } + + result, err = client.ListAtResourceGroupLevelResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "ListAtResourceGroupLevel", resp, "Failure responding to next results request") + } + + return +} + +// ListAtResourceGroupLevelComplete gets all elements from the list without paging. +func (client ManagementLocksClient) ListAtResourceGroupLevelComplete(resourceGroupName string, filter string, cancel <-chan struct{}) (<-chan ManagementLockObject, <-chan error) { + resultChan := make(chan ManagementLockObject) + errChan := make(chan error, 1) + go func() { + defer func() { + close(resultChan) + close(errChan) + }() + list, err := client.ListAtResourceGroupLevel(resourceGroupName, filter) + if err != nil { + errChan <- err + return + } + if list.Value != nil { + for _, item := range *list.Value { + select { + case <-cancel: + return + case resultChan <- item: + // Intentionally left blank + } + } + } + for list.NextLink != nil { + list, err = client.ListAtResourceGroupLevelNextResults(list) + if err != nil { + errChan <- err + return + } + if list.Value != nil { + for _, item := range *list.Value { + select { + case <-cancel: + return + case resultChan <- item: + // Intentionally left blank + } + } + } + } + }() + return resultChan, errChan +} + +// ListAtResourceLevel gets all the management locks for a resource or any level below resource. +// +// resourceGroupName is the name of the resource group containing the locked resource. The name is case insensitive. +// resourceProviderNamespace is the namespace of the resource provider. parentResourcePath is the parent resource +// identity. resourceType is the resource type of the locked resource. resourceName is the name of the locked resource. +// filter is the filter to apply on the operation. +func (client ManagementLocksClient) ListAtResourceLevel(resourceGroupName string, resourceProviderNamespace string, parentResourcePath string, resourceType string, resourceName string, filter string) (result ManagementLockListResult, err error) { + if err := validation.Validate([]validation.Validation{ + {TargetValue: resourceGroupName, + Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}, + {Target: "resourceGroupName", Name: validation.Pattern, Rule: `^[-\w\._\(\)]+$`, Chain: nil}}}}); err != nil { + return result, validation.NewErrorWithValidationError(err, "locks.ManagementLocksClient", "ListAtResourceLevel") + } + + req, err := client.ListAtResourceLevelPreparer(resourceGroupName, resourceProviderNamespace, parentResourcePath, resourceType, resourceName, filter) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "ListAtResourceLevel", nil, "Failure preparing request") + return + } + + resp, err := client.ListAtResourceLevelSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "ListAtResourceLevel", resp, "Failure sending request") + return + } + + result, err = client.ListAtResourceLevelResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "ListAtResourceLevel", resp, "Failure responding to request") + } + + return +} + +// ListAtResourceLevelPreparer prepares the ListAtResourceLevel request. +func (client ManagementLocksClient) ListAtResourceLevelPreparer(resourceGroupName string, resourceProviderNamespace string, parentResourcePath string, resourceType string, resourceName string, filter string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "parentResourcePath": parentResourcePath, + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "resourceName": autorest.Encode("path", resourceName), + "resourceProviderNamespace": autorest.Encode("path", resourceProviderNamespace), + "resourceType": resourceType, + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2016-09-01" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + if len(filter) > 0 { + queryParameters["$filter"] = autorest.Encode("query", filter) + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{parentResourcePath}/{resourceType}/{resourceName}/providers/Microsoft.Authorization/locks", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// ListAtResourceLevelSender sends the ListAtResourceLevel request. The method will close the +// http.Response Body if it receives an error. +func (client ManagementLocksClient) ListAtResourceLevelSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// ListAtResourceLevelResponder handles the response to the ListAtResourceLevel request. The method always +// closes the http.Response Body. +func (client ManagementLocksClient) ListAtResourceLevelResponder(resp *http.Response) (result ManagementLockListResult, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// ListAtResourceLevelNextResults retrieves the next set of results, if any. +func (client ManagementLocksClient) ListAtResourceLevelNextResults(lastResults ManagementLockListResult) (result ManagementLockListResult, err error) { + req, err := lastResults.ManagementLockListResultPreparer() + if err != nil { + return result, autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "ListAtResourceLevel", nil, "Failure preparing next results request") + } + if req == nil { + return + } + + resp, err := client.ListAtResourceLevelSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + return result, autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "ListAtResourceLevel", resp, "Failure sending next results request") + } + + result, err = client.ListAtResourceLevelResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "ListAtResourceLevel", resp, "Failure responding to next results request") + } + + return +} + +// ListAtResourceLevelComplete gets all elements from the list without paging. +func (client ManagementLocksClient) ListAtResourceLevelComplete(resourceGroupName string, resourceProviderNamespace string, parentResourcePath string, resourceType string, resourceName string, filter string, cancel <-chan struct{}) (<-chan ManagementLockObject, <-chan error) { + resultChan := make(chan ManagementLockObject) + errChan := make(chan error, 1) + go func() { + defer func() { + close(resultChan) + close(errChan) + }() + list, err := client.ListAtResourceLevel(resourceGroupName, resourceProviderNamespace, parentResourcePath, resourceType, resourceName, filter) + if err != nil { + errChan <- err + return + } + if list.Value != nil { + for _, item := range *list.Value { + select { + case <-cancel: + return + case resultChan <- item: + // Intentionally left blank + } + } + } + for list.NextLink != nil { + list, err = client.ListAtResourceLevelNextResults(list) + if err != nil { + errChan <- err + return + } + if list.Value != nil { + for _, item := range *list.Value { + select { + case <-cancel: + return + case resultChan <- item: + // Intentionally left blank + } + } + } + } + }() + return resultChan, errChan +} + +// ListAtSubscriptionLevel gets all the management locks for a subscription. +// +// filter is the filter to apply on the operation. +func (client ManagementLocksClient) ListAtSubscriptionLevel(filter string) (result ManagementLockListResult, err error) { + req, err := client.ListAtSubscriptionLevelPreparer(filter) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "ListAtSubscriptionLevel", nil, "Failure preparing request") + return + } + + resp, err := client.ListAtSubscriptionLevelSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "ListAtSubscriptionLevel", resp, "Failure sending request") + return + } + + result, err = client.ListAtSubscriptionLevelResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "ListAtSubscriptionLevel", resp, "Failure responding to request") + } + + return +} + +// ListAtSubscriptionLevelPreparer prepares the ListAtSubscriptionLevel request. +func (client ManagementLocksClient) ListAtSubscriptionLevelPreparer(filter string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2016-09-01" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + if len(filter) > 0 { + queryParameters["$filter"] = autorest.Encode("query", filter) + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/providers/Microsoft.Authorization/locks", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare(&http.Request{}) +} + +// ListAtSubscriptionLevelSender sends the ListAtSubscriptionLevel request. The method will close the +// http.Response Body if it receives an error. +func (client ManagementLocksClient) ListAtSubscriptionLevelSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req) +} + +// ListAtSubscriptionLevelResponder handles the response to the ListAtSubscriptionLevel request. The method always +// closes the http.Response Body. +func (client ManagementLocksClient) ListAtSubscriptionLevelResponder(resp *http.Response) (result ManagementLockListResult, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// ListAtSubscriptionLevelNextResults retrieves the next set of results, if any. +func (client ManagementLocksClient) ListAtSubscriptionLevelNextResults(lastResults ManagementLockListResult) (result ManagementLockListResult, err error) { + req, err := lastResults.ManagementLockListResultPreparer() + if err != nil { + return result, autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "ListAtSubscriptionLevel", nil, "Failure preparing next results request") + } + if req == nil { + return + } + + resp, err := client.ListAtSubscriptionLevelSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + return result, autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "ListAtSubscriptionLevel", resp, "Failure sending next results request") + } + + result, err = client.ListAtSubscriptionLevelResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "locks.ManagementLocksClient", "ListAtSubscriptionLevel", resp, "Failure responding to next results request") + } + + return +} + +// ListAtSubscriptionLevelComplete gets all elements from the list without paging. +func (client ManagementLocksClient) ListAtSubscriptionLevelComplete(filter string, cancel <-chan struct{}) (<-chan ManagementLockObject, <-chan error) { + resultChan := make(chan ManagementLockObject) + errChan := make(chan error, 1) + go func() { + defer func() { + close(resultChan) + close(errChan) + }() + list, err := client.ListAtSubscriptionLevel(filter) + if err != nil { + errChan <- err + return + } + if list.Value != nil { + for _, item := range *list.Value { + select { + case <-cancel: + return + case resultChan <- item: + // Intentionally left blank + } + } + } + for list.NextLink != nil { + list, err = client.ListAtSubscriptionLevelNextResults(list) + if err != nil { + errChan <- err + return + } + if list.Value != nil { + for _, item := range *list.Value { + select { + case <-cancel: + return + case resultChan <- item: + // Intentionally left blank + } + } + } + } + }() + return resultChan, errChan +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/locks/models.go b/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/locks/models.go new file mode 100755 index 000000000000..8373b88d7d87 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/locks/models.go @@ -0,0 +1,76 @@ +package locks + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/to" + "net/http" +) + +// LockLevel enumerates the values for lock level. +type LockLevel string + +const ( + // CanNotDelete specifies the can not delete state for lock level. + CanNotDelete LockLevel = "CanNotDelete" + // NotSpecified specifies the not specified state for lock level. + NotSpecified LockLevel = "NotSpecified" + // ReadOnly specifies the read only state for lock level. + ReadOnly LockLevel = "ReadOnly" +) + +// ManagementLockListResult is the list of locks. +type ManagementLockListResult struct { + autorest.Response `json:"-"` + Value *[]ManagementLockObject `json:"value,omitempty"` + NextLink *string `json:"nextLink,omitempty"` +} + +// ManagementLockListResultPreparer prepares a request to retrieve the next set of results. It returns +// nil if no more results exist. +func (client ManagementLockListResult) ManagementLockListResultPreparer() (*http.Request, error) { + if client.NextLink == nil || len(to.String(client.NextLink)) <= 0 { + return nil, nil + } + return autorest.Prepare(&http.Request{}, + autorest.AsJSON(), + autorest.AsGet(), + autorest.WithBaseURL(to.String(client.NextLink))) +} + +// ManagementLockObject is the lock information. +type ManagementLockObject struct { + autorest.Response `json:"-"` + *ManagementLockProperties `json:"properties,omitempty"` + ID *string `json:"id,omitempty"` + Type *string `json:"type,omitempty"` + Name *string `json:"name,omitempty"` +} + +// ManagementLockOwner is lock owner properties. +type ManagementLockOwner struct { + ApplicationID *string `json:"applicationId,omitempty"` +} + +// ManagementLockProperties is the lock properties. +type ManagementLockProperties struct { + Level LockLevel `json:"level,omitempty"` + Notes *string `json:"notes,omitempty"` + Owners *[]ManagementLockOwner `json:"owners,omitempty"` +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/locks/version.go b/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/locks/version.go new file mode 100755 index 000000000000..52819ed906c5 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/arm/resources/locks/version.go @@ -0,0 +1,28 @@ +package locks + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// UserAgent returns the UserAgent string to use when sending http.Requests. +func UserAgent() string { + return "Azure-SDK-For-Go/v11.0.0-beta arm-locks/2016-09-01" +} + +// Version returns the semantic version (see http://semver.org) of the client. +func Version() string { + return "v11.0.0-beta" +} diff --git a/vendor/vendor.json b/vendor/vendor.json index c713a5e3d24f..fefe4e7d2426 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -162,6 +162,14 @@ "version": "v11.1.0-beta", "versionExact": "v11.1.0-beta" }, + { + "checksumSHA1": "bhJG74moCO/VErpBcGNWg6P77ek=", + "path": "github.com/Azure/azure-sdk-for-go/arm/resources/locks", + "revision": "80f6cbcb1d6295ebb8fbeb5afab826a94aa67d11", + "revisionTime": "2017-10-06T17:43:09Z", + "version": "v11.1.0-beta", + "versionExact": "v11.1.0-beta" + }, { "checksumSHA1": "v3sNtVRCd3dMI1nSb8E5+IFuKsg=", "path": "github.com/Azure/azure-sdk-for-go/arm/resources/resources", From facd2a52e24de5842c86b18237a6f0508468fcbe Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Sat, 18 Nov 2017 11:15:03 +0100 Subject: [PATCH 17/55] New Resource: `azurerm_management_lock` Note: As the Subscription specific Locks will break other tests; these tests need to be run individually. As such I've introduced the `TF_ACC_SUBSCRIPTION_PARALLEL_LOCK` environment variable for this purpose. Tests pass: ``` $ TF_ACC_SUBSCRIPTION_PARALLEL_LOCK=1 acctests azurerm TestAccAzureRMManagementLock_ === RUN TestAccAzureRMManagementLock_importResourceGroupReadOnlyBasic --- PASS: TestAccAzureRMManagementLock_importResourceGroupReadOnlyBasic (61.52s) === RUN TestAccAzureRMManagementLock_importResourceGroupReadOnlyComplete --- PASS: TestAccAzureRMManagementLock_importResourceGroupReadOnlyComplete (58.75s) === RUN TestAccAzureRMManagementLock_importResourceGroupCanNotDeleteBasic --- PASS: TestAccAzureRMManagementLock_importResourceGroupCanNotDeleteBasic (53.38s) === RUN TestAccAzureRMManagementLock_importResourceGroupCanNotDeleteComplete --- PASS: TestAccAzureRMManagementLock_importResourceGroupCanNotDeleteComplete (46.87s) === RUN TestAccAzureRMManagementLock_importPublicIPCanNotDeleteBasic --- PASS: TestAccAzureRMManagementLock_importPublicIPCanNotDeleteBasic (80.46s) === RUN TestAccAzureRMManagementLock_importPublicIPReadOnlyBasic --- PASS: TestAccAzureRMManagementLock_importPublicIPReadOnlyBasic (68.53s) === RUN TestAccAzureRMManagementLock_resourceGroupReadOnlyBasic --- PASS: TestAccAzureRMManagementLock_resourceGroupReadOnlyBasic (61.24s) === RUN TestAccAzureRMManagementLock_resourceGroupReadOnlyComplete --- PASS: TestAccAzureRMManagementLock_resourceGroupReadOnlyComplete (64.10s) === RUN TestAccAzureRMManagementLock_resourceGroupCanNotDeleteBasic --- PASS: TestAccAzureRMManagementLock_resourceGroupCanNotDeleteBasic (72.49s) === RUN TestAccAzureRMManagementLock_resourceGroupCanNotDeleteComplete --- PASS: TestAccAzureRMManagementLock_resourceGroupCanNotDeleteComplete (113.71s) === RUN TestAccAzureRMManagementLock_publicIPReadOnlyBasic --- PASS: TestAccAzureRMManagementLock_publicIPReadOnlyBasic (64.05s) === RUN TestAccAzureRMManagementLock_publicIPCanNotDeleteBasic --- PASS: TestAccAzureRMManagementLock_publicIPCanNotDeleteBasic (94.53s) === RUN TestAccAzureRMManagementLock_subscriptionReadOnlyBasic --- PASS: TestAccAzureRMManagementLock_subscriptionReadOnlyBasic (17.98s) === RUN TestAccAzureRMManagementLock_subscriptionCanNotDeleteBasic --- PASS: TestAccAzureRMManagementLock_subscriptionCanNotDeleteBasic (15.20s) PASS ok github.com/terraform-providers/terraform-provider-azurerm/azurerm 872.839s ``` Fixes #23 --- azurerm/config.go | 13 + azurerm/import_arm_management_lock_test.go | 134 +++++++ azurerm/provider.go | 1 + azurerm/resource_arm_management_lock.go | 155 ++++++++ azurerm/resource_arm_management_lock_test.go | 372 +++++++++++++++++++ website/azurerm.erb | 23 +- website/docs/r/management_lock.html.markdown | 93 +++++ 7 files changed, 784 insertions(+), 7 deletions(-) create mode 100644 azurerm/import_arm_management_lock_test.go create mode 100644 azurerm/resource_arm_management_lock.go create mode 100644 azurerm/resource_arm_management_lock_test.go create mode 100644 website/docs/r/management_lock.html.markdown diff --git a/azurerm/config.go b/azurerm/config.go index 159a54726a8d..311a11aee16a 100644 --- a/azurerm/config.go +++ b/azurerm/config.go @@ -28,6 +28,7 @@ import ( "github.com/Azure/azure-sdk-for-go/arm/operationalinsights" "github.com/Azure/azure-sdk-for-go/arm/postgresql" "github.com/Azure/azure-sdk-for-go/arm/redis" + "github.com/Azure/azure-sdk-for-go/arm/resources/locks" "github.com/Azure/azure-sdk-for-go/arm/resources/resources" "github.com/Azure/azure-sdk-for-go/arm/resources/subscriptions" "github.com/Azure/azure-sdk-for-go/arm/scheduler" @@ -160,6 +161,9 @@ type ArmClient struct { sqlElasticPoolsClient sql.ElasticPoolsClient sqlFirewallRulesClient sql.FirewallRulesClient sqlServersClient sql.ServersClient + + // Resources + managementLocksClient locks.ManagementLocksClient } func withRequestLogging() autorest.SendDecorator { @@ -656,6 +660,7 @@ func (c *Config) getArmClient() (*ArmClient, error) { client.registerDisks(endpoint, c.SubscriptionID, auth, sender) client.registerKeyVaultClients(endpoint, c.SubscriptionID, auth, keyVaultAuth, sender) client.registerRedisClients(endpoint, c.SubscriptionID, auth, sender) + client.registerResourcesClients(endpoint, c.SubscriptionID, auth, sender) return &client, nil } @@ -805,6 +810,14 @@ func (c *ArmClient) registerRedisClients(endpoint, subscriptionId string, auth a c.redisPatchSchedulesClient = patchSchedulesClient } +func (c *ArmClient) registerResourcesClients(endpoint, subscriptionId string, auth autorest.Authorizer, sender autorest.Sender) { + locksClient := locks.NewManagementLocksClientWithBaseURI(endpoint, subscriptionId) + setUserAgent(&locksClient.Client) + locksClient.Authorizer = auth + locksClient.Sender = sender + c.managementLocksClient = locksClient +} + func (armClient *ArmClient) getKeyForStorageAccount(resourceGroupName, storageAccountName string) (string, bool, error) { accountKeys, err := armClient.storageServiceClient.ListKeys(resourceGroupName, storageAccountName) if accountKeys.StatusCode == http.StatusNotFound { diff --git a/azurerm/import_arm_management_lock_test.go b/azurerm/import_arm_management_lock_test.go new file mode 100644 index 000000000000..dcb2638648af --- /dev/null +++ b/azurerm/import_arm_management_lock_test.go @@ -0,0 +1,134 @@ +package azurerm + +import ( + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccAzureRMManagementLock_importResourceGroupReadOnlyBasic(t *testing.T) { + ri := acctest.RandInt() + config := testAccAzureRMManagementLock_resourceGroupReadOnlyBasic(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMManagementLockDestroy, + Steps: []resource.TestStep{ + { + Config: config, + }, + { + ResourceName: "azurerm_management_lock.test", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMManagementLock_importResourceGroupReadOnlyComplete(t *testing.T) { + ri := acctest.RandInt() + config := testAccAzureRMManagementLock_resourceGroupReadOnlyComplete(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMManagementLockDestroy, + Steps: []resource.TestStep{ + { + Config: config, + }, + { + ResourceName: "azurerm_management_lock.test", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMManagementLock_importResourceGroupCanNotDeleteBasic(t *testing.T) { + ri := acctest.RandInt() + config := testAccAzureRMManagementLock_resourceGroupCanNotDeleteBasic(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMManagementLockDestroy, + Steps: []resource.TestStep{ + { + Config: config, + }, + { + ResourceName: "azurerm_management_lock.test", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMManagementLock_importResourceGroupCanNotDeleteComplete(t *testing.T) { + ri := acctest.RandInt() + config := testAccAzureRMManagementLock_resourceGroupCanNotDeleteComplete(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMManagementLockDestroy, + Steps: []resource.TestStep{ + { + Config: config, + }, + { + ResourceName: "azurerm_management_lock.test", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMManagementLock_importPublicIPCanNotDeleteBasic(t *testing.T) { + ri := acctest.RandInt() + config := testAccAzureRMManagementLock_publicIPCanNotDeleteBasic(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMManagementLockDestroy, + Steps: []resource.TestStep{ + { + Config: config, + }, + { + ResourceName: "azurerm_management_lock.test", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMManagementLock_importPublicIPReadOnlyBasic(t *testing.T) { + ri := acctest.RandInt() + config := testAccAzureRMManagementLock_publicIPReadOnlyBasic(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMManagementLockDestroy, + Steps: []resource.TestStep{ + { + Config: config, + }, + { + ResourceName: "azurerm_management_lock.test", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} diff --git a/azurerm/provider.go b/azurerm/provider.go index 5f7dfea31866..97d482bcfaff 100644 --- a/azurerm/provider.go +++ b/azurerm/provider.go @@ -127,6 +127,7 @@ func Provider() terraform.ResourceProvider { "azurerm_local_network_gateway": resourceArmLocalNetworkGateway(), "azurerm_log_analytics_workspace": resourceArmLogAnalyticsWorkspace(), "azurerm_managed_disk": resourceArmManagedDisk(), + "azurerm_management_lock": resourceArmManagementLock(), "azurerm_mysql_configuration": resourceArmMySQLConfiguration(), "azurerm_mysql_database": resourceArmMySqlDatabase(), "azurerm_mysql_firewall_rule": resourceArmMySqlFirewallRule(), diff --git a/azurerm/resource_arm_management_lock.go b/azurerm/resource_arm_management_lock.go new file mode 100644 index 000000000000..b1888c3793a4 --- /dev/null +++ b/azurerm/resource_arm_management_lock.go @@ -0,0 +1,155 @@ +package azurerm + +import ( + "fmt" + "log" + "strings" + + "github.com/Azure/azure-sdk-for-go/arm/resources/locks" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmManagementLock() *schema.Resource { + return &schema.Resource{ + Create: resourceArmManagementLockCreateUpdate, + Read: resourceArmManagementLockRead, + Delete: resourceArmManagementLockDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + // TODO: validation + // The lock name can be a maximum of 260 characters. + // It cannot contain <, > %, &, :, \, ?, /, or any control characters. + }, + + "scope": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "lock_level": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + string(locks.CanNotDelete), + string(locks.ReadOnly), + }, false), + }, + + "notes": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(0, 512), + }, + }, + } +} + +func resourceArmManagementLockCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).managementLocksClient + log.Printf("[INFO] preparing arguments for AzureRM Management Lock creation.") + + name := d.Get("name").(string) + scope := d.Get("scope").(string) + lockLevel := d.Get("lock_level").(string) + notes := d.Get("notes").(string) + + lock := locks.ManagementLockObject{ + ManagementLockProperties: &locks.ManagementLockProperties{ + Level: locks.LockLevel(lockLevel), + Notes: utils.String(notes), + }, + } + + _, err := client.CreateOrUpdateByScope(scope, name, lock) + if err != nil { + return err + } + + read, err := client.GetByScope(scope, name) + if err != nil { + return err + } + + if read.ID == nil { + return fmt.Errorf("Cannot read ID of AzureRM Management Lock %q (Scope %q)", name, scope) + } + + d.SetId(*read.ID) + return resourceArmManagementLockRead(d, meta) +} + +func resourceArmManagementLockRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).managementLocksClient + + id, err := parseAzureRMLockId(d.Id()) + if err != nil { + return err + } + + resp, err := client.GetByScope(id.Scope, id.Name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("Error making Read request on AzureRM Management Lock %q (Scope %q): %+v", id.Name, id.Scope, err) + } + + d.Set("name", resp.Name) + d.Set("scope", id.Scope) + + if props := resp.ManagementLockProperties; props != nil { + d.Set("lock_level", string(props.Level)) + d.Set("notes", props.Notes) + } + + return nil +} + +func resourceArmManagementLockDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).managementLocksClient + + id, err := parseAzureRMLockId(d.Id()) + if err != nil { + return err + } + + resp, err := client.DeleteByScope(id.Scope, id.Name) + if err != nil { + if utils.ResponseWasNotFound(resp) { + return nil + } + + return fmt.Errorf("Error issuing AzureRM delete request for Management Lock %q (Scope %q): %+v", id.Name, id.Scope, err) + } + + return nil +} + +type AzureManagementLockId struct { + Scope string + Name string +} + +func parseAzureRMLockId(id string) (*AzureManagementLockId, error) { + segments := strings.Split(id, "/providers/Microsoft.Authorization/locks/") + scope := segments[0] + name := segments[1] + lockId := AzureManagementLockId{ + Scope: scope, + Name: name, + } + return &lockId, nil +} diff --git a/azurerm/resource_arm_management_lock_test.go b/azurerm/resource_arm_management_lock_test.go new file mode 100644 index 000000000000..382042d3b04a --- /dev/null +++ b/azurerm/resource_arm_management_lock_test.go @@ -0,0 +1,372 @@ +package azurerm + +import ( + "fmt" + "testing" + + "os" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestAccAzureRMManagementLock_resourceGroupReadOnlyBasic(t *testing.T) { + resourceName := "azurerm_management_lock.test" + ri := acctest.RandInt() + location := testLocation() + config := testAccAzureRMManagementLock_resourceGroupReadOnlyBasic(ri, location) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMManagementLockDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMManagementLockExists(resourceName), + ), + }, + }, + }) +} + +func TestAccAzureRMManagementLock_resourceGroupReadOnlyComplete(t *testing.T) { + resourceName := "azurerm_management_lock.test" + ri := acctest.RandInt() + location := testLocation() + config := testAccAzureRMManagementLock_resourceGroupReadOnlyComplete(ri, location) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMManagementLockDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMManagementLockExists(resourceName), + ), + }, + }, + }) +} + +func TestAccAzureRMManagementLock_resourceGroupCanNotDeleteBasic(t *testing.T) { + resourceName := "azurerm_management_lock.test" + ri := acctest.RandInt() + location := testLocation() + config := testAccAzureRMManagementLock_resourceGroupCanNotDeleteBasic(ri, location) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMManagementLockDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMManagementLockExists(resourceName), + ), + }, + }, + }) +} + +func TestAccAzureRMManagementLock_resourceGroupCanNotDeleteComplete(t *testing.T) { + resourceName := "azurerm_management_lock.test" + ri := acctest.RandInt() + location := testLocation() + config := testAccAzureRMManagementLock_resourceGroupCanNotDeleteComplete(ri, location) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMManagementLockDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMManagementLockExists(resourceName), + ), + }, + }, + }) +} + +func TestAccAzureRMManagementLock_publicIPReadOnlyBasic(t *testing.T) { + resourceName := "azurerm_management_lock.test" + ri := acctest.RandInt() + location := testLocation() + config := testAccAzureRMManagementLock_publicIPReadOnlyBasic(ri, location) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMManagementLockDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMManagementLockExists(resourceName), + ), + }, + }, + }) +} + +func TestAccAzureRMManagementLock_publicIPCanNotDeleteBasic(t *testing.T) { + resourceName := "azurerm_management_lock.test" + ri := acctest.RandInt() + location := testLocation() + config := testAccAzureRMManagementLock_publicIPCanNotDeleteBasic(ri, location) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMManagementLockDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMManagementLockExists(resourceName), + ), + }, + }, + }) +} + +func TestAccAzureRMManagementLock_subscriptionReadOnlyBasic(t *testing.T) { + _, exists := os.LookupEnv("TF_ACC_SUBSCRIPTION_PARALLEL_LOCK") + if !exists { + t.Skip("`TF_ACC_SUBSCRIPTION_PARALLEL_LOCK` isn't specified - skipping since this test can't be run in Parallel") + } + + resourceName := "azurerm_management_lock.test" + ri := acctest.RandInt() + config := testAccAzureRMManagementLock_subscriptionReadOnlyBasic(ri) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMManagementLockDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMManagementLockExists(resourceName), + ), + }, + }, + }) +} + +func TestAccAzureRMManagementLock_subscriptionCanNotDeleteBasic(t *testing.T) { + _, exists := os.LookupEnv("TF_ACC_SUBSCRIPTION_PARALLEL_LOCK") + if !exists { + t.Skip("`TF_ACC_SUBSCRIPTION_PARALLEL_LOCK` isn't specified - skipping since this test can't be run in Parallel") + } + + resourceName := "azurerm_management_lock.test" + ri := acctest.RandInt() + config := testAccAzureRMManagementLock_subscriptionCanNotDeleteBasic(ri) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMManagementLockDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMManagementLockExists(resourceName), + ), + }, + }, + }) +} + +func testCheckAzureRMManagementLockExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + // Ensure we have enough information in state to look up in API + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %q", resourceName) + } + + name := rs.Primary.Attributes["name"] + scope := rs.Primary.Attributes["scope"] + + client := testAccProvider.Meta().(*ArmClient).managementLocksClient + + resp, err := client.GetByScope(scope, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Management Lock %q (Scope %q) does not exist", name, scope) + } + + return fmt.Errorf("Bad: Get on managementLocksClient: %+v", err) + } + + return nil + } +} + +func testCheckAzureRMManagementLockDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*ArmClient).managementLocksClient + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_management_lock" { + continue + } + + name := rs.Primary.Attributes["name"] + scope := rs.Primary.Attributes["scope"] + + resp, err := client.GetByScope(scope, name) + + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return nil + } + + return err + } + } + + return nil +} + +func testAccAzureRMManagementLock_resourceGroupReadOnlyBasic(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_management_lock" "test" { + name = "acctestlock-%d" + scope = "${azurerm_resource_group.test.id}" + lock_level = "ReadOnly" +} +`, rInt, location, rInt) +} + +func testAccAzureRMManagementLock_resourceGroupReadOnlyComplete(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_management_lock" "test" { + name = "acctestlock-%d" + scope = "${azurerm_resource_group.test.id}" + lock_level = "ReadOnly" + notes = "Hello, World!" +} +`, rInt, location, rInt) +} + +func testAccAzureRMManagementLock_resourceGroupCanNotDeleteBasic(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_management_lock" "test" { + name = "acctestlock-%d" + scope = "${azurerm_resource_group.test.id}" + lock_level = "CanNotDelete" +} +`, rInt, location, rInt) +} + +func testAccAzureRMManagementLock_resourceGroupCanNotDeleteComplete(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_management_lock" "test" { + name = "acctestlock-%d" + scope = "${azurerm_resource_group.test.id}" + lock_level = "CanNotDelete" + notes = "Hello, World!" +} +`, rInt, location, rInt) +} + +func testAccAzureRMManagementLock_publicIPReadOnlyBasic(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_public_ip" "test" { + name = "acctestpublicip-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + public_ip_address_allocation = "static" + idle_timeout_in_minutes = 30 +} + +resource "azurerm_management_lock" "test" { + name = "acctestlock-%d" + scope = "${azurerm_public_ip.test.id}" + lock_level = "ReadOnly" +} +`, rInt, location, rInt, rInt) +} + +func testAccAzureRMManagementLock_publicIPCanNotDeleteBasic(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_public_ip" "test" { + name = "acctestpublicip-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + public_ip_address_allocation = "static" + idle_timeout_in_minutes = 30 +} + +resource "azurerm_management_lock" "test" { + name = "acctestlock-%d" + scope = "${azurerm_public_ip.test.id}" + lock_level = "CanNotDelete" +} +`, rInt, location, rInt, rInt) +} + +func testAccAzureRMManagementLock_subscriptionReadOnlyBasic(rInt int) string { + return fmt.Sprintf(` +data "azurerm_subscription" "current" {} + +resource "azurerm_management_lock" "test" { + name = "acctestlock-%d" + scope = "${data.azurerm_subscription.current.id}" + lock_level = "ReadOnly" +} +`, rInt) +} + +func testAccAzureRMManagementLock_subscriptionCanNotDeleteBasic(rInt int) string { + return fmt.Sprintf(` +data "azurerm_subscription" "current" {} + +resource "azurerm_management_lock" "test" { + name = "acctestlock-%d" + scope = "${data.azurerm_subscription.current.id}" + lock_level = "CanNotDelete" +} +`, rInt) +} diff --git a/website/azurerm.erb b/website/azurerm.erb index c01de2a7752d..5df07fd2fe9e 100644 --- a/website/azurerm.erb +++ b/website/azurerm.erb @@ -370,6 +370,15 @@ + > + Management Resources + + + > Messaging Resources