Skip to content

Commit

Permalink
azurerm_iothub - split create and update function (#24180)
Browse files Browse the repository at this point in the history
* `azurerm_iothub` - split create and udpate function

* add cosmosdb endpoint test case

* update per comment

* revert mistake edit
  • Loading branch information
ziyeqf authored Jan 4, 2024
1 parent 1c08aae commit 4a0ccb5
Show file tree
Hide file tree
Showing 2 changed files with 303 additions and 16 deletions.
203 changes: 187 additions & 16 deletions internal/services/iothub/iothub_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"strings"
"time"

"github.com/hashicorp/go-azure-helpers/lang/pointer"
"github.com/hashicorp/go-azure-helpers/resourcemanager/commonids"
"github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema"
"github.com/hashicorp/go-azure-helpers/resourcemanager/identity"
Expand Down Expand Up @@ -70,9 +71,9 @@ func suppressWhenAny(fs ...pluginsdk.SchemaDiffSuppressFunc) pluginsdk.SchemaDif

func resourceIotHub() *pluginsdk.Resource {
return &pluginsdk.Resource{
Create: resourceIotHubCreateUpdate,
Create: resourceIotHubCreate,
Read: resourceIotHubRead,
Update: resourceIotHubCreateUpdate,
Update: resourceIotHubUpdate,
Delete: resourceIotHubDelete,

SchemaVersion: 1,
Expand Down Expand Up @@ -661,7 +662,7 @@ func resourceIotHub() *pluginsdk.Resource {
}
}

func resourceIotHubCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error {
func resourceIotHubCreate(d *pluginsdk.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).IoTHub.ResourceClient
ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d)
defer cancel()
Expand All @@ -683,18 +684,15 @@ func resourceIotHubCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) err
if !utils.ResponseWasNotFound(existing.Response) {
return tf.ImportAsExistsError("azurerm_iothub", id.ID())
}
}

res, err := client.CheckNameAvailability(ctx, devices.OperationInputs{
Name: &id.Name,
})
if err != nil {
return fmt.Errorf("An error occurred checking if the IoTHub name was unique: %+v", err)
}
res, err := client.CheckNameAvailability(ctx, devices.OperationInputs{Name: &id.Name})
if err != nil {
return fmt.Errorf("An error occurred checking if the IoTHub name was unique: %+v", err)
}

if !*res.NameAvailable {
if _, err = client.Get(ctx, id.ResourceGroup, id.Name); err != nil {
return fmt.Errorf("An IoTHub already exists with the name %q - please choose an alternate name: %s", id.Name, string(res.Reason))
if !*res.NameAvailable {
if _, err = client.Get(ctx, id.ResourceGroup, id.Name); err == nil {
return fmt.Errorf("An IoTHub already exists with the name %q - please choose an alternate name: %s", id.Name, string(res.Reason))
}
}
}

Expand All @@ -720,6 +718,7 @@ func resourceIotHubCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) err
}

if _, ok := d.GetOk("endpoint"); ok {
var err error
routingProperties.Endpoints, err = expandIoTHubEndpoints(d, subscriptionId)
if err != nil {
return fmt.Errorf("expanding `endpoint`: %+v", err)
Expand Down Expand Up @@ -793,11 +792,183 @@ func resourceIotHubCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) err

future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.Name, props, "")
if err != nil {
return fmt.Errorf("creating/updating %s: %+v", id, err)
return fmt.Errorf("creating %s: %+v", id, err)
}

if err := future.WaitForCompletionRef(ctx, client.Client); err != nil {
return fmt.Errorf("waiting for creation/update of %q: %+v", id, err)
return fmt.Errorf("waiting for creation of %q: %+v", id, err)
}

d.SetId(id.ID())

return resourceIotHubRead(d, meta)
}

func resourceIotHubUpdate(d *pluginsdk.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).IoTHub.ResourceClient
ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d)
defer cancel()
subscriptionId := meta.(*clients.Client).Account.SubscriptionId

id, err := parse.IotHubID(d.Id())
if err != nil {
return fmt.Errorf("parsing %s: %+v", id, err)
}

locks.ByName(id.Name, IothubResourceName)
defer locks.UnlockByName(id.Name, IothubResourceName)

iothub, err := client.Get(ctx, id.ResourceGroup, id.Name)
if err != nil {
return fmt.Errorf("reading %s: %+v", id, err)
}

