Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Resource: azurerm_iothub #887

Merged
merged 73 commits into from
Mar 15, 2018
Merged
Show file tree
Hide file tree
Changes from 67 commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
e5c5c62
added iothub dependency
girishramnani Sep 28, 2017
ee1bf60
added the config and cleaned up the schema iothub resource schema
girishramnani Sep 28, 2017
bc22b50
added validation and params in the schemas
girishramnani Sep 28, 2017
f9bf64d
added minimal creation method
girishramnani Sep 28, 2017
531319c
added resource to the provider list
girishramnani Sep 28, 2017
12bd9e0
cleaned up the schema a bit and added read
girishramnani Sep 28, 2017
293ab2c
working read and create method on iothub resource
girishramnani Sep 29, 2017
bbd1481
added lifecycle methods for iothub
girishramnani Sep 29, 2017
7fda639
added tags
girishramnani Sep 29, 2017
e58ffde
basic integration tests added
girishramnani Sep 29, 2017
b569ba9
Added validations for the name and followed naming convention
girishramnani Sep 29, 2017
95b677a
updated the iothub vendor dependency
girishramnani Oct 9, 2017
6d8a41d
Added keys to the lifecycle and schema
girishramnani Oct 11, 2017
5dd8cef
resolved a keys struct related bug
girishramnani Oct 11, 2017
677794f
Added documentation for iothub
girishramnani Oct 12, 2017
f2acf20
Added importer in the iothub resource group
girishramnani Oct 15, 2017
0a1b3fc
Iothub consumer group initial commit
Dec 19, 2017
ffac304
added iothub dependency
girishramnani Sep 28, 2017
3d76c7a
added the config and cleaned up the schema iothub resource schema
girishramnani Sep 28, 2017
1ebedec
added validation and params in the schemas
girishramnani Sep 28, 2017
838271c
added minimal creation method
girishramnani Sep 28, 2017
f378215
added resource to the provider list
girishramnani Sep 28, 2017
77c92ff
cleaned up the schema a bit and added read
girishramnani Sep 28, 2017
14aaec9
working read and create method on iothub resource
girishramnani Sep 29, 2017
ff6e44d
added lifecycle methods for iothub
girishramnani Sep 29, 2017
c7d4aab
added tags
girishramnani Sep 29, 2017
9b6a7dc
basic integration tests added
girishramnani Sep 29, 2017
93c67b3
Added validations for the name and followed naming convention
girishramnani Sep 29, 2017
d6f58e7
updated the iothub vendor dependency
girishramnani Oct 9, 2017
96bf5f6
Added keys to the lifecycle and schema
girishramnani Oct 11, 2017
67469d3
resolved a keys struct related bug
girishramnani Oct 11, 2017
1baeb52
Added documentation for iothub
girishramnani Oct 12, 2017
a22eeb2
Added importer in the iothub resource group
girishramnani Oct 15, 2017
304b9d2
Iothub consumer group initial commit
Dec 19, 2017
eeb4804
Initial consumer group test
Dec 20, 2017
7be2c54
Fixed schema
Dec 20, 2017
6b69422
Modified configuration
Dec 20, 2017
faf8e4b
Iothub consumer group documentation
Dec 20, 2017
93c02b5
Added resource to provider
Dec 20, 2017
8e38252
Revised schema
Dec 20, 2017
60cd688
Modifications for acceptance tests
Dec 20, 2017
0c2e6aa
Corrected config
Dec 22, 2017
3468db1
creating new datasource files for iothub in terraform-providers-azurerm
Dec 22, 2017
92e0ede
merged config file
Dec 22, 2017
79cfb28
Delete config_REMOTE_7053.go
Dec 22, 2017
421eda6
keys datasource initial commit
Jan 3, 2018
67c093b
sku datasource initial commit
Jan 3, 2018
5a82725
added datasource to the provider list
Jan 3, 2018
4324420
initial keys test
Jan 3, 2018
bcf55f5
initial sku test
Jan 3, 2018
b425073
keys datasource documentation
Jan 3, 2018
abbedb0
sku datasource documentation
Jan 4, 2018
53a039a
Update iothub dependencies
Feb 26, 2018
44b85f2
Modify for dependencies
Feb 27, 2018
57a22e3
Modify IoTHub resource for updated SDK
Feb 27, 2018
4258525
Merge branch 'master' into iothub
shelleybess Feb 27, 2018
e96d5ca
Formatting
Feb 27, 2018
daa1ba8
Test changes
Feb 27, 2018
a907636
Error details and schema updates
Feb 28, 2018
ffd3c56
Resource now correctly updates. Removed Etag from Schema
Feb 28, 2018
106c0de
IoTHub doc update
Feb 28, 2018
fabaef4
Devices alphabetized
Feb 28, 2018
16cac29
accTest changes
Feb 28, 2018
3e927a6
IotHub import test
Feb 28, 2018
2ea2cfc
Modify IotHub docs
Mar 1, 2018
b68afc6
Config changes, remove un-needed eTag
Mar 1, 2018
4d9bfc8
Correct primary_key in iothub
Mar 8, 2018
4114e23
Downgrading to v12.5.0-beta of the Azure SDK for Go/v9.7 of AutoRest
tombuildsstuff Mar 14, 2018
ae9b604
Refactoring / issues identified in the PR
tombuildsstuff Mar 14, 2018
48cfcd7
Renaming to match the convention
tombuildsstuff Mar 14, 2018
fe759b8
Fixing the spacing identified in the PR
tombuildsstuff Mar 14, 2018
422862d
Adding back in `resourceGroup` to the Parameters body
tombuildsstuff Mar 14, 2018
d55a1b9
Adding a custom waiter to the delete method
tombuildsstuff Mar 14, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,5 @@ website/vendor
# Test exclusions
!command/test-fixtures/**/*.tfstate
!command/test-fixtures/**/.terraform/

