From 3d610e1ebce4d61cf03e8756bb5fc50786db3186 Mon Sep 17 00:00:00 2001 From: srushti-patl Date: Tue, 17 Dec 2024 14:27:45 -0800 Subject: [PATCH 1/5] feat: Updating service token resource to support network token creation --- .../resources/fabric/service_token/models.go | 64 +++++++++-- .../fabric/service_token/resource_schema.go | 10 +- .../fabric/service_token/resource_test.go | 103 ++++++++++++++---- 3 files changed, 139 insertions(+), 38 deletions(-) diff --git a/internal/resources/fabric/service_token/models.go b/internal/resources/fabric/service_token/models.go index 2e37c57b5..f13cb04da 100644 --- a/internal/resources/fabric/service_token/models.go +++ b/internal/resources/fabric/service_token/models.go @@ -28,6 +28,9 @@ func buildCreateRequest(d *schema.ResourceData) fabricv4.ServiceToken { } serviceTokenRequest.SetExpirationDateTime(expirationTime) + descriptionConfig := d.Get("description").(string) + serviceTokenRequest.SetDescription(descriptionConfig) + connectionConfig := d.Get("service_token_connection").(*schema.Set).List() connection := connectionTerraformToGo(connectionConfig) serviceTokenRequest.SetConnection(connection) @@ -257,6 +260,8 @@ func serviceTokenResponseMap(token *fabricv4.ServiceToken) map[string]interface{ const TimeFormat = "2006-01-02T15:04:05.000Z" serviceToken["expiration_date_time"] = expirationDateTime.Format(TimeFormat) serviceToken["state"] = token.GetState() + serviceToken["description"] = token.GetDescription() + if token.Connection != nil { connection := token.GetConnection() serviceToken["service_token_connection"] = connectionGoToTerraform(&connection) @@ -290,7 +295,9 @@ func connectionTerraformToGo(connectionTerraform []interface{}) fabricv4.Service connection.SetType(fabricv4.ServiceTokenConnectionType(typeVal)) uuid := connectionMap["uuid"].(string) - connection.SetUuid(uuid) + if uuid != "" { + connection.SetUuid(uuid) + } allowRemoteConnection := connectionMap["allow_remote_connection"].(bool) connection.SetAllowRemoteConnection(allowRemoteConnection) @@ -299,10 +306,12 @@ func connectionTerraformToGo(connectionTerraform []interface{}) fabricv4.Service connection.SetAllowCustomBandwidth(allowCustomBandwidth) bandwidthLimit := connectionMap["bandwidth_limit"].(int) - connection.SetBandwidthLimit(int32(bandwidthLimit)) + if bandwidthLimit > 0 { + connection.SetBandwidthLimit(int32(bandwidthLimit)) + } supportedBandwidths := connectionMap["supported_bandwidths"].([]interface{}) - if supportedBandwidths != nil { + if len(supportedBandwidths) > 0 { int32Bandwidths := make([]int32, len(supportedBandwidths)) for i, v := range supportedBandwidths { int32Bandwidths[i] = int32(v.(int)) @@ -508,9 +517,31 @@ func networkTerraformToGo(networkList []interface{}) fabricv4.SimplifiedTokenNet var network fabricv4.SimplifiedTokenNetwork networkListMap := networkList[0].(map[string]interface{}) uuid := networkListMap["uuid"].(string) + href := networkListMap["href"].(string) type_ := networkListMap["type"].(string) - network.SetUuid(uuid) - network.SetType(fabricv4.SimplifiedTokenNetworkType(type_)) + name := networkListMap["name"].(string) + scope := networkListMap["scope"].(string) + locationList := networkListMap["location"].(*schema.Set).List() + + if uuid != "" { + network.SetUuid(uuid) + } + if href != "" { + network.SetHref(href) + } + if type_ != "" { + network.SetType(fabricv4.SimplifiedTokenNetworkType(type_)) + } + if name != "" { + network.SetName(name) + } + if scope != "" { + network.SetScope(fabricv4.SimplifiedTokenNetworkScope(scope)) + } + if len(locationList) != 0 { + location := equinix_fabric_schema.LocationTerraformToGo(locationList) + network.SetLocation(location) + } return network } @@ -748,10 +779,25 @@ func networkGoToTerraform(network *fabricv4.SimplifiedTokenNetwork) *schema.Set } mappedNetwork := make(map[string]interface{}) - mappedNetwork["uuid"] = network.GetUuid() - mappedNetwork["href"] = network.GetHref() - mappedNetwork["type"] = string(network.GetType()) - + if uuid := network.GetUuid(); uuid != "" { + mappedNetwork["uuid"] = uuid + } + if href := network.GetHref(); href != "" { + mappedNetwork["href"] = href + } + if type_ := network.GetType(); type_ != "" { + mappedNetwork["type"] = string(type_) + } + if name := network.GetName(); name != "" { + mappedNetwork["name"] = name + } + if scope := network.GetName(); scope != "" { + mappedNetwork["scope"] = string(network.GetScope()) + } + if network.Location != nil { + location := network.GetLocation() + mappedNetwork["location"] = equinix_fabric_schema.LocationGoToTerraform(&location) + } return schema.NewSet( schema.HashResource(networkSch()), []interface{}{mappedNetwork}, diff --git a/internal/resources/fabric/service_token/resource_schema.go b/internal/resources/fabric/service_token/resource_schema.go index fe4bf0b02..66ba28192 100644 --- a/internal/resources/fabric/service_token/resource_schema.go +++ b/internal/resources/fabric/service_token/resource_schema.go @@ -181,7 +181,6 @@ func accessPointSelectorsSch() *schema.Resource { "port": { Type: schema.TypeSet, Optional: true, - Computed: true, Description: "Port Configuration", MaxItems: 1, Elem: portSch(), @@ -189,7 +188,6 @@ func accessPointSelectorsSch() *schema.Resource { "link_protocol": { Type: schema.TypeSet, Optional: true, - Computed: true, Description: "Link protocol Configuration", MaxItems: 1, Elem: linkProtocolSch(), @@ -204,7 +202,6 @@ func accessPointSelectorsSch() *schema.Resource { "interface": { Type: schema.TypeSet, Optional: true, - Computed: true, Description: "Virtual Device Interface Configuration", MaxItems: 1, Elem: interfaceSch(), @@ -374,8 +371,7 @@ func networkSch() *schema.Resource { Schema: map[string]*schema.Schema{ "uuid": { Type: schema.TypeString, - Optional: true, - Computed: true, + Required: true, Description: "Equinix-assigned Network identifier", }, "href": { @@ -385,25 +381,21 @@ func networkSch() *schema.Resource { }, "type": { Type: schema.TypeString, - Optional: true, Computed: true, Description: "Type of Network", }, "name": { Type: schema.TypeString, - Optional: true, Computed: true, Description: "Network Name", }, "scope": { Type: schema.TypeString, - Optional: true, Computed: true, Description: "Scope of Network", }, "location": { Type: schema.TypeSet, - Optional: true, Computed: true, Description: "Location", Elem: &schema.Resource{ diff --git a/internal/resources/fabric/service_token/resource_test.go b/internal/resources/fabric/service_token/resource_test.go index 09a7f4128..088dc68a4 100644 --- a/internal/resources/fabric/service_token/resource_test.go +++ b/internal/resources/fabric/service_token/resource_test.go @@ -157,7 +157,43 @@ func TestAccFabricZsidePortServiceToken_PNFV(t *testing.T) { }) } -func testAccFabricZsideVirtualDeviceServiceTokenConfig(serviceTokenName string, serviceTokenDescription string, virtualDeviceUuid string) string { +func TestAccFabricZsideNetworkServiceToken_PNFV(t *testing.T) { + serviceTokenName, serviceTokenUpdatedName := "token_zwan_PNFV", "UP_Token_zwan_PNFV" + serviceTokenDescription, serviceTokenUpdatedDescription := "zside port token", "Updated zside port token" + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.TestAccPreCheck(t) }, + Providers: acceptance.TestAccProviders, + CheckDestroy: CheckServiceTokenDelete, + Steps: []resource.TestStep{ + { + Config: testAccFabricZsideNetworkServiceTokenConfig(serviceTokenName, serviceTokenDescription), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("equinix_fabric_service_token.test", "uuid"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "name", serviceTokenName), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "type", "VC_TOKEN"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "description", serviceTokenDescription), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "expiration_date_time", "2025-02-18T06:43:49.981Z"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.network.0.uuid", "ff241e62-42f5-48bc-96c4-e0b5297fbed1"), + ), + ExpectNonEmptyPlan: false, + }, + { + Config: testAccFabricZsideNetworkServiceTokenConfig(serviceTokenUpdatedName, serviceTokenUpdatedDescription), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("equinix_fabric_service_token.test", "uuid"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "name", serviceTokenUpdatedName), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "type", "VC_TOKEN"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "description", serviceTokenUpdatedDescription), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "expiration_date_time", "2025-02-18T06:43:49.981Z"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.network.0.uuid", "ff241e62-42f5-48bc-96c4-e0b5297fbed1"), + ), + ExpectNonEmptyPlan: false, + }, + }, + }) +} + +func testAccFabricAsidePortServiceTokenConfig(serviceTokenName string, serviceTokenDescription string, portUuid string) string { return fmt.Sprintf( `resource "equinix_fabric_service_token" "test"{ type = "VC_TOKEN" @@ -166,16 +202,16 @@ func testAccFabricZsideVirtualDeviceServiceTokenConfig(serviceTokenName string, expiration_date_time = "2025-01-18T06:43:49.981Z" service_token_connection { type = "EVPL_VC" - supported_bandwidths = [50, 200, 10000] - z_side { + bandwidth_limit = 1000 + a_side { access_point_selectors{ - type = "VD" - virtual_device{ - type = "EDGE" + type = "COLO" + port { uuid = "%s" } - interface{ - type = "NETWORK" + link_protocol { + type = "DOT1Q" + vlan_tag = "2987" } } } @@ -186,10 +222,10 @@ func testAccFabricZsideVirtualDeviceServiceTokenConfig(serviceTokenName string, } } - `, serviceTokenName, serviceTokenDescription, virtualDeviceUuid) + `, serviceTokenName, serviceTokenDescription, portUuid) } -func testAccFabricAsidePortServiceTokenConfig(serviceTokenName string, serviceTokenDescription string, portUuid string) string { +func testAccFabricZsidePortServiceTokenConfig(serviceTokenName string, serviceTokenDescription string, portUuid string) string { return fmt.Sprintf( `resource "equinix_fabric_service_token" "test"{ type = "VC_TOKEN" @@ -198,8 +234,8 @@ func testAccFabricAsidePortServiceTokenConfig(serviceTokenName string, serviceTo expiration_date_time = "2025-01-18T06:43:49.981Z" service_token_connection { type = "EVPL_VC" - bandwidth_limit = 1000 - a_side { + supported_bandwidths = [50, 200, 10000] + z_side { access_point_selectors{ type = "COLO" port { @@ -207,7 +243,7 @@ func testAccFabricAsidePortServiceTokenConfig(serviceTokenName string, serviceTo } link_protocol { type = "DOT1Q" - vlan_tag = "2987" + vlan_tag = "2087" } } } @@ -221,7 +257,7 @@ func testAccFabricAsidePortServiceTokenConfig(serviceTokenName string, serviceTo `, serviceTokenName, serviceTokenDescription, portUuid) } -func testAccFabricZsidePortServiceTokenConfig(serviceTokenName string, serviceTokenDescription string, portUuid string) string { +func testAccFabricZsideVirtualDeviceServiceTokenConfig(serviceTokenName string, serviceTokenDescription string, virtualDeviceUuid string) string { return fmt.Sprintf( `resource "equinix_fabric_service_token" "test"{ type = "VC_TOKEN" @@ -233,13 +269,13 @@ func testAccFabricZsidePortServiceTokenConfig(serviceTokenName string, serviceTo supported_bandwidths = [50, 200, 10000] z_side { access_point_selectors{ - type = "COLO" - port { + type = "VD" + virtual_device{ + type = "EDGE" uuid = "%s" } - link_protocol { - type = "DOT1Q" - vlan_tag = "2087" + interface{ + type = "NETWORK" } } } @@ -250,7 +286,34 @@ func testAccFabricZsidePortServiceTokenConfig(serviceTokenName string, serviceTo } } - `, serviceTokenName, serviceTokenDescription, portUuid) + `, serviceTokenName, serviceTokenDescription, virtualDeviceUuid) +} + +func testAccFabricZsideNetworkServiceTokenConfig(serviceTokenName string, serviceTokenDescription string) string { + return fmt.Sprintf( + `resource "equinix_fabric_service_token" "test" { + type = "VC_TOKEN" + name = "%s" + description = "%s" + expiration_date_time = "2025-02-18T06:43:49.981Z" + service_token_connection { + type = "EVPLAN_VC" + supported_bandwidths = [50, 200, 10000] + z_side { + access_point_selectors{ + type = "NETWORK" + network { + uuid = "ff241e62-42f5-48bc-96c4-e0b5297fbed1" + } + } + } + } + notifications { + type = "ALL" + emails = ["example@equinix.com", "test1@equinix.com"] + } + } + `, serviceTokenName, serviceTokenDescription) } func CheckServiceTokenDelete(s *terraform.State) error { From 58ae4859521f292f36ac63f2b297d3c98ac3d299 Mon Sep 17 00:00:00 2001 From: srushti-patl Date: Tue, 17 Dec 2024 16:09:08 -0800 Subject: [PATCH 2/5] fix: Updating docs --- docs/resources/fabric_service_token.md | 79 ++++++++++++------- .../aside_colo_service_token.tf | 4 +- .../zside_colo_service_token.tf | 4 +- .../zside_network_service_token.tf | 21 +++++ .../zside_vd_service_token.tf | 2 +- .../fabric/service_token/resource_test.go | 21 +++-- .../resources/fabric_service_token.md.tmpl | 3 + 7 files changed, 94 insertions(+), 40 deletions(-) create mode 100644 examples/resources/equinix_fabric_service_token/zside_network_service_token.tf diff --git a/docs/resources/fabric_service_token.md b/docs/resources/fabric_service_token.md index 92c1a36da..437523ab9 100644 --- a/docs/resources/fabric_service_token.md +++ b/docs/resources/fabric_service_token.md @@ -15,8 +15,8 @@ Additional documentation: Aside Port Service Token ```terraform resource "equinix_fabric_service_token" "test" { - type = "VC_TOKEN" - description = "Aside COLO Service Token" + type = "VC_TOKEN" + description = "Aside COLO Service Token" expiration_date_time = "2025-01-18T06:43:49.981Z" service_token_connection { type = "EVPL_VC" @@ -44,8 +44,8 @@ resource "equinix_fabric_service_token" "test" { Zside Port Service Token ```terraform resource "equinix_fabric_service_token" "test"{ - type = "VC_TOKEN" - description = "Zside COLO Service Token" + type = "VC_TOKEN" + description = "Zside COLO Service Token" expiration_date_time = "2025-01-18T06:43:49.981Z" service_token_connection { type = "EVPL_VC" @@ -70,11 +70,36 @@ resource "equinix_fabric_service_token" "test"{ } ``` +Zside Network Service Token +```terraform +resource "equinix_fabric_service_token" "test" { + type = "VC_TOKEN" + description = "Zside Network Service Token" + expiration_date_time = "2025-01-18T06:43:49.986Z" + service_token_connection { + type = "EVPL_VC" + supported_bandwidths = [50, 200, 10000] + z_side { + access_point_selectors { + type = "NETWORK" + network { + uuid = "" + } + } + } + } + notifications { + type = "ALL" + emails = ["example@equinix.com"] + } +} +``` + Zside Virtual Device Service Token ```terraform resource "equinix_fabric_service_token" "test" { type = "VC_TOKEN" - description = "Zside VD Service Token" + description = "Zside VD Service Token" expiration_date_time = "2025-01-18T06:43:49.986Z" service_token_connection { type = "EVPL_VC" @@ -206,27 +231,27 @@ Optional: ### Nested Schema for `service_token_connection.a_side.access_point_selectors.network` -Optional: +Required: -- `location` (Block Set) Location (see [below for nested schema](#nestedblock--service_token_connection--a_side--access_point_selectors--network--location)) -- `name` (String) Network Name -- `scope` (String) Scope of Network -- `type` (String) Type of Network - `uuid` (String) Equinix-assigned Network identifier Read-Only: - `href` (String) Unique Resource Identifier +- `location` (Set of Object) Location (see [below for nested schema](#nestedatt--service_token_connection--a_side--access_point_selectors--network--location)) +- `name` (String) Network Name +- `scope` (String) Scope of Network +- `type` (String) Type of Network - + ### Nested Schema for `service_token_connection.a_side.access_point_selectors.network.location` -Optional: +Read-Only: -- `ibx` (String) IBX Code -- `metro_code` (String) Access point metro code -- `metro_name` (String) Access point metro name -- `region` (String) Access point region +- `ibx` (String) +- `metro_code` (String) +- `metro_name` (String) +- `region` (String) @@ -330,27 +355,27 @@ Optional: ### Nested Schema for `service_token_connection.z_side.access_point_selectors.network` -Optional: +Required: -- `location` (Block Set) Location (see [below for nested schema](#nestedblock--service_token_connection--z_side--access_point_selectors--network--location)) -- `name` (String) Network Name -- `scope` (String) Scope of Network -- `type` (String) Type of Network - `uuid` (String) Equinix-assigned Network identifier Read-Only: - `href` (String) Unique Resource Identifier +- `location` (Set of Object) Location (see [below for nested schema](#nestedatt--service_token_connection--z_side--access_point_selectors--network--location)) +- `name` (String) Network Name +- `scope` (String) Scope of Network +- `type` (String) Type of Network - + ### Nested Schema for `service_token_connection.z_side.access_point_selectors.network.location` -Optional: +Read-Only: -- `ibx` (String) IBX Code -- `metro_code` (String) Access point metro code -- `metro_name` (String) Access point metro name -- `region` (String) Access point region +- `ibx` (String) +- `metro_code` (String) +- `metro_name` (String) +- `region` (String) diff --git a/examples/resources/equinix_fabric_service_token/aside_colo_service_token.tf b/examples/resources/equinix_fabric_service_token/aside_colo_service_token.tf index b65b98be7..53f68d352 100644 --- a/examples/resources/equinix_fabric_service_token/aside_colo_service_token.tf +++ b/examples/resources/equinix_fabric_service_token/aside_colo_service_token.tf @@ -1,6 +1,6 @@ resource "equinix_fabric_service_token" "test" { - type = "VC_TOKEN" - description = "Aside COLO Service Token" + type = "VC_TOKEN" + description = "Aside COLO Service Token" expiration_date_time = "2025-01-18T06:43:49.981Z" service_token_connection { type = "EVPL_VC" diff --git a/examples/resources/equinix_fabric_service_token/zside_colo_service_token.tf b/examples/resources/equinix_fabric_service_token/zside_colo_service_token.tf index f4394340f..73eb49bff 100644 --- a/examples/resources/equinix_fabric_service_token/zside_colo_service_token.tf +++ b/examples/resources/equinix_fabric_service_token/zside_colo_service_token.tf @@ -1,6 +1,6 @@ resource "equinix_fabric_service_token" "test"{ - type = "VC_TOKEN" - description = "Zside COLO Service Token" + type = "VC_TOKEN" + description = "Zside COLO Service Token" expiration_date_time = "2025-01-18T06:43:49.981Z" service_token_connection { type = "EVPL_VC" diff --git a/examples/resources/equinix_fabric_service_token/zside_network_service_token.tf b/examples/resources/equinix_fabric_service_token/zside_network_service_token.tf new file mode 100644 index 000000000..f9cba0a53 --- /dev/null +++ b/examples/resources/equinix_fabric_service_token/zside_network_service_token.tf @@ -0,0 +1,21 @@ +resource "equinix_fabric_service_token" "test" { + type = "VC_TOKEN" + description = "Zside Network Service Token" + expiration_date_time = "2025-01-18T06:43:49.986Z" + service_token_connection { + type = "EVPL_VC" + supported_bandwidths = [50, 200, 10000] + z_side { + access_point_selectors { + type = "NETWORK" + network { + uuid = "" + } + } + } + } + notifications { + type = "ALL" + emails = ["example@equinix.com"] + } +} diff --git a/examples/resources/equinix_fabric_service_token/zside_vd_service_token.tf b/examples/resources/equinix_fabric_service_token/zside_vd_service_token.tf index f3f0b8b36..39ef02bf4 100644 --- a/examples/resources/equinix_fabric_service_token/zside_vd_service_token.tf +++ b/examples/resources/equinix_fabric_service_token/zside_vd_service_token.tf @@ -1,6 +1,6 @@ resource "equinix_fabric_service_token" "test" { type = "VC_TOKEN" - description = "Zside VD Service Token" + description = "Zside VD Service Token" expiration_date_time = "2025-01-18T06:43:49.986Z" service_token_connection { type = "EVPL_VC" diff --git a/internal/resources/fabric/service_token/resource_test.go b/internal/resources/fabric/service_token/resource_test.go index 088dc68a4..7e06720e8 100644 --- a/internal/resources/fabric/service_token/resource_test.go +++ b/internal/resources/fabric/service_token/resource_test.go @@ -158,34 +158,39 @@ func TestAccFabricZsidePortServiceToken_PNFV(t *testing.T) { } func TestAccFabricZsideNetworkServiceToken_PNFV(t *testing.T) { + connectionTestData := testing_helpers.GetFabricEnvConnectionTestData(t) + var networkUuid string + if len(connectionTestData) > 0 { + networkUuid = connectionTestData["pfcr"]["network"] + } serviceTokenName, serviceTokenUpdatedName := "token_zwan_PNFV", "UP_Token_zwan_PNFV" - serviceTokenDescription, serviceTokenUpdatedDescription := "zside port token", "Updated zside port token" + serviceTokenDescription, serviceTokenUpdatedDescription := "zside network token", "Updated zside network token" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acceptance.TestAccPreCheck(t) }, Providers: acceptance.TestAccProviders, CheckDestroy: CheckServiceTokenDelete, Steps: []resource.TestStep{ { - Config: testAccFabricZsideNetworkServiceTokenConfig(serviceTokenName, serviceTokenDescription), + Config: testAccFabricZsideNetworkServiceTokenConfig(serviceTokenName, serviceTokenDescription, networkUuid), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet("equinix_fabric_service_token.test", "uuid"), resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "name", serviceTokenName), resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "type", "VC_TOKEN"), resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "description", serviceTokenDescription), resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "expiration_date_time", "2025-02-18T06:43:49.981Z"), - resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.network.0.uuid", "ff241e62-42f5-48bc-96c4-e0b5297fbed1"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.network.0.uuid", networkUuid), ), ExpectNonEmptyPlan: false, }, { - Config: testAccFabricZsideNetworkServiceTokenConfig(serviceTokenUpdatedName, serviceTokenUpdatedDescription), + Config: testAccFabricZsideNetworkServiceTokenConfig(serviceTokenUpdatedName, serviceTokenUpdatedDescription, networkUuid), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet("equinix_fabric_service_token.test", "uuid"), resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "name", serviceTokenUpdatedName), resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "type", "VC_TOKEN"), resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "description", serviceTokenUpdatedDescription), resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "expiration_date_time", "2025-02-18T06:43:49.981Z"), - resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.network.0.uuid", "ff241e62-42f5-48bc-96c4-e0b5297fbed1"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.network.0.uuid", networkUuid), ), ExpectNonEmptyPlan: false, }, @@ -289,7 +294,7 @@ func testAccFabricZsideVirtualDeviceServiceTokenConfig(serviceTokenName string, `, serviceTokenName, serviceTokenDescription, virtualDeviceUuid) } -func testAccFabricZsideNetworkServiceTokenConfig(serviceTokenName string, serviceTokenDescription string) string { +func testAccFabricZsideNetworkServiceTokenConfig(serviceTokenName string, serviceTokenDescription string, networkUuid string) string { return fmt.Sprintf( `resource "equinix_fabric_service_token" "test" { type = "VC_TOKEN" @@ -303,7 +308,7 @@ func testAccFabricZsideNetworkServiceTokenConfig(serviceTokenName string, servic access_point_selectors{ type = "NETWORK" network { - uuid = "ff241e62-42f5-48bc-96c4-e0b5297fbed1" + uuid = "%s" } } } @@ -313,7 +318,7 @@ func testAccFabricZsideNetworkServiceTokenConfig(serviceTokenName string, servic emails = ["example@equinix.com", "test1@equinix.com"] } } - `, serviceTokenName, serviceTokenDescription) + `, serviceTokenName, serviceTokenDescription, networkUuid) } func CheckServiceTokenDelete(s *terraform.State) error { diff --git a/templates/resources/fabric_service_token.md.tmpl b/templates/resources/fabric_service_token.md.tmpl index 6df858e27..3dd70bc8f 100644 --- a/templates/resources/fabric_service_token.md.tmpl +++ b/templates/resources/fabric_service_token.md.tmpl @@ -22,6 +22,9 @@ Aside Port Service Token Zside Port Service Token {{tffile "examples/resources/equinix_fabric_service_token/zside_colo_service_token.tf"}} +Zside Network Service Token +{{tffile "examples/resources/equinix_fabric_service_token/zside_network_service_token.tf"}} + Zside Virtual Device Service Token {{tffile "examples/resources/equinix_fabric_service_token/zside_vd_service_token.tf"}} From 47b3cc58e96eab36c1dba9a9c06a40f7f1a86420 Mon Sep 17 00:00:00 2001 From: srushti-patl Date: Fri, 20 Dec 2024 11:29:20 -0800 Subject: [PATCH 3/5] fix: updating service token resource update method --- .../resources/fabric/service_token/models.go | 58 ++++++++++--------- .../fabric/service_token/resource.go | 57 +++++++++++++----- .../fabric/service_token/resource_test.go | 4 +- 3 files changed, 74 insertions(+), 45 deletions(-) diff --git a/internal/resources/fabric/service_token/models.go b/internal/resources/fabric/service_token/models.go index f13cb04da..d908b7674 100644 --- a/internal/resources/fabric/service_token/models.go +++ b/internal/resources/fabric/service_token/models.go @@ -2,6 +2,7 @@ package service_token import ( "fmt" + "log" "reflect" "sort" "time" @@ -43,33 +44,33 @@ func buildCreateRequest(d *schema.ResourceData) fabricv4.ServiceToken { } -func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOperation { - patches := make([]fabricv4.ServiceTokenChangeOperation, 0) +func buildUpdateRequest(d *schema.ResourceData) ([][]fabricv4.ServiceTokenChangeOperation, error) { + patches := make([][]fabricv4.ServiceTokenChangeOperation, 0) oldName, newName := d.GetChange("name") if oldName.(string) != newName.(string) { - patches = append(patches, fabricv4.ServiceTokenChangeOperation{ + patches = append(patches, []fabricv4.ServiceTokenChangeOperation{{ Op: "replace", Path: "/name", Value: newName.(string), - }) + }}) } oldDescription, newDescription := d.GetChange("description") if oldDescription.(string) != newDescription.(string) { - patches = append(patches, fabricv4.ServiceTokenChangeOperation{ + patches = append(patches, []fabricv4.ServiceTokenChangeOperation{{ Op: "replace", Path: "/description", Value: newDescription.(string), - }) + }}) } oldExpirationDate, newExpirationDate := d.GetChange("expiration_date_time") if oldExpirationDate.(string) != newExpirationDate.(string) { - patches = append(patches, fabricv4.ServiceTokenChangeOperation{ + patches = append(patches, []fabricv4.ServiceTokenChangeOperation{{ Op: "replace", Path: "/expirationDateTime", Value: newExpirationDate.(string), - }) + }}) } oldNotifications, newNotifications := d.GetChange("notifications") @@ -102,11 +103,11 @@ func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOpe } if !reflect.DeepEqual(oldNotificationEmails, newNotificationEmails) { - patches = append(patches, fabricv4.ServiceTokenChangeOperation{ + patches = append(patches, []fabricv4.ServiceTokenChangeOperation{{ Op: "replace", Path: "/notifications/emails", Value: newNotificationEmails, - }) + }}) } oldServiceTokenConnection, newServiceTokenConnection := d.GetChange("service_token_connection") @@ -117,11 +118,11 @@ func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOpe for _, connection := range oldServiceTokenConnection.(*schema.Set).List() { oldBandwidthLimitMap := connection.(map[string]interface{}) - if bandwidth, ok := oldBandwidthLimitMap["bandwidthLimit"]; ok { - oldBandwidthLimit := bandwidth.([]interface{}) - if len(oldBandwidthLimit) > 0 { - oldAsideBandwidthLimits := converters.IfArrToIntArr(oldBandwidthLimit) - oldAsideBandwidthLimit = oldAsideBandwidthLimits[0] + if bandwidth, ok := oldBandwidthLimitMap["bandwidth_limit"]; ok { + if bandwidthLimitValue, ok := bandwidth.(int); ok { + oldAsideBandwidthLimit = bandwidthLimitValue + } else { + log.Printf("[DEBUG] Expected bandwidthLimit to be an integer, but got %T", bandwidth) } } } @@ -131,22 +132,22 @@ func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOpe for _, connection := range newServiceTokenConnection.(*schema.Set).List() { newBandwidthLimitMap := connection.(map[string]interface{}) - if bandwidth, ok := newBandwidthLimitMap["bandwidthLimit"]; ok { - newBandwidthLimit := bandwidth.([]interface{}) - if len(newBandwidthLimit) > 0 { - newAsideBandwidthLimits := converters.IfArrToIntArr(newBandwidthLimit) - newAsideBandwidthLimit = newAsideBandwidthLimits[0] + if bandwidth, ok := newBandwidthLimitMap["bandwidth_limit"]; ok { + if bandwidthLimitValue, ok := bandwidth.(int); ok { + newAsideBandwidthLimit = bandwidthLimitValue + } else { + log.Printf("[DEBUG] Expected bandwidthLimit to be an integer, but got %T", bandwidth) } } } } if oldAsideBandwidthLimit != newAsideBandwidthLimit { - patches = append(patches, fabricv4.ServiceTokenChangeOperation{ + patches = append(patches, []fabricv4.ServiceTokenChangeOperation{{ Op: "replace", Path: "/connection/bandwidthLimit", Value: newAsideBandwidthLimit, - }) + }}) } var oldZsideBandwidth, newZsideBandwidth []int @@ -179,14 +180,15 @@ func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOpe } if !areSlicesEqual(oldZsideBandwidth, newZsideBandwidth) { - patches = append(patches, fabricv4.ServiceTokenChangeOperation{ - Op: "replace", - Path: "/connection/supportedBandwidths", - Value: newZsideBandwidth, - }) + patches = append(patches, []fabricv4.ServiceTokenChangeOperation{ + { + Op: "replace", + Path: "/connection/supportedBandwidths", + Value: newZsideBandwidth, + }}) } - return patches + return patches, nil } func areSlicesEqual(a, b []int) bool { diff --git a/internal/resources/fabric/service_token/resource.go b/internal/resources/fabric/service_token/resource.go index ec8f57cff..3133f83e7 100644 --- a/internal/resources/fabric/service_token/resource.go +++ b/internal/resources/fabric/service_token/resource.go @@ -2,6 +2,7 @@ package service_token import ( "context" + "fmt" "log" "strings" "time" @@ -65,7 +66,7 @@ func resourceCreate(ctx context.Context, d *schema.ResourceData, meta interface{ } createTimeout := d.Timeout(schema.TimeoutCreate) - 30*time.Second - time.Since(start) - if err = waitForStability(d.Id(), meta, d, ctx, createTimeout); err != nil { + if _, err = waitForStability(d.Id(), meta, d, ctx, createTimeout); err != nil { return diag.Errorf("error waiting for service token (%s) to be created: %s", d.Id(), err) } @@ -74,20 +75,35 @@ func resourceCreate(ctx context.Context, d *schema.ResourceData, meta interface{ func resourceUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*config.Config).NewFabricClientForSDK(d) - updateRequest := buildUpdateRequest(d) - start := time.Now() - serviceToken, _, err := client.ServiceTokensApi.UpdateServiceTokenByUuid(ctx, d.Id()).ServiceTokenChangeOperation(updateRequest).Execute() + updateTimeout := d.Timeout(schema.TimeoutUpdate) - 30*time.Second - time.Since(start) + dbToken, err := waitForStability(d.Id(), meta, d, ctx, updateTimeout) if err != nil { - return diag.FromErr(equinix_errors.FormatFabricError(err)) + return diag.Errorf("either timed out or errored out while fetching Fabric Service Token for uuid %s and error %v", d.Id(), err) } - - updateTimeout := d.Timeout(schema.TimeoutUpdate) - 30*time.Second - time.Since(start) - if err = waitForStability(d.Id(), meta, d, ctx, updateTimeout); err != nil { - return diag.Errorf("error waiting for service token (%s) to be updated: %s", d.Id(), err) + diags := diag.Diagnostics{} + updates, err := buildUpdateRequest(d) + if err != nil { + diags = append(diags, diag.Diagnostic{Severity: 1, Summary: err.Error()}) + return diags } + for _, update := range updates { + _, _, err = client.ServiceTokensApi.UpdateServiceTokenByUuid(ctx, d.Id()).ServiceTokenChangeOperation(update).Execute() + if err != nil { + diags = append(diags, diag.Diagnostic{Severity: 0, Summary: fmt.Sprintf("service token property update request error: %v [update payload: %v] (other updates will be successful if the payload is not shown)", equinix_errors.FormatFabricError(err), update)}) + continue + } + updateTimeout := d.Timeout(schema.TimeoutUpdate) - 30*time.Second - time.Since(start) + updateServiceToken, err := waitForStability(d.Id(), meta, d, ctx, updateTimeout) + if err != nil { + diags = append(diags, diag.Diagnostic{Severity: 0, Summary: fmt.Sprintf("service token property update completion timeout error: %v [update payload: %v] (other updates will be successful if the payload is not shown)", equinix_errors.FormatFabricError(err), update)}) + } else { + dbToken = updateServiceToken + } - return setServiceTokenMap(d, serviceToken) + } + d.SetId(dbToken.GetUuid()) + return append(diags, setServiceTokenMap(d, dbToken)...) } func resourceDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -115,7 +131,7 @@ func resourceDelete(ctx context.Context, d *schema.ResourceData, meta interface{ return diags } -func waitForStability(uuid string, meta interface{}, d *schema.ResourceData, ctx context.Context, tieout time.Duration) error { +func waitForStability(uuid string, meta interface{}, d *schema.ResourceData, ctx context.Context, timeout time.Duration) (*fabricv4.ServiceToken, error) { log.Printf("Waiting for service token to be created, uuid %s", uuid) stateConf := &retry.StateChangeConf{ Target: []string{ @@ -127,16 +143,27 @@ func waitForStability(uuid string, meta interface{}, d *schema.ResourceData, ctx if err != nil { return "", "", equinix_errors.FormatFabricError(err) } - return serviceToken, string(serviceToken.GetState()), nil + currentState := string(serviceToken.GetState()) + return serviceToken, currentState, nil }, - Timeout: tieout, + Timeout: timeout, Delay: 30 * time.Second, MinTimeout: 30 * time.Second, } - _, err := stateConf.WaitForStateContext(ctx) + inter, err := stateConf.WaitForStateContext(ctx) + var serviceToken *fabricv4.ServiceToken - return err + if err != nil { + log.Printf("[ERROR] Error while waiting for state: %v", err) + return nil, err + } + + serviceToken, ok := inter.(*fabricv4.ServiceToken) + if !ok { + return nil, fmt.Errorf("expected *fabricv4.ServiceToken, but got %T", inter) + } + return serviceToken, err } func WaitForDeletion(uuid string, meta interface{}, d *schema.ResourceData, ctx context.Context, timeout time.Duration) error { diff --git a/internal/resources/fabric/service_token/resource_test.go b/internal/resources/fabric/service_token/resource_test.go index 7e06720e8..c00ca3129 100644 --- a/internal/resources/fabric/service_token/resource_test.go +++ b/internal/resources/fabric/service_token/resource_test.go @@ -180,7 +180,7 @@ func TestAccFabricZsideNetworkServiceToken_PNFV(t *testing.T) { resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "expiration_date_time", "2025-02-18T06:43:49.981Z"), resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.network.0.uuid", networkUuid), ), - ExpectNonEmptyPlan: false, + ExpectNonEmptyPlan: true, }, { Config: testAccFabricZsideNetworkServiceTokenConfig(serviceTokenUpdatedName, serviceTokenUpdatedDescription, networkUuid), @@ -192,7 +192,7 @@ func TestAccFabricZsideNetworkServiceToken_PNFV(t *testing.T) { resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "expiration_date_time", "2025-02-18T06:43:49.981Z"), resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.network.0.uuid", networkUuid), ), - ExpectNonEmptyPlan: false, + ExpectNonEmptyPlan: true, }, }, }) From fac8e6900a27b13e5e0820ff0ce361ecb1777659 Mon Sep 17 00:00:00 2001 From: srushti-patl Date: Sun, 22 Dec 2024 23:15:19 -0800 Subject: [PATCH 4/5] fix: Addressing PR comments --- .../resources/fabric/service_token/models.go | 54 ++++++++++--------- .../fabric/service_token/resource.go | 2 +- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/internal/resources/fabric/service_token/models.go b/internal/resources/fabric/service_token/models.go index d908b7674..f89184dda 100644 --- a/internal/resources/fabric/service_token/models.go +++ b/internal/resources/fabric/service_token/models.go @@ -2,7 +2,6 @@ package service_token import ( "fmt" - "log" "reflect" "sort" "time" @@ -30,7 +29,9 @@ func buildCreateRequest(d *schema.ResourceData) fabricv4.ServiceToken { serviceTokenRequest.SetExpirationDateTime(expirationTime) descriptionConfig := d.Get("description").(string) - serviceTokenRequest.SetDescription(descriptionConfig) + if descriptionConfig != "" { + serviceTokenRequest.SetDescription(descriptionConfig) + } connectionConfig := d.Get("service_token_connection").(*schema.Set).List() connection := connectionTerraformToGo(connectionConfig) @@ -121,8 +122,6 @@ func buildUpdateRequest(d *schema.ResourceData) ([][]fabricv4.ServiceTokenChange if bandwidth, ok := oldBandwidthLimitMap["bandwidth_limit"]; ok { if bandwidthLimitValue, ok := bandwidth.(int); ok { oldAsideBandwidthLimit = bandwidthLimitValue - } else { - log.Printf("[DEBUG] Expected bandwidthLimit to be an integer, but got %T", bandwidth) } } } @@ -135,8 +134,6 @@ func buildUpdateRequest(d *schema.ResourceData) ([][]fabricv4.ServiceTokenChange if bandwidth, ok := newBandwidthLimitMap["bandwidth_limit"]; ok { if bandwidthLimitValue, ok := bandwidth.(int); ok { newAsideBandwidthLimit = bandwidthLimitValue - } else { - log.Printf("[DEBUG] Expected bandwidthLimit to be an integer, but got %T", bandwidth) } } } @@ -180,12 +177,11 @@ func buildUpdateRequest(d *schema.ResourceData) ([][]fabricv4.ServiceTokenChange } if !areSlicesEqual(oldZsideBandwidth, newZsideBandwidth) { - patches = append(patches, []fabricv4.ServiceTokenChangeOperation{ - { - Op: "replace", - Path: "/connection/supportedBandwidths", - Value: newZsideBandwidth, - }}) + patches = append(patches, []fabricv4.ServiceTokenChangeOperation{{ + Op: "replace", + Path: "/connection/supportedBandwidths", + Value: newZsideBandwidth, + }}) } return patches, nil @@ -414,9 +410,7 @@ func portTerraformToGo(portList []interface{}) fabricv4.SimplifiedMetadataEntity priority := portListMap["priority"].(string) locationList := portListMap["location"].(*schema.Set).List() - if uuid != "" { - port.SetUuid(uuid) - } + port.SetUuid(uuid) if href != "" { port.SetHref(href) } @@ -486,11 +480,20 @@ func virtualDeviceTerraformToGo(virtualDeviceList []interface{}) fabricv4.Simpli uuid := virtualDeviceMap["uuid"].(string) name := virtualDeviceMap["name"].(string) cluster := virtualDeviceMap["cluster"].(string) - virtualDevice.SetHref(href) - virtualDevice.SetType(fabricv4.SimplifiedVirtualDeviceType(type_)) + + if href != "" { + virtualDevice.SetHref(href) + } + if type_ != "" { + virtualDevice.SetType(fabricv4.SimplifiedVirtualDeviceType(type_)) + } virtualDevice.SetUuid(uuid) - virtualDevice.SetName(name) - virtualDevice.SetCluster(cluster) + if name != "" { + virtualDevice.SetName(name) + } + if cluster != "" { + virtualDevice.SetCluster(cluster) + } return virtualDevice } @@ -505,9 +508,14 @@ func interfaceTerraformToGo(interfaceList []interface{}) fabricv4.VirtualDeviceI uuid := interfaceMap["uuid"].(string) type_ := interfaceMap["type"].(string) id := interfaceMap["id"].(int) - interface_.SetUuid(uuid) + + if uuid != "" { + interface_.SetUuid(uuid) + } interface_.SetType(fabricv4.VirtualDeviceInterfaceType(type_)) - interface_.SetId(int32(id)) + if id >= 0 { + interface_.SetId(int32(id)) + } return interface_ } @@ -525,9 +533,7 @@ func networkTerraformToGo(networkList []interface{}) fabricv4.SimplifiedTokenNet scope := networkListMap["scope"].(string) locationList := networkListMap["location"].(*schema.Set).List() - if uuid != "" { - network.SetUuid(uuid) - } + network.SetUuid(uuid) if href != "" { network.SetHref(href) } diff --git a/internal/resources/fabric/service_token/resource.go b/internal/resources/fabric/service_token/resource.go index 3133f83e7..7bfc90b28 100644 --- a/internal/resources/fabric/service_token/resource.go +++ b/internal/resources/fabric/service_token/resource.go @@ -155,7 +155,7 @@ func waitForStability(uuid string, meta interface{}, d *schema.ResourceData, ctx var serviceToken *fabricv4.ServiceToken if err != nil { - log.Printf("[ERROR] Error while waiting for state: %v", err) + log.Printf("[ERROR] Error while waiting for service token to go to INACTIVE state: %v", err) return nil, err } From 53091c321b6d6e04555d5141c571ffa3f635df9b Mon Sep 17 00:00:00 2001 From: srushti-patl Date: Mon, 23 Dec 2024 14:14:08 -0800 Subject: [PATCH 5/5] fix: Addressing PR comments for service token response map --- .../resources/fabric/service_token/models.go | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/internal/resources/fabric/service_token/models.go b/internal/resources/fabric/service_token/models.go index f89184dda..821d7c2f6 100644 --- a/internal/resources/fabric/service_token/models.go +++ b/internal/resources/fabric/service_token/models.go @@ -252,18 +252,35 @@ func setServiceTokensData(d *schema.ResourceData, routeFilters *fabricv4.Service func serviceTokenResponseMap(token *fabricv4.ServiceToken) map[string]interface{} { serviceToken := make(map[string]interface{}) serviceToken["type"] = string(token.GetType()) - serviceToken["href"] = token.GetHref() - serviceToken["uuid"] = token.GetUuid() expirationDateTime := token.GetExpirationDateTime() const TimeFormat = "2006-01-02T15:04:05.000Z" serviceToken["expiration_date_time"] = expirationDateTime.Format(TimeFormat) - serviceToken["state"] = token.GetState() - serviceToken["description"] = token.GetDescription() - + if token.Href != nil { + serviceToken["href"] = token.GetHref() + } + if token.Uuid != nil { + serviceToken["uuid"] = token.GetUuid() + } + if token.State != nil { + serviceToken["state"] = token.GetState() + } + if token.IssuerSide != nil { + serviceToken["issuer_side"] = token.GetIssuerSide() + } + if token.Name != nil { + serviceToken["name"] = token.GetName() + } + if token.Description != nil { + serviceToken["description"] = token.GetDescription() + } if token.Connection != nil { connection := token.GetConnection() serviceToken["service_token_connection"] = connectionGoToTerraform(&connection) } + if token.Notifications != nil { + notifications := token.GetNotifications() + serviceToken["notifications"] = equinix_fabric_schema.NotificationsGoToTerraform(notifications) + } if token.Account != nil { account := token.GetAccount() serviceToken["account"] = equinix_fabric_schema.AccountGoToTerraform(&account)