if iothub.Properties == nil {
return fmt.Errorf("reading %s: properties was nil", id)
}

prop := *iothub.Properties

if d.HasChange("sku") {
iothub.Sku = expandIoTHubSku(d)
}

if d.HasChange("identity") {
identity, err := expandIotHubIdentity(d.Get("identity").([]interface{}))
if err != nil {
return fmt.Errorf("expanding `identity`: %+v", err)
}
iothub.Identity = identity
}

if d.HasChange("tags") {
iothub.Tags = tags.Expand(d.Get("tags").(map[string]interface{}))
}

if d.HasChange("route") {
if prop.Routing == nil {
prop.Routing = &devices.RoutingProperties{}
}
prop.Routing.Routes = expandIoTHubRoutes(d)
}

if d.HasChange("enrichment") {
if prop.Routing == nil {
prop.Routing = &devices.RoutingProperties{}
}
prop.Routing.Enrichments = expandIoTHubEnrichments(d)
}

if d.HasChange("fallback_route") {
if prop.Routing == nil {
prop.Routing = &devices.RoutingProperties{}
}
if _, ok := d.GetOk("fallback_route"); ok {
prop.Routing.FallbackRoute = expandIoTHubFallbackRoute(d)
} else {
prop.Routing.FallbackRoute = &devices.FallbackRouteProperties{
Source: utils.String(string(devices.RoutingSourceDeviceMessages)),
Condition: utils.String("true"),
EndpointNames: &[]string{"events"},
IsEnabled: utils.Bool(true),
}
}
}

if d.HasChange("endpoint") {
if prop.Routing == nil {
prop.Routing = &devices.RoutingProperties{}
}
prop.Routing.Endpoints, err = expandIoTHubEndpoints(d, subscriptionId)
if err != nil {
return fmt.Errorf("expanding `endpoint`: %+v", err)
}
}

if d.HasChange("file_upload") {
storageEndpoints, messagingEndpoints, enableFileUploadNotifications, err := expandIoTHubFileUpload(d)
if err != nil {
return fmt.Errorf("expanding `file_upload`: %+v", err)
}
prop.StorageEndpoints = storageEndpoints
prop.MessagingEndpoints = messagingEndpoints
prop.EnableFileUploadNotifications = &enableFileUploadNotifications
}

if d.HasChange("cloud_to_device") {
cloudToDeviceProperties := &devices.CloudToDeviceProperties{}
if _, ok := d.GetOk("cloud_to_device"); ok {
cloudToDeviceProperties = expandIoTHubCloudToDevice(d)
}
prop.CloudToDevice = cloudToDeviceProperties
}

if d.HasChange("network_rule_set") {
if _, ok := d.GetOk("network_rule_set"); ok {
prop.NetworkRuleSets = expandNetworkRuleSetProperties(d)
} else {
prop.NetworkRuleSets = &devices.NetworkRuleSetProperties{}
}
}

if d.HasChange("public_network_access_enabled") {
// nolint staticcheck
if v, ok := d.GetOkExists("public_network_access_enabled"); ok {
enabled := devices.PublicNetworkAccessDisabled
if v.(bool) {
enabled = devices.PublicNetworkAccessEnabled
}
prop.PublicNetworkAccess = enabled
}
}

if d.HasChange("event_hub_retention_in_days") {
retention, retentionOk := d.GetOk("event_hub_retention_in_days")
if prop.EventHubEndpoints == nil {
prop.EventHubEndpoints = make(map[string]*devices.EventHubProperties)
}
eh, ok := prop.EventHubEndpoints["events"]
if !ok {
prop.EventHubEndpoints["events"] = &devices.EventHubProperties{}
}
eh.RetentionTimeInDays = nil
if retentionOk {
eh.RetentionTimeInDays = pointer.To(int64(retention.(int)))
}
}

if d.HasChange("event_hub_partition_count") {
partition, partitionOk := d.GetOk("event_hub_partition_count")
if prop.EventHubEndpoints == nil {
prop.EventHubEndpoints = make(map[string]*devices.EventHubProperties)
}
if eh, ok := prop.EventHubEndpoints["events"]; ok {
eh.PartitionCount = nil
if partitionOk {
eh.PartitionCount = pointer.To(int32(partition.(int)))
}
}
}