.env.sh
14 changes: 14 additions & 0 deletions azurerm/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/Azure/azure-sdk-for-go/services/eventgrid/mgmt/2017-09-15-preview/eventgrid"
"github.com/Azure/azure-sdk-for-go/services/eventhub/mgmt/2017-04-01/eventhub"
"github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac"
"github.com/Azure/azure-sdk-for-go/services/iothub/mgmt/2017-07-01/devices"
keyVault "github.com/Azure/azure-sdk-for-go/services/keyvault/2016-10-01/keyvault"
"github.com/Azure/azure-sdk-for-go/services/keyvault/mgmt/2016-10-01/keyvault"
"github.com/Azure/azure-sdk-for-go/services/monitor/mgmt/2017-05-01-preview/insights"
Expand Down Expand Up @@ -111,6 +112,9 @@ type ArmClient struct {
vmImageClient compute.VirtualMachineImagesClient
vmClient compute.VirtualMachinesClient

// Devices
iothubResourceClient devices.IotHubResourceClient

// Databases
mysqlConfigurationsClient mysql.ConfigurationsClient
mysqlDatabasesClient mysql.DatabasesClient
Expand Down Expand Up @@ -340,6 +344,7 @@ func getArmClient(c *authentication.Config) (*ArmClient, error) {
client.registerContainerInstanceClients(endpoint, c.SubscriptionID, auth, sender)
client.registerContainerRegistryClients(endpoint, c.SubscriptionID, auth, sender)
client.registerDatabases(endpoint, c.SubscriptionID, auth, sender)
client.registerDeviceClients(endpoint, c.SubscriptionID, auth, sender)
client.registerDNSClients(endpoint, c.SubscriptionID, auth, sender)
client.registerEventGridClients(endpoint, c.SubscriptionID, auth, sender)
client.registerEventHubClients(endpoint, c.SubscriptionID, auth, sender)
Expand Down Expand Up @@ -585,6 +590,15 @@ func (c *ArmClient) registerDatabases(endpoint, subscriptionId string, auth auto
c.sqlServersClient = sqlSrvClient
}

func (c *ArmClient) registerDeviceClients(endpoint, subscriptionId string, auth autorest.Authorizer, sender autorest.Sender) {
ihrc := devices.NewIotHubResourceClientWithBaseURI(endpoint, subscriptionId)
setUserAgent(&ihrc.Client)
ihrc.Authorizer = auth
ihrc.Sender = sender
ihrc.SkipResourceProviderRegistration = c.skipProviderRegistration
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rather than these 4 lines:

setUserAgent(&ihrc.Client)
ihrc.Authorizer = auth
ihrc.Sender = sender
ihrc.SkipResourceProviderRegistration = c.skipProviderRegistration

can we swap this over to using the newer style registration function? e.g.

c.configureClient(&ihrc.Client, auth)

c.iothubResourceClient = ihrc
}

func (c *ArmClient) registerDNSClients(endpoint, subscriptionId string, auth autorest.Authorizer, sender autorest.Sender) {
dn := dns.NewRecordSetsClientWithBaseURI(endpoint, subscriptionId)
c.configureClient(&dn.Client, auth)
Expand Down
31 changes: 31 additions & 0 deletions azurerm/import_arm_iothub_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package azurerm

import (
"testing"

"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)

func TestAccAzureRMIotHub_importBasic(t *testing.T) {
resourceName := "azurerm_iothub.test"

ri := acctest.RandInt()
config := testAccAzureRMIotHub_basicStandard(ri, testLocation())

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMIotHubDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}
2 changes: 2 additions & 0 deletions azurerm/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ func Provider() terraform.ResourceProvider {
"azurerm_express_route_circuit": resourceArmExpressRouteCircuit(),
"azurerm_function_app": resourceArmFunctionApp(),
"azurerm_image": resourceArmImage(),
"azurerm_iothub": resourceArmIotHub(),
"azurerm_key_vault": resourceArmKeyVault(),
"azurerm_key_vault_certificate": resourceArmKeyVaultCertificate(),
"azurerm_key_vault_key": resourceArmKeyVaultKey(),
Expand Down Expand Up @@ -279,6 +280,7 @@ func determineAzureResourceProvidersToRegister(providerList []resources.Provider
"Microsoft.ContainerService": {},
"Microsoft.DBforMySQL": {},
"Microsoft.DBforPostgreSQL": {},
"Microsoft.Devices": {},
"Microsoft.DocumentDB": {},
"Microsoft.EventGrid": {},
"Microsoft.EventHub": {},
Expand Down
2 changes: 1 addition & 1 deletion azurerm/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ func TestProvider_impl(t *testing.T) {

func testAccPreCheck(t *testing.T) {
variables := []string{
"ARM_SUBSCRIPTION_ID",
"ARM_CLIENT_ID",
"ARM_CLIENT_SECRET",
"ARM_SUBSCRIPTION_ID",
"ARM_TENANT_ID",
"ARM_TEST_LOCATION",
"ARM_TEST_LOCATION_ALT",
Expand Down
271 changes: 271 additions & 0 deletions azurerm/resource_arm_iothub_resource_provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
package azurerm

import (
"errors"
"fmt"

"github.com/Azure/azure-sdk-for-go/services/iothub/mgmt/2017-07-01/devices"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response"
)

func resourceArmIotHub() *schema.Resource {
return &schema.Resource{
Create: resourceArmIotHubCreateAndUpdate,
Read: resourceArmIotHubRead,
Update: resourceArmIotHubCreateAndUpdate,
Delete: resourceArmIotHubDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

given we're supporting Import here - could we add a test for it? Here's an example of how we're doing this for Subnets

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is added :) I'll add more import and resource tests given the time.


Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"tags": tagsSchema(),
"location": locationSchema(),

"resource_group_name": resourceGroupNameSchema(),
"sku": {
Type: schema.TypeList,
MaxItems: 1,
Required: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
DiffSuppressFunc: ignoreCaseDiffSuppressFunc,
ValidateFunc: validation.StringInSlice([]string{
string(devices.F1),
string(devices.S1),
string(devices.S2),
string(devices.S3),
}, true),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since we're allowing this to be case insensitive, can we add:

DiffSuppressFunc: ignoreCaseDiffSuppressFunc,

},

"tier": {
Type: schema.TypeString,
Required: true,
DiffSuppressFunc: ignoreCaseDiffSuppressFunc,
ValidateFunc: validation.StringInSlice([]string{
string(devices.Free),
string(devices.Standard),
}, true),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since we're allowing this to be case insensitive, can we add:

DiffSuppressFunc: ignoreCaseDiffSuppressFunc,

},

"capacity": {
Type: schema.TypeInt,
Required: true,
ValidateFunc: validation.IntAtLeast(1),
},
},
},
},

"type": {
Type: schema.TypeString,
Computed: true,
},

"hostname": {
Type: schema.TypeString,
Computed: true,
},

"shared_access_policy": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"key_name": {
Type: schema.TypeString,
Computed: true,
},
"primary_key": {
Type: schema.TypeString,
Computed: true,
},
"secondary_key": {
Type: schema.TypeString,
Computed: true,
},
"permissions": {
Type: schema.TypeString,
Computed: true,
},
},
},
},
},
}

}

func resourceArmIotHubCreateAndUpdate(d *schema.ResourceData, meta interface{}) error {

armClient := meta.(*ArmClient)
iothubClient := armClient.iothubResourceClient
ctx := armClient.StopContext

rg := d.Get("resource_group_name").(string)
name := d.Get("name").(string)

res, err := iothubClient.CheckNameAvailability(ctx, devices.OperationInputs{
Name: &name,
})

if err != nil {
return err
}

if !*res.NameAvailable {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will this fail when updating a resource?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It updates the resource correctly. I want to add an accTest for this as well.

_, err := iothubClient.Get(ctx, rg, name)
if err != nil {
return errors.New(string(res.Reason))
}
}

location := d.Get("location").(string)

subscriptionID := armClient.subscriptionId
skuInfo := expandAzureRmIotHubSku(d)

desc := devices.IotHubDescription{
Resourcegroup: &rg,
Name: &name,
Location: &location,
Subscriptionid: &subscriptionID,
Sku: &skuInfo,
}

if tagsI, ok := d.GetOk("tags"); ok {
tags := tagsI.(map[string]interface{})
desc.Tags = *expandTags(tags)
}

future, err := iothubClient.CreateOrUpdate(ctx, rg, name, desc, "")
if err != nil {
return err
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we wrap this error to be more descriptive with what went wrong:


return fmt.Errorf("Error creating IoTHub %q (Resource Group %q): %+v", err)

}

err = future.WaitForCompletion(ctx, iothubClient.Client)
if err != nil {
return fmt.Errorf("Error creating or updating IotHub %q (Resource Group %q): %+v", name, rg, err)
}

desc, err = iothubClient.Get(ctx, rg, name)
if err != nil {
return err
}

d.SetId(*desc.ID)
return resourceArmIotHubRead(d, meta)

}

func expandAzureRmIotHubSku(d *schema.ResourceData) devices.IotHubSkuInfo {
skuList := d.Get("sku").([]interface{})
skuMap := skuList[0].(map[string]interface{})
cap := int64(skuMap["capacity"].(int))

name := skuMap["name"].(string)
tier := skuMap["tier"].(string)

return devices.IotHubSkuInfo{
Name: devices.IotHubSku(name),
Tier: devices.IotHubSkuTier(tier),
Capacity: &cap,
}

}

func resourceArmIotHubRead(d *schema.ResourceData, meta interface{}) error {
armClient := meta.(*ArmClient)
iothubClient := armClient.iothubResourceClient
ctx := armClient.StopContext

id, err := parseAzureResourceID(d.Id())

if err != nil {
return err
}

iothubName := id.Path["IotHubs"]
desc, err := iothubClient.Get(ctx, id.ResourceGroup, iothubName)

if err != nil {
return err
}

keysResp, err := iothubClient.ListKeys(ctx, id.ResourceGroup, iothubName)
keyList := keysResp.Response()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we check the error returned here?


var keys []map[string]interface{}
for _, key := range *keyList.Value {
keyMap := make(map[string]interface{})

if keyName := key.KeyName; keyName != nil {
keyMap["key_name"] = *keyName
}

if primaryKey := key.PrimaryKey; primaryKey != nil {
keyMap["primary_key"] = *primaryKey
}

if secondaryKey := key.SecondaryKey; secondaryKey != nil {
keyMap["secondary_key"] = *secondaryKey
}

keyMap["permissions"] = string(key.Rights)
keys = append(keys, keyMap)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we pull this out into a separate function flattenAzureIotSharedAccessPolicy?


if err := d.Set("shared_access_policy", keys); err != nil {
return fmt.Errorf("Error flattening `shared_access_policy` in IoTHub %q: %+v", iothubName, err)
}

if properties := desc.Properties; properties != nil {
d.Set("hostname", properties.HostName)
}

d.Set("type", desc.Type)
flattenAndSetTags(d, &desc.Tags)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we also set the sku block? This should be returned as part of the properties object

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so the Import test (TestAccAzureRMIotHub_importBasic ) currently fails due to the following fields not being set - can we set them:

  • location
  • name
  • resource_group_name
  • sku
    • capacity
    • name
    • tier

on that note - could we pull this out into a new flatten function? I believe this should be what's needed to flatten the SKU block:

func flattenArmIoTHubSku(input *devices.IotHubSkuInfo) []interface {
  sku := make(map[string]interface{}, 0)

  if capacity := input.Capacity; capacity != nil {
    sku["capacity"] = *capacity
  }

  if name := input.Name; name != nil {
    sku["name"] = *name
  }

  if tier := input.Tier; tier != nil {
    sku["tier"] = *tier
  }

  return []interface{}{result}
}


return nil
}

func resourceArmIotHubDelete(d *schema.ResourceData, meta interface{}) error {

id, err := parseAzureResourceID(d.Id())
if err != nil {
return err
}

iothubClient := meta.(*ArmClient).iothubResourceClient
ctx := meta.(*ArmClient).StopContext

iotHubName := id.Path["IotHubs"]

future, err := iothubClient.Delete(ctx, id.ResourceGroup, iotHubName)
if err != nil {
if response.WasNotFound(future.Response()) {
return nil
}
return err
}

err = future.WaitForCompletion(ctx, iothubClient.Client)
if err != nil {
if response.WasNotFound(future.Response()) {
return nil
}
return fmt.Errorf("Error waiting for the deletion of IoTHub %q", iotHubName)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a timeout issue here. IoTHub is deleted in Azure, but terraform times out with an error destroying resource.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so this timeout is configured in the client initialisation above: https://github.com/terraform-providers/terraform-provider-azurerm/pull/887/files/3e927a6d96d0c91b3dc132d9b9e83f4b1fc96650#diff-848235249455ee48aaf3ea5d431960aeR597

we can actually swap that out for this function:

https://github.com/terraform-providers/terraform-provider-azurerm/blob/master/azurerm/config.go#L435
which sets a 60m polling timeout, which should solve this issue. There's a separate task to move everything over to that at some point, but any changes to azure/config.go cause merge conflicts, so we're trying to do it piecemeal rather than bit by bit

Hope that helps :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very helpful, thank you!

}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should be able to make this:

err = future.WaitForCompletion(ctx, client.Client)
if err != nil {
  if response.WasNotFound(future.Response()) {
    return nil
  }

  return fmt.Errorf("Error waiting for the deletion...")
}

return nil


return nil
}
Loading