From c21d31edec36f11a65f3e02eae85beadda71558c Mon Sep 17 00:00:00 2001 From: Manu Bhat Date: Thu, 5 Oct 2023 14:48:05 -0700 Subject: [PATCH 1/8] Add equinix_fabric_network provider --- equinix/data_source_fabric_network.go | 22 +++ equinix/fabric_mapping_helper.go | 38 +++- equinix/fabric_network_read_schema.go | 104 +++++++++++ equinix/fabric_network_schema.go | 124 +++++++++++++ equinix/provider.go | 2 + equinix/resource_fabric_network.go | 241 ++++++++++++++++++++++++++ 6 files changed, 529 insertions(+), 2 deletions(-) create mode 100755 equinix/data_source_fabric_network.go create mode 100755 equinix/fabric_network_read_schema.go create mode 100755 equinix/fabric_network_schema.go create mode 100755 equinix/resource_fabric_network.go diff --git a/equinix/data_source_fabric_network.go b/equinix/data_source_fabric_network.go new file mode 100755 index 000000000..b5391a496 --- /dev/null +++ b/equinix/data_source_fabric_network.go @@ -0,0 +1,22 @@ +package equinix + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceNetwork() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceNetworkRead, + Schema: readNetworkResourceSchema(), + Description: "Fabric V4 API compatible data resource that allow user to fetch Fabric Network for a given UUID\n\n~> **Note** Equinix Fabric v4 resources and datasources are currently in Beta. The interfaces related to `equinix_fabric_` resources and datasources may change ahead of general availability. Please, do not hesitate to report any problems that you experience by opening a new [issue](https://github.com/equinix/terraform-provider-equinix/issues/new?template=bug.md)", + } +} + +func dataSourceNetworkRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + uuid, _ := d.Get("uuid").(string) + d.SetId(uuid) + return resourceNetworkRead(ctx, d, meta) +} diff --git a/equinix/fabric_mapping_helper.go b/equinix/fabric_mapping_helper.go index 5f0b79fd8..dffb4ed9e 100644 --- a/equinix/fabric_mapping_helper.go +++ b/equinix/fabric_mapping_helper.go @@ -2,10 +2,11 @@ package equinix import ( "fmt" - "log" - v4 "github.com/equinix-labs/fabric-go/fabric/v4" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "log" + "math/rand" + "time" ) func serviceTokenToFabric(serviceTokenRequest []interface{}) (v4.ServiceToken, error) { @@ -1255,3 +1256,36 @@ func getCloudRouterUpdateRequest(conn v4.CloudRouter, d *schema.ResourceData) (v } return changeOps, nil } + +func getNetworkUpdateRequest(network v4.Network, d *schema.ResourceData) (v4.NetworkChangeOperation, error) { + changeOps := v4.NetworkChangeOperation{} + existingName := network.Name + updateNameVal := d.Get("name") + + log.Printf("existing name %s, Update Name Request %s ", existingName, updateNameVal) + + if existingName != updateNameVal { + changeOps = v4.NetworkChangeOperation{Op: "replace", Path: "/name", Value: &updateNameVal} + } else { + return changeOps, fmt.Errorf("nothing to update for the Fabric Network: %s", existingName) + } + return changeOps, nil +} + +const allowed_charset = "abcdefghijklmnopqrstuvwxyz" + + "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789#$&@" + +var seededRand = rand.New( + rand.NewSource(time.Now().UnixNano())) + +func CorrelationIdWithCharset(length int, charset string) string { + b := make([]byte, length) + for i := range b { + b[i] = charset[seededRand.Intn(len(charset))] + } + return string(b) +} + +func CorrelationId(length int) string { + return CorrelationIdWithCharset(length, allowed_charset) +} diff --git a/equinix/fabric_network_read_schema.go b/equinix/fabric_network_read_schema.go new file mode 100755 index 000000000..37b4228a3 --- /dev/null +++ b/equinix/fabric_network_read_schema.go @@ -0,0 +1,104 @@ +package equinix + +import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + +func readChangeSch() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "href": { + Type: schema.TypeString, + Computed: true, + Description: "href", + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + Description: "UUID of Network", + }, + "type": { + Type: schema.TypeString, + Computed: true, + Description: "network type", + }, + } +} + +func readNetworkResourceSchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "uuid": { + Type: schema.TypeString, + Optional: true, + Description: "Equinix-assigned Fabric Network identifier", + }, + "href": { + Type: schema.TypeString, + Computed: true, + Description: "Fabric Network URI information", + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: "Fabric Network name. An alpha-numeric 24 characters string which can include only hyphens and underscores", + }, + "state": { + Type: schema.TypeString, + Computed: true, + Description: "Fabric Network overall state", + }, + "scope": { + Type: schema.TypeString, + Computed: true, + Description: "Network scope", + }, + "connections_count": { + Type: schema.TypeInt, + Computed: true, + Description: "", + }, + "change_log": { + Type: schema.TypeSet, + Computed: true, + Description: "Captures Fabric Network lifecycle change information", + Elem: &schema.Resource{ + Schema: readChangeLogSch(), + }, + }, + "type": { + Type: schema.TypeString, + Computed: true, + Description: "Defines the Fabric Network type like IPWAN", + }, + "location": { + Type: schema.TypeSet, + Computed: true, + Description: "Fabric Network location", + Elem: &schema.Resource{ + Schema: readLocationSch(), + }, + }, + "project": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Description: "Project information", + Elem: &schema.Resource{ + Schema: createGatewayProjectSch(), + }, + }, + "change": { + Type: schema.TypeSet, + Computed: true, + Description: "Order information related to this Fabric Network", + Elem: &schema.Resource{ + Schema: readChangeSch(), + }, + }, + "notifications": { + Type: schema.TypeList, + Computed: true, + Description: "Preferences for notifications on Fabric Network configuration or status changes", + Elem: &schema.Resource{ + Schema: readNotificationSch(), + }, + }, + } +} diff --git a/equinix/fabric_network_schema.go b/equinix/fabric_network_schema.go new file mode 100755 index 000000000..62a40ef97 --- /dev/null +++ b/equinix/fabric_network_schema.go @@ -0,0 +1,124 @@ +package equinix + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +var createNetworkAccountRes = &schema.Resource{ + Schema: createNetworkAccountSch(), +} + +func createNetworkAccountSch() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "account_number": { + Type: schema.TypeInt, + Computed: true, + Optional: true, + Description: "Account Number", + }, + } +} + +var createNetworkProjectSchRes = &schema.Resource{ + Schema: createNetworkProjectSch(), +} + +func createNetworkProjectSch() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "project_id": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: "Project Id", + }, + "href": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Unique Resource URL", + }, + } +} + +func createNetworkResourceSchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "href": { + Type: schema.TypeString, + Computed: true, + Description: "Fabric Network URI information", + }, + "name": { + Type: schema.TypeString, + Required: true, + Description: "Fabric Network name. An alpha-numeric 24 characters string which can include only hyphens and underscores", + }, + "state": { + Type: schema.TypeString, + Computed: true, + Description: "Fabric Network overall state", + }, + "equinix_asn": { + Type: schema.TypeInt, + Computed: true, + Description: "Equinix ASN", + }, + "change_log": { + Type: schema.TypeSet, + Computed: true, + Description: "Captures Fabric Network lifecycle change information", + Elem: &schema.Resource{ + Schema: createChangeLogSch(), + }, + }, + "type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"IP_WAN"}, true), + Description: "Defines the Network type like IP_WAN", + }, + "location": { + Type: schema.TypeSet, + Required: true, + Description: "Fabric Network location", + MaxItems: 1, + Elem: &schema.Resource{ + Schema: createLocationSch(), + }, + }, + "project": { + Type: schema.TypeSet, + Optional: true, + Description: "Fabric Network project", + Elem: &schema.Resource{ + Schema: createNetworkProjectSch(), + }, + }, + "account": { + Type: schema.TypeSet, + Optional: true, + Description: "Customer account information that is associated with this Fabric Network", + MaxItems: 1, + Elem: &schema.Resource{ + Schema: createNetworkAccountSch(), + }, + }, + "change": { + Type: schema.TypeSet, + Optional: true, + Description: "Order information related to this Fabric Network", + MaxItems: 1, + Elem: &schema.Resource{ + Schema: createChangeSch(), + }, + }, + "notifications": { + Type: schema.TypeList, + Required: true, + Description: "Preferences for notifications on Fabric Network configuration or status changes", + Elem: &schema.Resource{ + Schema: createNotificationSch(), + }, + }, + } +} diff --git a/equinix/provider.go b/equinix/provider.go index 2ff4a5dcf..1cb514ddc 100644 --- a/equinix/provider.go +++ b/equinix/provider.go @@ -110,6 +110,7 @@ func Provider() *schema.Provider { "equinix_fabric_routing_protocol": dataSourceRoutingProtocol(), "equinix_fabric_connection": dataSourceFabricConnection(), "equinix_fabric_cloud_router": dataSourceCloudRouter(), + "equinix_fabric_network": dataSourceNetwork(), "equinix_fabric_port": dataSourceFabricPort(), "equinix_fabric_ports": dataSourceFabricGetPortsByName(), "equinix_fabric_service_profile": dataSourceFabricServiceProfileReadByUuid(), @@ -147,6 +148,7 @@ func Provider() *schema.Provider { "equinix_ecx_l2_connection_accepter": resourceECXL2ConnectionAccepter(), "equinix_ecx_l2_serviceprofile": resourceECXL2ServiceProfile(), "equinix_fabric_cloud_router": resourceCloudRouter(), + "equinix_fabric_network": resourceNetwork(), "equinix_fabric_connection": resourceFabricConnection(), "equinix_fabric_routing_protocol": resourceFabricRoutingProtocol(), "equinix_fabric_service_profile": resourceFabricServiceProfile(), diff --git a/equinix/resource_fabric_network.go b/equinix/resource_fabric_network.go new file mode 100755 index 000000000..855f04523 --- /dev/null +++ b/equinix/resource_fabric_network.go @@ -0,0 +1,241 @@ +package equinix + +import ( + "context" + "fmt" + v4 "github.com/equinix-labs/fabric-go/fabric/v4" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "log" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceNetwork() *schema.Resource { + return &schema.Resource{ + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(6 * time.Minute), + Update: schema.DefaultTimeout(10 * time.Minute), + Delete: schema.DefaultTimeout(6 * time.Minute), + Read: schema.DefaultTimeout(6 * time.Minute), + }, + ReadContext: resourceNetworkRead, + CreateContext: resourceNetworkCreate, + UpdateContext: resourceNetworkUpdate, + DeleteContext: resourceNetworkDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + Schema: createNetworkResourceSchema(), + + Description: "Fabric V4 API compatible resource allows creation and management of Equinix Fabric Network\n\n~> **Note** Equinix Fabric v4 resources and datasources are currently in Beta. The interfaces related to `equinix_fabric_` resources and datasources may change ahead of general availability. Please, do not hesitate to report any problems that you experience by opening a new [issue](https://github.com/equinix/terraform-provider-equinix/issues/new?template=bug.md)", + } +} + +func resourceNetworkCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*Config).fabricClient + ctx = context.WithValue(ctx, v4.ContextAccessToken, meta.(*Config).FabricAuthToken) + schemaNotifications := d.Get("notifications").([]interface{}) + notifications := notificationToFabric(schemaNotifications) + schemaLocation := d.Get("location").(*schema.Set).List() + location := locationToFabric(schemaLocation) + project := v4.Project{} + schemaProject := d.Get("project").(*schema.Set).List() + if len(schemaProject) != 0 { + project = projectToFabric(schemaProject) + } + + createRequest := v4.NetworkPostRequest{ + Name: d.Get("name").(string), + Type_: d.Get("type").(*v4.NetworkType), + Scope: d.Get("type").(*v4.NetworkScope), + Location: &location, + Notifications: notifications, + Project: &project, + } + + fabricNetwork, _, err := client.NetworksApi.CreateNetwork(ctx, createRequest) + if err != nil { + return diag.FromErr(err) + } + d.SetId(fabricNetwork.uuid) + + if _, err = waitUntilNetworkIsProvisioned(d.Id(), meta, ctx); err != nil { + return diag.Errorf("error waiting for Cloud Router (%s) to be created: %s", d.Id(), err) + } + + return resourceNetworkRead(ctx, d, meta) +} + +func resourceNetworkRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*Config).fabricClient + ctx = context.WithValue(ctx, v4.ContextAccessToken, meta.(*Config).FabricAuthToken) + fabricNetwork, _, err := client.NetworksApi.GetNetworkByUuid(ctx, d.Id()) + if err != nil { + log.Printf("[WARN] Fabric Network %s not found , error %s", d.Id(), err) + if !strings.Contains(err.Error(), "500") { + d.SetId("") + } + return diag.FromErr(err) + } + d.SetId(fabricNetwork.Uuid) + return setNetworkMap(d, fabricNetwork) +} + +func setNetworkMap(d *schema.ResourceData, nt v4.Network) diag.Diagnostics { + diags := diag.Diagnostics{} + err := setMap(d, map[string]interface{}{ + "name": nt.Name, + "type": nt.Type_, + "scope": nt.Scope, + "location": locationToTerra(nt.Location), + "notifications": notificationToTerra(nt.Notifications), + "project": projectToTerra(nt.Project), + }) + if err != nil { + return diag.FromErr(err) + } + return diags +} + +func resourceNetworkUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*Config).fabricClient + ctx = context.WithValue(ctx, v4.ContextAccessToken, meta.(*Config).FabricAuthToken) + dbConn, err := waitUntilNetworkIsProvisioned(d.Id(), meta, ctx) + if err != nil { + if !strings.Contains(err.Error(), "500") { + d.SetId("") + } + return diag.Errorf("either timed out or errored out while fetching Fabric Network for uuid %s and error %v", d.Id(), err) + } + // TO-DO + update, err := getNetworkUpdateRequest(dbConn, d) + if err != nil { + return diag.FromErr(err) + } + updates := []v4.NetworkChangeOperation{update} + _, res, err := client.NetworksApi.UpdateNetworkByUuid(ctx, updates, d.Id()) + if err != nil { + return diag.FromErr(fmt.Errorf("error response for the Fabric Network update, response %v, error %v", res, err)) + } + updateFg := v4.Network{} + updateFg, err = waitForNetworkUpdateCompletion(d.Id(), meta, ctx) + + if err != nil { + if !strings.Contains(err.Error(), "500") { + d.SetId("") + } + return diag.FromErr(fmt.Errorf("errored while waiting for successful Fabric Network update, response %v, error %v", res, err)) + } + + d.SetId(updateFg.Uuid) + return setNetworkMap(d, updateFg) +} + +func waitForNetworkUpdateCompletion(uuid string, meta interface{}, ctx context.Context) (v4.Network, error) { + log.Printf("Waiting for Cloud Router update to complete, uuid %s", uuid) + stateConf := &resource.StateChangeConf{ + Target: []string{string(v4.PROVISIONED_NetworkEquinixStatus)}, + Refresh: func() (interface{}, string, error) { + client := meta.(*Config).fabricClient + dbConn, _, err := client.NetworksApi.GetNetworkByUuid(ctx, uuid) + if err != nil { + return "", "", err + } + return dbConn, string(*dbConn.State), nil + }, + Timeout: 2 * time.Minute, + Delay: 30 * time.Second, + MinTimeout: 30 * time.Second, + } + + inter, err := stateConf.WaitForStateContext(ctx) + dbConn := v4.Network{} + + if err == nil { + dbConn = inter.(v4.Network) + } + return dbConn, err +} + +func waitUntilNetworkIsProvisioned(uuid string, meta interface{}, ctx context.Context) (v4.Network, error) { + log.Printf("Waiting for Fabric Network to be provisioned, uuid %s", uuid) + stateConf := &resource.StateChangeConf{ + Pending: []string{ + string(v4.PROVISIONING_NetworkEquinixStatus), + }, + Target: []string{ + string(v4.PROVISIONED_NetworkEquinixStatus), + }, + Refresh: func() (interface{}, string, error) { + client := meta.(*Config).fabricClient + dbConn, _, err := client.NetworksApi.GetNetworkByUuid(ctx, uuid) + if err != nil { + return "", "", err + } + return dbConn, string(*dbConn.State), nil + }, + Timeout: 5 * time.Minute, + Delay: 30 * time.Second, + MinTimeout: 30 * time.Second, + } + + inter, err := stateConf.WaitForStateContext(ctx) + dbConn := v4.Network{} + + if err == nil { + dbConn = inter.(v4.Network) + } + return dbConn, err +} + +func resourceNetworkDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + diags := diag.Diagnostics{} + client := meta.(*Config).fabricClient + ctx = context.WithValue(ctx, v4.ContextAccessToken, meta.(*Config).FabricAuthToken) + _, resp, err := client.NetworksApi.DeleteNetworkByUuid(ctx, d.Id()) + if err != nil { + errors, ok := err.(v4.GenericSwaggerError).Model().([]v4.ModelError) + if ok { + // EQ-3040055 = There is an existing update in REQUESTED state + if hasModelErrorCode(errors, "EQ-3040055") { + return diags + } + } + return diag.FromErr(fmt.Errorf("error response for the Fabric Network delete. Error %v and response %v", err, resp)) + } + + err = waitUntilNetworkDeprovisioned(d.Id(), meta, ctx) + if err != nil { + return diag.FromErr(fmt.Errorf("API call failed while waiting for resource deletion. Error %v", err)) + } + return diags +} + +func waitUntilNetworkDeprovisioned(uuid string, meta interface{}, ctx context.Context) error { + log.Printf("Waiting for Fabric Network to be deprovisioned, uuid %s", uuid) + stateConf := &resource.StateChangeConf{ + Pending: []string{ + string(v4.DEPROVISIONING_NetworkEquinixStatus), + }, + Target: []string{ + string(v4.DEPROVISIONED_NetworkEquinixStatus), + }, + Refresh: func() (interface{}, string, error) { + client := meta.(*Config).fabricClient + dbConn, _, err := client.NetworksApi.GetNetworkByUuid(ctx, uuid) + if err != nil { + return "", "", err + } + return dbConn, string(*dbConn.State), nil + }, + Timeout: 5 * time.Minute, + Delay: 30 * time.Second, + MinTimeout: 30 * time.Second, + } + + _, err := stateConf.WaitForStateContext(ctx) + return err +} From c5785408285ddf7c6c260fb6054b37d5495e4937 Mon Sep 17 00:00:00 2001 From: Cindy Bai <74014751+xbai-equinix@users.noreply.github.com> Date: Tue, 17 Oct 2023 15:58:25 -0700 Subject: [PATCH 2/8] add example for network --- examples/fabric/v4/network/README.md | 34 +++++++++++++++++++ examples/fabric/v4/network/main.tf | 23 +++++++++++++ examples/fabric/v4/network/terraform.tf | 7 ++++ .../v4/network/terraform.tfvars.example | 10 ++++++ examples/fabric/v4/network/variables.tf | 9 +++++ 5 files changed, 83 insertions(+) create mode 100644 examples/fabric/v4/network/README.md create mode 100644 examples/fabric/v4/network/main.tf create mode 100644 examples/fabric/v4/network/terraform.tf create mode 100644 examples/fabric/v4/network/terraform.tfvars.example create mode 100644 examples/fabric/v4/network/variables.tf diff --git a/examples/fabric/v4/network/README.md b/examples/fabric/v4/network/README.md new file mode 100644 index 000000000..48e7d4c9c --- /dev/null +++ b/examples/fabric/v4/network/README.md @@ -0,0 +1,34 @@ +# ECX Fabric network CRUD operations +This example shows how to create Fabric network + +Note: Each time you need to create a new Fabric Network resource, +make a copy of the base folder - examples/fabric/v4/network and CD into this folder to perform all the CRUD operations. + +## Define values for the Fabric network create +At minimum, you must set below variables in `terraform.tfvars` file: + - `equinix_client_id` - Equinix client ID (consumer key), obtained after + registering app in the developer platform + - `equinix_client_secret` - Equinix client secret ID (consumer secret), + obtained same way as above + - `network_name` - Name of ECX Fabric network + - `network_type` - Fabric network type + - `network_scope` - Fabric network scope + - `notifications_type` - notification type + - `notifications_emails` - List of emails + +## Initialize +- First step is to initialize the terraform directory/resource we are going to work on. +In the given example, the folder to perform CRUD operations on an network resource can be found at examples/fabric-cloud-router/. + +- Change directory into - `CD examples/fabric-cloud-router/` +- Initialize Terraform plugins - `terraform init` + +## Fabric network : Create, Read, Update and Delete(CRUD) operations + Note: `–auto-approve` command does not prompt the user for validating the applying config. Remove it to get a prompt to confirm the operation. + +| Operation | Command | Description | +|:----------|:---------------------------------:|---------------------------------------------------------------------------:| +| CREATE | `terraform apply –auto-approve` | Creates an network resource | +| READ | `terraform show` | Reads/Shows the current state of the network resource | +| UPDATE | `terraform apply -refresh` | Updates the network resource with values provided in the terraform.tfvars file | +| DELETE | `terraform destroy –auto-approve` | Deletes the created network resource | \ No newline at end of file diff --git a/examples/fabric/v4/network/main.tf b/examples/fabric/v4/network/main.tf new file mode 100644 index 000000000..5372e89f1 --- /dev/null +++ b/examples/fabric/v4/network/main.tf @@ -0,0 +1,23 @@ +provider "equinix" { + client_id = var.equinix_client_id + client_secret = var.equinix_client_secret +} + +resource "equinix_fabric_network" "test"{ + name = var.network_name + type = var.network_type + scope = var.network_scope + + notifications{ + type=var.notifications_type + emails=var.notifications_emails + } + order { + purchase_order_number= var.purchase_order_number + } +} + +output "fcr_result" { + value = equinix_fabric_network.test.id +} + diff --git a/examples/fabric/v4/network/terraform.tf b/examples/fabric/v4/network/terraform.tf new file mode 100644 index 000000000..fd41df1c0 --- /dev/null +++ b/examples/fabric/v4/network/terraform.tf @@ -0,0 +1,7 @@ +terraform { + required_providers { + equinix = { + source = "equinix/equinix" + } + } +} diff --git a/examples/fabric/v4/network/terraform.tfvars.example b/examples/fabric/v4/network/terraform.tfvars.example new file mode 100644 index 000000000..ae3982dbc --- /dev/null +++ b/examples/fabric/v4/network/terraform.tfvars.example @@ -0,0 +1,10 @@ +equinix_client_id = "" +equinix_client_secret = "" + +network_name = "terra_e2e_network" +network_type = "IPWAN" +network_scope = "GLOBAL" +notifications_type = "ALL" +notifications_emails = ["example@equinix.com","test1@equinix.com"] +purchase_order_number = "1-323292" + diff --git a/examples/fabric/v4/network/variables.tf b/examples/fabric/v4/network/variables.tf new file mode 100644 index 000000000..a4ad37c10 --- /dev/null +++ b/examples/fabric/v4/network/variables.tf @@ -0,0 +1,9 @@ +variable "equinix_client_id" {} +variable "equinix_client_secret" {} + +variable "network_name" {} +variable "network_type" {} +variable "notifications_type" {} +variable "notifications_emails" {} +variable "purchase_order_number" {} +variable "network_scope" {} From 1fd88bc803d62b01c7eeb70f2371be5c0ff9dc5f Mon Sep 17 00:00:00 2001 From: Manu Bhat Date: Wed, 18 Oct 2023 14:06:17 -0700 Subject: [PATCH 3/8] Add func for network change schema --- equinix/fabric_network_schema.go | 28 ++++++++++++++++++++++++++-- equinix/resource_fabric_network.go | 6 +++--- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/equinix/fabric_network_schema.go b/equinix/fabric_network_schema.go index 62a40ef97..9301507b5 100755 --- a/equinix/fabric_network_schema.go +++ b/equinix/fabric_network_schema.go @@ -20,6 +20,30 @@ func createNetworkAccountSch() map[string]*schema.Schema { } } +var createNetworkChangeRes = &schema.Resource{ + Schema: createNetworkChangeSch(), +} + +func createNetworkChangeSch() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "href": { + Type: schema.TypeString, + Computed: true, + Description: "href", + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + Description: "UUID of Network", + }, + "type": { + Type: schema.TypeString, + Computed: true, + Description: "network type", + }, + } +} + var createNetworkProjectSchRes = &schema.Resource{ Schema: createNetworkProjectSch(), } @@ -106,10 +130,10 @@ func createNetworkResourceSchema() map[string]*schema.Schema { "change": { Type: schema.TypeSet, Optional: true, - Description: "Order information related to this Fabric Network", + Description: "Change information related to this Fabric Network", MaxItems: 1, Elem: &schema.Resource{ - Schema: createChangeSch(), + Schema: createNetworkChangeSch(), }, }, "notifications": { diff --git a/equinix/resource_fabric_network.go b/equinix/resource_fabric_network.go index 855f04523..d86ede70b 100755 --- a/equinix/resource_fabric_network.go +++ b/equinix/resource_fabric_network.go @@ -56,11 +56,11 @@ func resourceNetworkCreate(ctx context.Context, d *schema.ResourceData, meta int Project: &project, } - fabricNetwork, _, err := client.NetworksApi.CreateNetwork(ctx, createRequest) + fabricNetwork,_, err := client.NetworksApi.CreateNetwork(ctx, createRequest) if err != nil { return diag.FromErr(err) } - d.SetId(fabricNetwork.uuid) + d.SetId(fabricNetwork.) if _, err = waitUntilNetworkIsProvisioned(d.Id(), meta, ctx); err != nil { return diag.Errorf("error waiting for Cloud Router (%s) to be created: %s", d.Id(), err) @@ -82,7 +82,7 @@ func resourceNetworkRead(ctx context.Context, d *schema.ResourceData, meta inter } d.SetId(fabricNetwork.Uuid) return setNetworkMap(d, fabricNetwork) -} +}x func setNetworkMap(d *schema.ResourceData, nt v4.Network) diag.Diagnostics { diags := diag.Diagnostics{} From 210f80fe4295de22a92de2038dba4d09f06511dc Mon Sep 17 00:00:00 2001 From: Manu Bhat Date: Mon, 4 Dec 2023 16:09:34 -0800 Subject: [PATCH 4/8] Update fabric-go sdk and add code required for Network creation EVPLAN, EPLAN, IPWAN --- equinix/fabric_mapping_helper.go | 70 ++++++++++++------- equinix/fabric_network_read_schema.go | 5 +- equinix/fabric_network_schema.go | 62 ++++++++-------- equinix/resource_fabric_network.go | 26 ++++--- examples/fabric/v4/network/main.tf | 6 +- .../v4/network/terraform.tfvars.example | 5 +- examples/fabric/v4/network/variables.tf | 2 +- go.sum | 5 ++ 8 files changed, 106 insertions(+), 75 deletions(-) diff --git a/equinix/fabric_mapping_helper.go b/equinix/fabric_mapping_helper.go index dffb4ed9e..4ab235231 100644 --- a/equinix/fabric_mapping_helper.go +++ b/equinix/fabric_mapping_helper.go @@ -5,8 +5,6 @@ import ( v4 "github.com/equinix-labs/fabric-go/fabric/v4" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "log" - "math/rand" - "time" ) func serviceTokenToFabric(serviceTokenRequest []interface{}) (v4.ServiceToken, error) { @@ -439,6 +437,25 @@ func operationToTerra(operation *v4.ConnectionOperation) *schema.Set { return operationSet } +func NetworkOperationToTerra(operation *v4.NetworkOperation) *schema.Set { + if operation == nil { + return nil + } + operations := []*v4.NetworkOperation{operation} + mappedOperations := make([]interface{}, len(operations)) + for _, operation := range operations { + mappedOperation := make(map[string]interface{}) + mappedOperation["equinix_status"] = string(*operation.EquinixStatus) + mappedOperations = append(mappedOperations, mappedOperation) + } + + operationSet := schema.NewSet( + schema.HashResource(createNetworkOperationSchRes), + mappedOperations, + ) + return operationSet +} + func orderMappingToTerra(order *v4.Order) *schema.Set { if order == nil { return nil @@ -538,14 +555,17 @@ func notificationToTerra(notifications []v4.SimplifiedNotification) []map[string } func locationToTerra(location *v4.SimplifiedLocation) *schema.Set { + if location == nil { + return nil + } locations := []*v4.SimplifiedLocation{location} mappedLocations := make([]interface{}, len(locations)) - for i, location := range locations { + for i, loc := range locations { mappedLocations[i] = map[string]interface{}{ - "region": location.Region, - "metro_name": location.MetroName, - "metro_code": location.MetroCode, - "ibx": location.Ibx, + "region": loc.Region, + "metro_name": loc.MetroName, + "metro_code": loc.MetroCode, + "ibx": loc.Ibx, } } locationSet := schema.NewSet( @@ -820,6 +840,24 @@ func simplifiedServiceProfileToTerra(profile *v4.SimplifiedServiceProfile) *sche return profileSet } +func simplifiedNetworkChangeToTerra(networkChange *v4.SimplifiedNetworkChange) *schema.Set { + changes := []*v4.SimplifiedNetworkChange{networkChange} + mappedChanges := make([]interface{}, len(changes)) + for _, change := range changes { + mappedChange := make(map[string]interface{}) + mappedChange["href"] = change.Href + mappedChange["type"] = string(*change.Type_) + mappedChange["uuid"] = change.Uuid + mappedChanges = append(mappedChanges, mappedChange) + } + + changeSet := schema.NewSet( + schema.HashResource(createNetworkChangeRes), + mappedChanges, + ) + return changeSet +} + func accessPointTypeConfigToTerra(spAccessPointTypes []v4.ServiceProfileAccessPointType) []interface{} { mappedSpAccessPointTypes := make([]interface{}, len(spAccessPointTypes)) for index, spAccessPointType := range spAccessPointTypes { @@ -1271,21 +1309,3 @@ func getNetworkUpdateRequest(network v4.Network, d *schema.ResourceData) (v4.Net } return changeOps, nil } - -const allowed_charset = "abcdefghijklmnopqrstuvwxyz" + - "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789#$&@" - -var seededRand = rand.New( - rand.NewSource(time.Now().UnixNano())) - -func CorrelationIdWithCharset(length int, charset string) string { - b := make([]byte, length) - for i := range b { - b[i] = charset[seededRand.Intn(len(charset))] - } - return string(b) -} - -func CorrelationId(length int) string { - return CorrelationIdWithCharset(length, allowed_charset) -} diff --git a/equinix/fabric_network_read_schema.go b/equinix/fabric_network_read_schema.go index 37b4228a3..20b04ccd2 100755 --- a/equinix/fabric_network_read_schema.go +++ b/equinix/fabric_network_read_schema.go @@ -17,7 +17,7 @@ func readChangeSch() map[string]*schema.Schema { "type": { Type: schema.TypeString, Computed: true, - Description: "network type", + Description: "network change type", }, } } @@ -52,7 +52,7 @@ func readNetworkResourceSchema() map[string]*schema.Schema { "connections_count": { Type: schema.TypeInt, Computed: true, - Description: "", + Description: "Connections count", }, "change_log": { Type: schema.TypeSet, @@ -70,6 +70,7 @@ func readNetworkResourceSchema() map[string]*schema.Schema { "location": { Type: schema.TypeSet, Computed: true, + Optional: true, Description: "Fabric Network location", Elem: &schema.Resource{ Schema: readLocationSch(), diff --git a/equinix/fabric_network_schema.go b/equinix/fabric_network_schema.go index 9301507b5..7fa97505a 100755 --- a/equinix/fabric_network_schema.go +++ b/equinix/fabric_network_schema.go @@ -5,21 +5,6 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) -var createNetworkAccountRes = &schema.Resource{ - Schema: createNetworkAccountSch(), -} - -func createNetworkAccountSch() map[string]*schema.Schema { - return map[string]*schema.Schema{ - "account_number": { - Type: schema.TypeInt, - Computed: true, - Optional: true, - Description: "Account Number", - }, - } -} - var createNetworkChangeRes = &schema.Resource{ Schema: createNetworkChangeSch(), } @@ -44,8 +29,18 @@ func createNetworkChangeSch() map[string]*schema.Schema { } } -var createNetworkProjectSchRes = &schema.Resource{ - Schema: createNetworkProjectSch(), +var createNetworkOperationSchRes = &schema.Resource{ + Schema: createNetworkOperationSch(), +} + +func createNetworkOperationSch() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "equinix_status": { + Type: schema.TypeString, + Computed: true, + Description: "Network operation status", + }, + } } func createNetworkProjectSch() map[string]*schema.Schema { @@ -82,28 +77,25 @@ func createNetworkResourceSchema() map[string]*schema.Schema { Computed: true, Description: "Fabric Network overall state", }, + "scope": { + Type: schema.TypeString, + Required: true, + Description: "Fabric Network scope", + }, "equinix_asn": { Type: schema.TypeInt, Computed: true, Description: "Equinix ASN", }, - "change_log": { - Type: schema.TypeSet, - Computed: true, - Description: "Captures Fabric Network lifecycle change information", - Elem: &schema.Resource{ - Schema: createChangeLogSch(), - }, - }, "type": { Type: schema.TypeString, Required: true, - ValidateFunc: validation.StringInSlice([]string{"IP_WAN"}, true), - Description: "Defines the Network type like IP_WAN", + ValidateFunc: validation.StringInSlice([]string{"IPWAN", "EPLAN", "EVPLAN"}, true), + Description: "Supported Network types - EVPLAN, EPLAN, IPWAN", }, "location": { Type: schema.TypeSet, - Required: true, + Optional: true, Description: "Fabric Network location", MaxItems: 1, Elem: &schema.Resource{ @@ -118,13 +110,13 @@ func createNetworkResourceSchema() map[string]*schema.Schema { Schema: createNetworkProjectSch(), }, }, - "account": { + "operation": { Type: schema.TypeSet, Optional: true, - Description: "Customer account information that is associated with this Fabric Network", + Description: "Network operation information that is associated with this Fabric Network", MaxItems: 1, Elem: &schema.Resource{ - Schema: createNetworkAccountSch(), + Schema: createNetworkOperationSch(), }, }, "change": { @@ -144,5 +136,13 @@ func createNetworkResourceSchema() map[string]*schema.Schema { Schema: createNotificationSch(), }, }, + "change_log": { + Type: schema.TypeSet, + Computed: true, + Description: "Captures Fabric Network lifecycle change information", + Elem: &schema.Resource{ + Schema: createChangeLogSch(), + }, + }, } } diff --git a/equinix/resource_fabric_network.go b/equinix/resource_fabric_network.go index d86ede70b..7b4ee85cd 100755 --- a/equinix/resource_fabric_network.go +++ b/equinix/resource_fabric_network.go @@ -46,24 +46,26 @@ func resourceNetworkCreate(ctx context.Context, d *schema.ResourceData, meta int if len(schemaProject) != 0 { project = projectToFabric(schemaProject) } + netType := v4.NetworkType(d.Get("type").(string)) + netScope := v4.NetworkScope(d.Get("scope").(string)) createRequest := v4.NetworkPostRequest{ Name: d.Get("name").(string), - Type_: d.Get("type").(*v4.NetworkType), - Scope: d.Get("type").(*v4.NetworkScope), + Type_: &netType, + Scope: &netScope, Location: &location, Notifications: notifications, Project: &project, } - fabricNetwork,_, err := client.NetworksApi.CreateNetwork(ctx, createRequest) + fabricNetwork, _, err := client.NetworksApi.CreateNetwork(ctx, createRequest) if err != nil { return diag.FromErr(err) } - d.SetId(fabricNetwork.) + d.SetId(fabricNetwork.Uuid) if _, err = waitUntilNetworkIsProvisioned(d.Id(), meta, ctx); err != nil { - return diag.Errorf("error waiting for Cloud Router (%s) to be created: %s", d.Id(), err) + return diag.Errorf("error waiting for Network (%s) to be created: %s", d.Id(), err) } return resourceNetworkRead(ctx, d, meta) @@ -82,7 +84,7 @@ func resourceNetworkRead(ctx context.Context, d *schema.ResourceData, meta inter } d.SetId(fabricNetwork.Uuid) return setNetworkMap(d, fabricNetwork) -}x +} func setNetworkMap(d *schema.ResourceData, nt v4.Network) diag.Diagnostics { diags := diag.Diagnostics{} @@ -90,9 +92,13 @@ func setNetworkMap(d *schema.ResourceData, nt v4.Network) diag.Diagnostics { "name": nt.Name, "type": nt.Type_, "scope": nt.Scope, + "state": nt.State, + "operation": NetworkOperationToTerra(nt.Operation), + "change": simplifiedNetworkChangeToTerra(nt.Change), "location": locationToTerra(nt.Location), "notifications": notificationToTerra(nt.Notifications), "project": projectToTerra(nt.Project), + "change_log": changeLogToTerra(nt.ChangeLog), }) if err != nil { return diag.FromErr(err) @@ -135,7 +141,7 @@ func resourceNetworkUpdate(ctx context.Context, d *schema.ResourceData, meta int } func waitForNetworkUpdateCompletion(uuid string, meta interface{}, ctx context.Context) (v4.Network, error) { - log.Printf("Waiting for Cloud Router update to complete, uuid %s", uuid) + log.Printf("Waiting for Network update to complete, uuid %s", uuid) stateConf := &resource.StateChangeConf{ Target: []string{string(v4.PROVISIONED_NetworkEquinixStatus)}, Refresh: func() (interface{}, string, error) { @@ -144,7 +150,7 @@ func waitForNetworkUpdateCompletion(uuid string, meta interface{}, ctx context.C if err != nil { return "", "", err } - return dbConn, string(*dbConn.State), nil + return dbConn, string(*dbConn.Operation.EquinixStatus), nil }, Timeout: 2 * time.Minute, Delay: 30 * time.Second, @@ -175,7 +181,7 @@ func waitUntilNetworkIsProvisioned(uuid string, meta interface{}, ctx context.Co if err != nil { return "", "", err } - return dbConn, string(*dbConn.State), nil + return dbConn, string(*dbConn.Operation.EquinixStatus), nil }, Timeout: 5 * time.Minute, Delay: 30 * time.Second, @@ -229,7 +235,7 @@ func waitUntilNetworkDeprovisioned(uuid string, meta interface{}, ctx context.Co if err != nil { return "", "", err } - return dbConn, string(*dbConn.State), nil + return dbConn, string(*dbConn.Operation.EquinixStatus), nil }, Timeout: 5 * time.Minute, Delay: 30 * time.Second, diff --git a/examples/fabric/v4/network/main.tf b/examples/fabric/v4/network/main.tf index 5372e89f1..c3eede7cb 100644 --- a/examples/fabric/v4/network/main.tf +++ b/examples/fabric/v4/network/main.tf @@ -12,12 +12,12 @@ resource "equinix_fabric_network" "test"{ type=var.notifications_type emails=var.notifications_emails } - order { - purchase_order_number= var.purchase_order_number + project { + project_id= var.project_id } } -output "fcr_result" { +output "network_result" { value = equinix_fabric_network.test.id } diff --git a/examples/fabric/v4/network/terraform.tfvars.example b/examples/fabric/v4/network/terraform.tfvars.example index ae3982dbc..fbe5da4c4 100644 --- a/examples/fabric/v4/network/terraform.tfvars.example +++ b/examples/fabric/v4/network/terraform.tfvars.example @@ -1,10 +1,9 @@ equinix_client_id = "" equinix_client_secret = "" -network_name = "terra_e2e_network" +network_name = "terraform_fabric_network_example" network_type = "IPWAN" network_scope = "GLOBAL" notifications_type = "ALL" notifications_emails = ["example@equinix.com","test1@equinix.com"] -purchase_order_number = "1-323292" - +project_id = "1234568910" diff --git a/examples/fabric/v4/network/variables.tf b/examples/fabric/v4/network/variables.tf index a4ad37c10..e02586f34 100644 --- a/examples/fabric/v4/network/variables.tf +++ b/examples/fabric/v4/network/variables.tf @@ -5,5 +5,5 @@ variable "network_name" {} variable "network_type" {} variable "notifications_type" {} variable "notifications_emails" {} -variable "purchase_order_number" {} +variable "project_id" {} variable "network_scope" {} diff --git a/go.sum b/go.sum index dcf200490..18f2b9b49 100644 --- a/go.sum +++ b/go.sum @@ -261,8 +261,13 @@ github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go. github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/equinix-labs/fabric-go v0.7.1 h1:4yk0IKXMcc72rkRVbcYHokAEc1uUB06t6NXK+DtSsbs= github.com/equinix-labs/fabric-go v0.7.1/go.mod h1:oqgGS3GOI8hHGPJKsAwDOEX0qRHl52sJGvwA/zMSd90= +<<<<<<< HEAD github.com/equinix-labs/metal-go v0.27.0 h1:p5Bqus/gSs5oQezHWXWpc0IzkQl06+yZgbXT5jB7AWs= github.com/equinix-labs/metal-go v0.27.0/go.mod h1:SmxCklxW+KjmBLVMdEXgtFO5gD5/b4N0VxcNgUYbOH4= +======= +github.com/equinix-labs/metal-go v0.16.0 h1:4YmGx9SRFkDtHiEqRsSjlgJDztV6NHqH1eeaOZcK7d4= +github.com/equinix-labs/metal-go v0.16.0/go.mod h1:SmxCklxW+KjmBLVMdEXgtFO5gD5/b4N0VxcNgUYbOH4= +>>>>>>> Update fabric-go sdk and add code required for Network creation github.com/equinix/ecx-go/v2 v2.3.1 h1:gFcAIeyaEUw7S8ebqApmT7E/S7pC7Ac3wgScp89fkPU= github.com/equinix/ecx-go/v2 v2.3.1/go.mod h1:FvCdZ3jXU8Z4CPKig2DT+4J2HdwgRK17pIcznM7RXyk= github.com/equinix/ne-go v1.11.0 h1:ja6G2fmcGrLsOeV25Mq6pDfH+/cUlvxJbnE8uRXTGGk= From 76e25ee49e0c5c9f89defbf8a06e256ff6a7a9c9 Mon Sep 17 00:00:00 2001 From: Manu Bhat Date: Mon, 4 Dec 2023 19:19:02 -0800 Subject: [PATCH 5/8] Rebase on latest main branch and fix build --- equinix/resource_fabric_network.go | 29 ++++++++++--------- .../v4/network/terraform.tfvars.example | 2 +- go.sum | 5 ---- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/equinix/resource_fabric_network.go b/equinix/resource_fabric_network.go index 7b4ee85cd..10a7d2d3c 100755 --- a/equinix/resource_fabric_network.go +++ b/equinix/resource_fabric_network.go @@ -4,13 +4,14 @@ import ( "context" "fmt" v4 "github.com/equinix-labs/fabric-go/fabric/v4" + "github.com/equinix/terraform-provider-equinix/internal/config" + equinix_schema "github.com/equinix/terraform-provider-equinix/internal/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "log" "strings" "time" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func resourceNetwork() *schema.Resource { @@ -35,8 +36,8 @@ func resourceNetwork() *schema.Resource { } func resourceNetworkCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*Config).fabricClient - ctx = context.WithValue(ctx, v4.ContextAccessToken, meta.(*Config).FabricAuthToken) + client := meta.(*config.Config).FabricClient + ctx = context.WithValue(ctx, v4.ContextAccessToken, meta.(*config.Config).FabricAuthToken) schemaNotifications := d.Get("notifications").([]interface{}) notifications := notificationToFabric(schemaNotifications) schemaLocation := d.Get("location").(*schema.Set).List() @@ -72,8 +73,8 @@ func resourceNetworkCreate(ctx context.Context, d *schema.ResourceData, meta int } func resourceNetworkRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*Config).fabricClient - ctx = context.WithValue(ctx, v4.ContextAccessToken, meta.(*Config).FabricAuthToken) + client := meta.(*config.Config).FabricClient + ctx = context.WithValue(ctx, v4.ContextAccessToken, meta.(*config.Config).FabricAuthToken) fabricNetwork, _, err := client.NetworksApi.GetNetworkByUuid(ctx, d.Id()) if err != nil { log.Printf("[WARN] Fabric Network %s not found , error %s", d.Id(), err) @@ -88,7 +89,7 @@ func resourceNetworkRead(ctx context.Context, d *schema.ResourceData, meta inter func setNetworkMap(d *schema.ResourceData, nt v4.Network) diag.Diagnostics { diags := diag.Diagnostics{} - err := setMap(d, map[string]interface{}{ + err := equinix_schema.SetMap(d, map[string]interface{}{ "name": nt.Name, "type": nt.Type_, "scope": nt.Scope, @@ -107,8 +108,8 @@ func setNetworkMap(d *schema.ResourceData, nt v4.Network) diag.Diagnostics { } func resourceNetworkUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*Config).fabricClient - ctx = context.WithValue(ctx, v4.ContextAccessToken, meta.(*Config).FabricAuthToken) + client := meta.(*config.Config).FabricClient + ctx = context.WithValue(ctx, v4.ContextAccessToken, meta.(*config.Config).FabricAuthToken) dbConn, err := waitUntilNetworkIsProvisioned(d.Id(), meta, ctx) if err != nil { if !strings.Contains(err.Error(), "500") { @@ -145,7 +146,7 @@ func waitForNetworkUpdateCompletion(uuid string, meta interface{}, ctx context.C stateConf := &resource.StateChangeConf{ Target: []string{string(v4.PROVISIONED_NetworkEquinixStatus)}, Refresh: func() (interface{}, string, error) { - client := meta.(*Config).fabricClient + client := meta.(*config.Config).FabricClient dbConn, _, err := client.NetworksApi.GetNetworkByUuid(ctx, uuid) if err != nil { return "", "", err @@ -176,7 +177,7 @@ func waitUntilNetworkIsProvisioned(uuid string, meta interface{}, ctx context.Co string(v4.PROVISIONED_NetworkEquinixStatus), }, Refresh: func() (interface{}, string, error) { - client := meta.(*Config).fabricClient + client := meta.(*config.Config).FabricClient dbConn, _, err := client.NetworksApi.GetNetworkByUuid(ctx, uuid) if err != nil { return "", "", err @@ -199,8 +200,8 @@ func waitUntilNetworkIsProvisioned(uuid string, meta interface{}, ctx context.Co func resourceNetworkDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { diags := diag.Diagnostics{} - client := meta.(*Config).fabricClient - ctx = context.WithValue(ctx, v4.ContextAccessToken, meta.(*Config).FabricAuthToken) + client := meta.(*config.Config).FabricClient + ctx = context.WithValue(ctx, v4.ContextAccessToken, meta.(*config.Config).FabricAuthToken) _, resp, err := client.NetworksApi.DeleteNetworkByUuid(ctx, d.Id()) if err != nil { errors, ok := err.(v4.GenericSwaggerError).Model().([]v4.ModelError) @@ -230,7 +231,7 @@ func waitUntilNetworkDeprovisioned(uuid string, meta interface{}, ctx context.Co string(v4.DEPROVISIONED_NetworkEquinixStatus), }, Refresh: func() (interface{}, string, error) { - client := meta.(*Config).fabricClient + client := meta.(*config.Config).FabricClient dbConn, _, err := client.NetworksApi.GetNetworkByUuid(ctx, uuid) if err != nil { return "", "", err diff --git a/examples/fabric/v4/network/terraform.tfvars.example b/examples/fabric/v4/network/terraform.tfvars.example index fbe5da4c4..43435e65f 100644 --- a/examples/fabric/v4/network/terraform.tfvars.example +++ b/examples/fabric/v4/network/terraform.tfvars.example @@ -6,4 +6,4 @@ network_type = "IPWAN" network_scope = "GLOBAL" notifications_type = "ALL" notifications_emails = ["example@equinix.com","test1@equinix.com"] -project_id = "1234568910" +project_id = "1234568910" \ No newline at end of file diff --git a/go.sum b/go.sum index 18f2b9b49..dcf200490 100644 --- a/go.sum +++ b/go.sum @@ -261,13 +261,8 @@ github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go. github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/equinix-labs/fabric-go v0.7.1 h1:4yk0IKXMcc72rkRVbcYHokAEc1uUB06t6NXK+DtSsbs= github.com/equinix-labs/fabric-go v0.7.1/go.mod h1:oqgGS3GOI8hHGPJKsAwDOEX0qRHl52sJGvwA/zMSd90= -<<<<<<< HEAD github.com/equinix-labs/metal-go v0.27.0 h1:p5Bqus/gSs5oQezHWXWpc0IzkQl06+yZgbXT5jB7AWs= github.com/equinix-labs/metal-go v0.27.0/go.mod h1:SmxCklxW+KjmBLVMdEXgtFO5gD5/b4N0VxcNgUYbOH4= -======= -github.com/equinix-labs/metal-go v0.16.0 h1:4YmGx9SRFkDtHiEqRsSjlgJDztV6NHqH1eeaOZcK7d4= -github.com/equinix-labs/metal-go v0.16.0/go.mod h1:SmxCklxW+KjmBLVMdEXgtFO5gD5/b4N0VxcNgUYbOH4= ->>>>>>> Update fabric-go sdk and add code required for Network creation github.com/equinix/ecx-go/v2 v2.3.1 h1:gFcAIeyaEUw7S8ebqApmT7E/S7pC7Ac3wgScp89fkPU= github.com/equinix/ecx-go/v2 v2.3.1/go.mod h1:FvCdZ3jXU8Z4CPKig2DT+4J2HdwgRK17pIcznM7RXyk= github.com/equinix/ne-go v1.11.0 h1:ja6G2fmcGrLsOeV25Mq6pDfH+/cUlvxJbnE8uRXTGGk= From 697238b47f70a9c148deee39dfb4399b514f8aa3 Mon Sep 17 00:00:00 2001 From: Manu Bhat Date: Tue, 5 Dec 2023 10:06:04 -0800 Subject: [PATCH 6/8] Add acceptance test --- equinix/resource_fabric_network_acc_test.go | 91 +++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 equinix/resource_fabric_network_acc_test.go diff --git a/equinix/resource_fabric_network_acc_test.go b/equinix/resource_fabric_network_acc_test.go new file mode 100644 index 000000000..1b5e94254 --- /dev/null +++ b/equinix/resource_fabric_network_acc_test.go @@ -0,0 +1,91 @@ +package equinix + +import ( + "context" + "fmt" + "testing" + + "github.com/equinix/terraform-provider-equinix/internal/config" + + v4 "github.com/equinix-labs/fabric-go/fabric/v4" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccNetworkCreate(t *testing.T) { + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: checkNetworkDelete, + Steps: []resource.TestStep{ + { + Config: testAccNetworkCreateConfig("Ipwan_tf_acc_test"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "equinix_fabric_network.test", "name", "network_acc_test"), + ), + ExpectNonEmptyPlan: false, + }, + { + Config: testAccNetworkCreateConfig("Ipwan_tf_acc_update"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "equinix_fabric_network.test", "name", "network_name_update"), + ), + ExpectNonEmptyPlan: false, + }, + }, + }) +} + +func checkNetworkDelete(s *terraform.State) error { + ctx := context.Background() + ctx = context.WithValue(ctx, v4.ContextAccessToken, testAccProvider.Meta().(*config.Config).FabricAuthToken) + for _, rs := range s.RootModule().Resources { + if rs.Type != "equinix_fabric_network" { + continue + } + err := waitUntilNetworkDeprovisioned(rs.Primary.ID, testAccProvider.Meta(), ctx) + if err != nil { + return fmt.Errorf("API call failed while waiting for resource deletion") + } + } + return nil +} + +func testAccNetworkCreateConfig(name string) string { + return fmt.Sprintf(`resource "equinix_fabric_network" "test"{ + type = "EVPLAN" + name = "%s" + scope = "GLOBAL" + notifications{ + type = "ALL" + emails = [ + "test@equinix.com", + "test1@equinix.com" + ] + } + }`, name) +} + +func TestAccNetworkRead(t *testing.T) { + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccNetworkReadConfig(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "equinix_fabric_network.test", "name", "network_acc_test"), + ), + }, + }, + }) +} + +func testAccNetworkReadConfig() string { + return `data "equinix_fabric_network" "test" { + uuid = "" + }` +} From 427b127eeb4e0ee84de22b3698d9520491ef59ec Mon Sep 17 00:00:00 2001 From: Manu Bhat Date: Fri, 8 Dec 2023 12:46:38 -0800 Subject: [PATCH 7/8] fix func name --- equinix/fabric_mapping_helper.go | 2 +- equinix/resource_fabric_network.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/equinix/fabric_mapping_helper.go b/equinix/fabric_mapping_helper.go index 4ab235231..51e622497 100644 --- a/equinix/fabric_mapping_helper.go +++ b/equinix/fabric_mapping_helper.go @@ -437,7 +437,7 @@ func operationToTerra(operation *v4.ConnectionOperation) *schema.Set { return operationSet } -func NetworkOperationToTerra(operation *v4.NetworkOperation) *schema.Set { +func networkOperationToTerra(operation *v4.NetworkOperation) *schema.Set { if operation == nil { return nil } diff --git a/equinix/resource_fabric_network.go b/equinix/resource_fabric_network.go index 10a7d2d3c..2e40b2049 100755 --- a/equinix/resource_fabric_network.go +++ b/equinix/resource_fabric_network.go @@ -94,7 +94,7 @@ func setNetworkMap(d *schema.ResourceData, nt v4.Network) diag.Diagnostics { "type": nt.Type_, "scope": nt.Scope, "state": nt.State, - "operation": NetworkOperationToTerra(nt.Operation), + "operation": networkOperationToTerra(nt.Operation), "change": simplifiedNetworkChangeToTerra(nt.Change), "location": locationToTerra(nt.Location), "notifications": notificationToTerra(nt.Notifications), From b8f399424f1f51568ba849e15cd972653c7ddb27 Mon Sep 17 00:00:00 2001 From: srushti-patl Date: Tue, 30 Jan 2024 22:15:16 -0800 Subject: [PATCH 8/8] fix: Updating fabric network schema resources & data sources --- equinix/data_source_fabric_network.go | 21 ++- equinix/fabric_mapping_helper.go | 52 ------- equinix/fabric_network_read_schema.go | 105 ------------- equinix/fabric_network_schema.go | 148 ------------------ equinix/resource_fabric_network.go | 216 +++++++++++++++++++++++--- 5 files changed, 212 insertions(+), 330 deletions(-) delete mode 100755 equinix/fabric_network_read_schema.go delete mode 100755 equinix/fabric_network_schema.go diff --git a/equinix/data_source_fabric_network.go b/equinix/data_source_fabric_network.go index b5391a496..e73c42093 100755 --- a/equinix/data_source_fabric_network.go +++ b/equinix/data_source_fabric_network.go @@ -7,10 +7,27 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) +func readFabricNetworkResourceSchema() map[string]*schema.Schema { + sch := FabricNetworkResourceSchema() + for key, _ := range sch { + if key == "uuid" { + sch[key].Required = true + sch[key].Optional = false + sch[key].Computed = false + } else { + sch[key].Required = false + sch[key].Optional = false + sch[key].Computed = true + sch[key].MaxItems = 0 + sch[key].ValidateFunc = nil + } + } + return sch +} func dataSourceNetwork() *schema.Resource { return &schema.Resource{ ReadContext: dataSourceNetworkRead, - Schema: readNetworkResourceSchema(), + Schema: readFabricNetworkResourceSchema(), Description: "Fabric V4 API compatible data resource that allow user to fetch Fabric Network for a given UUID\n\n~> **Note** Equinix Fabric v4 resources and datasources are currently in Beta. The interfaces related to `equinix_fabric_` resources and datasources may change ahead of general availability. Please, do not hesitate to report any problems that you experience by opening a new [issue](https://github.com/equinix/terraform-provider-equinix/issues/new?template=bug.md)", } } @@ -18,5 +35,5 @@ func dataSourceNetwork() *schema.Resource { func dataSourceNetworkRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { uuid, _ := d.Get("uuid").(string) d.SetId(uuid) - return resourceNetworkRead(ctx, d, meta) + return resourceFabricNetworkRead(ctx, d, meta) } diff --git a/equinix/fabric_mapping_helper.go b/equinix/fabric_mapping_helper.go index 51e622497..c1e4a7ef1 100644 --- a/equinix/fabric_mapping_helper.go +++ b/equinix/fabric_mapping_helper.go @@ -437,25 +437,6 @@ func operationToTerra(operation *v4.ConnectionOperation) *schema.Set { return operationSet } -func networkOperationToTerra(operation *v4.NetworkOperation) *schema.Set { - if operation == nil { - return nil - } - operations := []*v4.NetworkOperation{operation} - mappedOperations := make([]interface{}, len(operations)) - for _, operation := range operations { - mappedOperation := make(map[string]interface{}) - mappedOperation["equinix_status"] = string(*operation.EquinixStatus) - mappedOperations = append(mappedOperations, mappedOperation) - } - - operationSet := schema.NewSet( - schema.HashResource(createNetworkOperationSchRes), - mappedOperations, - ) - return operationSet -} - func orderMappingToTerra(order *v4.Order) *schema.Set { if order == nil { return nil @@ -840,24 +821,6 @@ func simplifiedServiceProfileToTerra(profile *v4.SimplifiedServiceProfile) *sche return profileSet } -func simplifiedNetworkChangeToTerra(networkChange *v4.SimplifiedNetworkChange) *schema.Set { - changes := []*v4.SimplifiedNetworkChange{networkChange} - mappedChanges := make([]interface{}, len(changes)) - for _, change := range changes { - mappedChange := make(map[string]interface{}) - mappedChange["href"] = change.Href - mappedChange["type"] = string(*change.Type_) - mappedChange["uuid"] = change.Uuid - mappedChanges = append(mappedChanges, mappedChange) - } - - changeSet := schema.NewSet( - schema.HashResource(createNetworkChangeRes), - mappedChanges, - ) - return changeSet -} - func accessPointTypeConfigToTerra(spAccessPointTypes []v4.ServiceProfileAccessPointType) []interface{} { mappedSpAccessPointTypes := make([]interface{}, len(spAccessPointTypes)) for index, spAccessPointType := range spAccessPointTypes { @@ -1294,18 +1257,3 @@ func getCloudRouterUpdateRequest(conn v4.CloudRouter, d *schema.ResourceData) (v } return changeOps, nil } - -func getNetworkUpdateRequest(network v4.Network, d *schema.ResourceData) (v4.NetworkChangeOperation, error) { - changeOps := v4.NetworkChangeOperation{} - existingName := network.Name - updateNameVal := d.Get("name") - - log.Printf("existing name %s, Update Name Request %s ", existingName, updateNameVal) - - if existingName != updateNameVal { - changeOps = v4.NetworkChangeOperation{Op: "replace", Path: "/name", Value: &updateNameVal} - } else { - return changeOps, fmt.Errorf("nothing to update for the Fabric Network: %s", existingName) - } - return changeOps, nil -} diff --git a/equinix/fabric_network_read_schema.go b/equinix/fabric_network_read_schema.go deleted file mode 100755 index 20b04ccd2..000000000 --- a/equinix/fabric_network_read_schema.go +++ /dev/null @@ -1,105 +0,0 @@ -package equinix - -import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - -func readChangeSch() map[string]*schema.Schema { - return map[string]*schema.Schema{ - "href": { - Type: schema.TypeString, - Computed: true, - Description: "href", - }, - "uuid": { - Type: schema.TypeString, - Computed: true, - Description: "UUID of Network", - }, - "type": { - Type: schema.TypeString, - Computed: true, - Description: "network change type", - }, - } -} - -func readNetworkResourceSchema() map[string]*schema.Schema { - return map[string]*schema.Schema{ - "uuid": { - Type: schema.TypeString, - Optional: true, - Description: "Equinix-assigned Fabric Network identifier", - }, - "href": { - Type: schema.TypeString, - Computed: true, - Description: "Fabric Network URI information", - }, - "name": { - Type: schema.TypeString, - Computed: true, - Description: "Fabric Network name. An alpha-numeric 24 characters string which can include only hyphens and underscores", - }, - "state": { - Type: schema.TypeString, - Computed: true, - Description: "Fabric Network overall state", - }, - "scope": { - Type: schema.TypeString, - Computed: true, - Description: "Network scope", - }, - "connections_count": { - Type: schema.TypeInt, - Computed: true, - Description: "Connections count", - }, - "change_log": { - Type: schema.TypeSet, - Computed: true, - Description: "Captures Fabric Network lifecycle change information", - Elem: &schema.Resource{ - Schema: readChangeLogSch(), - }, - }, - "type": { - Type: schema.TypeString, - Computed: true, - Description: "Defines the Fabric Network type like IPWAN", - }, - "location": { - Type: schema.TypeSet, - Computed: true, - Optional: true, - Description: "Fabric Network location", - Elem: &schema.Resource{ - Schema: readLocationSch(), - }, - }, - "project": { - Type: schema.TypeSet, - Optional: true, - Computed: true, - Description: "Project information", - Elem: &schema.Resource{ - Schema: createGatewayProjectSch(), - }, - }, - "change": { - Type: schema.TypeSet, - Computed: true, - Description: "Order information related to this Fabric Network", - Elem: &schema.Resource{ - Schema: readChangeSch(), - }, - }, - "notifications": { - Type: schema.TypeList, - Computed: true, - Description: "Preferences for notifications on Fabric Network configuration or status changes", - Elem: &schema.Resource{ - Schema: readNotificationSch(), - }, - }, - } -} diff --git a/equinix/fabric_network_schema.go b/equinix/fabric_network_schema.go deleted file mode 100755 index 7fa97505a..000000000 --- a/equinix/fabric_network_schema.go +++ /dev/null @@ -1,148 +0,0 @@ -package equinix - -import ( - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" -) - -var createNetworkChangeRes = &schema.Resource{ - Schema: createNetworkChangeSch(), -} - -func createNetworkChangeSch() map[string]*schema.Schema { - return map[string]*schema.Schema{ - "href": { - Type: schema.TypeString, - Computed: true, - Description: "href", - }, - "uuid": { - Type: schema.TypeString, - Computed: true, - Description: "UUID of Network", - }, - "type": { - Type: schema.TypeString, - Computed: true, - Description: "network type", - }, - } -} - -var createNetworkOperationSchRes = &schema.Resource{ - Schema: createNetworkOperationSch(), -} - -func createNetworkOperationSch() map[string]*schema.Schema { - return map[string]*schema.Schema{ - "equinix_status": { - Type: schema.TypeString, - Computed: true, - Description: "Network operation status", - }, - } -} - -func createNetworkProjectSch() map[string]*schema.Schema { - return map[string]*schema.Schema{ - "project_id": { - Type: schema.TypeString, - Computed: true, - Optional: true, - Description: "Project Id", - }, - "href": { - Type: schema.TypeString, - Optional: true, - Computed: true, - Description: "Unique Resource URL", - }, - } -} - -func createNetworkResourceSchema() map[string]*schema.Schema { - return map[string]*schema.Schema{ - "href": { - Type: schema.TypeString, - Computed: true, - Description: "Fabric Network URI information", - }, - "name": { - Type: schema.TypeString, - Required: true, - Description: "Fabric Network name. An alpha-numeric 24 characters string which can include only hyphens and underscores", - }, - "state": { - Type: schema.TypeString, - Computed: true, - Description: "Fabric Network overall state", - }, - "scope": { - Type: schema.TypeString, - Required: true, - Description: "Fabric Network scope", - }, - "equinix_asn": { - Type: schema.TypeInt, - Computed: true, - Description: "Equinix ASN", - }, - "type": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{"IPWAN", "EPLAN", "EVPLAN"}, true), - Description: "Supported Network types - EVPLAN, EPLAN, IPWAN", - }, - "location": { - Type: schema.TypeSet, - Optional: true, - Description: "Fabric Network location", - MaxItems: 1, - Elem: &schema.Resource{ - Schema: createLocationSch(), - }, - }, - "project": { - Type: schema.TypeSet, - Optional: true, - Description: "Fabric Network project", - Elem: &schema.Resource{ - Schema: createNetworkProjectSch(), - }, - }, - "operation": { - Type: schema.TypeSet, - Optional: true, - Description: "Network operation information that is associated with this Fabric Network", - MaxItems: 1, - Elem: &schema.Resource{ - Schema: createNetworkOperationSch(), - }, - }, - "change": { - Type: schema.TypeSet, - Optional: true, - Description: "Change information related to this Fabric Network", - MaxItems: 1, - Elem: &schema.Resource{ - Schema: createNetworkChangeSch(), - }, - }, - "notifications": { - Type: schema.TypeList, - Required: true, - Description: "Preferences for notifications on Fabric Network configuration or status changes", - Elem: &schema.Resource{ - Schema: createNotificationSch(), - }, - }, - "change_log": { - Type: schema.TypeSet, - Computed: true, - Description: "Captures Fabric Network lifecycle change information", - Elem: &schema.Resource{ - Schema: createChangeLogSch(), - }, - }, - } -} diff --git a/equinix/resource_fabric_network.go b/equinix/resource_fabric_network.go index 2e40b2049..dd6420b74 100755 --- a/equinix/resource_fabric_network.go +++ b/equinix/resource_fabric_network.go @@ -9,11 +9,133 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "log" "strings" "time" ) +func FabricNetworkChangeSch() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "href": { + Type: schema.TypeString, + Computed: true, + Description: "href", + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + Description: "UUID of Network Change", + }, + "type": { + Type: schema.TypeString, + Computed: true, + Description: "network change type: NETWORK_CREATION, NETWORK_UPDATE, NETWORK_DELETION", + }, + } +} +func FabricNetworkOperationSch() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "equinix_status": { + Type: schema.TypeString, + Computed: true, + Description: "Network operation status", + }, + } +} +func FabricNetworkProjectSch() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "project_id": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: "Project Id", + }, + } +} +func FabricNetworkResourceSchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "href": { + Type: schema.TypeString, + Computed: true, + Description: "Fabric Network URI information", + }, + "name": { + Type: schema.TypeString, + Required: true, + Description: "Fabric Network name. An alpha-numeric 24 characters string which can include only hyphens and underscores", + }, + "state": { + Type: schema.TypeString, + Computed: true, + Description: "Fabric Network overall state", + }, + "scope": { + Type: schema.TypeString, + Required: true, + Description: "Fabric Network scope", + }, + "type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"IPWAN", "EPLAN", "EVPLAN"}, true), + Description: "Supported Network types - EVPLAN, EPLAN, IPWAN", + }, + "location": { + Type: schema.TypeSet, + Computed: true, + Optional: true, + Description: "Fabric Network location", + MaxItems: 1, + Elem: &schema.Resource{ + Schema: createLocationSch(), + }, + }, + "project": { + Type: schema.TypeSet, + Computed: true, + Optional: true, + Description: "Fabric Network project", + Elem: &schema.Resource{ + Schema: FabricNetworkProjectSch(), + }, + }, + "operation": { + Type: schema.TypeSet, + Computed: true, + Description: "Network operation information that is associated with this Fabric Network", + MaxItems: 1, + Elem: &schema.Resource{ + Schema: FabricNetworkOperationSch(), + }, + }, + "change": { + Type: schema.TypeSet, + Computed: true, + Description: "Change information related to this Fabric Network", + MaxItems: 1, + Elem: &schema.Resource{ + Schema: FabricNetworkChangeSch(), + }, + }, + "notifications": { + Type: schema.TypeList, + Required: true, + Description: "Preferences for notifications on Fabric Network configuration or status changes", + Elem: &schema.Resource{ + Schema: createNotificationSch(), + }, + }, + "change_log": { + Type: schema.TypeSet, + Computed: true, + Description: "Captures Fabric Network lifecycle change information", + Elem: &schema.Resource{ + Schema: createChangeLogSch(), + }, + }, + } +} func resourceNetwork() *schema.Resource { return &schema.Resource{ Timeouts: &schema.ResourceTimeout{ @@ -22,20 +144,20 @@ func resourceNetwork() *schema.Resource { Delete: schema.DefaultTimeout(6 * time.Minute), Read: schema.DefaultTimeout(6 * time.Minute), }, - ReadContext: resourceNetworkRead, - CreateContext: resourceNetworkCreate, - UpdateContext: resourceNetworkUpdate, - DeleteContext: resourceNetworkDelete, + ReadContext: resourceFabricNetworkRead, + CreateContext: resourceFabricNetworkCreate, + UpdateContext: resourceFabricNetworkUpdate, + DeleteContext: resourceFabricNetworkDelete, Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, - Schema: createNetworkResourceSchema(), + Schema: FabricNetworkResourceSchema(), Description: "Fabric V4 API compatible resource allows creation and management of Equinix Fabric Network\n\n~> **Note** Equinix Fabric v4 resources and datasources are currently in Beta. The interfaces related to `equinix_fabric_` resources and datasources may change ahead of general availability. Please, do not hesitate to report any problems that you experience by opening a new [issue](https://github.com/equinix/terraform-provider-equinix/issues/new?template=bug.md)", } } -func resourceNetworkCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func resourceFabricNetworkCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*config.Config).FabricClient ctx = context.WithValue(ctx, v4.ContextAccessToken, meta.(*config.Config).FabricAuthToken) schemaNotifications := d.Get("notifications").([]interface{}) @@ -65,14 +187,14 @@ func resourceNetworkCreate(ctx context.Context, d *schema.ResourceData, meta int } d.SetId(fabricNetwork.Uuid) - if _, err = waitUntilNetworkIsProvisioned(d.Id(), meta, ctx); err != nil { + if _, err = waitUntilFabricNetworkIsProvisioned(d.Id(), meta, ctx); err != nil { return diag.Errorf("error waiting for Network (%s) to be created: %s", d.Id(), err) } - return resourceNetworkRead(ctx, d, meta) + return resourceFabricNetworkRead(ctx, d, meta) } -func resourceNetworkRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func resourceFabricNetworkRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*config.Config).FabricClient ctx = context.WithValue(ctx, v4.ContextAccessToken, meta.(*config.Config).FabricAuthToken) fabricNetwork, _, err := client.NetworksApi.GetNetworkByUuid(ctx, d.Id()) @@ -84,18 +206,53 @@ func resourceNetworkRead(ctx context.Context, d *schema.ResourceData, meta inter return diag.FromErr(err) } d.SetId(fabricNetwork.Uuid) - return setNetworkMap(d, fabricNetwork) + return setFabricNetworkMap(d, fabricNetwork) } +func FabricNetworkOperationToTerra(operation *v4.NetworkOperation) *schema.Set { + if operation == nil { + return nil + } + operations := []*v4.NetworkOperation{operation} + mappedOperations := make([]interface{}, len(operations)) + for _, operation := range operations { + mappedOperation := make(map[string]interface{}) + mappedOperation["equinix_status"] = string(*operation.EquinixStatus) + mappedOperations = append(mappedOperations, mappedOperation) + } -func setNetworkMap(d *schema.ResourceData, nt v4.Network) diag.Diagnostics { + operationSet := schema.NewSet( + schema.HashResource(&schema.Resource{Schema: FabricNetworkOperationSch()}), + mappedOperations, + ) + return operationSet +} +func simplifiedFabricNetworkChangeToTerra(networkChange *v4.SimplifiedNetworkChange) *schema.Set { + changes := []*v4.SimplifiedNetworkChange{networkChange} + mappedChanges := make([]interface{}, len(changes)) + for _, change := range changes { + mappedChange := make(map[string]interface{}) + mappedChange["href"] = change.Href + mappedChange["type"] = string(*change.Type_) + mappedChange["uuid"] = change.Uuid + mappedChanges = append(mappedChanges, mappedChange) + } + + changeSet := schema.NewSet( + schema.HashResource(&schema.Resource{Schema: FabricNetworkChangeSch()}), + mappedChanges, + ) + return changeSet +} + +func setFabricNetworkMap(d *schema.ResourceData, nt v4.Network) diag.Diagnostics { diags := diag.Diagnostics{} err := equinix_schema.SetMap(d, map[string]interface{}{ "name": nt.Name, "type": nt.Type_, "scope": nt.Scope, "state": nt.State, - "operation": networkOperationToTerra(nt.Operation), - "change": simplifiedNetworkChangeToTerra(nt.Change), + "operation": FabricNetworkOperationToTerra(nt.Operation), + "change": simplifiedFabricNetworkChangeToTerra(nt.Change), "location": locationToTerra(nt.Location), "notifications": notificationToTerra(nt.Notifications), "project": projectToTerra(nt.Project), @@ -106,11 +263,24 @@ func setNetworkMap(d *schema.ResourceData, nt v4.Network) diag.Diagnostics { } return diags } +func getFabricNetworkUpdateRequest(network v4.Network, d *schema.ResourceData) (v4.NetworkChangeOperation, error) { + changeOps := v4.NetworkChangeOperation{} + existingName := network.Name + updateNameVal := d.Get("name") + + log.Printf("existing name %s, Update Name Request %s ", existingName, updateNameVal) -func resourceNetworkUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + if existingName != updateNameVal { + changeOps = v4.NetworkChangeOperation{Op: "replace", Path: "/name", Value: &updateNameVal} + } else { + return changeOps, fmt.Errorf("nothing to update for the Fabric Network: %s", existingName) + } + return changeOps, nil +} +func resourceFabricNetworkUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*config.Config).FabricClient ctx = context.WithValue(ctx, v4.ContextAccessToken, meta.(*config.Config).FabricAuthToken) - dbConn, err := waitUntilNetworkIsProvisioned(d.Id(), meta, ctx) + dbConn, err := waitUntilFabricNetworkIsProvisioned(d.Id(), meta, ctx) if err != nil { if !strings.Contains(err.Error(), "500") { d.SetId("") @@ -118,7 +288,7 @@ func resourceNetworkUpdate(ctx context.Context, d *schema.ResourceData, meta int return diag.Errorf("either timed out or errored out while fetching Fabric Network for uuid %s and error %v", d.Id(), err) } // TO-DO - update, err := getNetworkUpdateRequest(dbConn, d) + update, err := getFabricNetworkUpdateRequest(dbConn, d) if err != nil { return diag.FromErr(err) } @@ -128,7 +298,7 @@ func resourceNetworkUpdate(ctx context.Context, d *schema.ResourceData, meta int return diag.FromErr(fmt.Errorf("error response for the Fabric Network update, response %v, error %v", res, err)) } updateFg := v4.Network{} - updateFg, err = waitForNetworkUpdateCompletion(d.Id(), meta, ctx) + updateFg, err = waitForFabricNetworkUpdateCompletion(d.Id(), meta, ctx) if err != nil { if !strings.Contains(err.Error(), "500") { @@ -138,10 +308,10 @@ func resourceNetworkUpdate(ctx context.Context, d *schema.ResourceData, meta int } d.SetId(updateFg.Uuid) - return setNetworkMap(d, updateFg) + return setFabricNetworkMap(d, updateFg) } -func waitForNetworkUpdateCompletion(uuid string, meta interface{}, ctx context.Context) (v4.Network, error) { +func waitForFabricNetworkUpdateCompletion(uuid string, meta interface{}, ctx context.Context) (v4.Network, error) { log.Printf("Waiting for Network update to complete, uuid %s", uuid) stateConf := &resource.StateChangeConf{ Target: []string{string(v4.PROVISIONED_NetworkEquinixStatus)}, @@ -167,7 +337,7 @@ func waitForNetworkUpdateCompletion(uuid string, meta interface{}, ctx context.C return dbConn, err } -func waitUntilNetworkIsProvisioned(uuid string, meta interface{}, ctx context.Context) (v4.Network, error) { +func waitUntilFabricNetworkIsProvisioned(uuid string, meta interface{}, ctx context.Context) (v4.Network, error) { log.Printf("Waiting for Fabric Network to be provisioned, uuid %s", uuid) stateConf := &resource.StateChangeConf{ Pending: []string{ @@ -198,7 +368,7 @@ func waitUntilNetworkIsProvisioned(uuid string, meta interface{}, ctx context.Co return dbConn, err } -func resourceNetworkDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func resourceFabricNetworkDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { diags := diag.Diagnostics{} client := meta.(*config.Config).FabricClient ctx = context.WithValue(ctx, v4.ContextAccessToken, meta.(*config.Config).FabricAuthToken) @@ -214,14 +384,14 @@ func resourceNetworkDelete(ctx context.Context, d *schema.ResourceData, meta int return diag.FromErr(fmt.Errorf("error response for the Fabric Network delete. Error %v and response %v", err, resp)) } - err = waitUntilNetworkDeprovisioned(d.Id(), meta, ctx) + err = waitUntilFabricNetworkDeprovisioned(d.Id(), meta, ctx) if err != nil { return diag.FromErr(fmt.Errorf("API call failed while waiting for resource deletion. Error %v", err)) } return diags } -func waitUntilNetworkDeprovisioned(uuid string, meta interface{}, ctx context.Context) error { +func waitUntilFabricNetworkDeprovisioned(uuid string, meta interface{}, ctx context.Context) error { log.Printf("Waiting for Fabric Network to be deprovisioned, uuid %s", uuid) stateConf := &resource.StateChangeConf{ Pending: []string{