if d.HasChange("local_authentication_enabled") {
prop.DisableLocalAuth = pointer.To(!d.Get("local_authentication_enabled").(bool))
}

if d.HasChange("min_tls_version") {
prop.MinTLSVersion = nil
if v, ok := d.GetOk("min_tls_version"); ok {
prop.MinTLSVersion = pointer.To(v.(string))
}
}

iothub.Properties = &prop
future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.Name, iothub, "")
if err != nil {
return fmt.Errorf("updating %s: %+v", id, err)
}

if err := future.WaitForCompletionRef(ctx, client.Client); err != nil {
return fmt.Errorf("waiting for update of %q: %+v", id, err)
}

d.SetId(id.ID())
Expand Down
116 changes: 116 additions & 0 deletions internal/services/iothub/iothub_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,28 @@ func TestAccIotHub_endpointAuthenticationTypeUpdate(t *testing.T) {
})
}

func TestAccIotHub_cosmosDBRouteUpdate(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_iothub", "test")
r := IotHubResource{}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.updateWithCosmosDBRoute(data, false),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
{
Config: r.updateWithCosmosDBRoute(data, true),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
})
}

func (t IotHubResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) {
id, err := parse.IotHubID(state.ID)
if err != nil {
Expand Down Expand Up @@ -2082,3 +2104,97 @@ resource "azurerm_role_assignment" "test_azure_event_hubs_data_sender_system" {
}
`, data.RandomInteger, data.Locations.Primary)
}

func (IotHubResource) updateWithCosmosDBRoute(data acceptance.TestData, update bool) string {
tagsBlock := `
tags = {
test = "1"
}
`
if !update {
tagsBlock = ""
}

return fmt.Sprintf(`
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "iothub" {
name = "acctest-iothub-%[1]d"
location = "eastus"
}
resource "azurerm_resource_group" "endpoint" {
name = "acctest-iothub-db-%[1]d"
location = "eastus"
}
resource "azurerm_cosmosdb_account" "test" {
name = "acctest-ca-%[1]d"
location = azurerm_resource_group.endpoint.location
resource_group_name = azurerm_resource_group.endpoint.name
offer_type = "Standard"
kind = "GlobalDocumentDB"
consistency_policy {
consistency_level = "Strong"
}
geo_location {
location = azurerm_resource_group.endpoint.location
failover_priority = 0
}
}
resource "azurerm_cosmosdb_sql_database" "test" {
name = "acctest-%[1]d"
resource_group_name = azurerm_cosmosdb_account.test.resource_group_name
account_name = azurerm_cosmosdb_account.test.name
}
resource "azurerm_cosmosdb_sql_container" "test" {
name = "acctest-%[1]d"
resource_group_name = azurerm_cosmosdb_account.test.resource_group_name
account_name = azurerm_cosmosdb_account.test.name
database_name = azurerm_cosmosdb_sql_database.test.name
partition_key_path = "/definition/id"
}
resource "azurerm_iothub" "test" {
name = "acc-%[1]d"
resource_group_name = azurerm_resource_group.iothub.name
location = azurerm_resource_group.iothub.location
sku {
name = "B1"
capacity = "1"
}
%[2]s
}
resource "azurerm_iothub_endpoint_cosmosdb_account" "test" {
name = "acct-%[1]d"
resource_group_name = azurerm_resource_group.endpoint.name
iothub_id = azurerm_iothub.test.id
container_name = azurerm_cosmosdb_sql_container.test.name
database_name = azurerm_cosmosdb_sql_database.test.name
endpoint_uri = azurerm_cosmosdb_account.test.endpoint
primary_key = azurerm_cosmosdb_account.test.primary_key
secondary_key = azurerm_cosmosdb_account.test.secondary_key
}
resource "azurerm_iothub_route" "test" {
resource_group_name = azurerm_resource_group.iothub.name
iothub_name = azurerm_iothub.test.name
name = "acctest-%[1]d"
source = "DeviceMessages"
condition = "true"
endpoint_names = [azurerm_iothub_endpoint_cosmosdb_account.test.name]
enabled = false
depends_on = [azurerm_iothub_endpoint_cosmosdb_account.test]
}
`, data.RandomInteger, tagsBlock)
}

0 comments on commit 4a0ccb5

Please sign in to comment.