From b15b7e1c4064a7258f0a59a5c9726f4ac9a26e57 Mon Sep 17 00:00:00 2001 From: Tom Harvey Date: Mon, 7 Nov 2016 10:19:59 +0000 Subject: [PATCH] provider/azurerm: EventHubs (#9889) * Implemented EventHubs * Missing the sidebar link * Fixing the type * Fixing the docs for Namespace * Removing premium tests * Checking the correct status code on delete * Added a test case for the import * Documentation for importing * Fixing a typo --- builtin/providers/azurerm/config.go | 7 + .../azurerm/import_arm_eventhub_test.go | 34 +++ builtin/providers/azurerm/provider.go | 1 + .../azurerm/resource_arm_eventhub.go | 177 +++++++++++++ .../resource_arm_eventhub_namespace_test.go | 40 ++- .../azurerm/resource_arm_eventhub_test.go | 235 ++++++++++++++++++ .../azurerm/r/eventhub.html.markdown | 73 ++++++ .../r/eventhub_namespace.html.markdown | 14 +- website/source/layouts/azurerm.erb | 3 + 9 files changed, 578 insertions(+), 6 deletions(-) create mode 100644 builtin/providers/azurerm/import_arm_eventhub_test.go create mode 100644 builtin/providers/azurerm/resource_arm_eventhub.go create mode 100644 builtin/providers/azurerm/resource_arm_eventhub_test.go create mode 100644 website/source/docs/providers/azurerm/r/eventhub.html.markdown diff --git a/builtin/providers/azurerm/config.go b/builtin/providers/azurerm/config.go index d2f338d9d..436c82814 100644 --- a/builtin/providers/azurerm/config.go +++ b/builtin/providers/azurerm/config.go @@ -59,6 +59,7 @@ type ArmClient struct { cdnProfilesClient cdn.ProfilesClient cdnEndpointsClient cdn.EndpointsClient + eventHubClient eventhub.EventHubsClient eventHubNamespacesClient eventhub.NamespacesClient providers resources.ProvidersClient @@ -213,6 +214,12 @@ func (c *Config) getArmClient() (*ArmClient, error) { agc.Sender = autorest.CreateSender(withRequestLogging()) client.appGatewayClient = agc + ehc := eventhub.NewEventHubsClient(c.SubscriptionID) + setUserAgent(&ehc.Client) + ehc.Authorizer = spt + ehc.Sender = autorest.CreateSender(withRequestLogging()) + client.eventHubClient = ehc + ehnc := eventhub.NewNamespacesClient(c.SubscriptionID) setUserAgent(&ehnc.Client) ehnc.Authorizer = spt diff --git a/builtin/providers/azurerm/import_arm_eventhub_test.go b/builtin/providers/azurerm/import_arm_eventhub_test.go new file mode 100644 index 000000000..108af408a --- /dev/null +++ b/builtin/providers/azurerm/import_arm_eventhub_test.go @@ -0,0 +1,34 @@ +package azurerm + +import ( + "testing" + + "fmt" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccAzureRMEventHub_importBasic(t *testing.T) { + resourceName := "azurerm_eventhub.test" + + ri := acctest.RandInt() + config := fmt.Sprintf(testAccAzureRMEventHub_basic, ri, ri, ri) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMEventHubDestroy, + Steps: []resource.TestStep{ + { + Config: config, + }, + + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} diff --git a/builtin/providers/azurerm/provider.go b/builtin/providers/azurerm/provider.go index c65679fa4..50944f7b6 100644 --- a/builtin/providers/azurerm/provider.go +++ b/builtin/providers/azurerm/provider.go @@ -54,6 +54,7 @@ func Provider() terraform.ResourceProvider { "azurerm_cdn_endpoint": resourceArmCdnEndpoint(), "azurerm_cdn_profile": resourceArmCdnProfile(), + "azurerm_eventhub": resourceArmEventHub(), "azurerm_eventhub_namespace": resourceArmEventHubNamespace(), "azurerm_lb": resourceArmLoadBalancer(), diff --git a/builtin/providers/azurerm/resource_arm_eventhub.go b/builtin/providers/azurerm/resource_arm_eventhub.go new file mode 100644 index 000000000..bb70c6c00 --- /dev/null +++ b/builtin/providers/azurerm/resource_arm_eventhub.go @@ -0,0 +1,177 @@ +package azurerm + +import ( + "fmt" + "log" + + "net/http" + + "github.com/Azure/azure-sdk-for-go/arm/eventhub" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceArmEventHub() *schema.Resource { + return &schema.Resource{ + Create: resourceArmEventHubCreate, + Read: resourceArmEventHubRead, + Update: resourceArmEventHubCreate, + Delete: resourceArmEventHubDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "namespace_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "resource_group_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "location": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "partition_count": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validateEventHubPartitionCount, + }, + + "message_retention": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validateEventHubMessageRetentionCount, + }, + + "partition_ids": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + Computed: true, + }, + }, + } +} + +func resourceArmEventHubCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient) + eventhubClient := client.eventHubClient + log.Printf("[INFO] preparing arguments for Azure ARM EventHub creation.") + + name := d.Get("name").(string) + namespaceName := d.Get("namespace_name").(string) + location := d.Get("location").(string) + resGroup := d.Get("resource_group_name").(string) + partitionCount := int64(d.Get("partition_count").(int)) + messageRetention := int64(d.Get("message_retention").(int)) + + parameters := eventhub.CreateOrUpdateParameters{ + Location: &location, + Properties: &eventhub.Properties{ + PartitionCount: &partitionCount, + MessageRetentionInDays: &messageRetention, + }, + } + + _, err := eventhubClient.CreateOrUpdate(resGroup, namespaceName, name, parameters) + if err != nil { + return err + } + + read, err := eventhubClient.Get(resGroup, namespaceName, name) + if err != nil { + return err + } + + if read.ID == nil { + return fmt.Errorf("Cannot read EventHub %s (resource group %s) ID", name, resGroup) + } + + d.SetId(*read.ID) + + return resourceArmEventHubRead(d, meta) +} + +func resourceArmEventHubRead(d *schema.ResourceData, meta interface{}) error { + eventhubClient := meta.(*ArmClient).eventHubClient + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + resGroup := id.ResourceGroup + namespaceName := id.Path["namespaces"] + name := id.Path["eventhubs"] + + resp, err := eventhubClient.Get(resGroup, namespaceName, name) + if err != nil { + return fmt.Errorf("Error making Read request on Azure EventHub %s: %s", name, err) + } + if resp.StatusCode == http.StatusNotFound { + d.SetId("") + return nil + } + + d.Set("name", resp.Name) + d.Set("namespace_name", namespaceName) + d.Set("resource_group_name", resGroup) + d.Set("location", azureRMNormalizeLocation(*resp.Location)) + + d.Set("partition_count", resp.Properties.PartitionCount) + d.Set("message_retention", resp.Properties.MessageRetentionInDays) + d.Set("partition_ids", resp.Properties.PartitionIds) + + return nil +} + +func resourceArmEventHubDelete(d *schema.ResourceData, meta interface{}) error { + eventhubClient := meta.(*ArmClient).eventHubClient + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + resGroup := id.ResourceGroup + namespaceName := id.Path["namespaces"] + name := id.Path["eventhubs"] + + resp, err := eventhubClient.Delete(resGroup, namespaceName, name) + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("Error issuing Azure ARM delete request of EventHub'%s': %s", name, err) + } + + return nil +} + +func validateEventHubPartitionCount(v interface{}, k string) (ws []string, errors []error) { + value := v.(int) + + if !(32 >= value && value >= 2) { + errors = append(errors, fmt.Errorf("EventHub Partition Count has to be between 2 and 32")) + } + return +} + +func validateEventHubMessageRetentionCount(v interface{}, k string) (ws []string, errors []error) { + value := v.(int) + + if !(7 >= value && value >= 1) { + errors = append(errors, fmt.Errorf("EventHub Retention Count has to be between 1 and 7")) + } + return +} diff --git a/builtin/providers/azurerm/resource_arm_eventhub_namespace_test.go b/builtin/providers/azurerm/resource_arm_eventhub_namespace_test.go index 9b195439e..b6fc57af9 100644 --- a/builtin/providers/azurerm/resource_arm_eventhub_namespace_test.go +++ b/builtin/providers/azurerm/resource_arm_eventhub_namespace_test.go @@ -89,7 +89,27 @@ func TestAccAzureRMEventHubNamespace_basic(t *testing.T) { Providers: testAccProviders, CheckDestroy: testCheckAzureRMEventHubNamespaceDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMEventHubNamespaceExists("azurerm_eventhub_namespace.test"), + ), + }, + }, + }) +} + +func TestAccAzureRMEventHubNamespace_standard(t *testing.T) { + + ri := acctest.RandInt() + config := fmt.Sprintf(testAccAzureRMEventHubNamespace_standard, ri, ri) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMEventHubNamespaceDestroy, + Steps: []resource.TestStep{ + { Config: config, Check: resource.ComposeTestCheckFunc( testCheckAzureRMEventHubNamespaceExists("azurerm_eventhub_namespace.test"), @@ -108,7 +128,7 @@ func TestAccAzureRMEventHubNamespace_readDefaultKeys(t *testing.T) { Providers: testAccProviders, CheckDestroy: testCheckAzureRMEventHubNamespaceDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: config, Check: resource.ComposeTestCheckFunc( testCheckAzureRMEventHubNamespaceExists("azurerm_eventhub_namespace.test"), @@ -187,8 +207,22 @@ resource "azurerm_resource_group" "test" { } resource "azurerm_eventhub_namespace" "test" { name = "acctesteventhubnamespace-%d" - location = "West US" + location = "${azurerm_resource_group.test.location}" resource_group_name = "${azurerm_resource_group.test.name}" sku = "Basic" } ` + +var testAccAzureRMEventHubNamespace_standard = ` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "West US" +} +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" +} +` diff --git a/builtin/providers/azurerm/resource_arm_eventhub_test.go b/builtin/providers/azurerm/resource_arm_eventhub_test.go new file mode 100644 index 000000000..773d0ffca --- /dev/null +++ b/builtin/providers/azurerm/resource_arm_eventhub_test.go @@ -0,0 +1,235 @@ +package azurerm + +import ( + "fmt" + "net/http" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAzureRMEventHubPartitionCount_validation(t *testing.T) { + cases := []struct { + Value int + ErrCount int + }{ + { + Value: 1, + ErrCount: 1, + }, + { + Value: 2, + ErrCount: 0, + }, + { + Value: 3, + ErrCount: 0, + }, + { + Value: 21, + ErrCount: 0, + }, + { + Value: 32, + ErrCount: 0, + }, + { + Value: 33, + ErrCount: 1, + }, + } + + for _, tc := range cases { + _, errors := validateEventHubPartitionCount(tc.Value, "azurerm_eventhub") + + if len(errors) != tc.ErrCount { + t.Fatalf("Expected the Azure RM EventHub Partition Count to trigger a validation error") + } + } +} + +func TestAccAzureRMEventHubMessageRetentionCount_validation(t *testing.T) { + cases := []struct { + Value int + ErrCount int + }{ + { + Value: 0, + ErrCount: 1, + }, { + Value: 1, + ErrCount: 0, + }, { + Value: 2, + ErrCount: 0, + }, { + Value: 3, + ErrCount: 0, + }, { + Value: 4, + ErrCount: 0, + }, { + Value: 5, + ErrCount: 0, + }, { + Value: 6, + ErrCount: 0, + }, { + Value: 7, + ErrCount: 0, + }, { + Value: 8, + ErrCount: 1, + }, + } + + for _, tc := range cases { + _, errors := validateEventHubMessageRetentionCount(tc.Value, "azurerm_eventhub") + + if len(errors) != tc.ErrCount { + t.Fatalf("Expected the Azure RM EventHub Message Retention Count to trigger a validation error") + } + } +} + +func TestAccAzureRMEventHub_basic(t *testing.T) { + + ri := acctest.RandInt() + config := fmt.Sprintf(testAccAzureRMEventHub_basic, ri, ri, ri) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMEventHubDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMEventHubExists("azurerm_eventhub.test"), + ), + }, + }, + }) +} + +func TestAccAzureRMEventHub_standard(t *testing.T) { + + ri := acctest.RandInt() + config := fmt.Sprintf(testAccAzureRMEventHub_standard, ri, ri, ri) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMEventHubDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMEventHubExists("azurerm_eventhub.test"), + ), + }, + }, + }) +} + +func testCheckAzureRMEventHubDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*ArmClient).eventHubClient + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_eventhub" { + continue + } + + name := rs.Primary.Attributes["name"] + namespaceName := rs.Primary.Attributes["namespace_name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + resp, err := conn.Get(resourceGroup, namespaceName, name) + + if err != nil { + return nil + } + + if resp.StatusCode != http.StatusNotFound { + return fmt.Errorf("EventHub still exists:\n%#v", resp.Properties) + } + } + + return nil +} + +func testCheckAzureRMEventHubExists(name 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[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + name := rs.Primary.Attributes["name"] + namespaceName := rs.Primary.Attributes["namespace_name"] + resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] + if !hasResourceGroup { + return fmt.Errorf("Bad: no resource group found in state for Event Hub: %s", name) + } + + conn := testAccProvider.Meta().(*ArmClient).eventHubClient + + resp, err := conn.Get(resourceGroup, namespaceName, name) + if err != nil { + return fmt.Errorf("Bad: Get on eventHubClient: %s", err) + } + + if resp.StatusCode == http.StatusNotFound { + return fmt.Errorf("Bad: Event Hub %q (namespace %q / resource group: %q) does not exist", name, namespaceName, resourceGroup) + } + + return nil + } +} + +var testAccAzureRMEventHub_basic = ` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "West US" +} +resource "azurerm_eventhub_namespace" "test" { + name = "acctesteventhubnamespace-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Basic" +} + +resource "azurerm_eventhub" "test" { + name = "acctesteventhub-%d" + namespace_name = "${azurerm_eventhub_namespace.test.name}" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + partition_count = 2 + message_retention = 1 +} +` + +var testAccAzureRMEventHub_standard = ` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "West US" +} +resource "azurerm_eventhub_namespace" "test" { + name = "acctesteventhubnamespace-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" +} + +resource "azurerm_eventhub" "test" { + name = "acctesteventhub-%d" + namespace_name = "${azurerm_eventhub_namespace.test.name}" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + partition_count = 2 + message_retention = 7 +} +` diff --git a/website/source/docs/providers/azurerm/r/eventhub.html.markdown b/website/source/docs/providers/azurerm/r/eventhub.html.markdown new file mode 100644 index 000000000..498299627 --- /dev/null +++ b/website/source/docs/providers/azurerm/r/eventhub.html.markdown @@ -0,0 +1,73 @@ +--- +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_eventhub" +sidebar_current: "docs-azurerm-resource-eventhub" +description: |- + Creates a new Event Hub as a nested resource within an Event Hub Namespace. +--- + +# azurerm\_eventhub + +Creates a new Event Hub as a nested resource within a Event Hub Namespace. + +## Example Usage + +``` +resource "azurerm_resource_group" "test" { + name = "resourceGroup1" + location = "West US" +} + +resource "azurerm_eventhub_namespace" "test" { + name = "acceptanceTestEventHubNamespace" + location = "West US" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Basic" + capacity = 2 + + tags { + environment = "Production" + } +} + +resource "azurerm_eventhub" "test" { + name = "acceptanceTestEventHub" + namespace_name = "${azurerm_eventhub_namespace.test.name}" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + partition_count = 2 + message_retention = 2 +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) Specifies the name of the EventHub Namespace resource. Changing this forces a new resource to be created. + +* `namespace_name` - (Required) Specifies the name of the EventHub Namespace. Changing this forces a new resource to be created. + +* `resource_group_name` - (Required) The name of the resource group in which the EventHub's parent Namespace exists. 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. + +* `partition_count` - (Required) Specifies the current number of shards on the Event Hub. + +* `message_retention` - (Required) Specifies the number of days to retain the events for this Event Hub. Needs to be between 1 and 7 days; or 1 day when using a Basic SKU for the parent EventHub Namespace. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The EventHub ID. + +* `partition_ids` - The identifiers for partitions created for Event Hubs. + +## Import + +EventHub's can be imported using the `resource id`, e.g. + +``` +terraform import azurerm_eventhub.eventhub1 /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.EventHub/namespaces/namespace1/eventhubs/eventhub1 +``` diff --git a/website/source/docs/providers/azurerm/r/eventhub_namespace.html.markdown b/website/source/docs/providers/azurerm/r/eventhub_namespace.html.markdown index 3314cc067..83786c11e 100644 --- a/website/source/docs/providers/azurerm/r/eventhub_namespace.html.markdown +++ b/website/source/docs/providers/azurerm/r/eventhub_namespace.html.markdown @@ -39,13 +39,13 @@ The following arguments are supported: new resource to be created. * `resource_group_name` - (Required) The name of the resource group in which to - create the namespace. + 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. Options are Basic or Standard. -* `capacity` - (Optional) Specifies the capacity of a premium namespace. Can be 1, 2 or 4 +* `capacity` - (Optional) Specifies the capacity of a Standard namespace. Can be 1, 2 or 4 * `tags` - (Optional) A mapping of tags to assign to the resource. @@ -67,3 +67,11 @@ The following attributes are exported only if there is an authorization rule nam * `default_primary_key` - The primary access key for the authorization rule `RootManageSharedAccessKey`. * `default_secondary_key` - The secondary access key for the authorization rule `RootManageSharedAccessKey`. + +## Import + +EventHub Namespaces can be imported using the `resource id`, e.g. + +``` +terraform import azurerm_eventhub_namespace.namespace1 /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.EventHub/namespaces/namespace1 +``` diff --git a/website/source/layouts/azurerm.erb b/website/source/layouts/azurerm.erb index be3936974..fe250c7a3 100644 --- a/website/source/layouts/azurerm.erb +++ b/website/source/layouts/azurerm.erb @@ -87,6 +87,9 @@ > Event Hubs