From a9f49951bacccd74d440da18def2df52db0e78be Mon Sep 17 00:00:00 2001 From: srushti-patl Date: Wed, 16 Oct 2024 13:43:41 -0700 Subject: [PATCH 01/55] feat: Adding Fabric Service Token Resource and Data Source --- equinix/provider.go | 4 + .../fabric/service_token/datasources.go | 49 ++ .../service_token/datasources_schema.go | 175 +++++ .../resources/fabric/service_token/models.go | 728 ++++++++++++++++++ .../fabric/service_token/resource.go | 169 ++++ .../fabric/service_token/resource_schema.go | 424 ++++++++++ .../fabric/service_token/resource_test.go | 94 +++ 7 files changed, 1643 insertions(+) create mode 100644 internal/resources/fabric/service_token/datasources.go create mode 100644 internal/resources/fabric/service_token/datasources_schema.go create mode 100644 internal/resources/fabric/service_token/models.go create mode 100644 internal/resources/fabric/service_token/resource.go create mode 100644 internal/resources/fabric/service_token/resource_schema.go create mode 100644 internal/resources/fabric/service_token/resource_test.go diff --git a/equinix/provider.go b/equinix/provider.go index b96780f49..2ff55f43d 100644 --- a/equinix/provider.go +++ b/equinix/provider.go @@ -12,6 +12,7 @@ import ( fabric_network "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/network" fabric_route_filter "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/route_filter" fabric_route_filter_rule "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/route_filter_rule" + fabric_service_token "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/service_token" metal_device "github.com/equinix/terraform-provider-equinix/internal/resources/metal/device" metal_port "github.com/equinix/terraform-provider-equinix/internal/resources/metal/port" "github.com/equinix/terraform-provider-equinix/internal/resources/metal/virtual_circuit" @@ -99,6 +100,8 @@ func Provider() *schema.Provider { "equinix_fabric_route_filter_rules": fabric_route_filter_rule.DataSourceGetAllRules(), "equinix_fabric_service_profile": dataSourceFabricServiceProfileReadByUuid(), "equinix_fabric_service_profiles": dataSourceFabricSearchServiceProfilesByName(), + "equinix_fabric_service_token": fabric_service_token.DataSource(), + "equinix_fabric_service_tokens": fabric_service_token.DataSourceSearch(), "equinix_network_account": dataSourceNetworkAccount(), "equinix_network_device": dataSourceNetworkDevice(), "equinix_network_device_type": dataSourceNetworkDeviceType(), @@ -129,6 +132,7 @@ func Provider() *schema.Provider { "equinix_fabric_route_filter_rule": fabric_route_filter_rule.Resource(), "equinix_fabric_routing_protocol": resourceFabricRoutingProtocol(), "equinix_fabric_service_profile": resourceFabricServiceProfile(), + "equinix_fabric_service_token": fabric_service_token.Resource(), "equinix_network_device": resourceNetworkDevice(), "equinix_network_ssh_user": resourceNetworkSSHUser(), "equinix_network_bgp": resourceNetworkBGP(), diff --git a/internal/resources/fabric/service_token/datasources.go b/internal/resources/fabric/service_token/datasources.go new file mode 100644 index 000000000..adba0f1d6 --- /dev/null +++ b/internal/resources/fabric/service_token/datasources.go @@ -0,0 +1,49 @@ +package service_token + +import ( + "context" + "fmt" + "github.com/equinix/terraform-provider-equinix/internal/config" + equinix_errors "github.com/equinix/terraform-provider-equinix/internal/errors" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func DataSource() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceRead, + Schema: dataSourceBaseSchema(), + Description: `Fabric V4 API compatible data resource that allow user to fetch service token for a given UUID`, + } +} + +func dataSourceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + uuid, _ := d.Get("uuid").(string) + d.SetId(uuid) + return resourceRead(ctx, d, meta) +} + +func DataSourceSearch() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceSearch, + Schema: dataSourceSearchSchema(), + Description: `Fabric V4 API compatible data resource that allow user to fetch service token for a given search data set`, + } +} + +func dataSourceSearch(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*config.Config).NewFabricClientForSDK(d) + searchRequest := buildSearchRequest(d) + + serviceTokens, _, err := client.ServiceTokensApi.SearchServiceTokens(ctx).ServiceTokenSearchRequest(searchRequest).Execute() + if err != nil { + return diag.FromErr(equinix_errors.FormatFabricError(err)) + } + + if len(serviceTokens.Data) < 1 { + return diag.FromErr(fmt.Errorf("no records are found for the route filter search criteria provided - %d , please change the search criteria", len(serviceTokens.Data))) + } + + d.SetId(serviceTokens.Data[0].GetUuid()) + return setServiceTokensData(d, serviceTokens) +} diff --git a/internal/resources/fabric/service_token/datasources_schema.go b/internal/resources/fabric/service_token/datasources_schema.go new file mode 100644 index 000000000..046112cee --- /dev/null +++ b/internal/resources/fabric/service_token/datasources_schema.go @@ -0,0 +1,175 @@ +package service_token + +import ( + equinix_fabric_schema "github.com/equinix/terraform-provider-equinix/internal/fabric/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func dataSourceBaseSchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Computed: true, + Description: "Service Token Type; VC_TOKEN,EPL_TOKEN", + }, + "uuid": { + Type: schema.TypeString, + Required: true, + Description: "Equinix-assigned service token identifier", + }, + "href": { + Type: schema.TypeString, + Computed: true, + Description: "An absolute URL that is the subject of the link's context.", + }, + "issuer_side": { + Type: schema.TypeString, + Computed: true, + Description: "Information about token side; ASIDE, ZSIDE", + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: "Name of the Service Token", + }, + "description": { + Type: schema.TypeString, + Computed: true, + Description: "Optional Description to the Service Token you will be creating", + }, + "expiration_date_time": { + Type: schema.TypeString, + Computed: true, + Description: "Expiration date and time of the service token; 2020-11-06T07:00:00Z", + }, + "service_token_connection": { + Type: schema.TypeSet, + Computed: true, + Description: "Service Token Connection Type Information", + Elem: serviceTokenConnectionSch(), + }, + "state": { + Type: schema.TypeString, + Computed: true, + Description: "Service token state; ACTIVE, INACTIVE, EXPIRED, DELETED", + }, + "notifications": { + Type: schema.TypeSet, + Computed: true, + Description: "Preferences for notifications on Service Token configuration or status changes", + Elem: &schema.Resource{ + Schema: equinix_fabric_schema.NotificationSch(), + }, + }, + "account": { + Type: schema.TypeSet, + Computed: true, + Description: "Customer account information that is associated with this service token", + Elem: &schema.Resource{ + Schema: equinix_fabric_schema.AccountSch(), + }, + }, + "change_log": { + Type: schema.TypeSet, + Computed: true, + Description: "Captures connection lifecycle change information", + Elem: &schema.Resource{ + Schema: equinix_fabric_schema.ChangeLogSch(), + }, + }, + "project": { + Type: schema.TypeSet, + Computed: true, + Description: "Project information", + Elem: &schema.Resource{ + Schema: equinix_fabric_schema.ProjectSch(), + }, + }, + } +} + +func paginationSchema() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "offset": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "The page offset for the pagination request. Index of the first element. Default is 0.", + }, + "limit": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "Number of elements to be requested per page. Number must be between 1 and 100. Default is 20", + }, + "total": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "Total number of elements returned.", + }, + "next": { + Type: schema.TypeString, + Computed: true, + Description: "URL relative to the last item in the response.", + }, + "previous": { + Type: schema.TypeString, + Computed: true, + Description: "URL relative to the first item in the response.", + }, + }, + } +} + +func dataSourceSearchSchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "data": { + Type: schema.TypeList, + Computed: true, + Description: "List of Route Filters", + Elem: &schema.Resource{ + Schema: dataSourceBaseSchema(), + }, + }, + "filter": { + Type: schema.TypeList, + Required: true, + Description: "Filters for the Data Source Search Request. Maximum of 8 total filters.", + MaxItems: 10, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "property": { + Type: schema.TypeString, + Required: true, + Description: "The API response property which you want to filter your request on. Can be one of the following: \"/type\", \"/name\", \"/project/projectId\", \"/uuid\", \"/state\"", + ValidateFunc: validation.StringInSlice([]string{"/uuid", "/state", "/name", "/project/projectId"}, true), + }, + "operator": { + Type: schema.TypeString, + Required: true, + Description: "Possible operators to use on the filter property. Can be one of the following: [ \"=\", \"!=\", \"[NOT] LIKE\", \"[NOT] IN\", \"ILIKE\" ]", + ValidateFunc: validation.StringInSlice([]string{"=", "!=", "[NOT] LIKE", "[NOT] IN", "ILIKE"}, true), + }, + "values": { + Type: schema.TypeList, + Required: true, + Description: "The values that you want to apply the property+operator combination to in order to filter your data search", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "pagination": { + Type: schema.TypeSet, + Optional: true, + Description: "Pagination details for the Data Source Search Request", + MaxItems: 1, + Elem: paginationSchema(), + }, + } +} diff --git a/internal/resources/fabric/service_token/models.go b/internal/resources/fabric/service_token/models.go new file mode 100644 index 000000000..6fff6d489 --- /dev/null +++ b/internal/resources/fabric/service_token/models.go @@ -0,0 +1,728 @@ +package service_token + +import ( + "fmt" + "github.com/equinix/equinix-sdk-go/services/fabricv4" + "github.com/equinix/terraform-provider-equinix/internal/converters" + equinix_fabric_schema "github.com/equinix/terraform-provider-equinix/internal/fabric/schema" + 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/schema" + "log" + "reflect" + "sort" + "time" +) + +func buildCreateRequest(d *schema.ResourceData) fabricv4.ServiceToken { + serviceTokenRequest := fabricv4.ServiceToken{} + + typeConfig := d.Get("type").(string) + serviceTokenRequest.SetType(fabricv4.ServiceTokenType(typeConfig)) + + expirationDateTimeConfig := d.Get("expiration_date_time").(string) + log.Printf("[DEBUG] !!!! Expiration Date %v", expirationDateTimeConfig) + const TimeFormat = "2006-01-02T15:04:05.000Z" + expirationTime, err := time.Parse(TimeFormat, expirationDateTimeConfig) + if err != nil { + fmt.Print("Error Parsing expiration date time: ", err) + } + log.Printf("[DEBUG] !!! Parsed expiration date %v", expirationTime) + serviceTokenRequest.SetExpirationDateTime(expirationTime) + + connectionConfig := d.Get("service_token_connection").(*schema.Set).List() + connection := connectionTerraformToGo(connectionConfig) + serviceTokenRequest.SetConnection(connection) + + notificationsConfig := d.Get("notifications").(*schema.Set).List() + notifications := equinix_fabric_schema.NotificationsTerraformToGo(notificationsConfig) + serviceTokenRequest.SetNotifications(notifications) + + return serviceTokenRequest + +} + +func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOperation { + patches := make([]fabricv4.ServiceTokenChangeOperation, 0) + oldName, newName := d.GetChange("name") + if oldName.(string) != newName.(string) { + 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{ + Op: "replace", + Path: "/description", + Value: newDescription.(string), + }) + } + + oldExpirationDate, newExpirationDate := d.GetChange("expiration_date_time") + if oldExpirationDate.(string) != newExpirationDate.(string) { + patches = append(patches, fabricv4.ServiceTokenChangeOperation{ + Op: "replace", + Path: "/expirationDateTime", + Value: newExpirationDate.(string), + }) + } + + oldNotifications, newNotifications := d.GetChange("notifications") + + var oldNotificationEmails, newNotificationEmails []string + + if oldNotifications != nil { + for _, notification := range oldNotifications.(*schema.Set).List() { + notificationMap := notification.(map[string]interface{}) + + // Extract old emails list if it exists + if emails, ok := notificationMap["emails"]; ok { + oldEmailInterface := emails.([]interface{}) + if len(oldEmailInterface) > 0 { + oldNotificationEmails = converters.IfArrToStringArr(oldEmailInterface) + } + } + } + } + if newNotifications != nil { + for _, notification := range newNotifications.(*schema.Set).List() { + notificationMap := notification.(map[string]interface{}) + + // Extract old emails list if it exists + if emails, ok := notificationMap["emails"]; ok { + newEmailInterface := emails.([]interface{}) + if len(newEmailInterface) > 0 { + newNotificationEmails = converters.IfArrToStringArr(newEmailInterface) + } + } + } + } + + log.Print("!!! DEBUG value of new email", newNotificationEmails) + if !reflect.DeepEqual(oldNotificationEmails, newNotificationEmails) { + patches = append(patches, fabricv4.ServiceTokenChangeOperation{ + Op: "replace", + Path: "/notifications/emails", + Value: newNotificationEmails, + }) + } + + oldServiceTokenConnection, newServiceTokenConnection := d.GetChange("service_token_connection") + log.Printf("[DEBUG] !!! old ServiceToken Connection %v", oldServiceTokenConnection) + log.Printf("[DEBUG] !!! new ServiceToken Connection %v", newServiceTokenConnection) + + // Initialize variables for bandwidth limits + var oldAsideBandwidthLimit, newAsideBandwidthLimit int + + // Extract old bandwidth limit + if oldServiceTokenConnection != nil { + for _, connection := range oldServiceTokenConnection.(*schema.Set).List() { + notificationMap := connection.(map[string]interface{}) + + // Extract old bandwidth limit if it exists + if bandwidth, ok := notificationMap["bandwidthLimit"]; ok { + oldBandwidthLimit := bandwidth.([]interface{}) + if len(oldBandwidthLimit) > 0 { + oldAsideBandwidthLimits := converters.IfArrToIntArr(oldBandwidthLimit) + oldAsideBandwidthLimit = oldAsideBandwidthLimits[0] + } + } + } + } + + // Extract new bandwidth limit + if newServiceTokenConnection != nil { + for _, connection := range newServiceTokenConnection.(*schema.Set).List() { + notificationMap := connection.(map[string]interface{}) + + // Extract new bandwidth limit if it exists + if bandwidth, ok := notificationMap["bandwidthLimit"]; ok { + newBandwidthLimit := bandwidth.([]interface{}) + if len(newBandwidthLimit) > 0 { + newAsideBandwidthLimits := converters.IfArrToIntArr(newBandwidthLimit) + newAsideBandwidthLimit = newAsideBandwidthLimits[0] + } + } + } + } + + if oldAsideBandwidthLimit != newAsideBandwidthLimit { + patches = append(patches, fabricv4.ServiceTokenChangeOperation{ + Op: "replace", + Path: "/connection/bandwidthLimit", + Value: newAsideBandwidthLimit, + }) + } + + // Get the old and new values for bandwidth limit + var oldZsideBandwidth, newZsideBandwidth []int + + if oldServiceTokenConnection != nil { + for _, connection := range oldServiceTokenConnection.(*schema.Set).List() { + notificationMap := connection.(map[string]interface{}) + + // Extract old bandwidth limit if it exists + if bandwidth, ok := notificationMap["supported_bandwidths"]; ok { + oldSupportedBandwidth := bandwidth.([]interface{}) + if len(oldSupportedBandwidth) > 0 { + oldZsideBandwidth = converters.IfArrToIntArr(oldSupportedBandwidth) + } + } + } + } + + if newServiceTokenConnection != nil { + for _, connection := range newServiceTokenConnection.(*schema.Set).List() { + notificationMap := connection.(map[string]interface{}) + + // Extract new bandwidth limit if it exists + if bandwidth, ok := notificationMap["supported_bandwidths"]; ok { + newSupportedBandwidth := bandwidth.([]interface{}) + if len(newSupportedBandwidth) > 0 { + newZsideBandwidth = converters.IfArrToIntArr(newSupportedBandwidth) + + } + } + } + } + + log.Print("!!! DEBUG value of new supprted bandwidth", newZsideBandwidth) + log.Printf("[DEBUG] Value of aresliceequal fucntion %v", areSlicesEqual(oldZsideBandwidth, newZsideBandwidth)) + if !areSlicesEqual(oldZsideBandwidth, newZsideBandwidth) { + patches = append(patches, fabricv4.ServiceTokenChangeOperation{ + Op: "replace", + Path: "/connection/supportedBandwidths", + Value: newZsideBandwidth, + }) + } + + return patches +} + +func areSlicesEqual(a, b []int) bool { + if len(a) != len(b) { + return false + } + + // Sort both slices + sort.Ints(a) + sort.Ints(b) + + log.Printf("value of int a %v", a) + log.Printf("value of int b %v", b) + // Compare sorted slices + for i := range a { + if a[i] != b[i] { + return false + } + } + + return true +} + +func buildSearchRequest(d *schema.ResourceData) fabricv4.ServiceTokenSearchRequest { + searchRequest := fabricv4.ServiceTokenSearchRequest{} + + schemaFilters := d.Get("filter").([]interface{}) + filter := filtersTerraformToGo(schemaFilters) + searchRequest.SetFilter(filter) + + if schemaPagination, ok := d.GetOk("pagination"); ok { + pagination := paginationTerraformToGo(schemaPagination.(*schema.Set).List()) + searchRequest.SetPagination(pagination) + } + + return searchRequest +} +func setServiceTokenMap(d *schema.ResourceData, serviceToken *fabricv4.ServiceToken) diag.Diagnostics { + diags := diag.Diagnostics{} + serviceTokenMap := serviceTokenResponseMap(serviceToken) + err := equinix_schema.SetMap(d, serviceTokenMap) + if err != nil { + return diag.FromErr(err) + } + return diags +} + +func setServiceTokensData(d *schema.ResourceData, routeFilters *fabricv4.ServiceTokens) diag.Diagnostics { + diags := diag.Diagnostics{} + mappedRouteFilters := make([]map[string]interface{}, len(routeFilters.Data)) + pagination := routeFilters.GetPagination() + if routeFilters.Data != nil { + for index, routeFilter := range routeFilters.Data { + mappedRouteFilters[index] = serviceTokenResponseMap(&routeFilter) + } + } else { + mappedRouteFilters = nil + } + err := equinix_schema.SetMap(d, map[string]interface{}{ + "data": mappedRouteFilters, + "pagination": paginationGoToTerraform(&pagination), + }) + if err != nil { + return diag.FromErr(err) + } + return diags +} + +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() + 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) + } + if token.Changelog != nil { + changelog := token.GetChangelog() + serviceToken["change_log"] = equinix_fabric_schema.ChangeLogGoToTerraform(&changelog) + } + if token.Project != nil { + project := token.GetProject() + serviceToken["project"] = equinix_fabric_schema.ProjectGoToTerraform(&project) + } + + return serviceToken +} + +func connectionTerraformToGo(connectionTerraform []interface{}) fabricv4.ServiceTokenConnection { + if connectionTerraform == nil || len(connectionTerraform) == 0 { + return fabricv4.ServiceTokenConnection{} + } + + var connection fabricv4.ServiceTokenConnection + + connectionMap := connectionTerraform[0].(map[string]interface{}) + + typeVal := connectionMap["type"].(string) + connection.SetType(fabricv4.ServiceTokenConnectionType(typeVal)) + + uuid := connectionMap["uuid"].(string) + connection.SetUuid(uuid) + + allowRemoteConnection := connectionMap["allow_remote_connection"].(bool) + connection.SetAllowRemoteConnection(allowRemoteConnection) + + allowCustomBandwidth := connectionMap["allow_custom_bandwidth"].(bool) + connection.SetAllowCustomBandwidth(allowCustomBandwidth) + + bandwidthLimit := connectionMap["bandwidth_limit"].(int) + connection.SetBandwidthLimit(int32(bandwidthLimit)) + + supportedBandwidths := connectionMap["supported_bandwidths"].([]interface{}) + if supportedBandwidths != nil { + // Create a new slice to hold the int32 values + int32Bandwidths := make([]int32, len(supportedBandwidths)) + + // Convert []interface{} to []int32 + for i, v := range supportedBandwidths { + int32Bandwidths[i] = int32(v.(int)) // Assign directly to the slice at index i + } + // Set the converted []int32 to the connection + connection.SetSupportedBandwidths(int32Bandwidths) + } + + asideRequest := connectionMap["a_side"].(*schema.Set).List() + zsideRequest := connectionMap["z_side"].(*schema.Set).List() + if len(asideRequest) != 0 { + aside := accessPointTerraformToGo(asideRequest) + connection.SetASide(aside) + } + if len(zsideRequest) != 0 { + zside := accessPointTerraformToGo(zsideRequest) + connection.SetZSide(zside) + } + return connection +} + +func accessPointTerraformToGo(accessPoint []interface{}) fabricv4.ServiceTokenSide { + if accessPoint == nil || len(accessPoint) == 0 { + return fabricv4.ServiceTokenSide{} + } + + var apSide fabricv4.ServiceTokenSide + + accessPointMap := accessPoint[0].(map[string]interface{}) + accessPointSelectors := accessPointMap["access_point_selectors"].(*schema.Set).List() + if len(accessPointSelectors) != 0 { + aps := accessPointSelectorsTerraformToGo(accessPointSelectors) + apSide.SetAccessPointSelectors(aps) + } + return apSide +} + +func accessPointSelectorsTerraformToGo(accessPointSelectors []interface{}) []fabricv4.AccessPointSelector { + if accessPointSelectors == nil || len(accessPointSelectors) == 0 { + return []fabricv4.AccessPointSelector{} + } + + var apSelectors fabricv4.AccessPointSelector + + apSelectorsMap := accessPointSelectors[0].(map[string]interface{}) + typeVal := apSelectorsMap["type"].(string) + apSelectors.SetType(fabricv4.AccessPointSelectorType(typeVal)) + portList := apSelectorsMap["port"].(*schema.Set).List() + linkProtocolList := apSelectorsMap["link_protocol"].(*schema.Set).List() + virtualDeviceList := apSelectorsMap["virtual_device"].(*schema.Set).List() + interfaceList := apSelectorsMap["interface"].(*schema.Set).List() + networkList := apSelectorsMap["network"].(*schema.Set).List() + + if len(portList) != 0 { + port := portTerraformToGo(portList) + apSelectors.SetPort(port) + } + + if len(linkProtocolList) != 0 { + linkProtocol := linkProtocolTerraformToGo(linkProtocolList) + apSelectors.SetLinkProtocol(linkProtocol) + } + + if len(virtualDeviceList) != 0 { + virtualDevice := virtualDeviceTerraformToGo(virtualDeviceList) + apSelectors.SetVirtualDevice(virtualDevice) + } + + if len(interfaceList) != 0 { + interface_ := interfaceTerraformToGo(interfaceList) + apSelectors.SetInterface(interface_) + } + + if len(networkList) != 0 { + network := networkTerraformToGo(networkList) + apSelectors.SetNetwork(network) + } + + return []fabricv4.AccessPointSelector{apSelectors} +} + +func portTerraformToGo(portList []interface{}) fabricv4.SimplifiedMetadataEntity { + if portList == nil || len(portList) == 0 { + return fabricv4.SimplifiedMetadataEntity{} + } + var port fabricv4.SimplifiedMetadataEntity + portListMap := portList[0].(map[string]interface{}) + uuid := portListMap["uuid"].(string) + port.SetUuid(uuid) + + return port +} + +func linkProtocolTerraformToGo(linkProtocolList []interface{}) fabricv4.SimplifiedLinkProtocol { + if linkProtocolList == nil || len(linkProtocolList) == 0 { + return fabricv4.SimplifiedLinkProtocol{} + } + var linkProtocol fabricv4.SimplifiedLinkProtocol + lpMap := linkProtocolList[0].(map[string]interface{}) + lpType := lpMap["type"].(string) + lpVlanSTag := int32(lpMap["vlan_s_tag"].(int)) + lpVlanTag := int32(lpMap["vlan_tag"].(int)) + lpVlanCTag := int32(lpMap["vlan_c_tag"].(int)) + + linkProtocol.SetType(fabricv4.LinkProtocolType(lpType)) + if lpVlanSTag != 0 { + linkProtocol.SetVlanSTag(lpVlanSTag) + } + if lpVlanTag != 0 { + linkProtocol.SetVlanTag(lpVlanTag) + } + if lpVlanCTag != 0 { + linkProtocol.SetVlanCTag(lpVlanCTag) + } + + return linkProtocol +} + +func virtualDeviceTerraformToGo(virtualDeviceList []interface{}) fabricv4.SimplifiedVirtualDevice { + if virtualDeviceList == nil || len(virtualDeviceList) == 0 { + return fabricv4.SimplifiedVirtualDevice{} + } + + var virtualDevice fabricv4.SimplifiedVirtualDevice + virtualDeviceMap := virtualDeviceList[0].(map[string]interface{}) + href := virtualDeviceMap["href"].(string) + type_ := virtualDeviceMap["type"].(string) + uuid := virtualDeviceMap["uuid"].(string) + name := virtualDeviceMap["name"].(string) + cluster := virtualDeviceMap["cluster"].(string) + virtualDevice.SetHref(href) + virtualDevice.SetType(fabricv4.SimplifiedVirtualDeviceType(type_)) + virtualDevice.SetUuid(uuid) + virtualDevice.SetName(name) + virtualDevice.SetCluster(cluster) + + return virtualDevice +} + +func interfaceTerraformToGo(interfaceList []interface{}) fabricv4.VirtualDeviceInterface { + if interfaceList == nil || len(interfaceList) == 0 { + return fabricv4.VirtualDeviceInterface{} + } + + var interface_ fabricv4.VirtualDeviceInterface + interfaceMap := interfaceList[0].(map[string]interface{}) + uuid := interfaceMap["uuid"].(string) + type_ := interfaceMap["type"].(string) + id := interfaceMap["id"].(int) + interface_.SetUuid(uuid) + interface_.SetType(fabricv4.VirtualDeviceInterfaceType(type_)) + interface_.SetId(int32(id)) + + return interface_ +} + +func networkTerraformToGo(networkList []interface{}) fabricv4.SimplifiedTokenNetwork { + if networkList == nil || len(networkList) == 0 { + return fabricv4.SimplifiedTokenNetwork{} + } + var network fabricv4.SimplifiedTokenNetwork + networkListMap := networkList[0].(map[string]interface{}) + uuid := networkListMap["uuid"].(string) + type_ := networkListMap["type"].(string) + network.SetUuid(uuid) + network.SetType(fabricv4.SimplifiedTokenNetworkType(type_)) + return network +} + +func filtersTerraformToGo(tokens []interface{}) fabricv4.ServiceTokenSearchExpression { + if tokens == nil { + return fabricv4.ServiceTokenSearchExpression{} + } + + searchTokensList := make([]fabricv4.ServiceTokenSearchExpression, 0) + + for _, filter := range tokens { + filterMap := filter.(map[string]interface{}) + filterItem := fabricv4.ServiceTokenSearchExpression{} + if property, ok := filterMap["property"]; ok { + filterItem.SetProperty(fabricv4.ServiceTokenSearchFieldName(property.(string))) + } + if operator, ok := filterMap["operator"]; ok { + filterItem.SetOperator(fabricv4.ServiceTokenSearchExpressionOperator(operator.(string))) + } + if values, ok := filterMap["values"]; ok { + stringValues := converters.IfArrToStringArr(values.([]interface{})) + filterItem.SetValues(stringValues) + } + searchTokensList = append(searchTokensList, filterItem) + } + + searchTokens := fabricv4.ServiceTokenSearchExpression{} + searchTokens.SetAnd(searchTokensList) + + return searchTokens +} + +func paginationTerraformToGo(pagination []interface{}) fabricv4.PaginationRequest { + if pagination == nil { + return fabricv4.PaginationRequest{} + } + paginationRequest := fabricv4.PaginationRequest{} + for _, page := range pagination { + pageMap := page.(map[string]interface{}) + if offset, ok := pageMap["offset"]; ok { + paginationRequest.SetOffset(int32(offset.(int))) + } + if limit, ok := pageMap["limit"]; ok { + paginationRequest.SetLimit(int32(limit.(int))) + } + } + + return paginationRequest +} + +func connectionGoToTerraform(connection *fabricv4.ServiceTokenConnection) *schema.Set { + mappedConnection := make(map[string]interface{}) + mappedConnection["type"] = string(connection.GetType()) + mappedConnection["allow_remote_connection"] = connection.GetAllowRemoteConnection() + mappedConnection["allow_custom_bandwidth"] = connection.GetAllowCustomBandwidth() + if connection.SupportedBandwidths != nil { + supportedBandwidths := connection.GetSupportedBandwidths() + interfaceBandwidths := make([]interface{}, len(supportedBandwidths)) + + for i, v := range supportedBandwidths { + interfaceBandwidths[i] = int(v) // Convert each int32 to interface{} + } + + mappedConnection["supported_bandwidths"] = interfaceBandwidths + } + if connection.ASide != nil { + accessPoint := connection.GetASide() + mappedConnection["a_side"] = accessPointGoToTerraform(&accessPoint) + } + if connection.ZSide != nil { + accessPoint := connection.GetZSide() + mappedConnection["z_side"] = accessPointGoToTerraform(&accessPoint) + } + connectionSet := schema.NewSet( + schema.HashResource(serviceTokenConnectionSch()), + []interface{}{mappedConnection}, + ) + return connectionSet +} + +func accessPointGoToTerraform(accessPoint *fabricv4.ServiceTokenSide) *schema.Set { + mappedAccessPoint := make(map[string]interface{}) + if accessPoint.AccessPointSelectors != nil { + accessPointSelectors := accessPoint.GetAccessPointSelectors() + + apSelectorsSet := schema.NewSet( + schema.HashResource(accessPointSelectorsSch()), + nil, + ) + for _, selector := range accessPointSelectors { + mappedSelector := accessPointSelectorsGoToTerraform(&selector) + apSelectorsSet.Add(mappedSelector) + + } + mappedAccessPoint["access_point_selectors"] = apSelectorsSet + } + + accessPointSet := schema.NewSet( + schema.HashResource(accessPointSelectorsSch()), + []interface{}{mappedAccessPoint}, + ) + return accessPointSet +} + +func accessPointSelectorsGoToTerraform(apSelectors *fabricv4.AccessPointSelector) map[string]interface{} { + mappedAccessPointSelectors := make(map[string]interface{}) + if apSelectors.Type != nil { + mappedAccessPointSelectors["type"] = string(apSelectors.GetType()) + } + if apSelectors.Port != nil { + port := apSelectors.GetPort() + mappedAccessPointSelectors["port"] = portGoToTerraform(&port) + } + if apSelectors.LinkProtocol != nil { + linkProtocol := apSelectors.GetLinkProtocol() + mappedAccessPointSelectors["link_protocol"] = linkedProtocolGoToTerraform(&linkProtocol) + } + if apSelectors.VirtualDevice != nil { + virtualDevice := apSelectors.GetVirtualDevice() + mappedAccessPointSelectors["virtual_device"] = virtualDeviceGoToTerraform(&virtualDevice) + } + if apSelectors.Interface != nil { + interface_ := apSelectors.GetInterface() + mappedAccessPointSelectors["interface"] = interfaceGoToTerraform(&interface_) + } + if apSelectors.Network != nil { + network := apSelectors.GetNetwork() + mappedAccessPointSelectors["network"] = networkGoToTerraform(&network) + } + + return mappedAccessPointSelectors +} + +func portGoToTerraform(port *fabricv4.SimplifiedMetadataEntity) *schema.Set { + mappedPort := make(map[string]interface{}) + mappedPort["href"] = port.GetHref() + mappedPort["type"] = port.GetType() + mappedPort["uuid"] = port.GetUuid() + + portSet := schema.NewSet( + schema.HashResource(portSch()), + []interface{}{mappedPort}, + ) + return portSet +} + +func linkedProtocolGoToTerraform(linkedProtocol *fabricv4.SimplifiedLinkProtocol) *schema.Set { + + mappedLinkedProtocol := make(map[string]interface{}) + mappedLinkedProtocol["type"] = string(linkedProtocol.GetType()) + mappedLinkedProtocol["vlan_tag"] = int(linkedProtocol.GetVlanTag()) + mappedLinkedProtocol["vlan_s_tag"] = int(linkedProtocol.GetVlanSTag()) + mappedLinkedProtocol["vlan_c_tag"] = int(linkedProtocol.GetVlanCTag()) + + linkedProtocolSet := schema.NewSet( + schema.HashResource(linkProtocolSch()), + []interface{}{mappedLinkedProtocol}, + ) + return linkedProtocolSet +} + +func virtualDeviceGoToTerraform(virtualDevice *fabricv4.SimplifiedVirtualDevice) *schema.Set { + if virtualDevice == nil { + return nil + } + mappedVirtualDevice := make(map[string]interface{}) + mappedVirtualDevice["name"] = virtualDevice.GetName() + mappedVirtualDevice["href"] = virtualDevice.GetHref() + mappedVirtualDevice["type"] = string(virtualDevice.GetType()) + mappedVirtualDevice["uuid"] = virtualDevice.GetUuid() + if virtualDevice.Cluster != nil { + mappedVirtualDevice["cluster"] = virtualDevice.GetCluster() + } + + virtualDeviceSet := schema.NewSet( + schema.HashResource(virtualDeviceSch()), + []interface{}{mappedVirtualDevice}, + ) + return virtualDeviceSet +} + +func interfaceGoToTerraform(mInterface *fabricv4.VirtualDeviceInterface) *schema.Set { + if mInterface == nil { + return nil + } + mappedMInterface := make(map[string]interface{}) + mappedMInterface["id"] = int(mInterface.GetId()) + mappedMInterface["type"] = string(mInterface.GetType()) + mappedMInterface["uuid"] = mInterface.GetUuid() + + mInterfaceSet := schema.NewSet( + schema.HashResource(interfaceSch()), + []interface{}{mappedMInterface}, + ) + return mInterfaceSet +} + +func networkGoToTerraform(network *fabricv4.SimplifiedTokenNetwork) *schema.Set { + if network == nil { + return nil + } + + mappedNetwork := make(map[string]interface{}) + mappedNetwork["uuid"] = network.GetUuid() + mappedNetwork["href"] = network.GetHref() + mappedNetwork["type"] = string(network.GetType()) + + return schema.NewSet( + schema.HashResource(networkSch()), + []interface{}{mappedNetwork}, + ) +} + +func paginationGoToTerraform(pagination *fabricv4.Pagination) *schema.Set { + if pagination == nil { + return nil + } + mappedPagination := make(map[string]interface{}) + mappedPagination["offset"] = int(pagination.GetOffset()) + mappedPagination["limit"] = int(pagination.GetLimit()) + mappedPagination["total"] = int(pagination.GetTotal()) + mappedPagination["next"] = pagination.GetNext() + mappedPagination["previous"] = pagination.GetPrevious() + + return schema.NewSet( + schema.HashResource(paginationSchema()), + []interface{}{mappedPagination}, + ) +} diff --git a/internal/resources/fabric/service_token/resource.go b/internal/resources/fabric/service_token/resource.go new file mode 100644 index 000000000..94301e915 --- /dev/null +++ b/internal/resources/fabric/service_token/resource.go @@ -0,0 +1,169 @@ +package service_token + +import ( + "context" + "github.com/equinix/equinix-sdk-go/services/fabricv4" + "github.com/equinix/terraform-provider-equinix/internal/config" + equinix_errors "github.com/equinix/terraform-provider-equinix/internal/errors" + equinix_fabric_schema "github.com/equinix/terraform-provider-equinix/internal/fabric/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "log" + "strings" + "time" +) + +func Resource() *schema.Resource { + return &schema.Resource{ + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(10 * time.Minute), + Update: schema.DefaultTimeout(10 * time.Minute), + Delete: schema.DefaultTimeout(10 * time.Minute), + Read: schema.DefaultTimeout(10 * time.Minute), + }, + ReadContext: resourceRead, + CreateContext: resourceCreate, + UpdateContext: resourceUpdate, + DeleteContext: resourceDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + Schema: resourceSchema(), + Description: `Fabric V4 API compatible resource allows creation and management of Equinix Fabric Service Token`, + } +} + +func resourceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*config.Config).NewFabricClientForSDK(d) + serviceToken, _, err := client.ServiceTokensApi.GetServiceTokenByUuid(ctx, d.Id()).Execute() + if err != nil { + log.Printf("[WARN] Service Token %s not found , error %s", d.Id(), err) + if !strings.Contains(err.Error(), "500") { + d.SetId("") + } + return diag.FromErr(equinix_errors.FormatFabricError(err)) + } + d.SetId(serviceToken.GetUuid()) + return setServiceTokenMap(d, serviceToken) +} + +func resourceCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*config.Config).NewFabricClientForSDK(d) + createRequest := buildCreateRequest(d) + + start := time.Now() + serviceToken, _, err := client.ServiceTokensApi.CreateServiceToken(ctx).ServiceToken(createRequest).Execute() + if err != nil { + return diag.FromErr(equinix_errors.FormatFabricError(err)) + } + d.SetId(serviceToken.GetUuid()) + notificationsMap := equinix_fabric_schema.NotificationsGoToTerraform(createRequest.GetNotifications()) + if err = d.Set("notifications", notificationsMap); err != nil { + return diag.Errorf("error setting notifications config to state: %s", err) + } + + createTimeout := d.Timeout(schema.TimeoutCreate) - 30*time.Second - time.Since(start) + 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) + } + + return resourceRead(ctx, d, meta) +} + +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() + if err != nil { + return diag.FromErr(equinix_errors.FormatFabricError(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) + } + + return setServiceTokenMap(d, serviceToken) +} + +func resourceDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + diags := diag.Diagnostics{} + client := meta.(*config.Config).NewFabricClientForSDK(d) + + start := time.Now() + _, _, err := client.ServiceTokensApi.DeleteServiceTokenByUuid(ctx, d.Id()).Execute() + if err != nil { + if genericError, ok := err.(*fabricv4.GenericOpenAPIError); ok { + if fabricErrs, ok := genericError.Model().([]fabricv4.Error); ok { + if equinix_errors.HasErrorCode(fabricErrs, "") { + return diags + } + } + } + return diag.FromErr(equinix_errors.FormatFabricError(err)) + } + + deleteTimeout := d.Timeout(schema.TimeoutDelete) - 30*time.Second - time.Since(start) + if err = WaitForDeletion(d.Id(), meta, d, ctx, deleteTimeout); err != nil { + return diag.Errorf("error waiting for service token (%s) to be deleted: %s", d.Id(), err) + } + return diags +} + +func waitForStability(uuid string, meta interface{}, d *schema.ResourceData, ctx context.Context, tieout time.Duration) error { + log.Printf("Waiting for service token to be created, uuid %s", uuid) + stateConf := &retry.StateChangeConf{ + Target: []string{ + string(fabricv4.SERVICETOKENSTATE_INACTIVE), + }, + Refresh: func() (interface{}, string, error) { + client := meta.(*config.Config).NewFabricClientForSDK(d) + serviceToken, _, err := client.ServiceTokensApi.GetServiceTokenByUuid(ctx, uuid).Execute() + if err != nil { + return "", "", equinix_errors.FormatFabricError(err) + } + return serviceToken, string(serviceToken.GetState()), nil + }, + Timeout: tieout, + Delay: 30 * time.Second, + MinTimeout: 30 * time.Second, + } + + _, err := stateConf.WaitForStateContext(ctx) + + return err +} + +func WaitForDeletion(uuid string, meta interface{}, d *schema.ResourceData, ctx context.Context, timeout time.Duration) error { + log.Printf("Waiting for service token to be deleted, uuid %s", uuid) + stateConf := &retry.StateChangeConf{ + Pending: []string{ + string(fabricv4.SERVICETOKENSTATE_INACTIVE), + }, + Target: []string{ + string(fabricv4.SERVICETOKENSTATE_DELETED), + }, + Refresh: func() (interface{}, string, error) { + client := meta.(*config.Config).NewFabricClientForSDK(d) + serviceToken, body, err := client.ServiceTokensApi.GetServiceTokenByUuid(ctx, uuid).Execute() + if err != nil { + if body.StatusCode >= 400 && body.StatusCode <= 499 { + // Already deleted resource + return serviceToken, string(fabricv4.SERVICETOKENSTATE_DELETED), nil + } + return "", "", equinix_errors.FormatFabricError(err) + } + return serviceToken, string(serviceToken.GetState()), nil + }, + Timeout: timeout, + Delay: 30 * time.Second, + MinTimeout: 30 * time.Second, + } + + _, err := stateConf.WaitForStateContext(ctx) + + return err +} diff --git a/internal/resources/fabric/service_token/resource_schema.go b/internal/resources/fabric/service_token/resource_schema.go new file mode 100644 index 000000000..0b9b5f39e --- /dev/null +++ b/internal/resources/fabric/service_token/resource_schema.go @@ -0,0 +1,424 @@ +package service_token + +import ( + equinix_fabric_schema "github.com/equinix/terraform-provider-equinix/internal/fabric/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceSchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"VC_TOKEN", "EPL_TOKEN"}, false), + Description: "Service Token Type; VC_TOKEN,EPL_TOKEN", + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + Description: "Equinix-assigned service token identifier", + }, + "href": { + Type: schema.TypeString, + Computed: true, + Description: "An absolute URL that is the subject of the link's context.", + }, + "issuer_side": { + Type: schema.TypeString, + Computed: true, + Description: "Information about token side; ASIDE, ZSIDE", + }, + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Name of the Service Token", + }, + "description": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Optional Description to the Service Token you will be creating", + }, + "expiration_date_time": { + Type: schema.TypeString, + Required: true, + Description: "Expiration date and time of the service token; 2020-11-06T07:00:00Z", + }, + "service_token_connection": { + Type: schema.TypeSet, + Required: true, + Description: "Service Token Connection Type Information", + Elem: serviceTokenConnectionSch(), + }, + "state": { + Type: schema.TypeString, + Computed: true, + Description: "Service token state; ACTIVE, INACTIVE, EXPIRED, DELETED", + }, + "notifications": { + Type: schema.TypeSet, + Required: true, + Description: "Preferences for notifications on Service Token configuration or status changes", + Elem: &schema.Resource{ + Schema: equinix_fabric_schema.NotificationSch(), + }, + }, + "account": { + Type: schema.TypeSet, + Computed: true, + Description: "Customer account information that is associated with this service token", + Elem: &schema.Resource{ + Schema: equinix_fabric_schema.AccountSch(), + }, + }, + "change_log": { + Type: schema.TypeSet, + Computed: true, + Description: "Captures connection lifecycle change information", + Elem: &schema.Resource{ + Schema: equinix_fabric_schema.ChangeLogSch(), + }, + }, + "project": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Description: "Project information", + MaxItems: 1, + Elem: &schema.Resource{ + Schema: equinix_fabric_schema.ProjectSch(), + }, + }, + } +} + +func serviceTokenConnectionSch() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Required: true, + Description: "Type of Connection supported by Service Token you will create; EVPL_VC, EVPLAN_VC, EPLAN_VC, IPWAN_VC", + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + Description: "Equinix-assigned connection identifier", + }, + "allow_remote_connection": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + Description: "Authorization to connect remotely", + }, + "allow_custom_bandwidth": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + Description: "Allow custom bandwidth value", + }, + "bandwidth_limit": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + ValidateFunc: validation.IntBetween(1, 100000), + Description: "Connection bandwidth limit in Mbps", + }, + "supported_bandwidths": { + Type: schema.TypeList, + Required: true, + Description: "List of permitted bandwidths", + Elem: &schema.Schema{ + Type: schema.TypeInt, + }, + }, + "a_side": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Description: "A-Side Connection link protocol,virtual device or network configuration", + Elem: serviceTokenAccessPointSch(), + }, + "z_side": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Description: "Z-Side Connection link protocol,virtual device or network configuration", + Elem: serviceTokenAccessPointSch(), + }, + }, + } +} + +func serviceTokenAccessPointSch() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "access_point_selectors": { + Type: schema.TypeSet, + Required: true, + Description: "List of criteria for selecting network access points with optimal efficiency, security, compatibility, and availability", + Elem: accessPointSelectorsSch(), + }, + }, + } +} + +func accessPointSelectorsSch() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Type of Access point; COLO, VD, NETWORK", + }, + "port": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Description: "Port Configuration", + MaxItems: 1, + Elem: portSch(), + }, + "link_protocol": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Description: "Link protocol Configuration", + MaxItems: 1, + Elem: linkProtocolSch(), + }, + "virtual_device": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Description: "Virtual Device Configuration", + MaxItems: 1, + Elem: virtualDeviceSch(), + }, + "interface": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Description: "Virtual Device Interface Configuration", + MaxItems: 1, + Elem: interfaceSch(), + }, + "network": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Description: "Network Configuration", + MaxItems: 1, + Elem: networkSch(), + }, + }, + } +} + +func portSch() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "href": { + Type: schema.TypeString, + Computed: true, + Description: "Unique Resource Identifier", + }, + "uuid": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Equinix-assigned Port identifier", + }, + "type": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: "Type of Port", + }, + "cvp_id": { + Type: schema.TypeInt, + Computed: true, + Optional: true, + Description: "Customer virtual port Id", + }, + "bandwidth": { + Type: schema.TypeInt, + Computed: true, + Optional: true, + Description: "Port Bandwidth", + }, + "port_name": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: "Port Name", + }, + "encapsulation_protocol_type": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: "Port Encapsulation", + }, + "account_name": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: "Account Name", + }, + "priority": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: "Port Priority", + }, + "location": { + Type: schema.TypeSet, + Computed: true, + Optional: true, + Description: "Port Location", + Elem: &schema.Resource{ + Schema: equinix_fabric_schema.LocationSch(), + }, + }, + }, + } +} + +func virtualDeviceSch() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "href": { + Type: schema.TypeString, + Computed: true, + Description: "Unique Resource Identifier", + }, + "uuid": { + Type: schema.TypeString, + Required: true, + Description: "Equinix-assigned Virtual Device identifier", + }, + "type": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Virtual Device type", + }, + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Customer-assigned Virtual Device Name", + }, + "cluster": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Virtual Device Cluster Information", + }, + }, + } +} + +func linkProtocolSch() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Type of the link protocol - UNTAGGED, DOT1Q, QINQ, EVPN_VXLAN", + ValidateFunc: validation.StringInSlice([]string{"UNTAGGED", "DOT1Q", "QINQ", "EVPN_VXLAN"}, true), + }, + "vlan_tag": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "Vlan Tag information, vlanTag value specified for DOT1Q connections", + }, + "vlan_s_tag": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "Vlan Provider Tag information, vlanSTag value specified for QINQ connections", + }, + "vlan_c_tag": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "Vlan Customer Tag information, vlanCTag value specified for QINQ connections", + }, + }, + } +} + +func interfaceSch() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "uuid": { + Type: schema.TypeString, + Computed: true, + Description: "Equinix-assigned interface identifier", + }, + "id": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "id", + }, + "type": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Interface type", + }, + }, + } +} + +func networkSch() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "uuid": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Equinix-assigned Network identifier", + }, + "href": { + Type: schema.TypeString, + Computed: true, + Description: "Unique Resource Identifier", + }, + "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{ + Schema: equinix_fabric_schema.LocationSch(), + }, + }, + }, + } +} diff --git a/internal/resources/fabric/service_token/resource_test.go b/internal/resources/fabric/service_token/resource_test.go new file mode 100644 index 000000000..c571b4230 --- /dev/null +++ b/internal/resources/fabric/service_token/resource_test.go @@ -0,0 +1,94 @@ +package service_token_test + +import ( + "context" + "fmt" + "github.com/equinix/terraform-provider-equinix/internal/acceptance" + "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/service_token" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "testing" + "time" +) + +func TestAccFabricServiceToken_PNFV(t *testing.T) { + serviceTokenName, serviceTokenUpdatedName := "Service_token_PNFV", "UP_Service_Token_PNFV" + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.TestAccPreCheck(t) }, + Providers: acceptance.TestAccProviders, + CheckDestroy: CheckServiceTokenDelete, + Steps: []resource.TestStep{ + { + Config: testAccFabricServiceTokenConfig(serviceTokenName), + 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", "zside vd token"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "expiration_date_time", "2024-11-18T06:43:49.980Z"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.supported_bandwidths.#", "3"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid", "fcf0fcec-65f6-4544-8810-ae4756fab8c4"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id", "5"), + ), + ExpectNonEmptyPlan: true, + }, + { + Config: testAccFabricServiceTokenConfig(serviceTokenUpdatedName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("equinix_fabric_service_token.test", "uuid"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "name", serviceTokenUpdatedName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccFabricServiceTokenConfig(serviceTokenName string) string { + return fmt.Sprintf( + `resource "equinix_fabric_service_token" "test"{ + type = "VC_TOKEN" + name = "%s" + description = "zside vd token" + expiration_date_time = "2024-11-18T06:43:49.980Z" + service_token_connection { + type = "EVPL_VC" + supported_bandwidths = [50, 200, 10000] + z_side { + access_point_selectors{ + type = "VD" + virtual_device{ + type = "EDGE" + uuid = "fcf0fcec-65f6-4544-8810-ae4756fab8c4" + } + interface{ + type = "NETWORK" + id = 5 + } + } + } + } + notifications { + type = "ALL" + emails = ["example@equinix.com", "test1@equinix.com"] + } + + } + `, serviceTokenName) +} + +func CheckServiceTokenDelete(s *terraform.State) error { + ctx := context.Background() + for _, rs := range s.RootModule().Resources { + if rs.Type != "equinix_fabric_service_token" { + continue + } + + err := service_token.WaitForDeletion(rs.Primary.ID, acceptance.TestAccProvider.Meta(), &schema.ResourceData{}, ctx, 10*time.Minute) + if err != nil { + return fmt.Errorf("API call failed while waiting for resource deletion") + } + } + return nil +} From f59c2f984cba7a5942b46b03b249e207c1028527 Mon Sep 17 00:00:00 2001 From: kpdhulipala <84343462+kpdhulipala@users.noreply.github.com> Date: Wed, 23 Oct 2024 07:54:36 -0700 Subject: [PATCH 02/55] feat: Add optional attribute tier in Create Virtual Device request for C8000V and C8000V SDWAN (#771) feat: Add optional attribute tier in Create Virtual Device request for C8000V and C8000V SDWAN --- docs/data-sources/network_device.md | 1 + docs/resources/network_device.md | 60 +++++++++++++++++++ equinix/data_source_network_device.go | 8 +++ equinix/resource_network_device.go | 20 +++++++ .../c8000v_byol_with_bandwidth_throughput.tf | 27 +++++++++ .../c8000v_byol_with_bandwidth_tier.tf | 26 ++++++++ go.mod | 2 +- go.sum | 2 + templates/data-sources/network_device.md.tmpl | 1 + templates/resources/network_device.md.tmpl | 5 ++ 10 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 examples/resources/equinix_network_device/c8000v_byol_with_bandwidth_throughput.tf create mode 100644 examples/resources/equinix_network_device/c8000v_byol_with_bandwidth_tier.tf diff --git a/docs/data-sources/network_device.md b/docs/data-sources/network_device.md index 0bb9e3a87..21e5028d3 100644 --- a/docs/data-sources/network_device.md +++ b/docs/data-sources/network_device.md @@ -76,3 +76,4 @@ NOTE: Exactly one of either `uuid` or `name` must be specified. * `connectivity` - Device accessibility (INTERNET-ACCESS or PRIVATE or INTERNET-ACCESS-WITH-PRVT-MGMT) * `diverse_device_id` - diverse device uuid * `diverse_device_name` - Name of the device with diverse device UUID +* `tier` - Throughput Tier (applicable for C8000V, C8000V-SDWAN devices) diff --git a/docs/resources/network_device.md b/docs/resources/network_device.md index 3e9d2ad45..ebaea2d34 100644 --- a/docs/resources/network_device.md +++ b/docs/resources/network_device.md @@ -397,6 +397,65 @@ resource "equinix_network_device" "panw-cluster" { } ``` +```terraform +# Create C8000V BYOL device with bandwidth tier information + +data "equinix_network_account" "sv" { + metro_code = "SV" +} + +resource "equinix_network_device" "c8000v-byol-tier" { + name = "tf-c8000v-byol" + metro_code = data.equinix_network_account.sv.metro_code + type_code = "C8000V" + self_managed = true + byol = true + package_code = "VM100" + notifications = ["john@equinix.com", "marry@equinix.com", "fred@equinix.com"] + term_length = 12 + account_number = data.equinix_network_account.sv.number + version = "17.11.01a" + interface_count = 10 + core_count = 2 + tier = 1 + ssh_key { + username = "test" + key_name = "test-key" + } + acl_template_id = "0bff6e05-f0e7-44cd-804a-25b92b835f8b" +} +``` + +```terraform +# Create C8000V BYOL device with numeric bandwidth throughput information + +data "equinix_network_account" "sv" { + metro_code = "SV" +} + +resource "equinix_network_device" "c8000v-byol-throughput" { + name = "tf-c8000v-byol" + metro_code = data.equinix_network_account.sv.metro_code + type_code = "C8000V" + self_managed = true + byol = true + package_code = "VM100" + notifications = ["john@equinix.com", "marry@equinix.com", "fred@equinix.com"] + term_length = 12 + account_number = data.equinix_network_account.sv.number + version = "17.11.01a" + interface_count = 10 + core_count = 2 + throughput = "100" + throughput_unit = "Mbps" + ssh_key { + username = "test" + key_name = "test-key" + } + acl_template_id = "0bff6e05-f0e7-44cd-804a-25b92b835f8b" +} +``` + ```terraform # Create self configured single Aviatrix Transit Edge device with cloud init file @@ -444,6 +503,7 @@ The following arguments are supported: * `package_code` - (Required) Device software package code. * `version` - (Required) Device software software version. * `core_count` - (Required) Number of CPU cores used by device. (**NOTE: Use this field to resize your device. When resizing your HA devices, primary device will be upgraded first. If the upgrade failed, device will be automatically rolled back to the previous state with original core number.**) +* `tier` - (Optional, conflicts with `throughput`,`throughput_unit` ) Select bandwidth tier for your own license, i.e., `0` or `1` or `2` or `3`. Tiers applicable only for C8000V Autonomous or C8000V SDWAN (controller) device types. If not provided, tier is defaulted to '2'. * `term_length` - (Required) Device term length. * `self_managed` - (Optional) Boolean value that determines device management mode, i.e., `self-managed` or `Equinix-managed` (default). * `byol` - (Optional) Boolean value that determines device licensing mode, i.e., `bring your own license` or `subscription` (default). diff --git a/equinix/data_source_network_device.go b/equinix/data_source_network_device.go index 3e8e220b9..22d455a4a 100644 --- a/equinix/data_source_network_device.go +++ b/equinix/data_source_network_device.go @@ -295,6 +295,11 @@ func createDataSourceNetworkDeviceSchema() map[string]*schema.Schema { Computed: true, Description: neDeviceDescriptions["DiverseFromDeviceUUID"], }, + neDeviceSchemaNames["Tier"]: { + Type: schema.TypeInt, + Computed: true, + Description: neDeviceDescriptions["Tier"], + }, neDeviceSchemaNames["DiverseFromDeviceName"]: { Type: schema.TypeString, Computed: true, @@ -853,6 +858,9 @@ func updateDataSourceNetworkDeviceResource(primary *ne.Device, secondary *ne.Dev if err := d.Set(neDeviceSchemaNames["DiverseFromDeviceUUID"], primary.DiverseFromDeviceUUID); err != nil { return fmt.Errorf("error reading DiverseFromDeviceUUID: %s", err) } + if err := d.Set(neDeviceSchemaNames["Tier"], primary.Tier); err != nil { + return fmt.Errorf("error reading Tier: %s", err) + } if err := d.Set(neDeviceSchemaNames["DiverseFromDeviceName"], primary.DiverseFromDeviceName); err != nil { return fmt.Errorf("error reading DiverseFromDeviceName: %s", err) } diff --git a/equinix/resource_network_device.go b/equinix/resource_network_device.go index 813202dfb..87ed33fe7 100644 --- a/equinix/resource_network_device.go +++ b/equinix/resource_network_device.go @@ -70,6 +70,7 @@ var neDeviceSchemaNames = map[string]string{ "Connectivity": "connectivity", "DiverseFromDeviceUUID": "diverse_device_id", "DiverseFromDeviceName": "diverse_device_name", + "Tier": "tier", } var neDeviceDescriptions = map[string]string{ @@ -119,6 +120,7 @@ var neDeviceDescriptions = map[string]string{ "ProjectID": "The unique identifier of Project Resource to which device is scoped to", "DiverseFromDeviceUUID": "Unique ID of an existing device", "DiverseFromDeviceName": "Diverse Device Name of an existing device", + "Tier": "Bandwidth Tiers", } var neDeviceInterfaceSchemaNames = map[string]string{ @@ -439,6 +441,15 @@ func createNetworkDeviceSchema() map[string]*schema.Schema { ConflictsWith: []string{neDeviceSchemaNames["Secondary"]}, Description: neDeviceDescriptions["DiverseFromDeviceUUID"], }, + neDeviceSchemaNames["Tier"]: { + Type: schema.TypeInt, + ForceNew: true, + Computed: true, + Optional: true, + ValidateFunc: validation.IntInSlice([]int{0, 1, 2, 3}), + ConflictsWith: []string{neDeviceSchemaNames["Throughput"], neDeviceSchemaNames["ThroughputUnit"]}, + Description: neDeviceDescriptions["Tier"], + }, neDeviceSchemaNames["DiverseFromDeviceName"]: { Type: schema.TypeString, Computed: true, @@ -1137,6 +1148,9 @@ func createNetworkDevices(d *schema.ResourceData) (*ne.Device, *ne.Device) { if v, ok := d.GetOk(neDeviceSchemaNames["ProjectID"]); ok { primary.ProjectID = ne.String(v.(string)) } + if v, ok := d.GetOk(neDeviceSchemaNames["Tier"]); ok { + primary.Tier = ne.Int(v.(int)) + } if v, ok := d.GetOk(neDeviceSchemaNames["DiverseFromDeviceUUID"]); ok { primary.DiverseFromDeviceUUID = ne.String(v.(string)) } @@ -1231,6 +1245,9 @@ func updateNetworkDeviceResource(primary *ne.Device, secondary *ne.Device, d *sc if err := d.Set(neDeviceSchemaNames["Name"], primary.Name); err != nil { return fmt.Errorf("error reading Name: %s", err) } + if err := d.Set(neDeviceSchemaNames["Tier"], primary.Tier); err != nil { + return fmt.Errorf("error reading Tier: %s", err) + } if err := d.Set(neDeviceSchemaNames["ProjectID"], primary.ProjectID); err != nil { return fmt.Errorf("error reading ProjectID: %s", err) } @@ -1406,6 +1423,9 @@ func expandNetworkDeviceSecondary(devices []interface{}) *ne.Device { if v, ok := device[neDeviceSchemaNames["Name"]]; ok && !isEmpty(v) { transformed.Name = ne.String(v.(string)) } + if v, ok := device[neDeviceSchemaNames["Tier"]]; ok && !isEmpty(v) { + transformed.Tier = ne.Int(v.(int)) + } if v, ok := device[neDeviceSchemaNames["ProjectID"]]; ok && !isEmpty(v) { transformed.ProjectID = ne.String(v.(string)) } diff --git a/examples/resources/equinix_network_device/c8000v_byol_with_bandwidth_throughput.tf b/examples/resources/equinix_network_device/c8000v_byol_with_bandwidth_throughput.tf new file mode 100644 index 000000000..8ccce6b0d --- /dev/null +++ b/examples/resources/equinix_network_device/c8000v_byol_with_bandwidth_throughput.tf @@ -0,0 +1,27 @@ +# Create C8000V BYOL device with numeric bandwidth throughput information + +data "equinix_network_account" "sv" { + metro_code = "SV" +} + +resource "equinix_network_device" "c8000v-byol-throughput" { + name = "tf-c8000v-byol" + metro_code = data.equinix_network_account.sv.metro_code + type_code = "C8000V" + self_managed = true + byol = true + package_code = "VM100" + notifications = ["john@equinix.com", "marry@equinix.com", "fred@equinix.com"] + term_length = 12 + account_number = data.equinix_network_account.sv.number + version = "17.11.01a" + interface_count = 10 + core_count = 2 + throughput = "100" + throughput_unit = "Mbps" + ssh_key { + username = "test" + key_name = "test-key" + } + acl_template_id = "0bff6e05-f0e7-44cd-804a-25b92b835f8b" +} \ No newline at end of file diff --git a/examples/resources/equinix_network_device/c8000v_byol_with_bandwidth_tier.tf b/examples/resources/equinix_network_device/c8000v_byol_with_bandwidth_tier.tf new file mode 100644 index 000000000..eddcf1039 --- /dev/null +++ b/examples/resources/equinix_network_device/c8000v_byol_with_bandwidth_tier.tf @@ -0,0 +1,26 @@ +# Create C8000V BYOL device with bandwidth tier information + +data "equinix_network_account" "sv" { + metro_code = "SV" +} + +resource "equinix_network_device" "c8000v-byol-tier" { + name = "tf-c8000v-byol" + metro_code = data.equinix_network_account.sv.metro_code + type_code = "C8000V" + self_managed = true + byol = true + package_code = "VM100" + notifications = ["john@equinix.com", "marry@equinix.com", "fred@equinix.com"] + term_length = 12 + account_number = data.equinix_network_account.sv.number + version = "17.11.01a" + interface_count = 10 + core_count = 2 + tier = 1 + ssh_key { + username = "test" + key_name = "test-key" + } + acl_template_id = "0bff6e05-f0e7-44cd-804a-25b92b835f8b" +} \ No newline at end of file diff --git a/go.mod b/go.mod index 45f5af140..99eb76abc 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.22 require ( github.com/equinix/equinix-sdk-go v0.46.0 - github.com/equinix/ne-go v1.17.0 + github.com/equinix/ne-go v1.18.0 github.com/equinix/oauth2-go v1.0.0 github.com/equinix/rest-go v1.3.0 github.com/google/uuid v1.6.0 diff --git a/go.sum b/go.sum index 384ca31c6..9ebfb0925 100644 --- a/go.sum +++ b/go.sum @@ -43,6 +43,8 @@ github.com/equinix/equinix-sdk-go v0.46.0 h1:ldQo4GtXNr+0XsThQJf/pUdx5wcLFe9QpLF github.com/equinix/equinix-sdk-go v0.46.0/go.mod h1:hEb3XLaedz7xhl/dpPIS6eOIiXNPeqNiVoyDrT6paIg= github.com/equinix/ne-go v1.17.0 h1:+wZq0GNognpiTHTsBXtATOCphTFvnowF046NzQXj0n0= github.com/equinix/ne-go v1.17.0/go.mod h1:eHkkxM4nbTB7DZ9X9zGnwfYnxIJWIsU3aHA+FAoZ1EI= +github.com/equinix/ne-go v1.18.0 h1:5az4ai39y1XLNOq3+qQVT9wFG7BmaQfj941MNqqouhk= +github.com/equinix/ne-go v1.18.0/go.mod h1:eHkkxM4nbTB7DZ9X9zGnwfYnxIJWIsU3aHA+FAoZ1EI= github.com/equinix/oauth2-go v1.0.0 h1:fHtAPGq82PdgtK5vEThs8Vwz6f7D/8SX4tE3NJu+KcU= github.com/equinix/oauth2-go v1.0.0/go.mod h1:4pulXvUNMktJlewLPnUeJyMW52iCoF1aM+A/Z5xY1ws= github.com/equinix/rest-go v1.3.0 h1:m38scYTOfV6N+gcrwchgVDutDffYd+QoYCMm9Jn6jyk= diff --git a/templates/data-sources/network_device.md.tmpl b/templates/data-sources/network_device.md.tmpl index 47a4f8330..3d5e9f753 100644 --- a/templates/data-sources/network_device.md.tmpl +++ b/templates/data-sources/network_device.md.tmpl @@ -70,3 +70,4 @@ NOTE: Exactly one of either `uuid` or `name` must be specified. * `connectivity` - Device accessibility (INTERNET-ACCESS or PRIVATE or INTERNET-ACCESS-WITH-PRVT-MGMT) * `diverse_device_id` - diverse device uuid * `diverse_device_name` - Name of the device with diverse device UUID +* `tier` - Throughput Tier (applicable for C8000V, C8000V-SDWAN devices) diff --git a/templates/resources/network_device.md.tmpl b/templates/resources/network_device.md.tmpl index bce28fa68..cd8bc862e 100644 --- a/templates/resources/network_device.md.tmpl +++ b/templates/resources/network_device.md.tmpl @@ -40,6 +40,10 @@ In addition to management modes, there are two software license modes available: {{tffile "examples/resources/equinix_network_device/example_9.tf"}} +{{tffile "examples/resources/equinix_network_device/c8000v_byol_with_bandwidth_tier.tf"}} + +{{tffile "examples/resources/equinix_network_device/c8000v_byol_with_bandwidth_throughput.tf"}} + {{tffile "examples/resources/equinix_network_device/Aviatrix_Transit_Edge.tf"}} ## Argument Reference @@ -53,6 +57,7 @@ The following arguments are supported: * `package_code` - (Required) Device software package code. * `version` - (Required) Device software software version. * `core_count` - (Required) Number of CPU cores used by device. (**NOTE: Use this field to resize your device. When resizing your HA devices, primary device will be upgraded first. If the upgrade failed, device will be automatically rolled back to the previous state with original core number.**) +* `tier` - (Optional, conflicts with `throughput`,`throughput_unit` ) Select bandwidth tier for your own license, i.e., `0` or `1` or `2` or `3`. Tiers applicable only for C8000V Autonomous or C8000V SDWAN (controller) device types. If not provided, tier is defaulted to '2'. * `term_length` - (Required) Device term length. * `self_managed` - (Optional) Boolean value that determines device management mode, i.e., `self-managed` or `Equinix-managed` (default). * `byol` - (Optional) Boolean value that determines device licensing mode, i.e., `bring your own license` or `subscription` (default). From a89491e1d7633df28eeabdf55820159a77b3a165 Mon Sep 17 00:00:00 2001 From: Charles Treatman Date: Wed, 23 Oct 2024 11:52:20 -0500 Subject: [PATCH 03/55] refactor: finish removing packngo from metal_port code (#789) Some packngo usage was missed in #709. This completely removes packngo from metal_port, as well as calls to `FriendlyError` because that function has no impact for non-packngo errors. --- internal/resources/metal/port/helpers.go | 17 ++++++++++++++--- internal/resources/metal/port/resource_test.go | 12 ++++-------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/internal/resources/metal/port/helpers.go b/internal/resources/metal/port/helpers.go index ded05ded2..6fb07bee6 100644 --- a/internal/resources/metal/port/helpers.go +++ b/internal/resources/metal/port/helpers.go @@ -15,7 +15,6 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/packethost/packngo" "github.com/pkg/errors" ) @@ -196,8 +195,8 @@ func createAndWaitForBatch(ctx context.Context, start time.Time, cpr *ClientPort stateChangeConf := &retry.StateChangeConf{ Delay: 5 * time.Second, - Pending: []string{string(packngo.VLANAssignmentBatchQueued), string(packngo.VLANAssignmentBatchInProgress)}, - Target: []string{string(packngo.VLANAssignmentBatchCompleted)}, + Pending: []string{string(metalv1.PORTVLANASSIGNMENTBATCHSTATE_QUEUED), string(metalv1.PORTVLANASSIGNMENTBATCHSTATE_IN_PROGRESS)}, + Target: []string{string(metalv1.PORTVLANASSIGNMENTBATCHSTATE_COMPLETED)}, MinTimeout: 5 * time.Second, Timeout: ctxTimeout - time.Since(start) - 30*time.Second, Refresh: func() (result interface{}, state string, err error) { @@ -243,6 +242,9 @@ func updateNativeVlan(ctx context.Context, cpr *ClientPortResource) error { } func processBondAction(ctx context.Context, cpr *ClientPortResource, actionIsBond bool) error { + // There's no good alternative to GetOkExists until metal_port + // is converted to terraform-plugin-framework + // nolint:staticcheck wantsBondedRaw, wantsBondedOk := cpr.Resource.GetOkExists("bonded") wantsBonded := wantsBondedRaw.(bool) // only act if the necessary action is the one specified in doBond @@ -285,6 +287,9 @@ func makeDisbond(ctx context.Context, cpr *ClientPortResource) error { } func convertToL2(ctx context.Context, cpr *ClientPortResource) error { + // There's no good alternative to GetOkExists until metal_port + // is converted to terraform-plugin-framework + // nolint:staticcheck l2, l2Ok := cpr.Resource.GetOkExists("layer2") isLayer2 := slices.Contains(l2Types, cpr.Port.GetNetworkType()) @@ -299,6 +304,9 @@ func convertToL2(ctx context.Context, cpr *ClientPortResource) error { } func convertToL3(ctx context.Context, cpr *ClientPortResource) error { + // There's no good alternative to GetOkExists until metal_port + // is converted to terraform-plugin-framework + // nolint:staticcheck l2, l2Ok := cpr.Resource.GetOkExists("layer2") isLayer2 := slices.Contains(l2Types, cpr.Port.GetNetworkType()) @@ -324,6 +332,9 @@ func portSanityChecks(_ context.Context, cpr *ClientPortResource) error { isBondPort := cpr.Port.GetType() == "NetworkBondPort" // Constraint: Only bond ports have layer2 mode + // There's no good alternative to GetOkExists until metal_port + // is converted to terraform-plugin-framework + // nolint:staticcheck l2Raw, l2Ok := cpr.Resource.GetOkExists("layer2") if !isBondPort && l2Ok { return fmt.Errorf("layer2 flag can be set only for bond ports") diff --git a/internal/resources/metal/port/resource_test.go b/internal/resources/metal/port/resource_test.go index 1766a8c74..ece2d33f4 100644 --- a/internal/resources/metal/port/resource_test.go +++ b/internal/resources/metal/port/resource_test.go @@ -12,11 +12,9 @@ import ( "github.com/equinix/terraform-provider-equinix/internal/config" "github.com/equinix/terraform-provider-equinix/internal/resources/metal/port" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" - "github.com/packethost/packngo" ) var ( @@ -400,10 +398,8 @@ func testAccWaitForPortActive(deviceName, portName string) resource.ImportStateI } meta := acceptance.TestAccProvider.Meta() - rd := new(schema.ResourceData) - meta.(*config.Config).AddModuleToMetalUserAgent(rd) - client := meta.(*config.Config).Metal - device, _, err := client.Devices.Get(rs.Primary.ID, &packngo.GetOptions{Includes: []string{"ports"}}) + client := meta.(*config.Config).NewMetalClientForTesting() + device, _, err := client.DevicesApi.FindDeviceById(context.Background(), rs.Primary.ID).Include([]string{"ports"}).Execute() if err != nil { return "", fmt.Errorf("error while fetching device with Id [%s], error: %w", rs.Primary.ID, err) } @@ -415,8 +411,8 @@ func testAccWaitForPortActive(deviceName, portName string) resource.ImportStateI } for _, port := range device.NetworkPorts { - if port.Name == portName { - return port.ID, nil + if port.GetName() == portName { + return port.GetId(), nil } } From f331f320829352b045115bb25d89435f6e013788 Mon Sep 17 00:00:00 2001 From: srushti-patl Date: Wed, 23 Oct 2024 13:23:59 -0700 Subject: [PATCH 04/55] fix: Updating Service Token Resource & Data source --- .../fabric/service_token/datasources_test.go | 93 +++++++++++++++++++ .../resources/fabric/service_token/models.go | 26 ++++-- .../fabric/service_token/resource_schema.go | 2 + .../fabric/service_token/resource_test.go | 25 +++-- 4 files changed, 129 insertions(+), 17 deletions(-) create mode 100644 internal/resources/fabric/service_token/datasources_test.go diff --git a/internal/resources/fabric/service_token/datasources_test.go b/internal/resources/fabric/service_token/datasources_test.go new file mode 100644 index 000000000..943c67852 --- /dev/null +++ b/internal/resources/fabric/service_token/datasources_test.go @@ -0,0 +1,93 @@ +package service_token_test + +import ( + "fmt" + "github.com/equinix/terraform-provider-equinix/internal/acceptance" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "testing" +) + +func TestAccFabricServiceTokenDataSource_PNFV(t *testing.T) { + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.TestAccPreCheck(t) }, + Providers: acceptance.TestAccProviders, + CheckDestroy: CheckServiceTokenDelete, + Steps: []resource.TestStep{ + { + Config: testAccFabricServiceTokenConfigDataSourceConfig(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.equinix_fabric_service_token.service-token", "uuid"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token", "type", "VC_TOKEN"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token", "expiration_date_time", "2024-11-18T06:43:49.980Z"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token", "service_token_connection.0.supported_bandwidths.#", "3"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.type", "EDGE"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid", "fcf0fcec-65f6-4544-8810-ae4756fab8c4"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.type", "NETWORK"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id", "5"), + resource.TestCheckResourceAttrSet("data.equinix_fabric_service_tokens.service-tokens", "data.0.uuid"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "data.0.type", "VC_TOKEN"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "data.0.expiration_date_time", "2024-11-18T06:43:49.980Z"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "data.0.service_token_connection.0.supported_bandwidths.#", "3"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.type", "EDGE"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "data.0.service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid", "fcf0fcec-65f6-4544-8810-ae4756fab8c4"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.type", "NETWORK"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "data.0.service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id", "5"), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccFabricServiceTokenConfigDataSourceConfig() string { + return fmt.Sprintf( + `resource "equinix_fabric_service_token" "test"{ + type = "VC_TOKEN" + expiration_date_time = "2024-11-18T06:43:49.980Z" + service_token_connection { + type = "EVPL_VC" + supported_bandwidths = [50, 200, 10000] + z_side { + access_point_selectors{ + type = "VD" + virtual_device{ + type = "EDGE" + uuid = "fcf0fcec-65f6-4544-8810-ae4756fab8c4" + } + interface{ + type = "NETWORK" + id = 5 + } + } + } + } + notifications { + type = "ALL" + emails = ["example@equinix.com", "test1@equinix.com"] + } + } + + data "equinix_fabric_service_token" "service-token" { + uuid = equinix_fabric_service_token.test.id + } + + data "equinix_fabric_service_tokens" "service-tokens"{ + filter { + property = "/uuid" + operator = "=" + values = [equinix_fabric_service_token.test.id] + } + filter { + property = "/state" + operator = "=" + values = ["INACTIVE"] + } + pagination { + offset = 0 + limit = 5 + total = 25 + } + } + + `) +} diff --git a/internal/resources/fabric/service_token/models.go b/internal/resources/fabric/service_token/models.go index 6fff6d489..6d38f7e42 100644 --- a/internal/resources/fabric/service_token/models.go +++ b/internal/resources/fabric/service_token/models.go @@ -282,10 +282,6 @@ func serviceTokenResponseMap(token *fabricv4.ServiceToken) map[string]interface{ 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) @@ -562,6 +558,9 @@ func connectionGoToTerraform(connection *fabricv4.ServiceTokenConnection) *schem mappedConnection["supported_bandwidths"] = interfaceBandwidths } + if connection.BandwidthLimit != nil { + mappedConnection["bandwidth_limit"] = int(connection.GetBandwidthLimit()) + } if connection.ASide != nil { accessPoint := connection.GetASide() mappedConnection["a_side"] = accessPointGoToTerraform(&accessPoint) @@ -663,14 +662,21 @@ func virtualDeviceGoToTerraform(virtualDevice *fabricv4.SimplifiedVirtualDevice) return nil } mappedVirtualDevice := make(map[string]interface{}) - mappedVirtualDevice["name"] = virtualDevice.GetName() - mappedVirtualDevice["href"] = virtualDevice.GetHref() - mappedVirtualDevice["type"] = string(virtualDevice.GetType()) - mappedVirtualDevice["uuid"] = virtualDevice.GetUuid() - if virtualDevice.Cluster != nil { + if name := virtualDevice.GetName(); name != "" { + mappedVirtualDevice["name"] = name + } + if href := virtualDevice.GetHref(); href != "" { + mappedVirtualDevice["href"] = href + } + if virtualDevice.GetType() != "" { + mappedVirtualDevice["type"] = string(virtualDevice.GetType()) + } + if uuid := virtualDevice.GetUuid(); uuid != "" { + mappedVirtualDevice["uuid"] = uuid + } + if virtualDevice.Cluster != nil && virtualDevice.GetCluster() != "" { mappedVirtualDevice["cluster"] = virtualDevice.GetCluster() } - virtualDeviceSet := schema.NewSet( schema.HashResource(virtualDeviceSch()), []interface{}{mappedVirtualDevice}, diff --git a/internal/resources/fabric/service_token/resource_schema.go b/internal/resources/fabric/service_token/resource_schema.go index 0b9b5f39e..793ced695 100644 --- a/internal/resources/fabric/service_token/resource_schema.go +++ b/internal/resources/fabric/service_token/resource_schema.go @@ -111,12 +111,14 @@ func serviceTokenConnectionSch() *schema.Resource { Type: schema.TypeBool, Optional: true, Computed: true, + Default: true, Description: "Authorization to connect remotely", }, "allow_custom_bandwidth": { Type: schema.TypeBool, Optional: true, Computed: true, + Default: true, Description: "Allow custom bandwidth value", }, "bandwidth_limit": { diff --git a/internal/resources/fabric/service_token/resource_test.go b/internal/resources/fabric/service_token/resource_test.go index c571b4230..d3d9e6183 100644 --- a/internal/resources/fabric/service_token/resource_test.go +++ b/internal/resources/fabric/service_token/resource_test.go @@ -14,30 +14,41 @@ import ( func TestAccFabricServiceToken_PNFV(t *testing.T) { serviceTokenName, serviceTokenUpdatedName := "Service_token_PNFV", "UP_Service_Token_PNFV" + serviceTokenDescription, serviceTokenUpdatedDescription := "zside vd token", "Updated zside vd token" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acceptance.TestAccPreCheck(t) }, Providers: acceptance.TestAccProviders, CheckDestroy: CheckServiceTokenDelete, Steps: []resource.TestStep{ { - Config: testAccFabricServiceTokenConfig(serviceTokenName), + Config: testAccFabricServiceTokenConfig(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", "zside vd token"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "description", serviceTokenDescription), resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "expiration_date_time", "2024-11-18T06:43:49.980Z"), resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.supported_bandwidths.#", "3"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.type", "EDGE"), resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid", "fcf0fcec-65f6-4544-8810-ae4756fab8c4"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.type", "NETWORK"), resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id", "5"), ), ExpectNonEmptyPlan: true, }, { - Config: testAccFabricServiceTokenConfig(serviceTokenUpdatedName), + Config: testAccFabricServiceTokenConfig(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", "2024-11-18T06:43:49.980Z"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.supported_bandwidths.#", "3"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.type", "EDGE"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid", "fcf0fcec-65f6-4544-8810-ae4756fab8c4"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.type", "NETWORK"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id", "5"), ), ExpectNonEmptyPlan: true, }, @@ -45,13 +56,13 @@ func TestAccFabricServiceToken_PNFV(t *testing.T) { }) } -func testAccFabricServiceTokenConfig(serviceTokenName string) string { +func testAccFabricServiceTokenConfig(serviceTokenName string, serviceTokenDescription string) string { return fmt.Sprintf( `resource "equinix_fabric_service_token" "test"{ type = "VC_TOKEN" name = "%s" - description = "zside vd token" - expiration_date_time = "2024-11-18T06:43:49.980Z" + description = "%s" + expiration_date_time = "2024-11-18T06:43:49.98Z" service_token_connection { type = "EVPL_VC" supported_bandwidths = [50, 200, 10000] @@ -75,7 +86,7 @@ func testAccFabricServiceTokenConfig(serviceTokenName string) string { } } - `, serviceTokenName) + `, serviceTokenName, serviceTokenDescription) } func CheckServiceTokenDelete(s *terraform.State) error { From 93558b4cf9cec023499f43a15956e2aa1cbbe307 Mon Sep 17 00:00:00 2001 From: Charles Treatman Date: Thu, 24 Oct 2024 10:35:49 -0500 Subject: [PATCH 05/55] chore: fix NE codeowners to cover examples (#804) --- CODEOWNERS | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 7e2f7a1c0..ab4dcf5bb 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -3,11 +3,11 @@ * @equinix/governor-devrel-engineering /cmd/migration-tool @equinix/governor-metal-client-interfaces *metal* @equinix/governor-metal-client-interfaces -docs/guides/network_types.md @equinix/governor-metal-client-interfaces *fabric* @equinix/governor-digin-fabric *connection_e2e* @equinix/governor-digin-fabric *resource_network_* @equinix/governor-ne-network-edge-engineering *data_source_network_* @equinix/governor-ne-network-edge-engineering -**/edge-networking @equinix/governor-ne-network-edge-engineering -docs/**/network_* @equinix/governor-ne-network-edge-engineering -templates/**/network_* @equinix/governor-ne-network-edge-engineering +edge-networking @equinix/governor-ne-network-edge-engineering +network_* @equinix/governor-ne-network-edge-engineering +docs/guides/network_types.md @equinix/governor-metal-client-interfaces +equinix_network_* @equinix/governor-ne-network-edge-engineering From ee1f435be18a2b1147d9ff3fd815b725cbac63db Mon Sep 17 00:00:00 2001 From: Charles Treatman Date: Mon, 30 Sep 2024 14:26:23 -0500 Subject: [PATCH 06/55] force new VRF if metro or project ID changes --- docs/resources/metal_vrf.md | 24 ++++++++++++++---------- internal/resources/metal/vrf/resource.go | 6 ++++-- templates/resources/metal_vrf.md.tmpl | 15 +-------------- 3 files changed, 19 insertions(+), 26 deletions(-) diff --git a/docs/resources/metal_vrf.md b/docs/resources/metal_vrf.md index 92848e959..2927b1610 100644 --- a/docs/resources/metal_vrf.md +++ b/docs/resources/metal_vrf.md @@ -75,20 +75,24 @@ resource "equinix_metal_virtual_circuit" "example" { } ``` -## Argument Reference + +## Schema -The following arguments are supported: +### Required -* `name` - (Required) User-supplied name of the VRF, unique to the project -* `metro` - (Required) Metro ID or Code where the VRF will be deployed. -* `project_id` - (Required) Project ID where the VRF will be deployed. -* `description` - (Optional) Description of the VRF. -* `local_asn` - (Optional) The 4-byte ASN set on the VRF. -* `ip_ranges` - (Optional) All IPv4 and IPv6 Ranges that will be available to BGP Peers. IPv4 addresses must be /8 or smaller with a minimum size of /29. IPv6 must be /56 or smaller with a minimum size of /64. Ranges must not overlap other ranges within the VRF. +- `metro` (String) Metro ID or Code where the VRF will be deployed +- `name` (String) User-supplied name of the VRF, unique to the project +- `project_id` (String) Project ID where the VRF will be deployed -## Attributes Reference +### Optional -No additional attributes are exported. +- `description` (String) Description of the VRF +- `ip_ranges` (Set of String) All IPv4 and IPv6 Ranges that will be available to BGP Peers. IPv4 addresses must be /8 or smaller with a minimum size of /29. IPv6 must be /56 or smaller with a minimum size of /64. Ranges must not overlap other ranges within the VRF. +- `local_asn` (Number) The 4-byte ASN set on the VRF. + +### Read-Only + +- `id` (String) The ID of this resource. ## Import diff --git a/internal/resources/metal/vrf/resource.go b/internal/resources/metal/vrf/resource.go index 1f19bbe5b..812e7d425 100644 --- a/internal/resources/metal/vrf/resource.go +++ b/internal/resources/metal/vrf/resource.go @@ -39,7 +39,8 @@ func Resource() *schema.Resource { "metro": { Type: schema.TypeString, Required: true, - Description: "Metro Code", + Description: "Metro ID or Code where the VRF will be deployed", + ForceNew: true, }, "local_asn": { Type: schema.TypeInt, @@ -56,7 +57,8 @@ func Resource() *schema.Resource { "project_id": { Type: schema.TypeString, Required: true, - Description: "Project ID", + Description: "Project ID where the VRF will be deployed", + ForceNew: true, }, // TODO: created_by, created_at, updated_at, href }, diff --git a/templates/resources/metal_vrf.md.tmpl b/templates/resources/metal_vrf.md.tmpl index 511bb9abf..8dcf60a67 100644 --- a/templates/resources/metal_vrf.md.tmpl +++ b/templates/resources/metal_vrf.md.tmpl @@ -26,20 +26,7 @@ Attach a Virtual Circuit from a Dedicated Metal Connection to the Metal Gateway. {{tffile "examples/resources/equinix_metal_vrf/example_3.tf"}} -## Argument Reference - -The following arguments are supported: - -* `name` - (Required) User-supplied name of the VRF, unique to the project -* `metro` - (Required) Metro ID or Code where the VRF will be deployed. -* `project_id` - (Required) Project ID where the VRF will be deployed. -* `description` - (Optional) Description of the VRF. -* `local_asn` - (Optional) The 4-byte ASN set on the VRF. -* `ip_ranges` - (Optional) All IPv4 and IPv6 Ranges that will be available to BGP Peers. IPv4 addresses must be /8 or smaller with a minimum size of /29. IPv6 must be /56 or smaller with a minimum size of /64. Ranges must not overlap other ranges within the VRF. - -## Attributes Reference - -No additional attributes are exported. +{{ .SchemaMarkdown | trimspace }} ## Import From faf45b1237c371205cc47120e8e1920d046f0b19 Mon Sep 17 00:00:00 2001 From: thogarty <139183873+thogarty@users.noreply.github.com> Date: Tue, 8 Oct 2024 11:04:05 -0700 Subject: [PATCH 07/55] feat: Add BGP Metrics fields for fabric cloud router routing protocols (#794) * Added additional optional fields to routing protocols bgp_ipv4 and bgp_ipv6 objects * Included acceptance testing Acceptance Tests for Fabric will be failing intermittently because in UAT the Vlan Tags are not being released quickly (or possibly at all). I've included evidence of local passing tests in a screenshot below: image --- docs/data-sources/fabric_routing_protocol.md | 7 + docs/resources/fabric_routing_protocol.md | 27 +-- equinix/resource_fabric_routing_protocol.go | 128 ++++++++++++-- ...source_fabric_routing_protocol_acc_test.go | 22 ++- .../resources/fabric_routing_protocol.md.tmpl | 163 +----------------- 5 files changed, 150 insertions(+), 197 deletions(-) diff --git a/docs/data-sources/fabric_routing_protocol.md b/docs/data-sources/fabric_routing_protocol.md index a6a4eff2d..023db024f 100644 --- a/docs/data-sources/fabric_routing_protocol.md +++ b/docs/data-sources/fabric_routing_protocol.md @@ -79,6 +79,7 @@ output "customer_asn" { ### Read-Only +- `as_override_enabled` (Boolean) Enable AS number override - `bfd` (Set of Object) Bidirectional Forwarding Detection (see [below for nested schema](#nestedatt--bfd)) - `bgp_auth_key` (String) BGP authorization key - `bgp_ipv4` (Set of Object) Routing Protocol BGP IPv4 (see [below for nested schema](#nestedatt--bgp_ipv4)) @@ -114,6 +115,9 @@ Read-Only: - `customer_peer_ip` (String) - `enabled` (Boolean) - `equinix_peer_ip` (String) +- `inbound_med` (Number) +- `outbound_as_prepend_count` (String) +- `outbound_med` (Number) @@ -124,6 +128,9 @@ Read-Only: - `customer_peer_ip` (String) - `enabled` (Boolean) - `equinix_peer_ip` (String) +- `inbound_med` (Number) +- `outbound_as_prepend_count` (String) +- `outbound_med` (Number) diff --git a/docs/resources/fabric_routing_protocol.md b/docs/resources/fabric_routing_protocol.md index 74d054a6f..e56d2fb0f 100644 --- a/docs/resources/fabric_routing_protocol.md +++ b/docs/resources/fabric_routing_protocol.md @@ -86,7 +86,6 @@ resource "equinix_fabric_routing_protocol" "bgp" { ``` - ## Schema ### Required @@ -95,6 +94,7 @@ resource "equinix_fabric_routing_protocol" "bgp" { ### Optional +- `as_override_enabled` (Boolean) Enable AS number override - `bfd` (Block Set) Bidirectional Forwarding Detection (see [below for nested schema](#nestedblock--bfd)) - `bgp_auth_key` (String) BGP authorization key - `bgp_ipv4` (Block Set) Routing Protocol BGP IPv4 (see [below for nested schema](#nestedblock--bgp_ipv4)) @@ -119,7 +119,6 @@ resource "equinix_fabric_routing_protocol" "bgp" { - `state` (String) Routing Protocol overall state - ### Nested Schema for `bfd` Required: @@ -130,8 +129,8 @@ Optional: - `interval` (String) Interval range between the received BFD control packets - + ### Nested Schema for `bgp_ipv4` Required: @@ -141,13 +140,16 @@ Required: Optional: - `enabled` (Boolean) Admin status for the BGP session +- `inbound_med` (Number) Inbound Multi Exit Discriminator attribute +- `outbound_as_prepend_count` (String) AS path prepend count. One of: 0, 1, 3, 5 +- `outbound_med` (Number) Outbound Multi Exit Discriminator attribute Read-Only: - `equinix_peer_ip` (String) Equinix side peering ip - + ### Nested Schema for `bgp_ipv6` Required: @@ -157,29 +159,32 @@ Required: Optional: - `enabled` (Boolean) Admin status for the BGP session +- `inbound_med` (Number) Inbound Multi Exit Discriminator attribute +- `outbound_as_prepend_count` (String) AS path prepend count. One of: 0, 1, 3, 5 +- `outbound_med` (Number) Outbound Multi Exit Discriminator attribute Read-Only: - `equinix_peer_ip` (String) Equinix side peering ip - + ### Nested Schema for `direct_ipv4` Required: - `equinix_iface_ip` (String) Equinix side Interface IP address - + ### Nested Schema for `direct_ipv6` Optional: - `equinix_iface_ip` (String) Equinix side Interface IP address - + ### Nested Schema for `timeouts` Optional: @@ -189,8 +194,8 @@ Optional: - `read` (String) - `update` (String) - + ### Nested Schema for `change` Read-Only: @@ -199,8 +204,8 @@ Read-Only: - `type` (String) - `uuid` (String) - + ### Nested Schema for `change_log` Read-Only: @@ -218,8 +223,8 @@ Read-Only: - `updated_by_full_name` (String) - `updated_date_time` (String) - + ### Nested Schema for `operation` Read-Only: @@ -227,7 +232,6 @@ Read-Only: - `errors` (List of Object) (see [below for nested schema](#nestedobjatt--operation--errors)) - ### Nested Schema for `operation.errors` Read-Only: @@ -240,7 +244,6 @@ Read-Only: - `help` (String) - ### Nested Schema for `operation.errors.additional_info` Read-Only: diff --git a/equinix/resource_fabric_routing_protocol.go b/equinix/resource_fabric_routing_protocol.go index 1f286110c..08b0ea36c 100644 --- a/equinix/resource_fabric_routing_protocol.go +++ b/equinix/resource_fabric_routing_protocol.go @@ -61,6 +61,24 @@ func createBgpConnectionIpv4Sch() map[string]*schema.Schema { Default: true, Description: "Admin status for the BGP session", }, + "outbound_as_prepend_count": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "AS path prepend count. One of: 0, 1, 3, 5", + }, + "inbound_med": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "Inbound Multi Exit Discriminator attribute", + }, + "outbound_med": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "Outbound Multi Exit Discriminator attribute", + }, } } @@ -82,6 +100,24 @@ func createBgpConnectionIpv6Sch() map[string]*schema.Schema { Default: true, Description: "Admin status for the BGP session", }, + "outbound_as_prepend_count": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "AS path prepend count. One of: 0, 1, 3, 5", + }, + "inbound_med": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "Inbound Multi Exit Discriminator attribute", + }, + "outbound_med": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "Outbound Multi Exit Discriminator attribute", + }, } } @@ -245,6 +281,12 @@ func createFabricRoutingProtocolResourceSchema() map[string]*schema.Schema { Computed: true, Description: "BGP authorization key", }, + "as_override_enabled": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + Description: "Enable AS number override", + }, "bfd": { Type: schema.TypeSet, Optional: true, @@ -320,7 +362,10 @@ func resourceFabricRoutingProtocolCreate(ctx context.Context, d *schema.Resource start := time.Now() type_ := d.Get("type").(string) - createRequest := routingProtocolPayloadFromType(type_, d) + createRequest, err := routingProtocolPayloadFromType(type_, d) + if err != nil { + return diag.Errorf("error creating create request from Terraform configuration values: %s", err) + } fabricRoutingProtocolData, _, err := client.RoutingProtocolsApi.CreateConnectionRoutingProtocol(ctx, d.Get("connection_uuid").(string)).RoutingProtocolBase(createRequest).Execute() @@ -343,7 +388,10 @@ func resourceFabricRoutingProtocolUpdate(ctx context.Context, d *schema.Resource type_ := d.Get("type").(string) - updateRequest := routingProtocolPayloadFromType(type_, d) + updateRequest, err := routingProtocolPayloadFromType(type_, d) + if err != nil { + return diag.Errorf("error creating update request from Terraform configuration values: %s", err) + } start := time.Now() updatedRpResp, _, err := client.RoutingProtocolsApi.ReplaceConnectionRoutingProtocolByUuid(ctx, d.Id(), d.Get("connection_uuid").(string)).RoutingProtocolBase(updateRequest).Execute() @@ -418,7 +466,7 @@ func setIdFromAPIResponse(resp *fabricv4.RoutingProtocolData, isChange bool, d * return changeUuid } -func routingProtocolPayloadFromType(type_ string, d *schema.ResourceData) fabricv4.RoutingProtocolBase { +func routingProtocolPayloadFromType(type_ string, d *schema.ResourceData) (fabricv4.RoutingProtocolBase, error) { payload := fabricv4.RoutingProtocolBase{} if type_ == "BGP" { bgpRP := fabricv4.RoutingProtocolBGPType{} @@ -449,7 +497,10 @@ func routingProtocolPayloadFromType(type_ string, d *schema.ResourceData) fabric schemaBgpIpv4 := d.Get("bgp_ipv4") if schemaBgpIpv4 != nil { - bgpIpv4 := routingProtocolBgpIpv4TerraformToGo(schemaBgpIpv4.(*schema.Set).List()) + bgpIpv4, err := routingProtocolBgpIpv4TerraformToGo(schemaBgpIpv4.(*schema.Set).List()) + if err != nil { + return fabricv4.RoutingProtocolBase{}, err + } if !reflect.DeepEqual(bgpIpv4, fabricv4.BGPConnectionIpv4{}) { bgpRP.SetBgpIpv4(bgpIpv4) } @@ -457,12 +508,18 @@ func routingProtocolPayloadFromType(type_ string, d *schema.ResourceData) fabric schemaBgpIpv6 := d.Get("bgp_ipv6") if schemaBgpIpv6 != nil { - bgpIpv6 := routingProtocolBgpIpv6TerraformToGo(schemaBgpIpv6.(*schema.Set).List()) + bgpIpv6, err := routingProtocolBgpIpv6TerraformToGo(schemaBgpIpv6.(*schema.Set).List()) + if err != nil { + return fabricv4.RoutingProtocolBase{}, err + } if !reflect.DeepEqual(bgpIpv6, fabricv4.BGPConnectionIpv6{}) { bgpRP.SetBgpIpv6(bgpIpv6) } } + asOverrideEnabled := d.Get("as_override_enabled").(bool) + bgpRP.SetAsOverrideEnabled(asOverrideEnabled) + bfdSchema := d.Get("bfd") if bfdSchema != nil { bfd := routingProtocolBfdTerraformToGo(bfdSchema.(*schema.Set).List()) @@ -496,7 +553,7 @@ func routingProtocolPayloadFromType(type_ string, d *schema.ResourceData) fabric } payload = fabricv4.RoutingProtocolDirectTypeAsRoutingProtocolBase(&directRP) } - return payload + return payload, nil } func setFabricRoutingProtocolMap(d *schema.ResourceData, routingProtocolData *fabricv4.RoutingProtocolData) diag.Diagnostics { @@ -520,6 +577,7 @@ func FabricRoutingProtocolMap(routingProtocolData *fabricv4.RoutingProtocolData) routingProtocol["customer_asn"] = rp.GetCustomerAsn() routingProtocol["equinix_asn"] = rp.GetCustomerAsn() routingProtocol["bgp_auth_key"] = rp.GetBgpAuthKey() + routingProtocol["as_override_enabled"] = rp.GetAsOverrideEnabled() if rp.Operation != nil { operation := rp.GetOperation() routingProtocol["operation"] = routingProtocolOperationGoToTerraform(&operation) @@ -698,9 +756,9 @@ func routingProtocolDirectIpv6TerraformToGo(routingProtocolDirectIpv6Request []i return rpDirectIpv6 } -func routingProtocolBgpIpv4TerraformToGo(routingProtocolBgpIpv4Request []interface{}) fabricv4.BGPConnectionIpv4 { +func routingProtocolBgpIpv4TerraformToGo(routingProtocolBgpIpv4Request []interface{}) (fabricv4.BGPConnectionIpv4, error) { if len(routingProtocolBgpIpv4Request) == 0 { - return fabricv4.BGPConnectionIpv4{} + return fabricv4.BGPConnectionIpv4{}, nil } rpBgpIpv4 := fabricv4.BGPConnectionIpv4{} @@ -712,12 +770,26 @@ func routingProtocolBgpIpv4TerraformToGo(routingProtocolBgpIpv4Request []interfa enabled := bgpIpv4Map["enabled"].(bool) rpBgpIpv4.SetEnabled(enabled) - return rpBgpIpv4 + if outboundAsPrependCountStr := bgpIpv4Map["outbound_as_prepend_count"].(string); outboundAsPrependCountStr != "" { + outboundAsPrependCount, err := strconv.ParseInt(outboundAsPrependCountStr, 10, 64) + if err != nil { + return fabricv4.BGPConnectionIpv4{}, fmt.Errorf("error converting outbound_as_prepend_count from string to int64: %s", err) + } + rpBgpIpv4.SetOutboundASPrependCount(outboundAsPrependCount) + } + if inboundMed := bgpIpv4Map["inbound_med"].(int); inboundMed > 0 { + rpBgpIpv4.SetInboundMED(int64(inboundMed)) + } + if outboundMed := bgpIpv4Map["outbound_med"].(int); outboundMed > 0 { + rpBgpIpv4.SetOutboundMED(int64(outboundMed)) + } + + return rpBgpIpv4, nil } -func routingProtocolBgpIpv6TerraformToGo(routingProtocolBgpIpv6Request []interface{}) fabricv4.BGPConnectionIpv6 { +func routingProtocolBgpIpv6TerraformToGo(routingProtocolBgpIpv6Request []interface{}) (fabricv4.BGPConnectionIpv6, error) { if len(routingProtocolBgpIpv6Request) == 0 { - return fabricv4.BGPConnectionIpv6{} + return fabricv4.BGPConnectionIpv6{}, nil } rpBgpIpv6 := fabricv4.BGPConnectionIpv6{} @@ -729,7 +801,21 @@ func routingProtocolBgpIpv6TerraformToGo(routingProtocolBgpIpv6Request []interfa enabled := bgpIpv6Map["enabled"].(bool) rpBgpIpv6.SetEnabled(enabled) - return rpBgpIpv6 + if outboundAsPrependCountStr := bgpIpv6Map["outbound_as_prepend_count"].(string); outboundAsPrependCountStr != "" { + outboundAsPrependCount, err := strconv.ParseInt(outboundAsPrependCountStr, 10, 64) + if err != nil { + return fabricv4.BGPConnectionIpv6{}, err + } + rpBgpIpv6.SetOutboundASPrependCount(outboundAsPrependCount) + } + if inboundMed := bgpIpv6Map["inbound_med"].(int); inboundMed > 0 { + rpBgpIpv6.SetInboundMED(int64(inboundMed)) + } + if outboundMed := bgpIpv6Map["outbound_med"].(int); outboundMed > 0 { + rpBgpIpv6.SetOutboundMED(int64(outboundMed)) + } + + return rpBgpIpv6, nil } func routingProtocolBfdTerraformToGo(routingProtocolBfdRequest []interface{}) fabricv4.RoutingProtocolBFD { @@ -787,9 +873,12 @@ func routingProtocolBgpConnectionIpv4GoToTerraform(routingProtocolBgpIpv4 *fabri } mappedBgpIpv4 := map[string]interface{}{ - "customer_peer_ip": routingProtocolBgpIpv4.GetCustomerPeerIp(), - "equinix_peer_ip": routingProtocolBgpIpv4.GetEquinixPeerIp(), - "enabled": routingProtocolBgpIpv4.GetEnabled(), + "customer_peer_ip": routingProtocolBgpIpv4.GetCustomerPeerIp(), + "equinix_peer_ip": routingProtocolBgpIpv4.GetEquinixPeerIp(), + "enabled": routingProtocolBgpIpv4.GetEnabled(), + "outbound_as_prepend_count": strconv.FormatInt(routingProtocolBgpIpv4.GetOutboundASPrependCount(), 10), + "inbound_med": int(routingProtocolBgpIpv4.GetInboundMED()), + "outbound_med": int(routingProtocolBgpIpv4.GetOutboundMED()), } rpBgpIpv4Set := schema.NewSet( schema.HashResource(&schema.Resource{Schema: createBgpConnectionIpv4Sch()}), @@ -804,9 +893,12 @@ func routingProtocolBgpConnectionIpv6GoToTerraform(routingProtocolBgpIpv6 *fabri } mappedBgpIpv6 := map[string]interface{}{ - "customer_peer_ip": routingProtocolBgpIpv6.GetCustomerPeerIp(), - "equinix_peer_ip": routingProtocolBgpIpv6.GetEquinixPeerIp(), - "enabled": routingProtocolBgpIpv6.GetEnabled(), + "customer_peer_ip": routingProtocolBgpIpv6.GetCustomerPeerIp(), + "equinix_peer_ip": routingProtocolBgpIpv6.GetEquinixPeerIp(), + "enabled": routingProtocolBgpIpv6.GetEnabled(), + "outbound_as_prepend_count": strconv.FormatInt(routingProtocolBgpIpv6.GetOutboundASPrependCount(), 10), + "inbound_med": int(routingProtocolBgpIpv6.GetInboundMED()), + "outbound_med": int(routingProtocolBgpIpv6.GetOutboundMED()), } rpBgpIpv6Set := schema.NewSet( diff --git a/equinix/resource_fabric_routing_protocol_acc_test.go b/equinix/resource_fabric_routing_protocol_acc_test.go index d8ad6aec5..bd9103bc0 100644 --- a/equinix/resource_fabric_routing_protocol_acc_test.go +++ b/equinix/resource_fabric_routing_protocol_acc_test.go @@ -3,13 +3,14 @@ package equinix_test import ( "context" "fmt" - "github.com/equinix/terraform-provider-equinix/internal/fabric/testing_helpers" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "testing" "time" "github.com/equinix/terraform-provider-equinix/equinix" "github.com/equinix/terraform-provider-equinix/internal/acceptance" + "github.com/equinix/terraform-provider-equinix/internal/fabric/testing_helpers" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" ) @@ -53,9 +54,15 @@ func TestAccFabricCreateRoutingProtocols_PFCR(t *testing.T) { resource.TestCheckResourceAttr("equinix_fabric_routing_protocol.bgp", "bgp_ipv4.0.customer_peer_ip", "190.1.1.2"), resource.TestCheckResourceAttr("equinix_fabric_routing_protocol.bgp", "bgp_ipv4.0.equinix_peer_ip", "190.1.1.1"), resource.TestCheckResourceAttr("equinix_fabric_routing_protocol.bgp", "bgp_ipv4.0.enabled", "true"), + resource.TestCheckResourceAttr("equinix_fabric_routing_protocol.bgp", "bgp_ipv4.0.outbound_as_prepend_count", "1"), + resource.TestCheckResourceAttr("equinix_fabric_routing_protocol.bgp", "bgp_ipv4.0.inbound_med", "4"), + resource.TestCheckResourceAttr("equinix_fabric_routing_protocol.bgp", "bgp_ipv4.0.outbound_med", "7"), resource.TestCheckResourceAttr("equinix_fabric_routing_protocol.bgp", "bgp_ipv6.0.customer_peer_ip", "190::1:2"), resource.TestCheckResourceAttr("equinix_fabric_routing_protocol.bgp", "bgp_ipv6.0.equinix_peer_ip", "190::1:1"), resource.TestCheckResourceAttr("equinix_fabric_routing_protocol.bgp", "bgp_ipv6.0.enabled", "true"), + resource.TestCheckResourceAttr("equinix_fabric_routing_protocol.bgp", "bgp_ipv6.0.outbound_as_prepend_count", "1"), + resource.TestCheckResourceAttr("equinix_fabric_routing_protocol.bgp", "bgp_ipv6.0.inbound_med", "4"), + resource.TestCheckResourceAttr("equinix_fabric_routing_protocol.bgp", "bgp_ipv6.0.outbound_med", "7"), resource.TestCheckResourceAttr("equinix_fabric_routing_protocol.bgp", "customer_asn", "100"), resource.TestCheckResourceAttrSet("data.equinix_fabric_routing_protocol.direct", "id"), @@ -79,7 +86,6 @@ func TestAccFabricCreateRoutingProtocols_PFCR(t *testing.T) { resource.TestCheckResourceAttr("data.equinix_fabric_routing_protocol.bgp", "bgp_ipv6.0.enabled", "true"), resource.TestCheckResourceAttr("data.equinix_fabric_routing_protocol.bgp", "customer_asn", "100"), ), - ExpectNonEmptyPlan: true, }, }, }) @@ -145,7 +151,7 @@ resource "equinix_fabric_connection" "this" { } link_protocol { type= "DOT1Q" - vlan_tag= 2152 + vlan_tag= 2011 } location { metro_code = "SV" @@ -175,11 +181,17 @@ resource "equinix_fabric_routing_protocol" "bgp" { name = "rp_bgp_PFCR" bgp_ipv4{ customer_peer_ip = "190.1.1.2" + outbound_as_prepend_count = "1" + inbound_med = 4 + outbound_med = 7 } bgp_ipv6{ customer_peer_ip = "190::1:2" + outbound_as_prepend_count = "1" + inbound_med = 4 + outbound_med = 7 } - customer_asn = "100" + customer_asn = 100 } data "equinix_fabric_routing_protocol" "direct" { diff --git a/templates/resources/fabric_routing_protocol.md.tmpl b/templates/resources/fabric_routing_protocol.md.tmpl index f8ce5a7c8..128b202b6 100644 --- a/templates/resources/fabric_routing_protocol.md.tmpl +++ b/templates/resources/fabric_routing_protocol.md.tmpl @@ -32,165 +32,4 @@ Direct and BGP Routing Protocol (Requires Depends On to Handle Synchronization): {{tffile "examples/resources/equinix_fabric_routing_protocol/example_3.tf"}} - - -## Schema - -### Required - -- `connection_uuid` (String) Connection URI associated with Routing Protocol - -### Optional - -- `bfd` (Block Set) Bidirectional Forwarding Detection (see [below for nested schema](#nestedblock--bfd)) -- `bgp_auth_key` (String) BGP authorization key -- `bgp_ipv4` (Block Set) Routing Protocol BGP IPv4 (see [below for nested schema](#nestedblock--bgp_ipv4)) -- `bgp_ipv6` (Block Set) Routing Protocol BGP IPv6 (see [below for nested schema](#nestedblock--bgp_ipv6)) -- `customer_asn` (Number) Customer-provided ASN -- `description` (String) Customer-provided Fabric Routing Protocol description -- `direct_ipv4` (Block Set) Routing Protocol Direct IPv4 (see [below for nested schema](#nestedblock--direct_ipv4)) -- `direct_ipv6` (Block Set) Routing Protocol Direct IPv6 (see [below for nested schema](#nestedblock--direct_ipv6)) -- `name` (String) Routing Protocol name. An alpha-numeric 24 characters string which can include only hyphens and underscores -- `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts)) -- `type` (String) Defines the routing protocol type like BGP or DIRECT -- `uuid` (String) Equinix-assigned routing protocol identifier - -### Read-Only - -- `change` (Set of Object) Routing Protocol configuration Changes (see [below for nested schema](#nestedatt--change)) -- `change_log` (Set of Object) Captures Routing Protocol lifecycle change information (see [below for nested schema](#nestedatt--change_log)) -- `equinix_asn` (Number) Equinix ASN -- `href` (String) Routing Protocol URI information -- `id` (String) The ID of this resource. -- `operation` (Set of Object) Routing Protocol type-specific operational data (see [below for nested schema](#nestedatt--operation)) -- `state` (String) Routing Protocol overall state - - - -### Nested Schema for `bfd` - -Required: - -- `enabled` (Boolean) Bidirectional Forwarding Detection enablement - -Optional: - -- `interval` (String) Interval range between the received BFD control packets - - - -### Nested Schema for `bgp_ipv4` - -Required: - -- `customer_peer_ip` (String) Customer side peering ip - -Optional: - -- `enabled` (Boolean) Admin status for the BGP session - -Read-Only: - -- `equinix_peer_ip` (String) Equinix side peering ip - - - -### Nested Schema for `bgp_ipv6` - -Required: - -- `customer_peer_ip` (String) Customer side peering ip - -Optional: - -- `enabled` (Boolean) Admin status for the BGP session - -Read-Only: - -- `equinix_peer_ip` (String) Equinix side peering ip - - - -### Nested Schema for `direct_ipv4` - -Required: - -- `equinix_iface_ip` (String) Equinix side Interface IP address - - - -### Nested Schema for `direct_ipv6` - -Optional: - -- `equinix_iface_ip` (String) Equinix side Interface IP address - - - -### Nested Schema for `timeouts` - -Optional: - -- `create` (String) -- `delete` (String) -- `read` (String) -- `update` (String) - - - -### Nested Schema for `change` - -Read-Only: - -- `href` (String) -- `type` (String) -- `uuid` (String) - - - -### Nested Schema for `change_log` - -Read-Only: - -- `created_by` (String) -- `created_by_email` (String) -- `created_by_full_name` (String) -- `created_date_time` (String) -- `deleted_by` (String) -- `deleted_by_email` (String) -- `deleted_by_full_name` (String) -- `deleted_date_time` (String) -- `updated_by` (String) -- `updated_by_email` (String) -- `updated_by_full_name` (String) -- `updated_date_time` (String) - - - -### Nested Schema for `operation` - -Read-Only: - -- `errors` (List of Object) (see [below for nested schema](#nestedobjatt--operation--errors)) - - - -### Nested Schema for `operation.errors` - -Read-Only: - -- `additional_info` (List of Object) (see [below for nested schema](#nestedobjatt--operation--errors--additional_info)) -- `correlation_id` (String) -- `details` (String) -- `error_code` (String) -- `error_message` (String) -- `help` (String) - - - -### Nested Schema for `operation.errors.additional_info` - -Read-Only: - -- `property` (String) -- `reason` (String) +{{ .SchemaMarkdown | trimspace }} From 99cd65d429a44cd3f32a9d970c758b0cb735a7c2 Mon Sep 17 00:00:00 2001 From: kpdhulipala <84343462+kpdhulipala@users.noreply.github.com> Date: Thu, 10 Oct 2024 18:26:20 -0700 Subject: [PATCH 08/55] feat: Nfv 29975 - make ssh public key name optional in Create Virtual device request (#796) feat: Nfv 29975 - make ssh public key name optional in Create Virtual device request --- docs/resources/network_device.md | 2 +- equinix/resource_network_device.go | 9 +++------ templates/resources/network_device.md.tmpl | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/docs/resources/network_device.md b/docs/resources/network_device.md index 87f99a01e..747b3c572 100644 --- a/docs/resources/network_device.md +++ b/docs/resources/network_device.md @@ -460,7 +460,7 @@ The `secondary_device` block supports the following arguments: The `ssh_key` block supports the following arguments: * `username` - (Required) username associated with given key. -* `name` - (Required) reference by name to previously provisioned public SSH key. +* `name` - (Optional) reference by name to previously provisioned public SSH key. ### Cluster Details diff --git a/equinix/resource_network_device.go b/equinix/resource_network_device.go index ef5aacfdf..813202dfb 100644 --- a/equinix/resource_network_device.go +++ b/equinix/resource_network_device.go @@ -225,7 +225,7 @@ func resourceNetworkDevice() *schema.Resource { UpdateContext: resourceNetworkDeviceUpdate, DeleteContext: resourceNetworkDeviceDelete, Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, + StateContext: schema.ImportStatePassthroughContext, }, Schema: createNetworkDeviceSchema(), Timeouts: &schema.ResourceTimeout{ @@ -248,10 +248,7 @@ func createNetworkDeviceSchema() map[string]*schema.Schema { Type: schema.TypeString, Required: true, DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - if old == new+"-Node0" { - return true - } - return false + return old == new+"-Node0" }, ValidateFunc: validation.StringLenBetween(3, 50), Description: neDeviceDescriptions["Name"], @@ -832,7 +829,7 @@ func createNetworkDeviceUserKeySchema() map[string]*schema.Schema { }, neDeviceUserKeySchemaNames["KeyName"]: { Type: schema.TypeString, - Required: true, + Optional: true, ValidateFunc: validation.StringIsNotEmpty, Description: neDeviceUserKeyDescriptions["KeyName"], }, diff --git a/templates/resources/network_device.md.tmpl b/templates/resources/network_device.md.tmpl index 1356a6fc4..6fa571ce9 100644 --- a/templates/resources/network_device.md.tmpl +++ b/templates/resources/network_device.md.tmpl @@ -103,7 +103,7 @@ The `secondary_device` block supports the following arguments: The `ssh_key` block supports the following arguments: * `username` - (Required) username associated with given key. -* `name` - (Required) reference by name to previously provisioned public SSH key. +* `name` - (Optional) reference by name to previously provisioned public SSH key. ### Cluster Details From f2f699488108369775fa901cdb0b0397128d45a2 Mon Sep 17 00:00:00 2001 From: thogarty <139183873+thogarty@users.noreply.github.com> Date: Mon, 14 Oct 2024 12:27:50 -0700 Subject: [PATCH 09/55] feat: fabric resource connection_route_filter (#795) * Add equinix_fabric_connection_route_filter resource to attach route filter policies to fabric cloud router connections * Add data source for retrieving connection_route_filters by connection and route filter uuids * Add data source to get all route filters for a given connection uuid * Add docs with make docs * Add acceptance tests for resource and data sources Local tests passing: image --- .../fabric_connection_route_filter.md | 57 +++++ .../fabric_connection_route_filters.md | 75 +++++++ .../fabric_connection_route_filter.md | 72 +++++++ equinix/provider.go | 56 ++--- .../data-source.tf | 24 +++ .../data-source.tf | 23 ++ .../resource.tf | 25 +++ .../connection_route_filter/datasources.go | 56 +++++ .../datasources_schema.go | 110 ++++++++++ .../datasources_test.go | 3 + .../fabric/connection_route_filter/models.go | 67 ++++++ .../connection_route_filter/resource.go | 203 ++++++++++++++++++ .../resource_schema.go | 49 +++++ .../connection_route_filter/resource_test.go | 196 +++++++++++++++++ 14 files changed, 990 insertions(+), 26 deletions(-) create mode 100644 docs/data-sources/fabric_connection_route_filter.md create mode 100644 docs/data-sources/fabric_connection_route_filters.md create mode 100644 docs/resources/fabric_connection_route_filter.md create mode 100644 examples/data-sources/equinix_fabric_connection_route_filter/data-source.tf create mode 100644 examples/data-sources/equinix_fabric_connection_route_filters/data-source.tf create mode 100644 examples/resources/equinix_fabric_connection_route_filter/resource.tf create mode 100644 internal/resources/fabric/connection_route_filter/datasources.go create mode 100644 internal/resources/fabric/connection_route_filter/datasources_schema.go create mode 100644 internal/resources/fabric/connection_route_filter/datasources_test.go create mode 100644 internal/resources/fabric/connection_route_filter/models.go create mode 100644 internal/resources/fabric/connection_route_filter/resource.go create mode 100644 internal/resources/fabric/connection_route_filter/resource_schema.go create mode 100644 internal/resources/fabric/connection_route_filter/resource_test.go diff --git a/docs/data-sources/fabric_connection_route_filter.md b/docs/data-sources/fabric_connection_route_filter.md new file mode 100644 index 000000000..2511ed554 --- /dev/null +++ b/docs/data-sources/fabric_connection_route_filter.md @@ -0,0 +1,57 @@ +--- +subcategory: "Fabric" +--- + +# equinix_fabric_connection_route_filter (Data Source) + +Fabric V4 API compatible data resource that allow user to fetch route filter policy attachment to a fabric connection + +Additional Documentation: +* Getting Started: https://docs.equinix.com/en-us/Content/Interconnection/FCR/FCR-route-filters.htm +* API: https://developer.equinix.com/dev-docs/fabric/api-reference/fabric-v4-apis#route-filter-rules + +## Example Usage + +```terraform +data "equinix_fabric_connection_route_filter" "attached_policy" { + connection_id = "" + route_filter_id = "" +} + +output "connection_route_filter_id" { + value = data.equinix_fabric_connection_route_filter.attached_policy.id +} + +output "connection_route_filter_connection_id" { + value = data.equinix_fabric_connection_route_filter.attached_policy.connection_id +} + +output "connection_route_filter_direction" { + value = data.equinix_fabric_connection_route_filter.attached_policy.direction +} + +output "connection_route_filter_type" { + value = data.equinix_fabric_connection_route_filter.attached_policy.type +} + +output "connection_route_filter_attachment_status" { + value = data.equinix_fabric_connection_route_filter.attached_policy.attachment_status +} +``` + + +## Schema + +### Required + +- `connection_id` (String) Equinix Assigned UUID of the Equinix Connection to attach the Route Filter Policy to +- `route_filter_id` (String) Equinix Assigned UUID of the Route Filter Policy to attach to the Equinix Connection + +### Read-Only + +- `attachment_status` (String) Status of the Route Filter Policy attachment lifecycle +- `direction` (String) Direction of the filtering of the attached Route Filter Policy +- `href` (String) URI to the attached Route Filter Policy on the Connection +- `id` (String) The ID of this resource. +- `type` (String) Route Filter Type. One of [ "BGP_IPv4_PREFIX_FILTER", "BGP_IPv6_PREFIX_FILTER" ] +- `uuid` (String) Equinix Assigned ID for Route Filter Policy diff --git a/docs/data-sources/fabric_connection_route_filters.md b/docs/data-sources/fabric_connection_route_filters.md new file mode 100644 index 000000000..e6da98fce --- /dev/null +++ b/docs/data-sources/fabric_connection_route_filters.md @@ -0,0 +1,75 @@ +--- +subcategory: "Fabric" +--- + +# equinix_fabric_connection_route_filters (Data Source) + +Fabric V4 API compatible data resource that allow user to fetch all route filter policies attached to a fabric connection + +Additional Documentation: +* Getting Started: https://docs.equinix.com/en-us/Content/Interconnection/FCR/FCR-route-filters.htm +* API: https://developer.equinix.com/dev-docs/fabric/api-reference/fabric-v4-apis#route-filter-rules + +## Example Usage + +```terraform +data "equinix_connection_route_filters" "attached_policies" { + connection_id = "" +} + +output "connection_first_route_filter_uuid" { + value = data.equinix_fabric_connection_route_filter.attached_policies.0.uuid +} + +output "connection_first_route_filter_connection_id" { + value = data.equinix_fabric_connection_route_filter.attached_policies.0.connection_id +} + +output "connection_first_route_filter_direction" { + value = data.equinix_fabric_connection_route_filter.attached_policies.0.direction +} + +output "connection_first_route_filter_type" { + value = data.equinix_fabric_connection_route_filter.attached_policies.0.type +} + +output "connection_first_route_filter_attachment_status" { + value = data.equinix_fabric_connection_route_filter.attached_policies.0.attachment_status +} +``` + + +## Schema + +### Required + +- `connection_id` (String) Equinix Assigned UUID of the Equinix Connection to attach the Route Filter Policy to + +### Read-Only + +- `data` (List of Object) The list of Rules attached to the given Route Filter Policy UUID (see [below for nested schema](#nestedatt--data)) +- `id` (String) The ID of this resource. +- `pagination` (Set of Object) Pagination details for the Data Source Search Request (see [below for nested schema](#nestedatt--pagination)) + + +### Nested Schema for `data` + +Read-Only: + +- `attachment_status` (String) +- `direction` (String) +- `href` (String) +- `type` (String) +- `uuid` (String) + + + +### Nested Schema for `pagination` + +Read-Only: + +- `limit` (Number) +- `next` (String) +- `offset` (Number) +- `previous` (String) +- `total` (Number) diff --git a/docs/resources/fabric_connection_route_filter.md b/docs/resources/fabric_connection_route_filter.md new file mode 100644 index 000000000..302735413 --- /dev/null +++ b/docs/resources/fabric_connection_route_filter.md @@ -0,0 +1,72 @@ +--- +subcategory: "Fabric" +--- + +# equinix_fabric_connection_route_filter (Resource) + +Fabric V4 API compatible resource allows attachment of Route Filter Polices to Fabric Connections + +Additional Documentation: +* Getting Started: https://docs.equinix.com/en-us/Content/Interconnection/FCR/FCR-route-filters.htm +* API: https://developer.equinix.com/dev-docs/fabric/api-reference/fabric-v4-apis#route-filters + +## Example Usage + +```terraform +resource "equinix_fabric_connection_route_filter" "policy_attachment" { + connection_id = "" + route_filter_id = "" + direction = "INBOUND" +} + +output "connection_route_filter_id" { + value = equinix_fabric_connection_route_filter.policy_attachment.id +} + +output "connection_route_filter_connection_id" { + value = equinix_fabric_connection_route_filter.policy_attachment.connection_id +} + +output "connection_route_filter_direction" { + value = equinix_fabric_connection_route_filter.policy_attachment.direction +} + +output "connection_route_filter_type" { + value = equinix_fabric_connection_route_filter.policy_attachment.type +} + +output "connection_route_filter_attachment_status" { + value = equinix_fabric_connection_route_filter.policy_attachment.attachment_status +} +``` + + +## Schema + +### Required + +- `connection_id` (String) Equinix Assigned UUID of the Equinix Connection to attach the Route Filter Policy to +- `direction` (String) Direction of the filtering of the attached Route Filter Policy +- `route_filter_id` (String) Equinix Assigned UUID of the Route Filter Policy to attach to the Equinix Connection + +### Optional + +- `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts)) + +### Read-Only + +- `attachment_status` (String) Status of the Route Filter Policy attachment lifecycle +- `href` (String) URI to the attached Route Filter Policy on the Connection +- `id` (String) The ID of this resource. +- `type` (String) Route Filter Type. One of [ "BGP_IPv4_PREFIX_FILTER", "BGP_IPv6_PREFIX_FILTER" ] +- `uuid` (String) Equinix Assigned ID for Route Filter Policy + + +### Nested Schema for `timeouts` + +Optional: + +- `create` (String) +- `delete` (String) +- `read` (String) +- `update` (String) diff --git a/equinix/provider.go b/equinix/provider.go index 2ff55f43d..bf38b2592 100644 --- a/equinix/provider.go +++ b/equinix/provider.go @@ -8,6 +8,7 @@ import ( "github.com/equinix/terraform-provider-equinix/internal/config" fabric_connection "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/connection" + fabric_connection_route_filter "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/connection_route_filter" fabric_market_place_subscription "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/marketplace" fabric_network "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/network" fabric_route_filter "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/route_filter" @@ -87,6 +88,8 @@ func Provider() *schema.Provider { "equinix_fabric_routing_protocol": dataSourceRoutingProtocol(), "equinix_fabric_connection": fabric_connection.DataSource(), "equinix_fabric_connections": fabric_connection.DataSourceSearch(), + "equinix_fabric_connection_route_filter": fabric_connection_route_filter.DataSource(), + "equinix_fabric_connection_route_filters": fabric_connection_route_filter.DataSourceGetAllRules(), "equinix_fabric_cloud_router": dataSourceFabricCloudRouter(), "equinix_fabric_cloud_routers": dataSourceFabricGetCloudRouters(), "equinix_fabric_market_place_subscription": fabric_market_place_subscription.DataSourceFabricMarketplaceSubscription(), @@ -125,33 +128,34 @@ func Provider() *schema.Provider { "equinix_metal_vrf": vrf.DataSource(), }, ResourcesMap: map[string]*schema.Resource{ - "equinix_fabric_network": fabric_network.Resource(), - "equinix_fabric_cloud_router": resourceFabricCloudRouter(), - "equinix_fabric_connection": fabric_connection.Resource(), - "equinix_fabric_route_filter": fabric_route_filter.Resource(), - "equinix_fabric_route_filter_rule": fabric_route_filter_rule.Resource(), - "equinix_fabric_routing_protocol": resourceFabricRoutingProtocol(), - "equinix_fabric_service_profile": resourceFabricServiceProfile(), + "equinix_fabric_network": fabric_network.Resource(), + "equinix_fabric_cloud_router": resourceFabricCloudRouter(), + "equinix_fabric_connection": fabric_connection.Resource(), + "equinix_fabric_connection_route_filter": fabric_connection_route_filter.Resource(), + "equinix_fabric_route_filter": fabric_route_filter.Resource(), + "equinix_fabric_route_filter_rule": fabric_route_filter_rule.Resource(), + "equinix_fabric_routing_protocol": resourceFabricRoutingProtocol(), + "equinix_fabric_service_profile": resourceFabricServiceProfile(), "equinix_fabric_service_token": fabric_service_token.Resource(), - "equinix_network_device": resourceNetworkDevice(), - "equinix_network_ssh_user": resourceNetworkSSHUser(), - "equinix_network_bgp": resourceNetworkBGP(), - "equinix_network_ssh_key": resourceNetworkSSHKey(), - "equinix_network_acl_template": resourceNetworkACLTemplate(), - "equinix_network_device_link": resourceNetworkDeviceLink(), - "equinix_network_file": resourceNetworkFile(), - "equinix_metal_user_api_key": resourceMetalUserAPIKey(), - "equinix_metal_project_api_key": resourceMetalProjectAPIKey(), - "equinix_metal_device": metal_device.Resource(), - "equinix_metal_device_network_type": resourceMetalDeviceNetworkType(), - "equinix_metal_port": metal_port.Resource(), - "equinix_metal_reserved_ip_block": resourceMetalReservedIPBlock(), - "equinix_metal_ip_attachment": resourceMetalIPAttachment(), - "equinix_metal_spot_market_request": resourceMetalSpotMarketRequest(), - "equinix_metal_virtual_circuit": virtual_circuit.Resource(), - "equinix_metal_vrf": vrf.Resource(), - "equinix_metal_bgp_session": resourceMetalBGPSession(), - "equinix_metal_port_vlan_attachment": resourceMetalPortVlanAttachment(), + "equinix_network_device": resourceNetworkDevice(), + "equinix_network_ssh_user": resourceNetworkSSHUser(), + "equinix_network_bgp": resourceNetworkBGP(), + "equinix_network_ssh_key": resourceNetworkSSHKey(), + "equinix_network_acl_template": resourceNetworkACLTemplate(), + "equinix_network_device_link": resourceNetworkDeviceLink(), + "equinix_network_file": resourceNetworkFile(), + "equinix_metal_user_api_key": resourceMetalUserAPIKey(), + "equinix_metal_project_api_key": resourceMetalProjectAPIKey(), + "equinix_metal_device": metal_device.Resource(), + "equinix_metal_device_network_type": resourceMetalDeviceNetworkType(), + "equinix_metal_port": metal_port.Resource(), + "equinix_metal_reserved_ip_block": resourceMetalReservedIPBlock(), + "equinix_metal_ip_attachment": resourceMetalIPAttachment(), + "equinix_metal_spot_market_request": resourceMetalSpotMarketRequest(), + "equinix_metal_virtual_circuit": virtual_circuit.Resource(), + "equinix_metal_vrf": vrf.Resource(), + "equinix_metal_bgp_session": resourceMetalBGPSession(), + "equinix_metal_port_vlan_attachment": resourceMetalPortVlanAttachment(), }, ProviderMetaSchema: map[string]*schema.Schema{ "module_name": { diff --git a/examples/data-sources/equinix_fabric_connection_route_filter/data-source.tf b/examples/data-sources/equinix_fabric_connection_route_filter/data-source.tf new file mode 100644 index 000000000..728d0d1e7 --- /dev/null +++ b/examples/data-sources/equinix_fabric_connection_route_filter/data-source.tf @@ -0,0 +1,24 @@ +data "equinix_fabric_connection_route_filter" "attached_policy" { + connection_id = "" + route_filter_id = "" +} + +output "connection_route_filter_id" { + value = data.equinix_fabric_connection_route_filter.attached_policy.id +} + +output "connection_route_filter_connection_id" { + value = data.equinix_fabric_connection_route_filter.attached_policy.connection_id +} + +output "connection_route_filter_direction" { + value = data.equinix_fabric_connection_route_filter.attached_policy.direction +} + +output "connection_route_filter_type" { + value = data.equinix_fabric_connection_route_filter.attached_policy.type +} + +output "connection_route_filter_attachment_status" { + value = data.equinix_fabric_connection_route_filter.attached_policy.attachment_status +} diff --git a/examples/data-sources/equinix_fabric_connection_route_filters/data-source.tf b/examples/data-sources/equinix_fabric_connection_route_filters/data-source.tf new file mode 100644 index 000000000..c2e22a893 --- /dev/null +++ b/examples/data-sources/equinix_fabric_connection_route_filters/data-source.tf @@ -0,0 +1,23 @@ +data "equinix_connection_route_filters" "attached_policies" { + connection_id = "" +} + +output "connection_first_route_filter_uuid" { + value = data.equinix_fabric_connection_route_filter.attached_policies.0.uuid +} + +output "connection_first_route_filter_connection_id" { + value = data.equinix_fabric_connection_route_filter.attached_policies.0.connection_id +} + +output "connection_first_route_filter_direction" { + value = data.equinix_fabric_connection_route_filter.attached_policies.0.direction +} + +output "connection_first_route_filter_type" { + value = data.equinix_fabric_connection_route_filter.attached_policies.0.type +} + +output "connection_first_route_filter_attachment_status" { + value = data.equinix_fabric_connection_route_filter.attached_policies.0.attachment_status +} diff --git a/examples/resources/equinix_fabric_connection_route_filter/resource.tf b/examples/resources/equinix_fabric_connection_route_filter/resource.tf new file mode 100644 index 000000000..6ea69b7d8 --- /dev/null +++ b/examples/resources/equinix_fabric_connection_route_filter/resource.tf @@ -0,0 +1,25 @@ +resource "equinix_fabric_connection_route_filter" "policy_attachment" { + connection_id = "" + route_filter_id = "" + direction = "INBOUND" +} + +output "connection_route_filter_id" { + value = equinix_fabric_connection_route_filter.policy_attachment.id +} + +output "connection_route_filter_connection_id" { + value = equinix_fabric_connection_route_filter.policy_attachment.connection_id +} + +output "connection_route_filter_direction" { + value = equinix_fabric_connection_route_filter.policy_attachment.direction +} + +output "connection_route_filter_type" { + value = equinix_fabric_connection_route_filter.policy_attachment.type +} + +output "connection_route_filter_attachment_status" { + value = equinix_fabric_connection_route_filter.policy_attachment.attachment_status +} diff --git a/internal/resources/fabric/connection_route_filter/datasources.go b/internal/resources/fabric/connection_route_filter/datasources.go new file mode 100644 index 000000000..e8eaeb309 --- /dev/null +++ b/internal/resources/fabric/connection_route_filter/datasources.go @@ -0,0 +1,56 @@ +package connection_route_filter + +import ( + "context" + "fmt" + + "github.com/equinix/terraform-provider-equinix/internal/config" + equinix_errors "github.com/equinix/terraform-provider-equinix/internal/errors" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func DataSource() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceRead, + Schema: dataSourceByUUIDSchema(), + Description: `Fabric V4 API compatible data resource that allow user to fetch route filter policy attachment to a fabric connection + +Additional Documentation: +* Getting Started: https://docs.equinix.com/en-us/Content/Interconnection/FCR/FCR-route-filters.htm +* API: https://developer.equinix.com/dev-docs/fabric/api-reference/fabric-v4-apis#route-filter-rules`, + } +} + +func dataSourceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + uuid := d.Get("route_filter_id").(string) + d.SetId(uuid) + return resourceRead(ctx, d, meta) +} + +func DataSourceGetAllRules() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceGetAllFilters, + Schema: dataSourceAllFiltersSchema(), + Description: `Fabric V4 API compatible data resource that allow user to fetch all route filter policies attached to a fabric connection + +Additional Documentation: +* Getting Started: https://docs.equinix.com/en-us/Content/Interconnection/FCR/FCR-route-filters.htm +* API: https://developer.equinix.com/dev-docs/fabric/api-reference/fabric-v4-apis#route-filter-rules`, + } +} + +func dataSourceGetAllFilters(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*config.Config).NewFabricClientForSDK(d) + connectionId := d.Get("connection_id").(string) + connectionRouteFilters, _, err := client.RouteFiltersApi.GetConnectionRouteFilters(ctx, connectionId).Execute() + if err != nil { + return diag.FromErr(equinix_errors.FormatFabricError(err)) + } + if len(connectionRouteFilters.Data) < 1 { + return diag.FromErr(fmt.Errorf("no records are found for the connection (%s) - %d , please change the search criteria", connectionId, len(connectionRouteFilters.Data))) + } + d.SetId(connectionId) + return setConnectionRouteFilterData(d, connectionRouteFilters) +} diff --git a/internal/resources/fabric/connection_route_filter/datasources_schema.go b/internal/resources/fabric/connection_route_filter/datasources_schema.go new file mode 100644 index 000000000..42da7fc92 --- /dev/null +++ b/internal/resources/fabric/connection_route_filter/datasources_schema.go @@ -0,0 +1,110 @@ +package connection_route_filter + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceByUUIDSchema() map[string]*schema.Schema { + dsSchema := baseSchema() + dsSchema["connection_id"] = connectionIdSchema() + dsSchema["route_filter_id"] = routeFilterIdSchema() + return dsSchema +} + +func dataSourceAllFiltersSchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "connection_id": connectionIdSchema(), + "pagination": { + Type: schema.TypeSet, + Computed: true, + Description: "Pagination details for the Data Source Search Request", + Elem: paginationSchema(), + }, + "data": { + Type: schema.TypeList, + Computed: true, + Description: "The list of Rules attached to the given Route Filter Policy UUID", + Elem: &schema.Resource{ + Schema: baseSchema(), + }, + }, + } +} + +func baseSchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "direction": { + Type: schema.TypeString, + Computed: true, + Description: "Direction of the filtering of the attached Route Filter Policy", + }, + "type": { + Type: schema.TypeString, + Computed: true, + Description: "Route Filter Type. One of [ \"BGP_IPv4_PREFIX_FILTER\", \"BGP_IPv6_PREFIX_FILTER\" ] ", + }, + "href": { + Type: schema.TypeString, + Computed: true, + Description: "URI to the attached Route Filter Policy on the Connection", + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + Description: "Equinix Assigned ID for Route Filter Policy", + }, + "attachment_status": { + Type: schema.TypeString, + Computed: true, + Description: "Status of the Route Filter Policy attachment lifecycle", + }, + } +} + +func routeFilterIdSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeString, + Required: true, + Description: "Equinix Assigned UUID of the Route Filter Policy to attach to the Equinix Connection", + } +} + +func connectionIdSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeString, + Required: true, + Description: "Equinix Assigned UUID of the Equinix Connection to attach the Route Filter Policy to", + } +} + +func paginationSchema() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "offset": { + Type: schema.TypeInt, + Computed: true, + Description: "The page offset for the pagination request. Index of the first element. Default is 0.", + }, + "limit": { + Type: schema.TypeInt, + Computed: true, + Description: "Number of elements to be requested per page. Number must be between 1 and 100. Default is 20", + }, + "total": { + Type: schema.TypeInt, + Computed: true, + Description: "Total number of elements returned.", + }, + "next": { + Type: schema.TypeString, + Computed: true, + Description: "URL relative to the last item in the response.", + }, + "previous": { + Type: schema.TypeString, + Computed: true, + Description: "URL relative to the first item in the response.", + }, + }, + } +} diff --git a/internal/resources/fabric/connection_route_filter/datasources_test.go b/internal/resources/fabric/connection_route_filter/datasources_test.go new file mode 100644 index 000000000..2636823ca --- /dev/null +++ b/internal/resources/fabric/connection_route_filter/datasources_test.go @@ -0,0 +1,3 @@ +package connection_route_filter_test + +// Tested in resource_test.go because of the heavy resource setup constraints diff --git a/internal/resources/fabric/connection_route_filter/models.go b/internal/resources/fabric/connection_route_filter/models.go new file mode 100644 index 000000000..f59edd980 --- /dev/null +++ b/internal/resources/fabric/connection_route_filter/models.go @@ -0,0 +1,67 @@ +package connection_route_filter + +import ( + "github.com/equinix/equinix-sdk-go/services/fabricv4" + 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/schema" +) + +func setConnectionRouteFilterMap(d *schema.ResourceData, connectionRouteFilter *fabricv4.ConnectionRouteFilterData) diag.Diagnostics { + diags := diag.Diagnostics{} + routeFilterMap := connectionRouteFilterResponseMap(connectionRouteFilter) + err := equinix_schema.SetMap(d, routeFilterMap) + if err != nil { + return diag.FromErr(err) + } + return diags +} + +func setConnectionRouteFilterData(d *schema.ResourceData, connectionRouteFilters *fabricv4.GetAllConnectionRouteFiltersResponse) diag.Diagnostics { + diags := diag.Diagnostics{} + mappedRouteFilters := make([]map[string]interface{}, len(connectionRouteFilters.Data)) + pagination := connectionRouteFilters.GetPagination() + if connectionRouteFilters.Data != nil { + for index, routeFilter := range connectionRouteFilters.Data { + mappedRouteFilters[index] = connectionRouteFilterResponseMap(&routeFilter) + } + } else { + mappedRouteFilters = nil + } + err := equinix_schema.SetMap(d, map[string]interface{}{ + "data": mappedRouteFilters, + "pagination": paginationGoToTerraform(&pagination), + }) + if err != nil { + return diag.FromErr(err) + } + return diags +} + +func connectionRouteFilterResponseMap(data *fabricv4.ConnectionRouteFilterData) map[string]interface{} { + connectionRouteFilterMap := make(map[string]interface{}) + connectionRouteFilterMap["href"] = data.GetHref() + connectionRouteFilterMap["type"] = string(data.GetType()) + connectionRouteFilterMap["uuid"] = data.GetUuid() + connectionRouteFilterMap["attachment_status"] = string(data.GetAttachmentStatus()) + connectionRouteFilterMap["direction"] = string(data.GetDirection()) + + return connectionRouteFilterMap +} + +func paginationGoToTerraform(pagination *fabricv4.Pagination) *schema.Set { + if pagination == nil { + return nil + } + mappedPagination := make(map[string]interface{}) + mappedPagination["offset"] = int(pagination.GetOffset()) + mappedPagination["limit"] = int(pagination.GetLimit()) + mappedPagination["total"] = int(pagination.GetTotal()) + mappedPagination["next"] = pagination.GetNext() + mappedPagination["previous"] = pagination.GetPrevious() + + return schema.NewSet( + schema.HashResource(paginationSchema()), + []interface{}{mappedPagination}, + ) +} diff --git a/internal/resources/fabric/connection_route_filter/resource.go b/internal/resources/fabric/connection_route_filter/resource.go new file mode 100644 index 000000000..b65e36c35 --- /dev/null +++ b/internal/resources/fabric/connection_route_filter/resource.go @@ -0,0 +1,203 @@ +package connection_route_filter + +import ( + "context" + "log" + "strings" + "time" + + "github.com/equinix/terraform-provider-equinix/internal/config" + equinix_errors "github.com/equinix/terraform-provider-equinix/internal/errors" + + "github.com/equinix/equinix-sdk-go/services/fabricv4" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func Resource() *schema.Resource { + return &schema.Resource{ + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(10 * time.Minute), + Update: schema.DefaultTimeout(10 * time.Minute), + Delete: schema.DefaultTimeout(10 * time.Minute), + Read: schema.DefaultTimeout(10 * time.Minute), + }, + ReadContext: resourceRead, + CreateContext: resourceCreate, + UpdateContext: resourceUpdate, + DeleteContext: resourceDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + Schema: resourceSchema(), + Description: `Fabric V4 API compatible resource allows attachment of Route Filter Polices to Fabric Connections + +Additional Documentation: +* Getting Started: https://docs.equinix.com/en-us/Content/Interconnection/FCR/FCR-route-filters.htm +* API: https://developer.equinix.com/dev-docs/fabric/api-reference/fabric-v4-apis#route-filters`, + } +} + +func resourceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*config.Config).NewFabricClientForSDK(d) + connectionId := d.Get("connection_id").(string) + connectionRouteFilter, _, err := client.RouteFiltersApi.GetConnectionRouteFilterByUuid(ctx, d.Id(), connectionId).Execute() + if err != nil { + log.Printf("[WARN] Route Filter Policy %s not found on Connection %s, error %s", d.Id(), connectionId, err) + if !strings.Contains(err.Error(), "500") { + d.SetId("") + } + return diag.FromErr(equinix_errors.FormatFabricError(err)) + } + d.SetId(connectionRouteFilter.GetUuid()) + return setConnectionRouteFilterMap(d, connectionRouteFilter) +} + +func resourceCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*config.Config).NewFabricClientForSDK(d) + connectionId := d.Get("connection_id").(string) + routeFilterId := d.Get("route_filter_id").(string) + direction := d.Get("direction").(string) + + start := time.Now() + routeFilter, _, err := client.RouteFiltersApi. + AttachConnectionRouteFilter(ctx, routeFilterId, connectionId). + ConnectionRouteFiltersBase( + fabricv4.ConnectionRouteFiltersBase{ + Direction: fabricv4.ConnectionRouteFiltersBaseDirection(direction), + }, + ).Execute() + if err != nil { + return diag.FromErr(equinix_errors.FormatFabricError(err)) + } + + if err = d.Set("connection_id", connectionId); err != nil { + return diag.Errorf("error setting connection_id to state %s", err) + } + d.SetId(routeFilter.GetUuid()) + + createTimeout := d.Timeout(schema.TimeoutCreate) - 30*time.Second - time.Since(start) + if err = waitForStability(connectionId, d.Id(), meta, d, ctx, createTimeout); err != nil { + return diag.Errorf("error waiting for route filter (%s) to be attached to connection (%s): %s", d.Id(), connectionId, err) + } + + return resourceRead(ctx, d, meta) +} + +func resourceUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*config.Config).NewFabricClientForSDK(d) + connectionId := d.Get("connection_id").(string) + routeFilterId := d.Get("route_filter_id").(string) + oldDirection, newDirection := d.GetChange("direction") + if oldDirection.(string) == newDirection.(string) { + return diag.Diagnostics{} + } + + start := time.Now() + connectionRouteFilter, _, err := client.RouteFiltersApi. + AttachConnectionRouteFilter(ctx, routeFilterId, connectionId). + ConnectionRouteFiltersBase( + fabricv4.ConnectionRouteFiltersBase{ + Direction: fabricv4.ConnectionRouteFiltersBaseDirection(newDirection.(string)), + }, + ).Execute() + if err != nil { + return diag.FromErr(equinix_errors.FormatFabricError(err)) + } + + updateTimeout := d.Timeout(schema.TimeoutUpdate) - 30*time.Second - time.Since(start) + if err = waitForStability(routeFilterId, d.Id(), meta, d, ctx, updateTimeout); err != nil { + return diag.Errorf("error waiting for route filter policy (%s) on connection (%s) to be updated: %s", routeFilterId, connectionId, err) + } + + return setConnectionRouteFilterMap(d, connectionRouteFilter) +} + +func resourceDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + diags := diag.Diagnostics{} + client := meta.(*config.Config).NewFabricClientForSDK(d) + connectionId := d.Get("connection_id").(string) + + start := time.Now() + _, _, err := client.RouteFiltersApi.DetachConnectionRouteFilter(ctx, d.Id(), connectionId).Execute() + if err != nil { + if genericError, ok := err.(*fabricv4.GenericOpenAPIError); ok { + if fabricErrs, ok := genericError.Model().([]fabricv4.Error); ok { + // EQ-3142509 = Connection already deleted + if equinix_errors.HasErrorCode(fabricErrs, "EQ-3142509") { + return diags + } + } + } + return diag.FromErr(equinix_errors.FormatFabricError(err)) + } + + deleteTimeout := d.Timeout(schema.TimeoutDelete) - 30*time.Second - time.Since(start) + if err = WaitForDeletion(connectionId, d.Id(), meta, d, ctx, deleteTimeout); err != nil { + return diag.Errorf("error waiting for route filter (%s) to be detached from connection (%s): %s", d.Id(), connectionId, err) + } + return diags +} + +func waitForStability(connectionId, routeFilterId string, meta interface{}, d *schema.ResourceData, ctx context.Context, timeout time.Duration) error { + log.Printf("Waiting for route filter policy (%x) attachment to connection (%s) to be stable", connectionId, routeFilterId) + stateConf := &retry.StateChangeConf{ + Pending: []string{ + string(fabricv4.CONNECTIONROUTEFILTERDATAATTACHMENTSTATUS_ATTACHING), + }, + Target: []string{ + string(fabricv4.CONNECTIONROUTEFILTERDATAATTACHMENTSTATUS_ATTACHED), + string(fabricv4.CONNECTIONROUTEFILTERDATAATTACHMENTSTATUS_PENDING_BGP_CONFIGURATION), + }, + Refresh: func() (interface{}, string, error) { + client := meta.(*config.Config).NewFabricClientForSDK(d) + connectionRouteFilter, _, err := client.RouteFiltersApi.GetConnectionRouteFilterByUuid(ctx, routeFilterId, connectionId).Execute() + if err != nil { + return "", "", equinix_errors.FormatFabricError(err) + } + return connectionRouteFilter, string(connectionRouteFilter.GetAttachmentStatus()), nil + }, + Timeout: timeout, + Delay: 30 * time.Second, + MinTimeout: 30 * time.Second, + } + + _, err := stateConf.WaitForStateContext(ctx) + + return err +} + +func WaitForDeletion(connectionId, routeFilterId string, meta interface{}, d *schema.ResourceData, ctx context.Context, timeout time.Duration) error { + log.Printf("Waiting for route filter policy (%s) to be detached from connection (%s)", routeFilterId, connectionId) + stateConf := &retry.StateChangeConf{ + Pending: []string{ + string(fabricv4.CONNECTIONROUTEFILTERDATAATTACHMENTSTATUS_ATTACHED), + string(fabricv4.CONNECTIONROUTEFILTERDATAATTACHMENTSTATUS_DETACHING), + string(fabricv4.CONNECTIONROUTEFILTERDATAATTACHMENTSTATUS_PENDING_BGP_CONFIGURATION), + }, + Target: []string{ + string(fabricv4.CONNECTIONROUTEFILTERDATAATTACHMENTSTATUS_DETACHED), + }, + Refresh: func() (interface{}, string, error) { + client := meta.(*config.Config).NewFabricClientForSDK(d) + connectionRouteFilter, body, err := client.RouteFiltersApi.GetConnectionRouteFilterByUuid(ctx, routeFilterId, connectionId).Execute() + if err != nil { + if body.StatusCode >= 400 && body.StatusCode <= 499 { + // Already deleted resource + return connectionRouteFilter, string(fabricv4.ROUTEFILTERSTATE_DEPROVISIONED), nil + } + return "", "", equinix_errors.FormatFabricError(err) + } + return connectionRouteFilter, string(connectionRouteFilter.GetAttachmentStatus()), nil + }, + Timeout: timeout, + Delay: 30 * time.Second, + MinTimeout: 30 * time.Second, + } + + _, err := stateConf.WaitForStateContext(ctx) + + return err +} diff --git a/internal/resources/fabric/connection_route_filter/resource_schema.go b/internal/resources/fabric/connection_route_filter/resource_schema.go new file mode 100644 index 000000000..528628f7b --- /dev/null +++ b/internal/resources/fabric/connection_route_filter/resource_schema.go @@ -0,0 +1,49 @@ +package connection_route_filter + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceSchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "connection_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Equinix Assigned UUID of the Equinix Connection to attach the Route Filter Policy to", + }, + "route_filter_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Equinix Assigned UUID of the Route Filter Policy to attach to the Equinix Connection", + }, + "direction": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"INBOUND", "OUTBOUND"}, false), + Description: "Direction of the filtering of the attached Route Filter Policy", + }, + "type": { + Type: schema.TypeString, + Computed: true, + Description: "Route Filter Type. One of [ \"BGP_IPv4_PREFIX_FILTER\", \"BGP_IPv6_PREFIX_FILTER\" ] ", + }, + "href": { + Type: schema.TypeString, + Computed: true, + Description: "URI to the attached Route Filter Policy on the Connection", + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + Description: "Equinix Assigned ID for Route Filter Policy", + }, + "attachment_status": { + Type: schema.TypeString, + Computed: true, + Description: "Status of the Route Filter Policy attachment lifecycle", + }, + } +} diff --git a/internal/resources/fabric/connection_route_filter/resource_test.go b/internal/resources/fabric/connection_route_filter/resource_test.go new file mode 100644 index 000000000..59463ce4d --- /dev/null +++ b/internal/resources/fabric/connection_route_filter/resource_test.go @@ -0,0 +1,196 @@ +package connection_route_filter_test + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/equinix/terraform-provider-equinix/internal/acceptance" + "github.com/equinix/terraform-provider-equinix/internal/fabric/testing_helpers" + "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/connection_route_filter" + + "github.com/equinix/equinix-sdk-go/services/fabricv4" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" +) + +func TestAccFabricConnectionRouteFilter_PFCR(t *testing.T) { + ports := testing_helpers.GetFabricEnvPorts(t) + var portUuid string + if len(ports) > 0 { + portUuid = ports["pfcr"]["dot1q"][0].GetUuid() + } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.TestAccPreCheck(t); acceptance.TestAccPreCheckProviderConfigured(t) }, + Providers: acceptance.TestAccProviders, + CheckDestroy: CheckConnectionRouteFilterDelete, + Steps: []resource.TestStep{ + { + Config: testAccFabricConnectionRouteFilterConfig(portUuid), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("equinix_fabric_connection_route_filter.test", "id"), + resource.TestCheckResourceAttrSet("equinix_fabric_connection_route_filter.test", "connection_id"), + resource.TestCheckResourceAttrSet("equinix_fabric_connection_route_filter.test", "route_filter_id"), + + resource.TestCheckResourceAttr( + "equinix_fabric_connection_route_filter.test", "direction", "INBOUND"), + resource.TestCheckResourceAttr( + "equinix_fabric_connection_route_filter.test", "type", "BGP_IPv4_PREFIX_FILTER"), + resource.TestCheckResourceAttr( + "equinix_fabric_connection_route_filter.test", "attachment_status", string(fabricv4.CONNECTIONROUTEFILTERDATAATTACHMENTSTATUS_PENDING_BGP_CONFIGURATION)), + resource.TestCheckResourceAttrSet("data.equinix_fabric_connection_route_filter.test", "id"), + resource.TestCheckResourceAttrSet("data.equinix_fabric_connection_route_filter.test", "connection_id"), + resource.TestCheckResourceAttrSet("data.equinix_fabric_connection_route_filter.test", "route_filter_id"), + + resource.TestCheckResourceAttr( + "data.equinix_fabric_connection_route_filter.test", "direction", "INBOUND"), + resource.TestCheckResourceAttr( + "data.equinix_fabric_connection_route_filter.test", "type", "BGP_IPv4_PREFIX_FILTER"), + resource.TestCheckResourceAttr( + "data.equinix_fabric_connection_route_filter.test", "attachment_status", string(fabricv4.CONNECTIONROUTEFILTERDATAATTACHMENTSTATUS_PENDING_BGP_CONFIGURATION)), + resource.TestCheckResourceAttrSet("data.equinix_fabric_connection_route_filters.test", "id"), + resource.TestCheckResourceAttrSet("data.equinix_fabric_connection_route_filters.test", "connection_id"), + resource.TestCheckResourceAttrSet("data.equinix_fabric_connection_route_filters.test", "data.0.uuid"), + + resource.TestCheckResourceAttr( + "data.equinix_fabric_connection_route_filters.test", "data.0.direction", "INBOUND"), + resource.TestCheckResourceAttr( + "data.equinix_fabric_connection_route_filters.test", "data.0.type", "BGP_IPv4_PREFIX_FILTER"), + resource.TestCheckResourceAttr( + "data.equinix_fabric_connection_route_filters.test", "data.0.attachment_status", string(fabricv4.CONNECTIONROUTEFILTERDATAATTACHMENTSTATUS_PENDING_BGP_CONFIGURATION)), + ), + ExpectNonEmptyPlan: false, + }, + }, + }) + +} + +func testAccFabricConnectionRouteFilterConfig(portUuid string) string { + return fmt.Sprintf(` + resource "equinix_fabric_cloud_router" "test" { + type = "XF_ROUTER" + name = "RF_CR_PFCR" + location { + metro_code = "DC" + } + package { + code = "STANDARD" + } + order { + purchase_order_number = "1-234567" + } + notifications { + type = "ALL" + emails = [ + "test@equinix.com", + "test1@equinix.com" + ] + } + project { + project_id = "291639000636552" + } + account { + account_number = 201257 + } + } + + resource "equinix_fabric_connection" "test" { + type = "IP_VC" + name = "RF_CR_Connection_PFCR" + notifications { + type = "ALL" + emails = ["test@equinix.com","test1@equinix.com"] + } + order { + purchase_order_number = "123485" + } + bandwidth = 50 + redundancy { + priority= "PRIMARY" + } + a_side { + access_point { + type = "CLOUD_ROUTER" + router { + uuid = equinix_fabric_cloud_router.test.id + } + } + } + project { + project_id = "291639000636552" + } + z_side { + access_point { + type = "COLO" + port{ + uuid = "%s" + } + link_protocol { + type= "DOT1Q" + vlan_tag= 2571 + } + location { + metro_code = "DC" + } + } + } + } + + resource "equinix_fabric_route_filter" "test" { + name = "rf_test_PFCR" + project { + project_id = "291639000636552" + } + type = "BGP_IPv4_PREFIX_FILTER" + description = "Route Filter Policy for X Purpose" + } + + resource "equinix_fabric_route_filter_rule" "test" { + route_filter_id = equinix_fabric_route_filter.test.id + name = "RF_Rule_PFCR" + prefix = "192.168.0.0/24" + prefix_match = "exact" + description = "Route Filter Rule for X Purpose" + } + + resource "equinix_fabric_connection_route_filter" "test" { + depends_on = [ equinix_fabric_route_filter_rule.test ] + connection_id = equinix_fabric_connection.test.id + route_filter_id = equinix_fabric_route_filter.test.id + direction = "INBOUND" + } + + data "equinix_fabric_connection_route_filter" "test" { + depends_on = [ equinix_fabric_connection_route_filter.test ] + connection_id = equinix_fabric_connection.test.id + route_filter_id = equinix_fabric_route_filter.test.id + } + + data "equinix_fabric_connection_route_filters" "test" { + depends_on = [ equinix_fabric_connection_route_filter.test ] + connection_id = equinix_fabric_connection.test.id + } + + `, portUuid) +} + +func CheckConnectionRouteFilterDelete(s *terraform.State) error { + ctx := context.Background() + for _, rs := range s.RootModule().Resources { + if rs.Type != "equinix_fabric_connection_route_filter" { + continue + } + + connectionId := rs.Primary.Attributes["connection_id"] + + err := connection_route_filter.WaitForDeletion(connectionId, rs.Primary.ID, acceptance.TestAccProvider.Meta(), &schema.ResourceData{}, ctx, 10*time.Minute) + if err != nil { + return fmt.Errorf("API call failed while waiting for resource deletion") + } + } + return nil +} From 905bb851172dc8f768f120b740fdfe9404309c03 Mon Sep 17 00:00:00 2001 From: thogarty <139183873+thogarty@users.noreply.github.com> Date: Mon, 21 Oct 2024 10:49:28 -0700 Subject: [PATCH 10/55] =?UTF-8?q?fix:=20Update=20Fabric=20Terraform=20Reso?= =?UTF-8?q?urces=20and=20Data=20sources=20for=20latest=20Fabric=20?= =?UTF-8?q?=E2=80=A6=20(#799)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Upgrade equinix-sdk-go to v0.46.0 * Update Port models to match changes in new Fabric SDK * Update SimplifiedAccount to match changes in new Fabric SDK --- equinix/data_source_fabric_port_acc_test.go | 14 +++-- equinix/resource_fabric_port.go | 6 +- go.mod | 2 +- go.sum | 4 +- .../schema/fabric_common_mapping_helpers.go | 55 ++++++------------- internal/fabric/testing_helpers/env_data.go | 2 +- .../fabric/connection/resource_test.go | 9 +-- .../resources/fabric/marketplace/models.go | 2 +- 8 files changed, 38 insertions(+), 56 deletions(-) diff --git a/equinix/data_source_fabric_port_acc_test.go b/equinix/data_source_fabric_port_acc_test.go index b23bcb7d5..f7a815355 100644 --- a/equinix/data_source_fabric_port_acc_test.go +++ b/equinix/data_source_fabric_port_acc_test.go @@ -2,17 +2,19 @@ package equinix_test import ( "fmt" - "github.com/equinix/equinix-sdk-go/services/fabricv4" - "github.com/equinix/terraform-provider-equinix/internal/fabric/testing_helpers" "testing" "github.com/equinix/terraform-provider-equinix/internal/acceptance" + "github.com/equinix/terraform-provider-equinix/internal/fabric/testing_helpers" + + "github.com/equinix/equinix-sdk-go/services/fabricv4" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) func TestAccDataSourceFabricPort_PNFV(t *testing.T) { ports := testing_helpers.GetFabricEnvPorts(t) - var port fabricv4.PortResponse + var port fabricv4.Port var portType, portState, portEncapsulationType, portRedundancyPriority string if len(ports) > 0 { port = ports["pnfv"]["dot1q"][0] @@ -65,7 +67,7 @@ func testDataSourceFabricPort(port_uuid string) string { func TestAccDataSourceFabricPorts_PNFV(t *testing.T) { ports := testing_helpers.GetFabricEnvPorts(t) - var port fabricv4.PortResponse + var port fabricv4.Port var portType, portState, portEncapsulationType, portRedundancyPriority string if len(ports) > 0 { port = ports["pnfv"]["dot1q"][0] @@ -120,7 +122,7 @@ func testDataSourceFabricPorts(port_name string) string { func TestAccDataSourceFabricPort_PPDS(t *testing.T) { ports := testing_helpers.GetFabricEnvPorts(t) - var port fabricv4.PortResponse + var port fabricv4.Port var portType, portState, portEncapsulationType, portRedundancyPriority string if len(ports) > 0 { port = ports["ppds"]["dot1q"][0] @@ -165,7 +167,7 @@ func TestAccDataSourceFabricPort_PPDS(t *testing.T) { func TestAccDataSourceFabricPorts_PPDS(t *testing.T) { ports := testing_helpers.GetFabricEnvPorts(t) - var port fabricv4.PortResponse + var port fabricv4.Port var portType, portState, portEncapsulationType, portRedundancyPriority string if len(ports) > 0 { port = ports["ppds"]["dot1q"][0] diff --git a/equinix/resource_fabric_port.go b/equinix/resource_fabric_port.go index 588b72687..85146f93f 100644 --- a/equinix/resource_fabric_port.go +++ b/equinix/resource_fabric_port.go @@ -368,11 +368,11 @@ func resourceFabricPortRead(ctx context.Context, d *schema.ResourceData, meta in return setFabricPortMap(d, port) } -func fabricPortMap(port *fabricv4.PortResponse) map[string]interface{} { +func fabricPortMap(port *fabricv4.Port) map[string]interface{} { operation := port.GetOperation() redundancy := port.GetRedundancy() account := port.GetAccount() - changelog := port.GetChangelog() + changelog := port.GetChangeLog() location := port.GetLocation() device := port.GetDevice() encapsulation := port.GetEncapsulation() @@ -398,7 +398,7 @@ func fabricPortMap(port *fabricv4.PortResponse) map[string]interface{} { } } -func setFabricPortMap(d *schema.ResourceData, port *fabricv4.PortResponse) diag.Diagnostics { +func setFabricPortMap(d *schema.ResourceData, port *fabricv4.Port) diag.Diagnostics { diags := diag.Diagnostics{} err := equinix_schema.SetMap(d, fabricPortMap(port)) if err != nil { diff --git a/go.mod b/go.mod index 6763c9fa5..45f5af140 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/equinix/terraform-provider-equinix go 1.22 require ( - github.com/equinix/equinix-sdk-go v0.45.0 + github.com/equinix/equinix-sdk-go v0.46.0 github.com/equinix/ne-go v1.17.0 github.com/equinix/oauth2-go v1.0.0 github.com/equinix/rest-go v1.3.0 diff --git a/go.sum b/go.sum index ba24b2df6..384ca31c6 100644 --- a/go.sum +++ b/go.sum @@ -39,8 +39,8 @@ github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= -github.com/equinix/equinix-sdk-go v0.45.0 h1:M1moRw4Zt/nkxskDqzslfBc5TgB+QbqBiqBEfPkhl/Y= -github.com/equinix/equinix-sdk-go v0.45.0/go.mod h1:hEb3XLaedz7xhl/dpPIS6eOIiXNPeqNiVoyDrT6paIg= +github.com/equinix/equinix-sdk-go v0.46.0 h1:ldQo4GtXNr+0XsThQJf/pUdx5wcLFe9QpLFtAwonqH8= +github.com/equinix/equinix-sdk-go v0.46.0/go.mod h1:hEb3XLaedz7xhl/dpPIS6eOIiXNPeqNiVoyDrT6paIg= github.com/equinix/ne-go v1.17.0 h1:+wZq0GNognpiTHTsBXtATOCphTFvnowF046NzQXj0n0= github.com/equinix/ne-go v1.17.0/go.mod h1:eHkkxM4nbTB7DZ9X9zGnwfYnxIJWIsU3aHA+FAoZ1EI= github.com/equinix/oauth2-go v1.0.0 h1:fHtAPGq82PdgtK5vEThs8Vwz6f7D/8SX4tE3NJu+KcU= diff --git a/internal/fabric/schema/fabric_common_mapping_helpers.go b/internal/fabric/schema/fabric_common_mapping_helpers.go index 387248bd1..986b93cb9 100644 --- a/internal/fabric/schema/fabric_common_mapping_helpers.go +++ b/internal/fabric/schema/fabric_common_mapping_helpers.go @@ -4,11 +4,10 @@ import ( "github.com/equinix/equinix-sdk-go/services/fabricv4" "github.com/equinix/terraform-provider-equinix/internal/converters" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "strconv" ) func OrderTerraformToGo(orderTerraform []interface{}) fabricv4.Order { - if orderTerraform == nil || len(orderTerraform) == 0 { + if len(orderTerraform) == 0 { return fabricv4.Order{} } var order fabricv4.Order @@ -50,39 +49,19 @@ func OrderGoToTerraform(order *fabricv4.Order) *schema.Set { return orderSet } -func AccountGoToTerraform[accountType *fabricv4.SimplifiedAccount | *fabricv4.SimplifiedAccountPortResponse](accountParam accountType) *schema.Set { - if accountParam == nil { +func AccountGoToTerraform(account *fabricv4.SimplifiedAccount) *schema.Set { + if account == nil { return nil } - - mappedAccount := map[string]interface{}{} - - switch account := (interface{})(accountParam).(type) { - case *fabricv4.SimplifiedAccount: - mappedAccount = map[string]interface{}{ - "account_number": int(account.GetAccountNumber()), - "account_name": account.GetAccountName(), - "org_id": int(account.GetOrgId()), - "organization_name": account.GetOrganizationName(), - "global_org_id": account.GetGlobalOrgId(), - "global_organization_name": account.GetGlobalOrganizationName(), - "global_cust_id": account.GetGlobalCustId(), - "ucm_id": account.GetUcmId(), - } - case *fabricv4.SimplifiedAccountPortResponse: - accountNumber, _ := strconv.Atoi(account.GetAccountNumber()) - orgId, _ := strconv.Atoi(account.GetOrgId()) - - mappedAccount = map[string]interface{}{ - "account_number": accountNumber, - "account_name": account.GetAccountName(), - "org_id": orgId, - "organization_name": account.GetOrganizationName(), - "global_org_id": account.GetGlobalOrgId(), - "global_organization_name": account.GetGlobalOrganizationName(), - "global_cust_id": account.GetGlobalCustId(), - "ucm_id": account.GetUcmId(), - } + mappedAccount := map[string]interface{}{ + "account_number": int(account.GetAccountNumber()), + "account_name": account.GetAccountName(), + "org_id": int(account.GetOrgId()), + "organization_name": account.GetOrganizationName(), + "global_org_id": account.GetGlobalOrgId(), + "global_organization_name": account.GetGlobalOrganizationName(), + "global_cust_id": account.GetGlobalCustId(), + "ucm_id": account.GetUcmId(), } accountSet := schema.NewSet( @@ -94,7 +73,7 @@ func AccountGoToTerraform[accountType *fabricv4.SimplifiedAccount | *fabricv4.Si } func NotificationsTerraformToGo(notificationsTerraform []interface{}) []fabricv4.SimplifiedNotification { - if notificationsTerraform == nil || len(notificationsTerraform) == 0 { + if len(notificationsTerraform) == 0 { return nil } notifications := make([]fabricv4.SimplifiedNotification, len(notificationsTerraform)) @@ -131,7 +110,7 @@ func NotificationsGoToTerraform(notifications []fabricv4.SimplifiedNotification) } func LocationTerraformToGo(locationList []interface{}) fabricv4.SimplifiedLocation { - if locationList == nil || len(locationList) == 0 { + if len(locationList) == 0 { return fabricv4.SimplifiedLocation{} } @@ -175,7 +154,7 @@ func LocationGoToTerraform(location *fabricv4.SimplifiedLocation) *schema.Set { } func LocationWithoutIBXTerraformToGo(locationList []interface{}) fabricv4.SimplifiedLocationWithoutIBX { - if locationList == nil || len(locationList) == 0 { + if len(locationList) == 0 { return fabricv4.SimplifiedLocationWithoutIBX{} } @@ -201,7 +180,7 @@ func LocationWithoutIBXGoToTerraform(location *fabricv4.SimplifiedLocationWithou } func ProjectTerraformToGo(projectTerraform []interface{}) fabricv4.Project { - if projectTerraform == nil || len(projectTerraform) == 0 { + if len(projectTerraform) == 0 { return fabricv4.Project{} } var project fabricv4.Project @@ -253,7 +232,7 @@ func ChangeLogGoToTerraform(changeLog *fabricv4.Changelog) *schema.Set { } func ErrorGoToTerraform(errors []fabricv4.Error) []interface{} { - if errors == nil || len(errors) == 0 { + if len(errors) == 0 { return nil } mappedErrors := make([]interface{}, len(errors)) diff --git a/internal/fabric/testing_helpers/env_data.go b/internal/fabric/testing_helpers/env_data.go index b0c02ed87..07cd5db86 100644 --- a/internal/fabric/testing_helpers/env_data.go +++ b/internal/fabric/testing_helpers/env_data.go @@ -14,7 +14,7 @@ const ( FabricSubscriptionEnvVar = "TF_ACC_FABRIC_MARKET_PLACE_SUBSCRIPTION_ID" ) -type EnvPorts map[string]map[string][]fabricv4.PortResponse +type EnvPorts map[string]map[string][]fabricv4.Port func GetFabricEnvPorts(t *testing.T) EnvPorts { var ports EnvPorts diff --git a/internal/resources/fabric/connection/resource_test.go b/internal/resources/fabric/connection/resource_test.go index f728fa2ee..93193f11e 100644 --- a/internal/resources/fabric/connection/resource_test.go +++ b/internal/resources/fabric/connection/resource_test.go @@ -3,13 +3,14 @@ package connection_test import ( "context" "fmt" + "testing" + "time" + "github.com/equinix/terraform-provider-equinix/internal/acceptance" "github.com/equinix/terraform-provider-equinix/internal/fabric/testing_helpers" "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/connection" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "testing" - "time" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" ) @@ -325,7 +326,7 @@ func testAccFabricCreateCloudRouter2PortConnectionConfig(name, portUuid string) resource "equinix_fabric_cloud_router" "this" { type = "XF_ROUTER" - name = "Test_PFCR" + name = "Conn_Test_PFCR" location{ metro_code = "SV" } diff --git a/internal/resources/fabric/marketplace/models.go b/internal/resources/fabric/marketplace/models.go index 5e8c328d9..6eda1574e 100644 --- a/internal/resources/fabric/marketplace/models.go +++ b/internal/resources/fabric/marketplace/models.go @@ -21,7 +21,7 @@ func subscriptionMap(subs *fabricv4.SubscriptionResponse) map[string]interface{} subscription := make(map[string]interface{}) subscription["href"] = subs.GetHref() subscription["uuid"] = subs.GetUuid() - subscription["status"] = subs.GetStatus() + subscription["status"] = subs.GetState() subscription["marketplace"] = subs.GetMarketplace() subscription["offer_type"] = subs.GetOfferType() subscription["is_auto_renew"] = subs.GetIsAutoRenew() From 7d94286289bfcbde6e0f3931871641bac410bd13 Mon Sep 17 00:00:00 2001 From: kpdhulipala <84343462+kpdhulipala@users.noreply.github.com> Date: Mon, 21 Oct 2024 12:01:53 -0700 Subject: [PATCH 11/55] feat: Update docs for New device type - "Aviatrix Transit Edge" under Aviatrix Vendor (#801) feat: Update docs for New device type - "Aviatrix Transit Edge" under Aviatrix Vendor --- docs/resources/network_device.md | 36 +++++++++++++++++++ .../Aviatrix_Transit_Edge.tf | 33 +++++++++++++++++ templates/resources/network_device.md.tmpl | 2 ++ 3 files changed, 71 insertions(+) create mode 100644 examples/resources/equinix_network_device/Aviatrix_Transit_Edge.tf diff --git a/docs/resources/network_device.md b/docs/resources/network_device.md index 747b3c572..3e9d2ad45 100644 --- a/docs/resources/network_device.md +++ b/docs/resources/network_device.md @@ -397,6 +397,42 @@ resource "equinix_network_device" "panw-cluster" { } ``` +```terraform +# Create self configured single Aviatrix Transit Edge device with cloud init file + +data "equinix_network_account" "sv" { + metro_code = "SV" +} + +variable "filepath" { default = "cloudInitFileFolder/TF-AVX-cloud-init-file.txt" } + +resource "equinix_network_file" "aviatrix-cloudinit-file" { + file_name = "TF-AVX-cloud-init-file.txt" + content = file("${path.module}/${var.filepath}") + metro_code = data.equinix_network_account.sv.metro_code + device_type_code = "AVIATRIX_TRANSIT_EDGE" + process_type = "CLOUD_INIT" + self_managed = true + byol = true +} + +resource "equinix_network_device" "aviatrix-transit-edge-single" { + name = "tf-aviatrix" + metro_code = data.equinix_network_account.sv.metro_code + type_code = "AVIATRIX_TRANSIT_EDGE" + self_managed = true + byol = true + package_code = "STD" + notifications = ["john@equinix.com"] + term_length = 12 + account_number = data.equinix_network_account.sv.number + version = "7.2.a" + core_count = 2 + cloud_init_file_id = equinix_network_file.aviatrix-cloudinit-file.uuid + acl_template_id = "c06150ea-b604-4ad1-832a-d63936e9b938" +} +``` + ## Argument Reference The following arguments are supported: diff --git a/examples/resources/equinix_network_device/Aviatrix_Transit_Edge.tf b/examples/resources/equinix_network_device/Aviatrix_Transit_Edge.tf new file mode 100644 index 000000000..a5bfbf0aa --- /dev/null +++ b/examples/resources/equinix_network_device/Aviatrix_Transit_Edge.tf @@ -0,0 +1,33 @@ +# Create self configured single Aviatrix Transit Edge device with cloud init file + +data "equinix_network_account" "sv" { + metro_code = "SV" +} + +variable "filepath" { default = "cloudInitFileFolder/TF-AVX-cloud-init-file.txt" } + +resource "equinix_network_file" "aviatrix-cloudinit-file" { + file_name = "TF-AVX-cloud-init-file.txt" + content = file("${path.module}/${var.filepath}") + metro_code = data.equinix_network_account.sv.metro_code + device_type_code = "AVIATRIX_TRANSIT_EDGE" + process_type = "CLOUD_INIT" + self_managed = true + byol = true +} + +resource "equinix_network_device" "aviatrix-transit-edge-single" { + name = "tf-aviatrix" + metro_code = data.equinix_network_account.sv.metro_code + type_code = "AVIATRIX_TRANSIT_EDGE" + self_managed = true + byol = true + package_code = "STD" + notifications = ["john@equinix.com"] + term_length = 12 + account_number = data.equinix_network_account.sv.number + version = "7.2.a" + core_count = 2 + cloud_init_file_id = equinix_network_file.aviatrix-cloudinit-file.uuid + acl_template_id = "c06150ea-b604-4ad1-832a-d63936e9b938" +} diff --git a/templates/resources/network_device.md.tmpl b/templates/resources/network_device.md.tmpl index 6fa571ce9..bce28fa68 100644 --- a/templates/resources/network_device.md.tmpl +++ b/templates/resources/network_device.md.tmpl @@ -40,6 +40,8 @@ In addition to management modes, there are two software license modes available: {{tffile "examples/resources/equinix_network_device/example_9.tf"}} +{{tffile "examples/resources/equinix_network_device/Aviatrix_Transit_Edge.tf"}} + ## Argument Reference The following arguments are supported: From 1805f8d89661a159635242ba2d253cfb833a0f07 Mon Sep 17 00:00:00 2001 From: kpdhulipala <84343462+kpdhulipala@users.noreply.github.com> Date: Wed, 23 Oct 2024 07:54:36 -0700 Subject: [PATCH 12/55] feat: Add optional attribute tier in Create Virtual Device request for C8000V and C8000V SDWAN (#771) feat: Add optional attribute tier in Create Virtual Device request for C8000V and C8000V SDWAN --- docs/data-sources/network_device.md | 1 + docs/resources/network_device.md | 60 +++++++++++++++++++ equinix/data_source_network_device.go | 8 +++ equinix/resource_network_device.go | 20 +++++++ .../c8000v_byol_with_bandwidth_throughput.tf | 27 +++++++++ .../c8000v_byol_with_bandwidth_tier.tf | 26 ++++++++ go.mod | 2 +- go.sum | 2 + templates/data-sources/network_device.md.tmpl | 1 + templates/resources/network_device.md.tmpl | 5 ++ 10 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 examples/resources/equinix_network_device/c8000v_byol_with_bandwidth_throughput.tf create mode 100644 examples/resources/equinix_network_device/c8000v_byol_with_bandwidth_tier.tf diff --git a/docs/data-sources/network_device.md b/docs/data-sources/network_device.md index 0bb9e3a87..21e5028d3 100644 --- a/docs/data-sources/network_device.md +++ b/docs/data-sources/network_device.md @@ -76,3 +76,4 @@ NOTE: Exactly one of either `uuid` or `name` must be specified. * `connectivity` - Device accessibility (INTERNET-ACCESS or PRIVATE or INTERNET-ACCESS-WITH-PRVT-MGMT) * `diverse_device_id` - diverse device uuid * `diverse_device_name` - Name of the device with diverse device UUID +* `tier` - Throughput Tier (applicable for C8000V, C8000V-SDWAN devices) diff --git a/docs/resources/network_device.md b/docs/resources/network_device.md index 3e9d2ad45..ebaea2d34 100644 --- a/docs/resources/network_device.md +++ b/docs/resources/network_device.md @@ -397,6 +397,65 @@ resource "equinix_network_device" "panw-cluster" { } ``` +```terraform +# Create C8000V BYOL device with bandwidth tier information + +data "equinix_network_account" "sv" { + metro_code = "SV" +} + +resource "equinix_network_device" "c8000v-byol-tier" { + name = "tf-c8000v-byol" + metro_code = data.equinix_network_account.sv.metro_code + type_code = "C8000V" + self_managed = true + byol = true + package_code = "VM100" + notifications = ["john@equinix.com", "marry@equinix.com", "fred@equinix.com"] + term_length = 12 + account_number = data.equinix_network_account.sv.number + version = "17.11.01a" + interface_count = 10 + core_count = 2 + tier = 1 + ssh_key { + username = "test" + key_name = "test-key" + } + acl_template_id = "0bff6e05-f0e7-44cd-804a-25b92b835f8b" +} +``` + +```terraform +# Create C8000V BYOL device with numeric bandwidth throughput information + +data "equinix_network_account" "sv" { + metro_code = "SV" +} + +resource "equinix_network_device" "c8000v-byol-throughput" { + name = "tf-c8000v-byol" + metro_code = data.equinix_network_account.sv.metro_code + type_code = "C8000V" + self_managed = true + byol = true + package_code = "VM100" + notifications = ["john@equinix.com", "marry@equinix.com", "fred@equinix.com"] + term_length = 12 + account_number = data.equinix_network_account.sv.number + version = "17.11.01a" + interface_count = 10 + core_count = 2 + throughput = "100" + throughput_unit = "Mbps" + ssh_key { + username = "test" + key_name = "test-key" + } + acl_template_id = "0bff6e05-f0e7-44cd-804a-25b92b835f8b" +} +``` + ```terraform # Create self configured single Aviatrix Transit Edge device with cloud init file @@ -444,6 +503,7 @@ The following arguments are supported: * `package_code` - (Required) Device software package code. * `version` - (Required) Device software software version. * `core_count` - (Required) Number of CPU cores used by device. (**NOTE: Use this field to resize your device. When resizing your HA devices, primary device will be upgraded first. If the upgrade failed, device will be automatically rolled back to the previous state with original core number.**) +* `tier` - (Optional, conflicts with `throughput`,`throughput_unit` ) Select bandwidth tier for your own license, i.e., `0` or `1` or `2` or `3`. Tiers applicable only for C8000V Autonomous or C8000V SDWAN (controller) device types. If not provided, tier is defaulted to '2'. * `term_length` - (Required) Device term length. * `self_managed` - (Optional) Boolean value that determines device management mode, i.e., `self-managed` or `Equinix-managed` (default). * `byol` - (Optional) Boolean value that determines device licensing mode, i.e., `bring your own license` or `subscription` (default). diff --git a/equinix/data_source_network_device.go b/equinix/data_source_network_device.go index 3e8e220b9..22d455a4a 100644 --- a/equinix/data_source_network_device.go +++ b/equinix/data_source_network_device.go @@ -295,6 +295,11 @@ func createDataSourceNetworkDeviceSchema() map[string]*schema.Schema { Computed: true, Description: neDeviceDescriptions["DiverseFromDeviceUUID"], }, + neDeviceSchemaNames["Tier"]: { + Type: schema.TypeInt, + Computed: true, + Description: neDeviceDescriptions["Tier"], + }, neDeviceSchemaNames["DiverseFromDeviceName"]: { Type: schema.TypeString, Computed: true, @@ -853,6 +858,9 @@ func updateDataSourceNetworkDeviceResource(primary *ne.Device, secondary *ne.Dev if err := d.Set(neDeviceSchemaNames["DiverseFromDeviceUUID"], primary.DiverseFromDeviceUUID); err != nil { return fmt.Errorf("error reading DiverseFromDeviceUUID: %s", err) } + if err := d.Set(neDeviceSchemaNames["Tier"], primary.Tier); err != nil { + return fmt.Errorf("error reading Tier: %s", err) + } if err := d.Set(neDeviceSchemaNames["DiverseFromDeviceName"], primary.DiverseFromDeviceName); err != nil { return fmt.Errorf("error reading DiverseFromDeviceName: %s", err) } diff --git a/equinix/resource_network_device.go b/equinix/resource_network_device.go index 813202dfb..87ed33fe7 100644 --- a/equinix/resource_network_device.go +++ b/equinix/resource_network_device.go @@ -70,6 +70,7 @@ var neDeviceSchemaNames = map[string]string{ "Connectivity": "connectivity", "DiverseFromDeviceUUID": "diverse_device_id", "DiverseFromDeviceName": "diverse_device_name", + "Tier": "tier", } var neDeviceDescriptions = map[string]string{ @@ -119,6 +120,7 @@ var neDeviceDescriptions = map[string]string{ "ProjectID": "The unique identifier of Project Resource to which device is scoped to", "DiverseFromDeviceUUID": "Unique ID of an existing device", "DiverseFromDeviceName": "Diverse Device Name of an existing device", + "Tier": "Bandwidth Tiers", } var neDeviceInterfaceSchemaNames = map[string]string{ @@ -439,6 +441,15 @@ func createNetworkDeviceSchema() map[string]*schema.Schema { ConflictsWith: []string{neDeviceSchemaNames["Secondary"]}, Description: neDeviceDescriptions["DiverseFromDeviceUUID"], }, + neDeviceSchemaNames["Tier"]: { + Type: schema.TypeInt, + ForceNew: true, + Computed: true, + Optional: true, + ValidateFunc: validation.IntInSlice([]int{0, 1, 2, 3}), + ConflictsWith: []string{neDeviceSchemaNames["Throughput"], neDeviceSchemaNames["ThroughputUnit"]}, + Description: neDeviceDescriptions["Tier"], + }, neDeviceSchemaNames["DiverseFromDeviceName"]: { Type: schema.TypeString, Computed: true, @@ -1137,6 +1148,9 @@ func createNetworkDevices(d *schema.ResourceData) (*ne.Device, *ne.Device) { if v, ok := d.GetOk(neDeviceSchemaNames["ProjectID"]); ok { primary.ProjectID = ne.String(v.(string)) } + if v, ok := d.GetOk(neDeviceSchemaNames["Tier"]); ok { + primary.Tier = ne.Int(v.(int)) + } if v, ok := d.GetOk(neDeviceSchemaNames["DiverseFromDeviceUUID"]); ok { primary.DiverseFromDeviceUUID = ne.String(v.(string)) } @@ -1231,6 +1245,9 @@ func updateNetworkDeviceResource(primary *ne.Device, secondary *ne.Device, d *sc if err := d.Set(neDeviceSchemaNames["Name"], primary.Name); err != nil { return fmt.Errorf("error reading Name: %s", err) } + if err := d.Set(neDeviceSchemaNames["Tier"], primary.Tier); err != nil { + return fmt.Errorf("error reading Tier: %s", err) + } if err := d.Set(neDeviceSchemaNames["ProjectID"], primary.ProjectID); err != nil { return fmt.Errorf("error reading ProjectID: %s", err) } @@ -1406,6 +1423,9 @@ func expandNetworkDeviceSecondary(devices []interface{}) *ne.Device { if v, ok := device[neDeviceSchemaNames["Name"]]; ok && !isEmpty(v) { transformed.Name = ne.String(v.(string)) } + if v, ok := device[neDeviceSchemaNames["Tier"]]; ok && !isEmpty(v) { + transformed.Tier = ne.Int(v.(int)) + } if v, ok := device[neDeviceSchemaNames["ProjectID"]]; ok && !isEmpty(v) { transformed.ProjectID = ne.String(v.(string)) } diff --git a/examples/resources/equinix_network_device/c8000v_byol_with_bandwidth_throughput.tf b/examples/resources/equinix_network_device/c8000v_byol_with_bandwidth_throughput.tf new file mode 100644 index 000000000..8ccce6b0d --- /dev/null +++ b/examples/resources/equinix_network_device/c8000v_byol_with_bandwidth_throughput.tf @@ -0,0 +1,27 @@ +# Create C8000V BYOL device with numeric bandwidth throughput information + +data "equinix_network_account" "sv" { + metro_code = "SV" +} + +resource "equinix_network_device" "c8000v-byol-throughput" { + name = "tf-c8000v-byol" + metro_code = data.equinix_network_account.sv.metro_code + type_code = "C8000V" + self_managed = true + byol = true + package_code = "VM100" + notifications = ["john@equinix.com", "marry@equinix.com", "fred@equinix.com"] + term_length = 12 + account_number = data.equinix_network_account.sv.number + version = "17.11.01a" + interface_count = 10 + core_count = 2 + throughput = "100" + throughput_unit = "Mbps" + ssh_key { + username = "test" + key_name = "test-key" + } + acl_template_id = "0bff6e05-f0e7-44cd-804a-25b92b835f8b" +} \ No newline at end of file diff --git a/examples/resources/equinix_network_device/c8000v_byol_with_bandwidth_tier.tf b/examples/resources/equinix_network_device/c8000v_byol_with_bandwidth_tier.tf new file mode 100644 index 000000000..eddcf1039 --- /dev/null +++ b/examples/resources/equinix_network_device/c8000v_byol_with_bandwidth_tier.tf @@ -0,0 +1,26 @@ +# Create C8000V BYOL device with bandwidth tier information + +data "equinix_network_account" "sv" { + metro_code = "SV" +} + +resource "equinix_network_device" "c8000v-byol-tier" { + name = "tf-c8000v-byol" + metro_code = data.equinix_network_account.sv.metro_code + type_code = "C8000V" + self_managed = true + byol = true + package_code = "VM100" + notifications = ["john@equinix.com", "marry@equinix.com", "fred@equinix.com"] + term_length = 12 + account_number = data.equinix_network_account.sv.number + version = "17.11.01a" + interface_count = 10 + core_count = 2 + tier = 1 + ssh_key { + username = "test" + key_name = "test-key" + } + acl_template_id = "0bff6e05-f0e7-44cd-804a-25b92b835f8b" +} \ No newline at end of file diff --git a/go.mod b/go.mod index 45f5af140..99eb76abc 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.22 require ( github.com/equinix/equinix-sdk-go v0.46.0 - github.com/equinix/ne-go v1.17.0 + github.com/equinix/ne-go v1.18.0 github.com/equinix/oauth2-go v1.0.0 github.com/equinix/rest-go v1.3.0 github.com/google/uuid v1.6.0 diff --git a/go.sum b/go.sum index 384ca31c6..9ebfb0925 100644 --- a/go.sum +++ b/go.sum @@ -43,6 +43,8 @@ github.com/equinix/equinix-sdk-go v0.46.0 h1:ldQo4GtXNr+0XsThQJf/pUdx5wcLFe9QpLF github.com/equinix/equinix-sdk-go v0.46.0/go.mod h1:hEb3XLaedz7xhl/dpPIS6eOIiXNPeqNiVoyDrT6paIg= github.com/equinix/ne-go v1.17.0 h1:+wZq0GNognpiTHTsBXtATOCphTFvnowF046NzQXj0n0= github.com/equinix/ne-go v1.17.0/go.mod h1:eHkkxM4nbTB7DZ9X9zGnwfYnxIJWIsU3aHA+FAoZ1EI= +github.com/equinix/ne-go v1.18.0 h1:5az4ai39y1XLNOq3+qQVT9wFG7BmaQfj941MNqqouhk= +github.com/equinix/ne-go v1.18.0/go.mod h1:eHkkxM4nbTB7DZ9X9zGnwfYnxIJWIsU3aHA+FAoZ1EI= github.com/equinix/oauth2-go v1.0.0 h1:fHtAPGq82PdgtK5vEThs8Vwz6f7D/8SX4tE3NJu+KcU= github.com/equinix/oauth2-go v1.0.0/go.mod h1:4pulXvUNMktJlewLPnUeJyMW52iCoF1aM+A/Z5xY1ws= github.com/equinix/rest-go v1.3.0 h1:m38scYTOfV6N+gcrwchgVDutDffYd+QoYCMm9Jn6jyk= diff --git a/templates/data-sources/network_device.md.tmpl b/templates/data-sources/network_device.md.tmpl index 47a4f8330..3d5e9f753 100644 --- a/templates/data-sources/network_device.md.tmpl +++ b/templates/data-sources/network_device.md.tmpl @@ -70,3 +70,4 @@ NOTE: Exactly one of either `uuid` or `name` must be specified. * `connectivity` - Device accessibility (INTERNET-ACCESS or PRIVATE or INTERNET-ACCESS-WITH-PRVT-MGMT) * `diverse_device_id` - diverse device uuid * `diverse_device_name` - Name of the device with diverse device UUID +* `tier` - Throughput Tier (applicable for C8000V, C8000V-SDWAN devices) diff --git a/templates/resources/network_device.md.tmpl b/templates/resources/network_device.md.tmpl index bce28fa68..cd8bc862e 100644 --- a/templates/resources/network_device.md.tmpl +++ b/templates/resources/network_device.md.tmpl @@ -40,6 +40,10 @@ In addition to management modes, there are two software license modes available: {{tffile "examples/resources/equinix_network_device/example_9.tf"}} +{{tffile "examples/resources/equinix_network_device/c8000v_byol_with_bandwidth_tier.tf"}} + +{{tffile "examples/resources/equinix_network_device/c8000v_byol_with_bandwidth_throughput.tf"}} + {{tffile "examples/resources/equinix_network_device/Aviatrix_Transit_Edge.tf"}} ## Argument Reference @@ -53,6 +57,7 @@ The following arguments are supported: * `package_code` - (Required) Device software package code. * `version` - (Required) Device software software version. * `core_count` - (Required) Number of CPU cores used by device. (**NOTE: Use this field to resize your device. When resizing your HA devices, primary device will be upgraded first. If the upgrade failed, device will be automatically rolled back to the previous state with original core number.**) +* `tier` - (Optional, conflicts with `throughput`,`throughput_unit` ) Select bandwidth tier for your own license, i.e., `0` or `1` or `2` or `3`. Tiers applicable only for C8000V Autonomous or C8000V SDWAN (controller) device types. If not provided, tier is defaulted to '2'. * `term_length` - (Required) Device term length. * `self_managed` - (Optional) Boolean value that determines device management mode, i.e., `self-managed` or `Equinix-managed` (default). * `byol` - (Optional) Boolean value that determines device licensing mode, i.e., `bring your own license` or `subscription` (default). From 0206851f3f959fb4afaee18cca3ab8f7ad2af786 Mon Sep 17 00:00:00 2001 From: Charles Treatman Date: Wed, 23 Oct 2024 11:52:20 -0500 Subject: [PATCH 13/55] refactor: finish removing packngo from metal_port code (#789) Some packngo usage was missed in #709. This completely removes packngo from metal_port, as well as calls to `FriendlyError` because that function has no impact for non-packngo errors. --- internal/resources/metal/port/helpers.go | 17 ++++++++++++++--- internal/resources/metal/port/resource_test.go | 12 ++++-------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/internal/resources/metal/port/helpers.go b/internal/resources/metal/port/helpers.go index ded05ded2..6fb07bee6 100644 --- a/internal/resources/metal/port/helpers.go +++ b/internal/resources/metal/port/helpers.go @@ -15,7 +15,6 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/packethost/packngo" "github.com/pkg/errors" ) @@ -196,8 +195,8 @@ func createAndWaitForBatch(ctx context.Context, start time.Time, cpr *ClientPort stateChangeConf := &retry.StateChangeConf{ Delay: 5 * time.Second, - Pending: []string{string(packngo.VLANAssignmentBatchQueued), string(packngo.VLANAssignmentBatchInProgress)}, - Target: []string{string(packngo.VLANAssignmentBatchCompleted)}, + Pending: []string{string(metalv1.PORTVLANASSIGNMENTBATCHSTATE_QUEUED), string(metalv1.PORTVLANASSIGNMENTBATCHSTATE_IN_PROGRESS)}, + Target: []string{string(metalv1.PORTVLANASSIGNMENTBATCHSTATE_COMPLETED)}, MinTimeout: 5 * time.Second, Timeout: ctxTimeout - time.Since(start) - 30*time.Second, Refresh: func() (result interface{}, state string, err error) { @@ -243,6 +242,9 @@ func updateNativeVlan(ctx context.Context, cpr *ClientPortResource) error { } func processBondAction(ctx context.Context, cpr *ClientPortResource, actionIsBond bool) error { + // There's no good alternative to GetOkExists until metal_port + // is converted to terraform-plugin-framework + // nolint:staticcheck wantsBondedRaw, wantsBondedOk := cpr.Resource.GetOkExists("bonded") wantsBonded := wantsBondedRaw.(bool) // only act if the necessary action is the one specified in doBond @@ -285,6 +287,9 @@ func makeDisbond(ctx context.Context, cpr *ClientPortResource) error { } func convertToL2(ctx context.Context, cpr *ClientPortResource) error { + // There's no good alternative to GetOkExists until metal_port + // is converted to terraform-plugin-framework + // nolint:staticcheck l2, l2Ok := cpr.Resource.GetOkExists("layer2") isLayer2 := slices.Contains(l2Types, cpr.Port.GetNetworkType()) @@ -299,6 +304,9 @@ func convertToL2(ctx context.Context, cpr *ClientPortResource) error { } func convertToL3(ctx context.Context, cpr *ClientPortResource) error { + // There's no good alternative to GetOkExists until metal_port + // is converted to terraform-plugin-framework + // nolint:staticcheck l2, l2Ok := cpr.Resource.GetOkExists("layer2") isLayer2 := slices.Contains(l2Types, cpr.Port.GetNetworkType()) @@ -324,6 +332,9 @@ func portSanityChecks(_ context.Context, cpr *ClientPortResource) error { isBondPort := cpr.Port.GetType() == "NetworkBondPort" // Constraint: Only bond ports have layer2 mode + // There's no good alternative to GetOkExists until metal_port + // is converted to terraform-plugin-framework + // nolint:staticcheck l2Raw, l2Ok := cpr.Resource.GetOkExists("layer2") if !isBondPort && l2Ok { return fmt.Errorf("layer2 flag can be set only for bond ports") diff --git a/internal/resources/metal/port/resource_test.go b/internal/resources/metal/port/resource_test.go index 1766a8c74..ece2d33f4 100644 --- a/internal/resources/metal/port/resource_test.go +++ b/internal/resources/metal/port/resource_test.go @@ -12,11 +12,9 @@ import ( "github.com/equinix/terraform-provider-equinix/internal/config" "github.com/equinix/terraform-provider-equinix/internal/resources/metal/port" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" - "github.com/packethost/packngo" ) var ( @@ -400,10 +398,8 @@ func testAccWaitForPortActive(deviceName, portName string) resource.ImportStateI } meta := acceptance.TestAccProvider.Meta() - rd := new(schema.ResourceData) - meta.(*config.Config).AddModuleToMetalUserAgent(rd) - client := meta.(*config.Config).Metal - device, _, err := client.Devices.Get(rs.Primary.ID, &packngo.GetOptions{Includes: []string{"ports"}}) + client := meta.(*config.Config).NewMetalClientForTesting() + device, _, err := client.DevicesApi.FindDeviceById(context.Background(), rs.Primary.ID).Include([]string{"ports"}).Execute() if err != nil { return "", fmt.Errorf("error while fetching device with Id [%s], error: %w", rs.Primary.ID, err) } @@ -415,8 +411,8 @@ func testAccWaitForPortActive(deviceName, portName string) resource.ImportStateI } for _, port := range device.NetworkPorts { - if port.Name == portName { - return port.ID, nil + if port.GetName() == portName { + return port.GetId(), nil } } From 51708f2f91a1dae4ba3285c0e17517ea408356ad Mon Sep 17 00:00:00 2001 From: srushti-patl Date: Wed, 16 Oct 2024 13:43:41 -0700 Subject: [PATCH 14/55] feat: Adding Fabric Service Token Resource and Data Source --- equinix/provider.go | 54 +++++++++---------- .../fabric/service_token/resource_schema.go | 2 - 2 files changed, 26 insertions(+), 30 deletions(-) diff --git a/equinix/provider.go b/equinix/provider.go index bf38b2592..cb962d277 100644 --- a/equinix/provider.go +++ b/equinix/provider.go @@ -88,8 +88,6 @@ func Provider() *schema.Provider { "equinix_fabric_routing_protocol": dataSourceRoutingProtocol(), "equinix_fabric_connection": fabric_connection.DataSource(), "equinix_fabric_connections": fabric_connection.DataSourceSearch(), - "equinix_fabric_connection_route_filter": fabric_connection_route_filter.DataSource(), - "equinix_fabric_connection_route_filters": fabric_connection_route_filter.DataSourceGetAllRules(), "equinix_fabric_cloud_router": dataSourceFabricCloudRouter(), "equinix_fabric_cloud_routers": dataSourceFabricGetCloudRouters(), "equinix_fabric_market_place_subscription": fabric_market_place_subscription.DataSourceFabricMarketplaceSubscription(), @@ -128,34 +126,34 @@ func Provider() *schema.Provider { "equinix_metal_vrf": vrf.DataSource(), }, ResourcesMap: map[string]*schema.Resource{ - "equinix_fabric_network": fabric_network.Resource(), - "equinix_fabric_cloud_router": resourceFabricCloudRouter(), - "equinix_fabric_connection": fabric_connection.Resource(), + "equinix_fabric_network": fabric_network.Resource(), + "equinix_fabric_cloud_router": resourceFabricCloudRouter(), + "equinix_fabric_connection": fabric_connection.Resource(), "equinix_fabric_connection_route_filter": fabric_connection_route_filter.Resource(), - "equinix_fabric_route_filter": fabric_route_filter.Resource(), - "equinix_fabric_route_filter_rule": fabric_route_filter_rule.Resource(), - "equinix_fabric_routing_protocol": resourceFabricRoutingProtocol(), - "equinix_fabric_service_profile": resourceFabricServiceProfile(), + "equinix_fabric_route_filter": fabric_route_filter.Resource(), + "equinix_fabric_route_filter_rule": fabric_route_filter_rule.Resource(), + "equinix_fabric_routing_protocol": resourceFabricRoutingProtocol(), + "equinix_fabric_service_profile": resourceFabricServiceProfile(), "equinix_fabric_service_token": fabric_service_token.Resource(), - "equinix_network_device": resourceNetworkDevice(), - "equinix_network_ssh_user": resourceNetworkSSHUser(), - "equinix_network_bgp": resourceNetworkBGP(), - "equinix_network_ssh_key": resourceNetworkSSHKey(), - "equinix_network_acl_template": resourceNetworkACLTemplate(), - "equinix_network_device_link": resourceNetworkDeviceLink(), - "equinix_network_file": resourceNetworkFile(), - "equinix_metal_user_api_key": resourceMetalUserAPIKey(), - "equinix_metal_project_api_key": resourceMetalProjectAPIKey(), - "equinix_metal_device": metal_device.Resource(), - "equinix_metal_device_network_type": resourceMetalDeviceNetworkType(), - "equinix_metal_port": metal_port.Resource(), - "equinix_metal_reserved_ip_block": resourceMetalReservedIPBlock(), - "equinix_metal_ip_attachment": resourceMetalIPAttachment(), - "equinix_metal_spot_market_request": resourceMetalSpotMarketRequest(), - "equinix_metal_virtual_circuit": virtual_circuit.Resource(), - "equinix_metal_vrf": vrf.Resource(), - "equinix_metal_bgp_session": resourceMetalBGPSession(), - "equinix_metal_port_vlan_attachment": resourceMetalPortVlanAttachment(), + "equinix_network_device": resourceNetworkDevice(), + "equinix_network_ssh_user": resourceNetworkSSHUser(), + "equinix_network_bgp": resourceNetworkBGP(), + "equinix_network_ssh_key": resourceNetworkSSHKey(), + "equinix_network_acl_template": resourceNetworkACLTemplate(), + "equinix_network_device_link": resourceNetworkDeviceLink(), + "equinix_network_file": resourceNetworkFile(), + "equinix_metal_user_api_key": resourceMetalUserAPIKey(), + "equinix_metal_project_api_key": resourceMetalProjectAPIKey(), + "equinix_metal_device": metal_device.Resource(), + "equinix_metal_device_network_type": resourceMetalDeviceNetworkType(), + "equinix_metal_port": metal_port.Resource(), + "equinix_metal_reserved_ip_block": resourceMetalReservedIPBlock(), + "equinix_metal_ip_attachment": resourceMetalIPAttachment(), + "equinix_metal_spot_market_request": resourceMetalSpotMarketRequest(), + "equinix_metal_virtual_circuit": virtual_circuit.Resource(), + "equinix_metal_vrf": vrf.Resource(), + "equinix_metal_bgp_session": resourceMetalBGPSession(), + "equinix_metal_port_vlan_attachment": resourceMetalPortVlanAttachment(), }, ProviderMetaSchema: map[string]*schema.Schema{ "module_name": { diff --git a/internal/resources/fabric/service_token/resource_schema.go b/internal/resources/fabric/service_token/resource_schema.go index 793ced695..0b9b5f39e 100644 --- a/internal/resources/fabric/service_token/resource_schema.go +++ b/internal/resources/fabric/service_token/resource_schema.go @@ -111,14 +111,12 @@ func serviceTokenConnectionSch() *schema.Resource { Type: schema.TypeBool, Optional: true, Computed: true, - Default: true, Description: "Authorization to connect remotely", }, "allow_custom_bandwidth": { Type: schema.TypeBool, Optional: true, Computed: true, - Default: true, Description: "Allow custom bandwidth value", }, "bandwidth_limit": { From ed4acc03f08ef38a97dd41ec49dda924f050c60a Mon Sep 17 00:00:00 2001 From: srushti-patl Date: Sun, 27 Oct 2024 23:09:40 -0700 Subject: [PATCH 15/55] feat: Adding doc changes for Service Token resource and data sources --- docs/data-sources/fabric_service_token.md | 350 +++++++++++++++ docs/data-sources/fabric_service_tokens.md | 405 ++++++++++++++++++ docs/resources/fabric_service_token.md | 397 +++++++++++++++++ .../data-source.tf | 35 ++ .../data-source.tf | 53 +++ .../fabric/service_token/datasources.go | 12 +- .../resources/fabric/service_token/models.go | 109 ++--- .../fabric/service_token/resource.go | 3 +- .../fabric/service_token/resource_schema.go | 8 +- .../resources/fabric_service_token.md.tmpl | 23 + 10 files changed, 1317 insertions(+), 78 deletions(-) create mode 100644 docs/data-sources/fabric_service_token.md create mode 100644 docs/data-sources/fabric_service_tokens.md create mode 100644 docs/resources/fabric_service_token.md create mode 100644 examples/data-sources/equinix_fabric_service_token/data-source.tf create mode 100644 examples/data-sources/equinix_fabric_service_tokens/data-source.tf create mode 100644 templates/resources/fabric_service_token.md.tmpl diff --git a/docs/data-sources/fabric_service_token.md b/docs/data-sources/fabric_service_token.md new file mode 100644 index 000000000..136996f62 --- /dev/null +++ b/docs/data-sources/fabric_service_token.md @@ -0,0 +1,350 @@ +--- +subcategory: "Fabric" +--- + +# equinix_fabric_service_token (Data Source) + +Fabric V4 API compatible data resource that allow user to fetch service token for a given UUID + +Additional documentation: +* Getting Started: https://docs.equinix.com/en-us/Content/Interconnection/Fabric/service%20tokens/Fabric-Service-Tokens.htm +* API: https://docs.equinix.com/en-us/Content/KnowledgeCenter/Fabric/GettingStarted/Integrating-with-Fabric-V4-APIs/ConnectUsingServiceToken.htm + +## Example Usage + +```terraform +data "equinix_fabric_service_token" "service-token" { + uuid = "" +} + +output "id" { + value = data.equinix_fabric_service_token.service-token.id +} + +output "type" { + value = data.equinix_fabric_service_token.service-token.type +} + +output "expiration_date_time" { + value = data.equinix_fabric_service_token.service-token.expiration_date_time +} + +output "supported_bandwidths" { + value = data.equinix_fabric_service_token.service-token.service_token_connection.0.supported_bandwidths +} + +output "virtual_device_type" { + value = data.equinix_fabric_service_token.service-token.service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.type +} + +output "virtual_device_uuid" { + value = data.equinix_fabric_service_token.service-token.service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid +} + +output "interface_type" { + value = data.equinix_fabric_service_token.service-token.service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.type +} + +output "interface_uuid" { + value = data.equinix_fabric_service_token.service-token.service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id +} +``` + + +## Schema + +### Required + +- `uuid` (String) Equinix-assigned service token identifier + +### Read-Only + +- `account` (Set of Object) Customer account information that is associated with this service token (see [below for nested schema](#nestedatt--account)) +- `change_log` (Set of Object) Captures connection lifecycle change information (see [below for nested schema](#nestedatt--change_log)) +- `description` (String) Optional Description to the Service Token you will be creating +- `expiration_date_time` (String) Expiration date and time of the service token; 2020-11-06T07:00:00Z +- `href` (String) An absolute URL that is the subject of the link's context. +- `id` (String) The ID of this resource. +- `issuer_side` (String) Information about token side; ASIDE, ZSIDE +- `name` (String) Name of the Service Token +- `notifications` (Set of Object) Preferences for notifications on Service Token configuration or status changes (see [below for nested schema](#nestedatt--notifications)) +- `project` (Set of Object) Project information (see [below for nested schema](#nestedatt--project)) +- `service_token_connection` (Set of Object) Service Token Connection Type Information (see [below for nested schema](#nestedatt--service_token_connection)) +- `state` (String) Service token state; ACTIVE, INACTIVE, EXPIRED, DELETED +- `type` (String) Service Token Type; VC_TOKEN,EPL_TOKEN + + +### Nested Schema for `account` + +Read-Only: + +- `account_name` (String) +- `account_number` (Number) +- `global_cust_id` (String) +- `global_org_id` (String) +- `global_organization_name` (String) +- `org_id` (Number) +- `organization_name` (String) +- `ucm_id` (String) + + + +### Nested Schema for `change_log` + +Read-Only: + +- `created_by` (String) +- `created_by_email` (String) +- `created_by_full_name` (String) +- `created_date_time` (String) +- `deleted_by` (String) +- `deleted_by_email` (String) +- `deleted_by_full_name` (String) +- `deleted_date_time` (String) +- `updated_by` (String) +- `updated_by_email` (String) +- `updated_by_full_name` (String) +- `updated_date_time` (String) + + + +### Nested Schema for `notifications` + +Read-Only: + +- `emails` (List of String) +- `send_interval` (String) +- `type` (String) + + + +### Nested Schema for `project` + +Read-Only: + +- `href` (String) +- `project_id` (String) + + + +### Nested Schema for `service_token_connection` + +Read-Only: + +- `a_side` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--a_side)) +- `allow_custom_bandwidth` (Boolean) +- `allow_remote_connection` (Boolean) +- `bandwidth_limit` (Number) +- `supported_bandwidths` (List of Number) +- `type` (String) +- `uuid` (String) +- `z_side` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--z_side)) + + +### Nested Schema for `service_token_connection.a_side` + +Read-Only: + +- `access_point_selectors` (List of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--a_side--access_point_selectors)) + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors` + +Read-Only: + +- `interface` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--a_side--access_point_selectors--interface)) +- `link_protocol` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--a_side--access_point_selectors--link_protocol)) +- `network` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--a_side--access_point_selectors--network)) +- `port` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--a_side--access_point_selectors--port)) +- `type` (String) +- `virtual_device` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--a_side--access_point_selectors--virtual_device)) + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.interface` + +Read-Only: + +- `id` (Number) +- `type` (String) +- `uuid` (String) + + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.link_protocol` + +Read-Only: + +- `type` (String) +- `vlan_c_tag` (Number) +- `vlan_s_tag` (Number) +- `vlan_tag` (Number) + + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.network` + +Read-Only: + +- `href` (String) +- `location` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--a_side--access_point_selectors--network--location)) +- `name` (String) +- `scope` (String) +- `type` (String) +- `uuid` (String) + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.network.location` + +Read-Only: + +- `ibx` (String) +- `metro_code` (String) +- `metro_name` (String) +- `region` (String) + + + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.port` + +Read-Only: + +- `account_name` (String) +- `bandwidth` (Number) +- `cvp_id` (Number) +- `encapsulation_protocol_type` (String) +- `href` (String) +- `location` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--a_side--access_point_selectors--port--location)) +- `port_name` (String) +- `priority` (String) +- `type` (String) +- `uuid` (String) + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.port.location` + +Read-Only: + +- `ibx` (String) +- `metro_code` (String) +- `metro_name` (String) +- `region` (String) + + + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.virtual_device` + +Read-Only: + +- `cluster` (String) +- `href` (String) +- `name` (String) +- `type` (String) +- `uuid` (String) + + + + + +### Nested Schema for `service_token_connection.z_side` + +Read-Only: + +- `access_point_selectors` (List of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--z_side--access_point_selectors)) + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors` + +Read-Only: + +- `interface` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--z_side--access_point_selectors--interface)) +- `link_protocol` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--z_side--access_point_selectors--link_protocol)) +- `network` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--z_side--access_point_selectors--network)) +- `port` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--z_side--access_point_selectors--port)) +- `type` (String) +- `virtual_device` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--z_side--access_point_selectors--virtual_device)) + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.interface` + +Read-Only: + +- `id` (Number) +- `type` (String) +- `uuid` (String) + + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.link_protocol` + +Read-Only: + +- `type` (String) +- `vlan_c_tag` (Number) +- `vlan_s_tag` (Number) +- `vlan_tag` (Number) + + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.network` + +Read-Only: + +- `href` (String) +- `location` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--z_side--access_point_selectors--network--location)) +- `name` (String) +- `scope` (String) +- `type` (String) +- `uuid` (String) + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.network.location` + +Read-Only: + +- `ibx` (String) +- `metro_code` (String) +- `metro_name` (String) +- `region` (String) + + + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.port` + +Read-Only: + +- `account_name` (String) +- `bandwidth` (Number) +- `cvp_id` (Number) +- `encapsulation_protocol_type` (String) +- `href` (String) +- `location` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--z_side--access_point_selectors--port--location)) +- `port_name` (String) +- `priority` (String) +- `type` (String) +- `uuid` (String) + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.port.location` + +Read-Only: + +- `ibx` (String) +- `metro_code` (String) +- `metro_name` (String) +- `region` (String) + + + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.virtual_device` + +Read-Only: + +- `cluster` (String) +- `href` (String) +- `name` (String) +- `type` (String) +- `uuid` (String) diff --git a/docs/data-sources/fabric_service_tokens.md b/docs/data-sources/fabric_service_tokens.md new file mode 100644 index 000000000..2379afcfc --- /dev/null +++ b/docs/data-sources/fabric_service_tokens.md @@ -0,0 +1,405 @@ +--- +subcategory: "Fabric" +--- + +# equinix_fabric_service_tokens (Data Source) + +Fabric V4 API compatible data resource that allow user to fetch service token for a given search data set + +Additional documentation: +* Getting Started: https://docs.equinix.com/en-us/Content/Interconnection/Fabric/service%20tokens/Fabric-Service-Tokens.htm +* API: https://docs.equinix.com/en-us/Content/KnowledgeCenter/Fabric/GettingStarted/Integrating-with-Fabric-V4-APIs/ConnectUsingServiceToken.htm + +## Example Usage + +```terraform +data "equinix_fabric_service_tokens" "service-tokens" { + filter { + property = "/type" + operator = "=" + values = "EVPL_VC" + } + filter { + property = "/state" + operator = "=" + values = ["INACTIVE"] + } + pagination { + offset = 0 + limit = 5 + total = 25 + } +} + +output "number_of_returned_service_tokens" { + value = length(data.equinix_fabric_service_tokens.service-tokens.data) +} + +output "first_service_token_id" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.id +} + +output "first_service_token_type" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.type +} + +output "first_service_token_expiration_date_time" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.expiration_date_time +} + +output "first_service_token_supported_bandwidths" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.service_token_connection.0.supported_bandwidths +} + +output "first_service_token_virtual_device_type" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.type +} + +output "first_service_token_virtual_device_uuid" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid +} + +output "first_service_token_interface_type" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.type +} + +output "first_service_token_interface_uuid" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id +} +``` + + +## Schema + +### Required + +- `filter` (Block List, Min: 1, Max: 10) Filters for the Data Source Search Request. Maximum of 8 total filters. (see [below for nested schema](#nestedblock--filter)) + +### Optional + +- `pagination` (Block Set, Max: 1) Pagination details for the Data Source Search Request (see [below for nested schema](#nestedblock--pagination)) + +### Read-Only + +- `data` (List of Object) List of Route Filters (see [below for nested schema](#nestedatt--data)) +- `id` (String) The ID of this resource. + + +### Nested Schema for `filter` + +Required: + +- `operator` (String) Possible operators to use on the filter property. Can be one of the following: [ "=", "!=", "[NOT] LIKE", "[NOT] IN", "ILIKE" ] +- `property` (String) The API response property which you want to filter your request on. Can be one of the following: "/type", "/name", "/project/projectId", "/uuid", "/state" +- `values` (List of String) The values that you want to apply the property+operator combination to in order to filter your data search + + + +### Nested Schema for `pagination` + +Optional: + +- `limit` (Number) Number of elements to be requested per page. Number must be between 1 and 100. Default is 20 +- `offset` (Number) The page offset for the pagination request. Index of the first element. Default is 0. +- `total` (Number) Total number of elements returned. + +Read-Only: + +- `next` (String) URL relative to the last item in the response. +- `previous` (String) URL relative to the first item in the response. + + + +### Nested Schema for `data` + +Read-Only: + +- `account` (Set of Object) (see [below for nested schema](#nestedobjatt--data--account)) +- `change_log` (Set of Object) (see [below for nested schema](#nestedobjatt--data--change_log)) +- `description` (String) +- `expiration_date_time` (String) +- `href` (String) +- `issuer_side` (String) +- `name` (String) +- `notifications` (Set of Object) (see [below for nested schema](#nestedobjatt--data--notifications)) +- `project` (Set of Object) (see [below for nested schema](#nestedobjatt--data--project)) +- `service_token_connection` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection)) +- `state` (String) +- `type` (String) +- `uuid` (String) + + +### Nested Schema for `data.account` + +Read-Only: + +- `account_name` (String) +- `account_number` (Number) +- `global_cust_id` (String) +- `global_org_id` (String) +- `global_organization_name` (String) +- `org_id` (Number) +- `organization_name` (String) +- `ucm_id` (String) + + + +### Nested Schema for `data.change_log` + +Read-Only: + +- `created_by` (String) +- `created_by_email` (String) +- `created_by_full_name` (String) +- `created_date_time` (String) +- `deleted_by` (String) +- `deleted_by_email` (String) +- `deleted_by_full_name` (String) +- `deleted_date_time` (String) +- `updated_by` (String) +- `updated_by_email` (String) +- `updated_by_full_name` (String) +- `updated_date_time` (String) + + + +### Nested Schema for `data.notifications` + +Read-Only: + +- `emails` (List of String) +- `send_interval` (String) +- `type` (String) + + + +### Nested Schema for `data.project` + +Read-Only: + +- `href` (String) +- `project_id` (String) + + + +### Nested Schema for `data.service_token_connection` + +Read-Only: + +- `a_side` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--a_side)) +- `allow_custom_bandwidth` (Boolean) +- `allow_remote_connection` (Boolean) +- `bandwidth_limit` (Number) +- `supported_bandwidths` (List of Number) +- `type` (String) +- `uuid` (String) +- `z_side` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--z_side)) + + +### Nested Schema for `data.service_token_connection.a_side` + +Read-Only: + +- `access_point_selectors` (List of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--a_side--access_point_selectors)) + + +### Nested Schema for `data.service_token_connection.a_side.access_point_selectors` + +Read-Only: + +- `interface` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--a_side--access_point_selectors--interface)) +- `link_protocol` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--a_side--access_point_selectors--link_protocol)) +- `network` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--a_side--access_point_selectors--network)) +- `port` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--a_side--access_point_selectors--port)) +- `type` (String) +- `virtual_device` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--a_side--access_point_selectors--virtual_device)) + + +### Nested Schema for `data.service_token_connection.a_side.access_point_selectors.interface` + +Read-Only: + +- `id` (Number) +- `type` (String) +- `uuid` (String) + + + +### Nested Schema for `data.service_token_connection.a_side.access_point_selectors.link_protocol` + +Read-Only: + +- `type` (String) +- `vlan_c_tag` (Number) +- `vlan_s_tag` (Number) +- `vlan_tag` (Number) + + + +### Nested Schema for `data.service_token_connection.a_side.access_point_selectors.network` + +Read-Only: + +- `href` (String) +- `location` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--a_side--access_point_selectors--network--location)) +- `name` (String) +- `scope` (String) +- `type` (String) +- `uuid` (String) + + +### Nested Schema for `data.service_token_connection.a_side.access_point_selectors.network.location` + +Read-Only: + +- `ibx` (String) +- `metro_code` (String) +- `metro_name` (String) +- `region` (String) + + + + +### Nested Schema for `data.service_token_connection.a_side.access_point_selectors.port` + +Read-Only: + +- `account_name` (String) +- `bandwidth` (Number) +- `cvp_id` (Number) +- `encapsulation_protocol_type` (String) +- `href` (String) +- `location` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--a_side--access_point_selectors--port--location)) +- `port_name` (String) +- `priority` (String) +- `type` (String) +- `uuid` (String) + + +### Nested Schema for `data.service_token_connection.a_side.access_point_selectors.port.location` + +Read-Only: + +- `ibx` (String) +- `metro_code` (String) +- `metro_name` (String) +- `region` (String) + + + + +### Nested Schema for `data.service_token_connection.a_side.access_point_selectors.virtual_device` + +Read-Only: + +- `cluster` (String) +- `href` (String) +- `name` (String) +- `type` (String) +- `uuid` (String) + + + + + +### Nested Schema for `data.service_token_connection.z_side` + +Read-Only: + +- `access_point_selectors` (List of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--z_side--access_point_selectors)) + + +### Nested Schema for `data.service_token_connection.z_side.access_point_selectors` + +Read-Only: + +- `interface` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--z_side--access_point_selectors--interface)) +- `link_protocol` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--z_side--access_point_selectors--link_protocol)) +- `network` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--z_side--access_point_selectors--network)) +- `port` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--z_side--access_point_selectors--port)) +- `type` (String) +- `virtual_device` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--z_side--access_point_selectors--virtual_device)) + + +### Nested Schema for `data.service_token_connection.z_side.access_point_selectors.interface` + +Read-Only: + +- `id` (Number) +- `type` (String) +- `uuid` (String) + + + +### Nested Schema for `data.service_token_connection.z_side.access_point_selectors.link_protocol` + +Read-Only: + +- `type` (String) +- `vlan_c_tag` (Number) +- `vlan_s_tag` (Number) +- `vlan_tag` (Number) + + + +### Nested Schema for `data.service_token_connection.z_side.access_point_selectors.network` + +Read-Only: + +- `href` (String) +- `location` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--z_side--access_point_selectors--network--location)) +- `name` (String) +- `scope` (String) +- `type` (String) +- `uuid` (String) + + +### Nested Schema for `data.service_token_connection.z_side.access_point_selectors.network.location` + +Read-Only: + +- `ibx` (String) +- `metro_code` (String) +- `metro_name` (String) +- `region` (String) + + + + +### Nested Schema for `data.service_token_connection.z_side.access_point_selectors.port` + +Read-Only: + +- `account_name` (String) +- `bandwidth` (Number) +- `cvp_id` (Number) +- `encapsulation_protocol_type` (String) +- `href` (String) +- `location` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--z_side--access_point_selectors--port--location)) +- `port_name` (String) +- `priority` (String) +- `type` (String) +- `uuid` (String) + + +### Nested Schema for `data.service_token_connection.z_side.access_point_selectors.port.location` + +Read-Only: + +- `ibx` (String) +- `metro_code` (String) +- `metro_name` (String) +- `region` (String) + + + + +### Nested Schema for `data.service_token_connection.z_side.access_point_selectors.virtual_device` + +Read-Only: + +- `cluster` (String) +- `href` (String) +- `name` (String) +- `type` (String) +- `uuid` (String) diff --git a/docs/resources/fabric_service_token.md b/docs/resources/fabric_service_token.md new file mode 100644 index 000000000..fdbec61bf --- /dev/null +++ b/docs/resources/fabric_service_token.md @@ -0,0 +1,397 @@ +--- +subcategory: "Fabric" +--- + +# equinix_fabric_service_token (Resource) + +Fabric V4 API compatible resource allows creation and management of [Equinix Fabric Service Token](https://docs.equinix.com/en-us/Content/Interconnection/Fabric/service%20tokens/Fabric-Service-Tokens.htm). + +Additional documentation: +* Getting Started: https://docs.equinix.com/en-us/Content/Interconnection/Fabric/service%20tokens/Fabric-Service-Tokens.htm +* API: https://docs.equinix.com/en-us/Content/KnowledgeCenter/Fabric/GettingStarted/Integrating-with-Fabric-V4-APIs/ConnectUsingServiceToken.htm + +## Example Usage + +Zside Virtual Device Service Token +```terraform +resource "equinix_fabric_service_token" "test" { + type = "VC_TOKEN" + expiration_date_time = "2025-01-18T06:43:49.980Z" + service_token_connection { + type = "EVPL_VC" + supported_bandwidths = [50, 200, 10000] + z_side { + access_point_selectors { + type = "VD" + virtual_device { + type = "EDGE" + uuid = " +## Schema + +### Required + +- `expiration_date_time` (String) Expiration date and time of the service token; 2020-11-06T07:00:00Z +- `notifications` (Block Set, Min: 1) Preferences for notifications on Service Token configuration or status changes (see [below for nested schema](#nestedblock--notifications)) +- `service_token_connection` (Block Set, Min: 1) Service Token Connection Type Information (see [below for nested schema](#nestedblock--service_token_connection)) +- `type` (String) Service Token Type; VC_TOKEN,EPL_TOKEN + +### Optional + +- `description` (String) Optional Description to the Service Token you will be creating +- `name` (String) Name of the Service Token +- `project` (Block Set, Max: 1) Project information (see [below for nested schema](#nestedblock--project)) +- `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts)) + +### Read-Only + +- `account` (Set of Object) Customer account information that is associated with this service token (see [below for nested schema](#nestedatt--account)) +- `change_log` (Set of Object) Captures connection lifecycle change information (see [below for nested schema](#nestedatt--change_log)) +- `href` (String) An absolute URL that is the subject of the link's context. +- `id` (String) The ID of this resource. +- `issuer_side` (String) Information about token side; ASIDE, ZSIDE +- `state` (String) Service token state; ACTIVE, INACTIVE, EXPIRED, DELETED +- `uuid` (String) Equinix-assigned service token identifier + + +### Nested Schema for `notifications` + +Required: + +- `emails` (List of String) Array of contact emails +- `type` (String) Notification Type - ALL,CONNECTION_APPROVAL,SALES_REP_NOTIFICATIONS, NOTIFICATIONS + +Optional: + +- `send_interval` (String) Send interval + + + +### Nested Schema for `service_token_connection` + +Required: + +- `supported_bandwidths` (List of Number) List of permitted bandwidths' For Port +- `type` (String) Type of Connection supported by Service Token you will create; EVPL_VC, EVPLAN_VC, EPLAN_VC, IPWAN_VC + +Optional: + +- `a_side` (Block Set) A-Side Connection link protocol,virtual device or network configuration (see [below for nested schema](#nestedblock--service_token_connection--a_side)) +- `allow_custom_bandwidth` (Boolean) Allow custom bandwidth value +- `allow_remote_connection` (Boolean) Authorization to connect remotely +- `bandwidth_limit` (Number) Connection bandwidth limit in Mbps +- `z_side` (Block Set) Z-Side Connection link protocol,virtual device or network configuration (see [below for nested schema](#nestedblock--service_token_connection--z_side)) + +Read-Only: + +- `uuid` (String) Equinix-assigned connection identifier + + +### Nested Schema for `service_token_connection.a_side` + +Required: + +- `access_point_selectors` (Block List, Min: 1) List of criteria for selecting network access points with optimal efficiency, security, compatibility, and availability (see [below for nested schema](#nestedblock--service_token_connection--a_side--access_point_selectors)) + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors` + +Optional: + +- `interface` (Block Set, Max: 1) Virtual Device Interface Configuration (see [below for nested schema](#nestedblock--service_token_connection--a_side--access_point_selectors--interface)) +- `link_protocol` (Block Set, Max: 1) Link protocol Configuration (see [below for nested schema](#nestedblock--service_token_connection--a_side--access_point_selectors--link_protocol)) +- `network` (Block Set, Max: 1) Network Configuration (see [below for nested schema](#nestedblock--service_token_connection--a_side--access_point_selectors--network)) +- `port` (Block Set, Max: 1) Port Configuration (see [below for nested schema](#nestedblock--service_token_connection--a_side--access_point_selectors--port)) +- `type` (String) Type of Access point; COLO, VD, NETWORK +- `virtual_device` (Block Set, Max: 1) Virtual Device Configuration (see [below for nested schema](#nestedblock--service_token_connection--a_side--access_point_selectors--virtual_device)) + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.interface` + +Optional: + +- `id` (Number) id +- `type` (String) Interface type + +Read-Only: + +- `uuid` (String) Equinix-assigned interface identifier + + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.link_protocol` + +Optional: + +- `type` (String) Type of the link protocol - UNTAGGED, DOT1Q, QINQ, EVPN_VXLAN +- `vlan_c_tag` (Number) Vlan Customer Tag information, vlanCTag value specified for QINQ connections +- `vlan_s_tag` (Number) Vlan Provider Tag information, vlanSTag value specified for QINQ connections +- `vlan_tag` (Number) Vlan Tag information, vlanTag value specified for DOT1Q connections + + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.network` + +Optional: + +- `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 + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.network.location` + +Optional: + +- `ibx` (String) IBX Code +- `metro_code` (String) Access point metro code +- `metro_name` (String) Access point metro name +- `region` (String) Access point region + + + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.port` + +Optional: + +- `account_name` (String) Account Name +- `bandwidth` (Number) Port Bandwidth +- `cvp_id` (Number) Customer virtual port Id +- `encapsulation_protocol_type` (String) Port Encapsulation +- `location` (Block Set) Port Location (see [below for nested schema](#nestedblock--service_token_connection--a_side--access_point_selectors--port--location)) +- `port_name` (String) Port Name +- `priority` (String) Port Priority +- `type` (String) Type of Port +- `uuid` (String) Equinix-assigned Port identifier + +Read-Only: + +- `href` (String) Unique Resource Identifier + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.port.location` + +Optional: + +- `ibx` (String) IBX Code +- `metro_code` (String) Access point metro code +- `metro_name` (String) Access point metro name +- `region` (String) Access point region + + + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.virtual_device` + +Required: + +- `uuid` (String) Equinix-assigned Virtual Device identifier + +Optional: + +- `cluster` (String) Virtual Device Cluster Information +- `name` (String) Customer-assigned Virtual Device Name +- `type` (String) Virtual Device type + +Read-Only: + +- `href` (String) Unique Resource Identifier + + + + + +### Nested Schema for `service_token_connection.z_side` + +Required: + +- `access_point_selectors` (Block List, Min: 1) List of criteria for selecting network access points with optimal efficiency, security, compatibility, and availability (see [below for nested schema](#nestedblock--service_token_connection--z_side--access_point_selectors)) + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors` + +Optional: + +- `interface` (Block Set, Max: 1) Virtual Device Interface Configuration (see [below for nested schema](#nestedblock--service_token_connection--z_side--access_point_selectors--interface)) +- `link_protocol` (Block Set, Max: 1) Link protocol Configuration (see [below for nested schema](#nestedblock--service_token_connection--z_side--access_point_selectors--link_protocol)) +- `network` (Block Set, Max: 1) Network Configuration (see [below for nested schema](#nestedblock--service_token_connection--z_side--access_point_selectors--network)) +- `port` (Block Set, Max: 1) Port Configuration (see [below for nested schema](#nestedblock--service_token_connection--z_side--access_point_selectors--port)) +- `type` (String) Type of Access point; COLO, VD, NETWORK +- `virtual_device` (Block Set, Max: 1) Virtual Device Configuration (see [below for nested schema](#nestedblock--service_token_connection--z_side--access_point_selectors--virtual_device)) + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.interface` + +Optional: + +- `id` (Number) id +- `type` (String) Interface type + +Read-Only: + +- `uuid` (String) Equinix-assigned interface identifier + + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.link_protocol` + +Optional: + +- `type` (String) Type of the link protocol - UNTAGGED, DOT1Q, QINQ, EVPN_VXLAN +- `vlan_c_tag` (Number) Vlan Customer Tag information, vlanCTag value specified for QINQ connections +- `vlan_s_tag` (Number) Vlan Provider Tag information, vlanSTag value specified for QINQ connections +- `vlan_tag` (Number) Vlan Tag information, vlanTag value specified for DOT1Q connections + + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.network` + +Optional: + +- `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 + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.network.location` + +Optional: + +- `ibx` (String) IBX Code +- `metro_code` (String) Access point metro code +- `metro_name` (String) Access point metro name +- `region` (String) Access point region + + + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.port` + +Optional: + +- `account_name` (String) Account Name +- `bandwidth` (Number) Port Bandwidth +- `cvp_id` (Number) Customer virtual port Id +- `encapsulation_protocol_type` (String) Port Encapsulation +- `location` (Block Set) Port Location (see [below for nested schema](#nestedblock--service_token_connection--z_side--access_point_selectors--port--location)) +- `port_name` (String) Port Name +- `priority` (String) Port Priority +- `type` (String) Type of Port +- `uuid` (String) Equinix-assigned Port identifier + +Read-Only: + +- `href` (String) Unique Resource Identifier + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.port.location` + +Optional: + +- `ibx` (String) IBX Code +- `metro_code` (String) Access point metro code +- `metro_name` (String) Access point metro name +- `region` (String) Access point region + + + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.virtual_device` + +Required: + +- `uuid` (String) Equinix-assigned Virtual Device identifier + +Optional: + +- `cluster` (String) Virtual Device Cluster Information +- `name` (String) Customer-assigned Virtual Device Name +- `type` (String) Virtual Device type + +Read-Only: + +- `href` (String) Unique Resource Identifier + + + + + + +### Nested Schema for `project` + +Optional: + +- `project_id` (String) Project Id + +Read-Only: + +- `href` (String) Unique Resource URL + + + +### Nested Schema for `timeouts` + +Optional: + +- `create` (String) +- `delete` (String) +- `read` (String) +- `update` (String) + + + +### Nested Schema for `account` + +Read-Only: + +- `account_name` (String) +- `account_number` (Number) +- `global_cust_id` (String) +- `global_org_id` (String) +- `global_organization_name` (String) +- `org_id` (Number) +- `organization_name` (String) +- `ucm_id` (String) + + + +### Nested Schema for `change_log` + +Read-Only: + +- `created_by` (String) +- `created_by_email` (String) +- `created_by_full_name` (String) +- `created_date_time` (String) +- `deleted_by` (String) +- `deleted_by_email` (String) +- `deleted_by_full_name` (String) +- `deleted_date_time` (String) +- `updated_by` (String) +- `updated_by_email` (String) +- `updated_by_full_name` (String) +- `updated_date_time` (String) \ No newline at end of file diff --git a/examples/data-sources/equinix_fabric_service_token/data-source.tf b/examples/data-sources/equinix_fabric_service_token/data-source.tf new file mode 100644 index 000000000..a234ba32e --- /dev/null +++ b/examples/data-sources/equinix_fabric_service_token/data-source.tf @@ -0,0 +1,35 @@ +data "equinix_fabric_service_token" "service-token" { + uuid = "" +} + +output "id" { + value = data.equinix_fabric_service_token.service-token.id +} + +output "type" { + value = data.equinix_fabric_service_token.service-token.type +} + +output "expiration_date_time" { + value = data.equinix_fabric_service_token.service-token.expiration_date_time +} + +output "supported_bandwidths" { + value = data.equinix_fabric_service_token.service-token.service_token_connection.0.supported_bandwidths +} + +output "virtual_device_type" { + value = data.equinix_fabric_service_token.service-token.service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.type +} + +output "virtual_device_uuid" { + value = data.equinix_fabric_service_token.service-token.service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid +} + +output "interface_type" { + value = data.equinix_fabric_service_token.service-token.service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.type +} + +output "interface_uuid" { + value = data.equinix_fabric_service_token.service-token.service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id +} \ No newline at end of file diff --git a/examples/data-sources/equinix_fabric_service_tokens/data-source.tf b/examples/data-sources/equinix_fabric_service_tokens/data-source.tf new file mode 100644 index 000000000..45d8a76f7 --- /dev/null +++ b/examples/data-sources/equinix_fabric_service_tokens/data-source.tf @@ -0,0 +1,53 @@ +data "equinix_fabric_service_tokens" "service-tokens" { + filter { + property = "/type" + operator = "=" + values = "EVPL_VC" + } + filter { + property = "/state" + operator = "=" + values = ["INACTIVE"] + } + pagination { + offset = 0 + limit = 5 + total = 25 + } +} + +output "number_of_returned_service_tokens" { + value = length(data.equinix_fabric_service_tokens.service-tokens.data) +} + +output "first_service_token_id" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.id +} + +output "first_service_token_type" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.type +} + +output "first_service_token_expiration_date_time" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.expiration_date_time +} + +output "first_service_token_supported_bandwidths" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.service_token_connection.0.supported_bandwidths +} + +output "first_service_token_virtual_device_type" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.type +} + +output "first_service_token_virtual_device_uuid" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid +} + +output "first_service_token_interface_type" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.type +} + +output "first_service_token_interface_uuid" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id +} \ No newline at end of file diff --git a/internal/resources/fabric/service_token/datasources.go b/internal/resources/fabric/service_token/datasources.go index adba0f1d6..f28b54c71 100644 --- a/internal/resources/fabric/service_token/datasources.go +++ b/internal/resources/fabric/service_token/datasources.go @@ -13,7 +13,11 @@ func DataSource() *schema.Resource { return &schema.Resource{ ReadContext: dataSourceRead, Schema: dataSourceBaseSchema(), - Description: `Fabric V4 API compatible data resource that allow user to fetch service token for a given UUID`, + Description: `Fabric V4 API compatible data resource that allow user to fetch service token for a given UUID + +Additional documentation: +* Getting Started: https://docs.equinix.com/en-us/Content/Interconnection/Fabric/service%20tokens/Fabric-Service-Tokens.htm +* API: https://docs.equinix.com/en-us/Content/KnowledgeCenter/Fabric/GettingStarted/Integrating-with-Fabric-V4-APIs/ConnectUsingServiceToken.htm`, } } @@ -27,7 +31,11 @@ func DataSourceSearch() *schema.Resource { return &schema.Resource{ ReadContext: dataSourceSearch, Schema: dataSourceSearchSchema(), - Description: `Fabric V4 API compatible data resource that allow user to fetch service token for a given search data set`, + Description: `Fabric V4 API compatible data resource that allow user to fetch service token for a given search data set + +Additional documentation: +* Getting Started: https://docs.equinix.com/en-us/Content/Interconnection/Fabric/service%20tokens/Fabric-Service-Tokens.htm +* API: https://docs.equinix.com/en-us/Content/KnowledgeCenter/Fabric/GettingStarted/Integrating-with-Fabric-V4-APIs/ConnectUsingServiceToken.htm`, } } diff --git a/internal/resources/fabric/service_token/models.go b/internal/resources/fabric/service_token/models.go index 6d38f7e42..b8b0168f3 100644 --- a/internal/resources/fabric/service_token/models.go +++ b/internal/resources/fabric/service_token/models.go @@ -8,7 +8,6 @@ import ( 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/schema" - "log" "reflect" "sort" "time" @@ -21,13 +20,11 @@ func buildCreateRequest(d *schema.ResourceData) fabricv4.ServiceToken { serviceTokenRequest.SetType(fabricv4.ServiceTokenType(typeConfig)) expirationDateTimeConfig := d.Get("expiration_date_time").(string) - log.Printf("[DEBUG] !!!! Expiration Date %v", expirationDateTimeConfig) const TimeFormat = "2006-01-02T15:04:05.000Z" expirationTime, err := time.Parse(TimeFormat, expirationDateTimeConfig) if err != nil { fmt.Print("Error Parsing expiration date time: ", err) } - log.Printf("[DEBUG] !!! Parsed expiration date %v", expirationTime) serviceTokenRequest.SetExpirationDateTime(expirationTime) connectionConfig := d.Get("service_token_connection").(*schema.Set).List() @@ -79,7 +76,6 @@ func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOpe for _, notification := range oldNotifications.(*schema.Set).List() { notificationMap := notification.(map[string]interface{}) - // Extract old emails list if it exists if emails, ok := notificationMap["emails"]; ok { oldEmailInterface := emails.([]interface{}) if len(oldEmailInterface) > 0 { @@ -92,7 +88,6 @@ func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOpe for _, notification := range newNotifications.(*schema.Set).List() { notificationMap := notification.(map[string]interface{}) - // Extract old emails list if it exists if emails, ok := notificationMap["emails"]; ok { newEmailInterface := emails.([]interface{}) if len(newEmailInterface) > 0 { @@ -102,7 +97,6 @@ func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOpe } } - log.Print("!!! DEBUG value of new email", newNotificationEmails) if !reflect.DeepEqual(oldNotificationEmails, newNotificationEmails) { patches = append(patches, fabricv4.ServiceTokenChangeOperation{ Op: "replace", @@ -112,18 +106,13 @@ func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOpe } oldServiceTokenConnection, newServiceTokenConnection := d.GetChange("service_token_connection") - log.Printf("[DEBUG] !!! old ServiceToken Connection %v", oldServiceTokenConnection) - log.Printf("[DEBUG] !!! new ServiceToken Connection %v", newServiceTokenConnection) - // Initialize variables for bandwidth limits var oldAsideBandwidthLimit, newAsideBandwidthLimit int - // Extract old bandwidth limit if oldServiceTokenConnection != nil { for _, connection := range oldServiceTokenConnection.(*schema.Set).List() { notificationMap := connection.(map[string]interface{}) - // Extract old bandwidth limit if it exists if bandwidth, ok := notificationMap["bandwidthLimit"]; ok { oldBandwidthLimit := bandwidth.([]interface{}) if len(oldBandwidthLimit) > 0 { @@ -134,12 +123,10 @@ func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOpe } } - // Extract new bandwidth limit if newServiceTokenConnection != nil { for _, connection := range newServiceTokenConnection.(*schema.Set).List() { notificationMap := connection.(map[string]interface{}) - // Extract new bandwidth limit if it exists if bandwidth, ok := notificationMap["bandwidthLimit"]; ok { newBandwidthLimit := bandwidth.([]interface{}) if len(newBandwidthLimit) > 0 { @@ -158,14 +145,12 @@ func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOpe }) } - // Get the old and new values for bandwidth limit var oldZsideBandwidth, newZsideBandwidth []int if oldServiceTokenConnection != nil { for _, connection := range oldServiceTokenConnection.(*schema.Set).List() { notificationMap := connection.(map[string]interface{}) - // Extract old bandwidth limit if it exists if bandwidth, ok := notificationMap["supported_bandwidths"]; ok { oldSupportedBandwidth := bandwidth.([]interface{}) if len(oldSupportedBandwidth) > 0 { @@ -179,7 +164,6 @@ func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOpe for _, connection := range newServiceTokenConnection.(*schema.Set).List() { notificationMap := connection.(map[string]interface{}) - // Extract new bandwidth limit if it exists if bandwidth, ok := notificationMap["supported_bandwidths"]; ok { newSupportedBandwidth := bandwidth.([]interface{}) if len(newSupportedBandwidth) > 0 { @@ -190,8 +174,6 @@ func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOpe } } - log.Print("!!! DEBUG value of new supprted bandwidth", newZsideBandwidth) - log.Printf("[DEBUG] Value of aresliceequal fucntion %v", areSlicesEqual(oldZsideBandwidth, newZsideBandwidth)) if !areSlicesEqual(oldZsideBandwidth, newZsideBandwidth) { patches = append(patches, fabricv4.ServiceTokenChangeOperation{ Op: "replace", @@ -208,13 +190,9 @@ func areSlicesEqual(a, b []int) bool { return false } - // Sort both slices sort.Ints(a) sort.Ints(b) - log.Printf("value of int a %v", a) - log.Printf("value of int b %v", b) - // Compare sorted slices for i := range a { if a[i] != b[i] { return false @@ -324,14 +302,10 @@ func connectionTerraformToGo(connectionTerraform []interface{}) fabricv4.Service supportedBandwidths := connectionMap["supported_bandwidths"].([]interface{}) if supportedBandwidths != nil { - // Create a new slice to hold the int32 values int32Bandwidths := make([]int32, len(supportedBandwidths)) - - // Convert []interface{} to []int32 for i, v := range supportedBandwidths { - int32Bandwidths[i] = int32(v.(int)) // Assign directly to the slice at index i + int32Bandwidths[i] = int32(v.(int)) } - // Set the converted []int32 to the connection connection.SetSupportedBandwidths(int32Bandwidths) } @@ -356,7 +330,7 @@ func accessPointTerraformToGo(accessPoint []interface{}) fabricv4.ServiceTokenSi var apSide fabricv4.ServiceTokenSide accessPointMap := accessPoint[0].(map[string]interface{}) - accessPointSelectors := accessPointMap["access_point_selectors"].(*schema.Set).List() + accessPointSelectors := accessPointMap["access_point_selectors"].([]interface{}) if len(accessPointSelectors) != 0 { aps := accessPointSelectorsTerraformToGo(accessPointSelectors) apSide.SetAccessPointSelectors(aps) @@ -577,56 +551,45 @@ func connectionGoToTerraform(connection *fabricv4.ServiceTokenConnection) *schem } func accessPointGoToTerraform(accessPoint *fabricv4.ServiceTokenSide) *schema.Set { - mappedAccessPoint := make(map[string]interface{}) - if accessPoint.AccessPointSelectors != nil { - accessPointSelectors := accessPoint.GetAccessPointSelectors() - - apSelectorsSet := schema.NewSet( - schema.HashResource(accessPointSelectorsSch()), - nil, - ) - for _, selector := range accessPointSelectors { - mappedSelector := accessPointSelectorsGoToTerraform(&selector) - apSelectorsSet.Add(mappedSelector) - - } - mappedAccessPoint["access_point_selectors"] = apSelectorsSet - } - - accessPointSet := schema.NewSet( - schema.HashResource(accessPointSelectorsSch()), - []interface{}{mappedAccessPoint}, + return schema.NewSet( + schema.HashResource(serviceTokenAccessPointSch()), + []interface{}{map[string]interface{}{ + "access_point_selectors": accessPointSelectorsGoToTerraform(accessPoint.GetAccessPointSelectors()), + }}, ) - return accessPointSet } -func accessPointSelectorsGoToTerraform(apSelectors *fabricv4.AccessPointSelector) map[string]interface{} { - mappedAccessPointSelectors := make(map[string]interface{}) - if apSelectors.Type != nil { - mappedAccessPointSelectors["type"] = string(apSelectors.GetType()) - } - if apSelectors.Port != nil { - port := apSelectors.GetPort() - mappedAccessPointSelectors["port"] = portGoToTerraform(&port) - } - if apSelectors.LinkProtocol != nil { - linkProtocol := apSelectors.GetLinkProtocol() - mappedAccessPointSelectors["link_protocol"] = linkedProtocolGoToTerraform(&linkProtocol) - } - if apSelectors.VirtualDevice != nil { - virtualDevice := apSelectors.GetVirtualDevice() - mappedAccessPointSelectors["virtual_device"] = virtualDeviceGoToTerraform(&virtualDevice) - } - if apSelectors.Interface != nil { - interface_ := apSelectors.GetInterface() - mappedAccessPointSelectors["interface"] = interfaceGoToTerraform(&interface_) - } - if apSelectors.Network != nil { - network := apSelectors.GetNetwork() - mappedAccessPointSelectors["network"] = networkGoToTerraform(&network) +func accessPointSelectorsGoToTerraform(apSelectors []fabricv4.AccessPointSelector) []interface{} { + mappedSelectors := make([]interface{}, len(apSelectors)) + for index, selector := range apSelectors { + mappedAccessPointSelector := make(map[string]interface{}) + if selector.Type != nil { + mappedAccessPointSelector["type"] = string(selector.GetType()) + } + if selector.Port != nil { + port := selector.GetPort() + mappedAccessPointSelector["port"] = portGoToTerraform(&port) + } + if selector.LinkProtocol != nil { + linkProtocol := selector.GetLinkProtocol() + mappedAccessPointSelector["link_protocol"] = linkedProtocolGoToTerraform(&linkProtocol) + } + if selector.VirtualDevice != nil { + virtualDevice := selector.GetVirtualDevice() + mappedAccessPointSelector["virtual_device"] = virtualDeviceGoToTerraform(&virtualDevice) + } + if selector.Interface != nil { + interface_ := selector.GetInterface() + mappedAccessPointSelector["interface"] = interfaceGoToTerraform(&interface_) + } + if selector.Network != nil { + network := selector.GetNetwork() + mappedAccessPointSelector["network"] = networkGoToTerraform(&network) + } + mappedSelectors[index] = mappedAccessPointSelector } - return mappedAccessPointSelectors + return mappedSelectors } func portGoToTerraform(port *fabricv4.SimplifiedMetadataEntity) *schema.Set { diff --git a/internal/resources/fabric/service_token/resource.go b/internal/resources/fabric/service_token/resource.go index 94301e915..14d04ebf6 100644 --- a/internal/resources/fabric/service_token/resource.go +++ b/internal/resources/fabric/service_token/resource.go @@ -98,7 +98,8 @@ func resourceDelete(ctx context.Context, d *schema.ResourceData, meta interface{ if err != nil { if genericError, ok := err.(*fabricv4.GenericOpenAPIError); ok { if fabricErrs, ok := genericError.Model().([]fabricv4.Error); ok { - if equinix_errors.HasErrorCode(fabricErrs, "") { + // EQ-3034019 = Service Token already deleted + if equinix_errors.HasErrorCode(fabricErrs, "EQ-3034019") { return diags } } diff --git a/internal/resources/fabric/service_token/resource_schema.go b/internal/resources/fabric/service_token/resource_schema.go index 0b9b5f39e..199e3cfc9 100644 --- a/internal/resources/fabric/service_token/resource_schema.go +++ b/internal/resources/fabric/service_token/resource_schema.go @@ -51,6 +51,7 @@ func resourceSchema() map[string]*schema.Schema { Required: true, Description: "Service Token Connection Type Information", Elem: serviceTokenConnectionSch(), + Set: schema.HashResource(serviceTokenConnectionSch()), }, "state": { Type: schema.TypeString, @@ -129,7 +130,7 @@ func serviceTokenConnectionSch() *schema.Resource { "supported_bandwidths": { Type: schema.TypeList, Required: true, - Description: "List of permitted bandwidths", + Description: "List of permitted bandwidths' For Port ", Elem: &schema.Schema{ Type: schema.TypeInt, }, @@ -140,6 +141,7 @@ func serviceTokenConnectionSch() *schema.Resource { Computed: true, Description: "A-Side Connection link protocol,virtual device or network configuration", Elem: serviceTokenAccessPointSch(), + Set: schema.HashResource(serviceTokenAccessPointSch()), }, "z_side": { Type: schema.TypeSet, @@ -147,6 +149,7 @@ func serviceTokenConnectionSch() *schema.Resource { Computed: true, Description: "Z-Side Connection link protocol,virtual device or network configuration", Elem: serviceTokenAccessPointSch(), + Set: schema.HashResource(serviceTokenAccessPointSch()), }, }, } @@ -156,10 +159,11 @@ func serviceTokenAccessPointSch() *schema.Resource { return &schema.Resource{ Schema: map[string]*schema.Schema{ "access_point_selectors": { - Type: schema.TypeSet, + Type: schema.TypeList, Required: true, Description: "List of criteria for selecting network access points with optimal efficiency, security, compatibility, and availability", Elem: accessPointSelectorsSch(), + Set: schema.HashResource(accessPointSelectorsSch()), }, }, } diff --git a/templates/resources/fabric_service_token.md.tmpl b/templates/resources/fabric_service_token.md.tmpl new file mode 100644 index 000000000..4d8d656f7 --- /dev/null +++ b/templates/resources/fabric_service_token.md.tmpl @@ -0,0 +1,23 @@ +--- +subcategory: "Fabric" +--- + +{{/* This template serves as a starting point for documentation generation, and can be customized with hardcoded values and/or doc gen templates. + +For example, the {{ .SchemaMarkdown }} template can be used to replace manual schema documentation if descriptions of schema attributes are added in the provider source code. */ -}} + +# equinix_fabric_service_token (Resource) + +Fabric V4 API compatible resource allows creation and management of [Equinix Fabric Service Token](https://docs.equinix.com/en-us/Content/Interconnection/Fabric/service%20tokens/Fabric-Service-Tokens.htm). + +Additional documentation: +* Getting Started: https://docs.equinix.com/en-us/Content/Interconnection/Fabric/service%20tokens/Fabric-Service-Tokens.htm +* API: https://docs.equinix.com/en-us/Content/KnowledgeCenter/Fabric/GettingStarted/Integrating-with-Fabric-V4-APIs/ConnectUsingServiceToken.htm + +## Example Usage + +Zside Virtual Device Service Token +{{tffile "examples/resources/equinix_fabric_service_token/zside_vd_service_token.tf"}} + + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file From 6ab8a37320da7195efb64218a347674aa7c6eecf Mon Sep 17 00:00:00 2001 From: srushti-patl Date: Wed, 16 Oct 2024 13:43:41 -0700 Subject: [PATCH 16/55] feat: Adding Fabric Service Token Resource and Data Source --- equinix/provider.go | 4 + .../fabric/service_token/datasources.go | 49 ++ .../service_token/datasources_schema.go | 175 +++++ .../resources/fabric/service_token/models.go | 728 ++++++++++++++++++ .../fabric/service_token/resource.go | 169 ++++ .../fabric/service_token/resource_schema.go | 424 ++++++++++ .../fabric/service_token/resource_test.go | 94 +++ 7 files changed, 1643 insertions(+) create mode 100644 internal/resources/fabric/service_token/datasources.go create mode 100644 internal/resources/fabric/service_token/datasources_schema.go create mode 100644 internal/resources/fabric/service_token/models.go create mode 100644 internal/resources/fabric/service_token/resource.go create mode 100644 internal/resources/fabric/service_token/resource_schema.go create mode 100644 internal/resources/fabric/service_token/resource_test.go diff --git a/equinix/provider.go b/equinix/provider.go index fe8b02d7a..bf38b2592 100644 --- a/equinix/provider.go +++ b/equinix/provider.go @@ -13,6 +13,7 @@ import ( fabric_network "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/network" fabric_route_filter "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/route_filter" fabric_route_filter_rule "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/route_filter_rule" + fabric_service_token "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/service_token" metal_device "github.com/equinix/terraform-provider-equinix/internal/resources/metal/device" metal_port "github.com/equinix/terraform-provider-equinix/internal/resources/metal/port" "github.com/equinix/terraform-provider-equinix/internal/resources/metal/virtual_circuit" @@ -102,6 +103,8 @@ func Provider() *schema.Provider { "equinix_fabric_route_filter_rules": fabric_route_filter_rule.DataSourceGetAllRules(), "equinix_fabric_service_profile": dataSourceFabricServiceProfileReadByUuid(), "equinix_fabric_service_profiles": dataSourceFabricSearchServiceProfilesByName(), + "equinix_fabric_service_token": fabric_service_token.DataSource(), + "equinix_fabric_service_tokens": fabric_service_token.DataSourceSearch(), "equinix_network_account": dataSourceNetworkAccount(), "equinix_network_device": dataSourceNetworkDevice(), "equinix_network_device_type": dataSourceNetworkDeviceType(), @@ -133,6 +136,7 @@ func Provider() *schema.Provider { "equinix_fabric_route_filter_rule": fabric_route_filter_rule.Resource(), "equinix_fabric_routing_protocol": resourceFabricRoutingProtocol(), "equinix_fabric_service_profile": resourceFabricServiceProfile(), + "equinix_fabric_service_token": fabric_service_token.Resource(), "equinix_network_device": resourceNetworkDevice(), "equinix_network_ssh_user": resourceNetworkSSHUser(), "equinix_network_bgp": resourceNetworkBGP(), diff --git a/internal/resources/fabric/service_token/datasources.go b/internal/resources/fabric/service_token/datasources.go new file mode 100644 index 000000000..adba0f1d6 --- /dev/null +++ b/internal/resources/fabric/service_token/datasources.go @@ -0,0 +1,49 @@ +package service_token + +import ( + "context" + "fmt" + "github.com/equinix/terraform-provider-equinix/internal/config" + equinix_errors "github.com/equinix/terraform-provider-equinix/internal/errors" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func DataSource() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceRead, + Schema: dataSourceBaseSchema(), + Description: `Fabric V4 API compatible data resource that allow user to fetch service token for a given UUID`, + } +} + +func dataSourceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + uuid, _ := d.Get("uuid").(string) + d.SetId(uuid) + return resourceRead(ctx, d, meta) +} + +func DataSourceSearch() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceSearch, + Schema: dataSourceSearchSchema(), + Description: `Fabric V4 API compatible data resource that allow user to fetch service token for a given search data set`, + } +} + +func dataSourceSearch(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*config.Config).NewFabricClientForSDK(d) + searchRequest := buildSearchRequest(d) + + serviceTokens, _, err := client.ServiceTokensApi.SearchServiceTokens(ctx).ServiceTokenSearchRequest(searchRequest).Execute() + if err != nil { + return diag.FromErr(equinix_errors.FormatFabricError(err)) + } + + if len(serviceTokens.Data) < 1 { + return diag.FromErr(fmt.Errorf("no records are found for the route filter search criteria provided - %d , please change the search criteria", len(serviceTokens.Data))) + } + + d.SetId(serviceTokens.Data[0].GetUuid()) + return setServiceTokensData(d, serviceTokens) +} diff --git a/internal/resources/fabric/service_token/datasources_schema.go b/internal/resources/fabric/service_token/datasources_schema.go new file mode 100644 index 000000000..046112cee --- /dev/null +++ b/internal/resources/fabric/service_token/datasources_schema.go @@ -0,0 +1,175 @@ +package service_token + +import ( + equinix_fabric_schema "github.com/equinix/terraform-provider-equinix/internal/fabric/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func dataSourceBaseSchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Computed: true, + Description: "Service Token Type; VC_TOKEN,EPL_TOKEN", + }, + "uuid": { + Type: schema.TypeString, + Required: true, + Description: "Equinix-assigned service token identifier", + }, + "href": { + Type: schema.TypeString, + Computed: true, + Description: "An absolute URL that is the subject of the link's context.", + }, + "issuer_side": { + Type: schema.TypeString, + Computed: true, + Description: "Information about token side; ASIDE, ZSIDE", + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: "Name of the Service Token", + }, + "description": { + Type: schema.TypeString, + Computed: true, + Description: "Optional Description to the Service Token you will be creating", + }, + "expiration_date_time": { + Type: schema.TypeString, + Computed: true, + Description: "Expiration date and time of the service token; 2020-11-06T07:00:00Z", + }, + "service_token_connection": { + Type: schema.TypeSet, + Computed: true, + Description: "Service Token Connection Type Information", + Elem: serviceTokenConnectionSch(), + }, + "state": { + Type: schema.TypeString, + Computed: true, + Description: "Service token state; ACTIVE, INACTIVE, EXPIRED, DELETED", + }, + "notifications": { + Type: schema.TypeSet, + Computed: true, + Description: "Preferences for notifications on Service Token configuration or status changes", + Elem: &schema.Resource{ + Schema: equinix_fabric_schema.NotificationSch(), + }, + }, + "account": { + Type: schema.TypeSet, + Computed: true, + Description: "Customer account information that is associated with this service token", + Elem: &schema.Resource{ + Schema: equinix_fabric_schema.AccountSch(), + }, + }, + "change_log": { + Type: schema.TypeSet, + Computed: true, + Description: "Captures connection lifecycle change information", + Elem: &schema.Resource{ + Schema: equinix_fabric_schema.ChangeLogSch(), + }, + }, + "project": { + Type: schema.TypeSet, + Computed: true, + Description: "Project information", + Elem: &schema.Resource{ + Schema: equinix_fabric_schema.ProjectSch(), + }, + }, + } +} + +func paginationSchema() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "offset": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "The page offset for the pagination request. Index of the first element. Default is 0.", + }, + "limit": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "Number of elements to be requested per page. Number must be between 1 and 100. Default is 20", + }, + "total": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "Total number of elements returned.", + }, + "next": { + Type: schema.TypeString, + Computed: true, + Description: "URL relative to the last item in the response.", + }, + "previous": { + Type: schema.TypeString, + Computed: true, + Description: "URL relative to the first item in the response.", + }, + }, + } +} + +func dataSourceSearchSchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "data": { + Type: schema.TypeList, + Computed: true, + Description: "List of Route Filters", + Elem: &schema.Resource{ + Schema: dataSourceBaseSchema(), + }, + }, + "filter": { + Type: schema.TypeList, + Required: true, + Description: "Filters for the Data Source Search Request. Maximum of 8 total filters.", + MaxItems: 10, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "property": { + Type: schema.TypeString, + Required: true, + Description: "The API response property which you want to filter your request on. Can be one of the following: \"/type\", \"/name\", \"/project/projectId\", \"/uuid\", \"/state\"", + ValidateFunc: validation.StringInSlice([]string{"/uuid", "/state", "/name", "/project/projectId"}, true), + }, + "operator": { + Type: schema.TypeString, + Required: true, + Description: "Possible operators to use on the filter property. Can be one of the following: [ \"=\", \"!=\", \"[NOT] LIKE\", \"[NOT] IN\", \"ILIKE\" ]", + ValidateFunc: validation.StringInSlice([]string{"=", "!=", "[NOT] LIKE", "[NOT] IN", "ILIKE"}, true), + }, + "values": { + Type: schema.TypeList, + Required: true, + Description: "The values that you want to apply the property+operator combination to in order to filter your data search", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "pagination": { + Type: schema.TypeSet, + Optional: true, + Description: "Pagination details for the Data Source Search Request", + MaxItems: 1, + Elem: paginationSchema(), + }, + } +} diff --git a/internal/resources/fabric/service_token/models.go b/internal/resources/fabric/service_token/models.go new file mode 100644 index 000000000..6fff6d489 --- /dev/null +++ b/internal/resources/fabric/service_token/models.go @@ -0,0 +1,728 @@ +package service_token + +import ( + "fmt" + "github.com/equinix/equinix-sdk-go/services/fabricv4" + "github.com/equinix/terraform-provider-equinix/internal/converters" + equinix_fabric_schema "github.com/equinix/terraform-provider-equinix/internal/fabric/schema" + 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/schema" + "log" + "reflect" + "sort" + "time" +) + +func buildCreateRequest(d *schema.ResourceData) fabricv4.ServiceToken { + serviceTokenRequest := fabricv4.ServiceToken{} + + typeConfig := d.Get("type").(string) + serviceTokenRequest.SetType(fabricv4.ServiceTokenType(typeConfig)) + + expirationDateTimeConfig := d.Get("expiration_date_time").(string) + log.Printf("[DEBUG] !!!! Expiration Date %v", expirationDateTimeConfig) + const TimeFormat = "2006-01-02T15:04:05.000Z" + expirationTime, err := time.Parse(TimeFormat, expirationDateTimeConfig) + if err != nil { + fmt.Print("Error Parsing expiration date time: ", err) + } + log.Printf("[DEBUG] !!! Parsed expiration date %v", expirationTime) + serviceTokenRequest.SetExpirationDateTime(expirationTime) + + connectionConfig := d.Get("service_token_connection").(*schema.Set).List() + connection := connectionTerraformToGo(connectionConfig) + serviceTokenRequest.SetConnection(connection) + + notificationsConfig := d.Get("notifications").(*schema.Set).List() + notifications := equinix_fabric_schema.NotificationsTerraformToGo(notificationsConfig) + serviceTokenRequest.SetNotifications(notifications) + + return serviceTokenRequest + +} + +func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOperation { + patches := make([]fabricv4.ServiceTokenChangeOperation, 0) + oldName, newName := d.GetChange("name") + if oldName.(string) != newName.(string) { + 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{ + Op: "replace", + Path: "/description", + Value: newDescription.(string), + }) + } + + oldExpirationDate, newExpirationDate := d.GetChange("expiration_date_time") + if oldExpirationDate.(string) != newExpirationDate.(string) { + patches = append(patches, fabricv4.ServiceTokenChangeOperation{ + Op: "replace", + Path: "/expirationDateTime", + Value: newExpirationDate.(string), + }) + } + + oldNotifications, newNotifications := d.GetChange("notifications") + + var oldNotificationEmails, newNotificationEmails []string + + if oldNotifications != nil { + for _, notification := range oldNotifications.(*schema.Set).List() { + notificationMap := notification.(map[string]interface{}) + + // Extract old emails list if it exists + if emails, ok := notificationMap["emails"]; ok { + oldEmailInterface := emails.([]interface{}) + if len(oldEmailInterface) > 0 { + oldNotificationEmails = converters.IfArrToStringArr(oldEmailInterface) + } + } + } + } + if newNotifications != nil { + for _, notification := range newNotifications.(*schema.Set).List() { + notificationMap := notification.(map[string]interface{}) + + // Extract old emails list if it exists + if emails, ok := notificationMap["emails"]; ok { + newEmailInterface := emails.([]interface{}) + if len(newEmailInterface) > 0 { + newNotificationEmails = converters.IfArrToStringArr(newEmailInterface) + } + } + } + } + + log.Print("!!! DEBUG value of new email", newNotificationEmails) + if !reflect.DeepEqual(oldNotificationEmails, newNotificationEmails) { + patches = append(patches, fabricv4.ServiceTokenChangeOperation{ + Op: "replace", + Path: "/notifications/emails", + Value: newNotificationEmails, + }) + } + + oldServiceTokenConnection, newServiceTokenConnection := d.GetChange("service_token_connection") + log.Printf("[DEBUG] !!! old ServiceToken Connection %v", oldServiceTokenConnection) + log.Printf("[DEBUG] !!! new ServiceToken Connection %v", newServiceTokenConnection) + + // Initialize variables for bandwidth limits + var oldAsideBandwidthLimit, newAsideBandwidthLimit int + + // Extract old bandwidth limit + if oldServiceTokenConnection != nil { + for _, connection := range oldServiceTokenConnection.(*schema.Set).List() { + notificationMap := connection.(map[string]interface{}) + + // Extract old bandwidth limit if it exists + if bandwidth, ok := notificationMap["bandwidthLimit"]; ok { + oldBandwidthLimit := bandwidth.([]interface{}) + if len(oldBandwidthLimit) > 0 { + oldAsideBandwidthLimits := converters.IfArrToIntArr(oldBandwidthLimit) + oldAsideBandwidthLimit = oldAsideBandwidthLimits[0] + } + } + } + } + + // Extract new bandwidth limit + if newServiceTokenConnection != nil { + for _, connection := range newServiceTokenConnection.(*schema.Set).List() { + notificationMap := connection.(map[string]interface{}) + + // Extract new bandwidth limit if it exists + if bandwidth, ok := notificationMap["bandwidthLimit"]; ok { + newBandwidthLimit := bandwidth.([]interface{}) + if len(newBandwidthLimit) > 0 { + newAsideBandwidthLimits := converters.IfArrToIntArr(newBandwidthLimit) + newAsideBandwidthLimit = newAsideBandwidthLimits[0] + } + } + } + } + + if oldAsideBandwidthLimit != newAsideBandwidthLimit { + patches = append(patches, fabricv4.ServiceTokenChangeOperation{ + Op: "replace", + Path: "/connection/bandwidthLimit", + Value: newAsideBandwidthLimit, + }) + } + + // Get the old and new values for bandwidth limit + var oldZsideBandwidth, newZsideBandwidth []int + + if oldServiceTokenConnection != nil { + for _, connection := range oldServiceTokenConnection.(*schema.Set).List() { + notificationMap := connection.(map[string]interface{}) + + // Extract old bandwidth limit if it exists + if bandwidth, ok := notificationMap["supported_bandwidths"]; ok { + oldSupportedBandwidth := bandwidth.([]interface{}) + if len(oldSupportedBandwidth) > 0 { + oldZsideBandwidth = converters.IfArrToIntArr(oldSupportedBandwidth) + } + } + } + } + + if newServiceTokenConnection != nil { + for _, connection := range newServiceTokenConnection.(*schema.Set).List() { + notificationMap := connection.(map[string]interface{}) + + // Extract new bandwidth limit if it exists + if bandwidth, ok := notificationMap["supported_bandwidths"]; ok { + newSupportedBandwidth := bandwidth.([]interface{}) + if len(newSupportedBandwidth) > 0 { + newZsideBandwidth = converters.IfArrToIntArr(newSupportedBandwidth) + + } + } + } + } + + log.Print("!!! DEBUG value of new supprted bandwidth", newZsideBandwidth) + log.Printf("[DEBUG] Value of aresliceequal fucntion %v", areSlicesEqual(oldZsideBandwidth, newZsideBandwidth)) + if !areSlicesEqual(oldZsideBandwidth, newZsideBandwidth) { + patches = append(patches, fabricv4.ServiceTokenChangeOperation{ + Op: "replace", + Path: "/connection/supportedBandwidths", + Value: newZsideBandwidth, + }) + } + + return patches +} + +func areSlicesEqual(a, b []int) bool { + if len(a) != len(b) { + return false + } + + // Sort both slices + sort.Ints(a) + sort.Ints(b) + + log.Printf("value of int a %v", a) + log.Printf("value of int b %v", b) + // Compare sorted slices + for i := range a { + if a[i] != b[i] { + return false + } + } + + return true +} + +func buildSearchRequest(d *schema.ResourceData) fabricv4.ServiceTokenSearchRequest { + searchRequest := fabricv4.ServiceTokenSearchRequest{} + + schemaFilters := d.Get("filter").([]interface{}) + filter := filtersTerraformToGo(schemaFilters) + searchRequest.SetFilter(filter) + + if schemaPagination, ok := d.GetOk("pagination"); ok { + pagination := paginationTerraformToGo(schemaPagination.(*schema.Set).List()) + searchRequest.SetPagination(pagination) + } + + return searchRequest +} +func setServiceTokenMap(d *schema.ResourceData, serviceToken *fabricv4.ServiceToken) diag.Diagnostics { + diags := diag.Diagnostics{} + serviceTokenMap := serviceTokenResponseMap(serviceToken) + err := equinix_schema.SetMap(d, serviceTokenMap) + if err != nil { + return diag.FromErr(err) + } + return diags +} + +func setServiceTokensData(d *schema.ResourceData, routeFilters *fabricv4.ServiceTokens) diag.Diagnostics { + diags := diag.Diagnostics{} + mappedRouteFilters := make([]map[string]interface{}, len(routeFilters.Data)) + pagination := routeFilters.GetPagination() + if routeFilters.Data != nil { + for index, routeFilter := range routeFilters.Data { + mappedRouteFilters[index] = serviceTokenResponseMap(&routeFilter) + } + } else { + mappedRouteFilters = nil + } + err := equinix_schema.SetMap(d, map[string]interface{}{ + "data": mappedRouteFilters, + "pagination": paginationGoToTerraform(&pagination), + }) + if err != nil { + return diag.FromErr(err) + } + return diags +} + +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() + 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) + } + if token.Changelog != nil { + changelog := token.GetChangelog() + serviceToken["change_log"] = equinix_fabric_schema.ChangeLogGoToTerraform(&changelog) + } + if token.Project != nil { + project := token.GetProject() + serviceToken["project"] = equinix_fabric_schema.ProjectGoToTerraform(&project) + } + + return serviceToken +} + +func connectionTerraformToGo(connectionTerraform []interface{}) fabricv4.ServiceTokenConnection { + if connectionTerraform == nil || len(connectionTerraform) == 0 { + return fabricv4.ServiceTokenConnection{} + } + + var connection fabricv4.ServiceTokenConnection + + connectionMap := connectionTerraform[0].(map[string]interface{}) + + typeVal := connectionMap["type"].(string) + connection.SetType(fabricv4.ServiceTokenConnectionType(typeVal)) + + uuid := connectionMap["uuid"].(string) + connection.SetUuid(uuid) + + allowRemoteConnection := connectionMap["allow_remote_connection"].(bool) + connection.SetAllowRemoteConnection(allowRemoteConnection) + + allowCustomBandwidth := connectionMap["allow_custom_bandwidth"].(bool) + connection.SetAllowCustomBandwidth(allowCustomBandwidth) + + bandwidthLimit := connectionMap["bandwidth_limit"].(int) + connection.SetBandwidthLimit(int32(bandwidthLimit)) + + supportedBandwidths := connectionMap["supported_bandwidths"].([]interface{}) + if supportedBandwidths != nil { + // Create a new slice to hold the int32 values + int32Bandwidths := make([]int32, len(supportedBandwidths)) + + // Convert []interface{} to []int32 + for i, v := range supportedBandwidths { + int32Bandwidths[i] = int32(v.(int)) // Assign directly to the slice at index i + } + // Set the converted []int32 to the connection + connection.SetSupportedBandwidths(int32Bandwidths) + } + + asideRequest := connectionMap["a_side"].(*schema.Set).List() + zsideRequest := connectionMap["z_side"].(*schema.Set).List() + if len(asideRequest) != 0 { + aside := accessPointTerraformToGo(asideRequest) + connection.SetASide(aside) + } + if len(zsideRequest) != 0 { + zside := accessPointTerraformToGo(zsideRequest) + connection.SetZSide(zside) + } + return connection +} + +func accessPointTerraformToGo(accessPoint []interface{}) fabricv4.ServiceTokenSide { + if accessPoint == nil || len(accessPoint) == 0 { + return fabricv4.ServiceTokenSide{} + } + + var apSide fabricv4.ServiceTokenSide + + accessPointMap := accessPoint[0].(map[string]interface{}) + accessPointSelectors := accessPointMap["access_point_selectors"].(*schema.Set).List() + if len(accessPointSelectors) != 0 { + aps := accessPointSelectorsTerraformToGo(accessPointSelectors) + apSide.SetAccessPointSelectors(aps) + } + return apSide +} + +func accessPointSelectorsTerraformToGo(accessPointSelectors []interface{}) []fabricv4.AccessPointSelector { + if accessPointSelectors == nil || len(accessPointSelectors) == 0 { + return []fabricv4.AccessPointSelector{} + } + + var apSelectors fabricv4.AccessPointSelector + + apSelectorsMap := accessPointSelectors[0].(map[string]interface{}) + typeVal := apSelectorsMap["type"].(string) + apSelectors.SetType(fabricv4.AccessPointSelectorType(typeVal)) + portList := apSelectorsMap["port"].(*schema.Set).List() + linkProtocolList := apSelectorsMap["link_protocol"].(*schema.Set).List() + virtualDeviceList := apSelectorsMap["virtual_device"].(*schema.Set).List() + interfaceList := apSelectorsMap["interface"].(*schema.Set).List() + networkList := apSelectorsMap["network"].(*schema.Set).List() + + if len(portList) != 0 { + port := portTerraformToGo(portList) + apSelectors.SetPort(port) + } + + if len(linkProtocolList) != 0 { + linkProtocol := linkProtocolTerraformToGo(linkProtocolList) + apSelectors.SetLinkProtocol(linkProtocol) + } + + if len(virtualDeviceList) != 0 { + virtualDevice := virtualDeviceTerraformToGo(virtualDeviceList) + apSelectors.SetVirtualDevice(virtualDevice) + } + + if len(interfaceList) != 0 { + interface_ := interfaceTerraformToGo(interfaceList) + apSelectors.SetInterface(interface_) + } + + if len(networkList) != 0 { + network := networkTerraformToGo(networkList) + apSelectors.SetNetwork(network) + } + + return []fabricv4.AccessPointSelector{apSelectors} +} + +func portTerraformToGo(portList []interface{}) fabricv4.SimplifiedMetadataEntity { + if portList == nil || len(portList) == 0 { + return fabricv4.SimplifiedMetadataEntity{} + } + var port fabricv4.SimplifiedMetadataEntity + portListMap := portList[0].(map[string]interface{}) + uuid := portListMap["uuid"].(string) + port.SetUuid(uuid) + + return port +} + +func linkProtocolTerraformToGo(linkProtocolList []interface{}) fabricv4.SimplifiedLinkProtocol { + if linkProtocolList == nil || len(linkProtocolList) == 0 { + return fabricv4.SimplifiedLinkProtocol{} + } + var linkProtocol fabricv4.SimplifiedLinkProtocol + lpMap := linkProtocolList[0].(map[string]interface{}) + lpType := lpMap["type"].(string) + lpVlanSTag := int32(lpMap["vlan_s_tag"].(int)) + lpVlanTag := int32(lpMap["vlan_tag"].(int)) + lpVlanCTag := int32(lpMap["vlan_c_tag"].(int)) + + linkProtocol.SetType(fabricv4.LinkProtocolType(lpType)) + if lpVlanSTag != 0 { + linkProtocol.SetVlanSTag(lpVlanSTag) + } + if lpVlanTag != 0 { + linkProtocol.SetVlanTag(lpVlanTag) + } + if lpVlanCTag != 0 { + linkProtocol.SetVlanCTag(lpVlanCTag) + } + + return linkProtocol +} + +func virtualDeviceTerraformToGo(virtualDeviceList []interface{}) fabricv4.SimplifiedVirtualDevice { + if virtualDeviceList == nil || len(virtualDeviceList) == 0 { + return fabricv4.SimplifiedVirtualDevice{} + } + + var virtualDevice fabricv4.SimplifiedVirtualDevice + virtualDeviceMap := virtualDeviceList[0].(map[string]interface{}) + href := virtualDeviceMap["href"].(string) + type_ := virtualDeviceMap["type"].(string) + uuid := virtualDeviceMap["uuid"].(string) + name := virtualDeviceMap["name"].(string) + cluster := virtualDeviceMap["cluster"].(string) + virtualDevice.SetHref(href) + virtualDevice.SetType(fabricv4.SimplifiedVirtualDeviceType(type_)) + virtualDevice.SetUuid(uuid) + virtualDevice.SetName(name) + virtualDevice.SetCluster(cluster) + + return virtualDevice +} + +func interfaceTerraformToGo(interfaceList []interface{}) fabricv4.VirtualDeviceInterface { + if interfaceList == nil || len(interfaceList) == 0 { + return fabricv4.VirtualDeviceInterface{} + } + + var interface_ fabricv4.VirtualDeviceInterface + interfaceMap := interfaceList[0].(map[string]interface{}) + uuid := interfaceMap["uuid"].(string) + type_ := interfaceMap["type"].(string) + id := interfaceMap["id"].(int) + interface_.SetUuid(uuid) + interface_.SetType(fabricv4.VirtualDeviceInterfaceType(type_)) + interface_.SetId(int32(id)) + + return interface_ +} + +func networkTerraformToGo(networkList []interface{}) fabricv4.SimplifiedTokenNetwork { + if networkList == nil || len(networkList) == 0 { + return fabricv4.SimplifiedTokenNetwork{} + } + var network fabricv4.SimplifiedTokenNetwork + networkListMap := networkList[0].(map[string]interface{}) + uuid := networkListMap["uuid"].(string) + type_ := networkListMap["type"].(string) + network.SetUuid(uuid) + network.SetType(fabricv4.SimplifiedTokenNetworkType(type_)) + return network +} + +func filtersTerraformToGo(tokens []interface{}) fabricv4.ServiceTokenSearchExpression { + if tokens == nil { + return fabricv4.ServiceTokenSearchExpression{} + } + + searchTokensList := make([]fabricv4.ServiceTokenSearchExpression, 0) + + for _, filter := range tokens { + filterMap := filter.(map[string]interface{}) + filterItem := fabricv4.ServiceTokenSearchExpression{} + if property, ok := filterMap["property"]; ok { + filterItem.SetProperty(fabricv4.ServiceTokenSearchFieldName(property.(string))) + } + if operator, ok := filterMap["operator"]; ok { + filterItem.SetOperator(fabricv4.ServiceTokenSearchExpressionOperator(operator.(string))) + } + if values, ok := filterMap["values"]; ok { + stringValues := converters.IfArrToStringArr(values.([]interface{})) + filterItem.SetValues(stringValues) + } + searchTokensList = append(searchTokensList, filterItem) + } + + searchTokens := fabricv4.ServiceTokenSearchExpression{} + searchTokens.SetAnd(searchTokensList) + + return searchTokens +} + +func paginationTerraformToGo(pagination []interface{}) fabricv4.PaginationRequest { + if pagination == nil { + return fabricv4.PaginationRequest{} + } + paginationRequest := fabricv4.PaginationRequest{} + for _, page := range pagination { + pageMap := page.(map[string]interface{}) + if offset, ok := pageMap["offset"]; ok { + paginationRequest.SetOffset(int32(offset.(int))) + } + if limit, ok := pageMap["limit"]; ok { + paginationRequest.SetLimit(int32(limit.(int))) + } + } + + return paginationRequest +} + +func connectionGoToTerraform(connection *fabricv4.ServiceTokenConnection) *schema.Set { + mappedConnection := make(map[string]interface{}) + mappedConnection["type"] = string(connection.GetType()) + mappedConnection["allow_remote_connection"] = connection.GetAllowRemoteConnection() + mappedConnection["allow_custom_bandwidth"] = connection.GetAllowCustomBandwidth() + if connection.SupportedBandwidths != nil { + supportedBandwidths := connection.GetSupportedBandwidths() + interfaceBandwidths := make([]interface{}, len(supportedBandwidths)) + + for i, v := range supportedBandwidths { + interfaceBandwidths[i] = int(v) // Convert each int32 to interface{} + } + + mappedConnection["supported_bandwidths"] = interfaceBandwidths + } + if connection.ASide != nil { + accessPoint := connection.GetASide() + mappedConnection["a_side"] = accessPointGoToTerraform(&accessPoint) + } + if connection.ZSide != nil { + accessPoint := connection.GetZSide() + mappedConnection["z_side"] = accessPointGoToTerraform(&accessPoint) + } + connectionSet := schema.NewSet( + schema.HashResource(serviceTokenConnectionSch()), + []interface{}{mappedConnection}, + ) + return connectionSet +} + +func accessPointGoToTerraform(accessPoint *fabricv4.ServiceTokenSide) *schema.Set { + mappedAccessPoint := make(map[string]interface{}) + if accessPoint.AccessPointSelectors != nil { + accessPointSelectors := accessPoint.GetAccessPointSelectors() + + apSelectorsSet := schema.NewSet( + schema.HashResource(accessPointSelectorsSch()), + nil, + ) + for _, selector := range accessPointSelectors { + mappedSelector := accessPointSelectorsGoToTerraform(&selector) + apSelectorsSet.Add(mappedSelector) + + } + mappedAccessPoint["access_point_selectors"] = apSelectorsSet + } + + accessPointSet := schema.NewSet( + schema.HashResource(accessPointSelectorsSch()), + []interface{}{mappedAccessPoint}, + ) + return accessPointSet +} + +func accessPointSelectorsGoToTerraform(apSelectors *fabricv4.AccessPointSelector) map[string]interface{} { + mappedAccessPointSelectors := make(map[string]interface{}) + if apSelectors.Type != nil { + mappedAccessPointSelectors["type"] = string(apSelectors.GetType()) + } + if apSelectors.Port != nil { + port := apSelectors.GetPort() + mappedAccessPointSelectors["port"] = portGoToTerraform(&port) + } + if apSelectors.LinkProtocol != nil { + linkProtocol := apSelectors.GetLinkProtocol() + mappedAccessPointSelectors["link_protocol"] = linkedProtocolGoToTerraform(&linkProtocol) + } + if apSelectors.VirtualDevice != nil { + virtualDevice := apSelectors.GetVirtualDevice() + mappedAccessPointSelectors["virtual_device"] = virtualDeviceGoToTerraform(&virtualDevice) + } + if apSelectors.Interface != nil { + interface_ := apSelectors.GetInterface() + mappedAccessPointSelectors["interface"] = interfaceGoToTerraform(&interface_) + } + if apSelectors.Network != nil { + network := apSelectors.GetNetwork() + mappedAccessPointSelectors["network"] = networkGoToTerraform(&network) + } + + return mappedAccessPointSelectors +} + +func portGoToTerraform(port *fabricv4.SimplifiedMetadataEntity) *schema.Set { + mappedPort := make(map[string]interface{}) + mappedPort["href"] = port.GetHref() + mappedPort["type"] = port.GetType() + mappedPort["uuid"] = port.GetUuid() + + portSet := schema.NewSet( + schema.HashResource(portSch()), + []interface{}{mappedPort}, + ) + return portSet +} + +func linkedProtocolGoToTerraform(linkedProtocol *fabricv4.SimplifiedLinkProtocol) *schema.Set { + + mappedLinkedProtocol := make(map[string]interface{}) + mappedLinkedProtocol["type"] = string(linkedProtocol.GetType()) + mappedLinkedProtocol["vlan_tag"] = int(linkedProtocol.GetVlanTag()) + mappedLinkedProtocol["vlan_s_tag"] = int(linkedProtocol.GetVlanSTag()) + mappedLinkedProtocol["vlan_c_tag"] = int(linkedProtocol.GetVlanCTag()) + + linkedProtocolSet := schema.NewSet( + schema.HashResource(linkProtocolSch()), + []interface{}{mappedLinkedProtocol}, + ) + return linkedProtocolSet +} + +func virtualDeviceGoToTerraform(virtualDevice *fabricv4.SimplifiedVirtualDevice) *schema.Set { + if virtualDevice == nil { + return nil + } + mappedVirtualDevice := make(map[string]interface{}) + mappedVirtualDevice["name"] = virtualDevice.GetName() + mappedVirtualDevice["href"] = virtualDevice.GetHref() + mappedVirtualDevice["type"] = string(virtualDevice.GetType()) + mappedVirtualDevice["uuid"] = virtualDevice.GetUuid() + if virtualDevice.Cluster != nil { + mappedVirtualDevice["cluster"] = virtualDevice.GetCluster() + } + + virtualDeviceSet := schema.NewSet( + schema.HashResource(virtualDeviceSch()), + []interface{}{mappedVirtualDevice}, + ) + return virtualDeviceSet +} + +func interfaceGoToTerraform(mInterface *fabricv4.VirtualDeviceInterface) *schema.Set { + if mInterface == nil { + return nil + } + mappedMInterface := make(map[string]interface{}) + mappedMInterface["id"] = int(mInterface.GetId()) + mappedMInterface["type"] = string(mInterface.GetType()) + mappedMInterface["uuid"] = mInterface.GetUuid() + + mInterfaceSet := schema.NewSet( + schema.HashResource(interfaceSch()), + []interface{}{mappedMInterface}, + ) + return mInterfaceSet +} + +func networkGoToTerraform(network *fabricv4.SimplifiedTokenNetwork) *schema.Set { + if network == nil { + return nil + } + + mappedNetwork := make(map[string]interface{}) + mappedNetwork["uuid"] = network.GetUuid() + mappedNetwork["href"] = network.GetHref() + mappedNetwork["type"] = string(network.GetType()) + + return schema.NewSet( + schema.HashResource(networkSch()), + []interface{}{mappedNetwork}, + ) +} + +func paginationGoToTerraform(pagination *fabricv4.Pagination) *schema.Set { + if pagination == nil { + return nil + } + mappedPagination := make(map[string]interface{}) + mappedPagination["offset"] = int(pagination.GetOffset()) + mappedPagination["limit"] = int(pagination.GetLimit()) + mappedPagination["total"] = int(pagination.GetTotal()) + mappedPagination["next"] = pagination.GetNext() + mappedPagination["previous"] = pagination.GetPrevious() + + return schema.NewSet( + schema.HashResource(paginationSchema()), + []interface{}{mappedPagination}, + ) +} diff --git a/internal/resources/fabric/service_token/resource.go b/internal/resources/fabric/service_token/resource.go new file mode 100644 index 000000000..94301e915 --- /dev/null +++ b/internal/resources/fabric/service_token/resource.go @@ -0,0 +1,169 @@ +package service_token + +import ( + "context" + "github.com/equinix/equinix-sdk-go/services/fabricv4" + "github.com/equinix/terraform-provider-equinix/internal/config" + equinix_errors "github.com/equinix/terraform-provider-equinix/internal/errors" + equinix_fabric_schema "github.com/equinix/terraform-provider-equinix/internal/fabric/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "log" + "strings" + "time" +) + +func Resource() *schema.Resource { + return &schema.Resource{ + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(10 * time.Minute), + Update: schema.DefaultTimeout(10 * time.Minute), + Delete: schema.DefaultTimeout(10 * time.Minute), + Read: schema.DefaultTimeout(10 * time.Minute), + }, + ReadContext: resourceRead, + CreateContext: resourceCreate, + UpdateContext: resourceUpdate, + DeleteContext: resourceDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + Schema: resourceSchema(), + Description: `Fabric V4 API compatible resource allows creation and management of Equinix Fabric Service Token`, + } +} + +func resourceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*config.Config).NewFabricClientForSDK(d) + serviceToken, _, err := client.ServiceTokensApi.GetServiceTokenByUuid(ctx, d.Id()).Execute() + if err != nil { + log.Printf("[WARN] Service Token %s not found , error %s", d.Id(), err) + if !strings.Contains(err.Error(), "500") { + d.SetId("") + } + return diag.FromErr(equinix_errors.FormatFabricError(err)) + } + d.SetId(serviceToken.GetUuid()) + return setServiceTokenMap(d, serviceToken) +} + +func resourceCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*config.Config).NewFabricClientForSDK(d) + createRequest := buildCreateRequest(d) + + start := time.Now() + serviceToken, _, err := client.ServiceTokensApi.CreateServiceToken(ctx).ServiceToken(createRequest).Execute() + if err != nil { + return diag.FromErr(equinix_errors.FormatFabricError(err)) + } + d.SetId(serviceToken.GetUuid()) + notificationsMap := equinix_fabric_schema.NotificationsGoToTerraform(createRequest.GetNotifications()) + if err = d.Set("notifications", notificationsMap); err != nil { + return diag.Errorf("error setting notifications config to state: %s", err) + } + + createTimeout := d.Timeout(schema.TimeoutCreate) - 30*time.Second - time.Since(start) + 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) + } + + return resourceRead(ctx, d, meta) +} + +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() + if err != nil { + return diag.FromErr(equinix_errors.FormatFabricError(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) + } + + return setServiceTokenMap(d, serviceToken) +} + +func resourceDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + diags := diag.Diagnostics{} + client := meta.(*config.Config).NewFabricClientForSDK(d) + + start := time.Now() + _, _, err := client.ServiceTokensApi.DeleteServiceTokenByUuid(ctx, d.Id()).Execute() + if err != nil { + if genericError, ok := err.(*fabricv4.GenericOpenAPIError); ok { + if fabricErrs, ok := genericError.Model().([]fabricv4.Error); ok { + if equinix_errors.HasErrorCode(fabricErrs, "") { + return diags + } + } + } + return diag.FromErr(equinix_errors.FormatFabricError(err)) + } + + deleteTimeout := d.Timeout(schema.TimeoutDelete) - 30*time.Second - time.Since(start) + if err = WaitForDeletion(d.Id(), meta, d, ctx, deleteTimeout); err != nil { + return diag.Errorf("error waiting for service token (%s) to be deleted: %s", d.Id(), err) + } + return diags +} + +func waitForStability(uuid string, meta interface{}, d *schema.ResourceData, ctx context.Context, tieout time.Duration) error { + log.Printf("Waiting for service token to be created, uuid %s", uuid) + stateConf := &retry.StateChangeConf{ + Target: []string{ + string(fabricv4.SERVICETOKENSTATE_INACTIVE), + }, + Refresh: func() (interface{}, string, error) { + client := meta.(*config.Config).NewFabricClientForSDK(d) + serviceToken, _, err := client.ServiceTokensApi.GetServiceTokenByUuid(ctx, uuid).Execute() + if err != nil { + return "", "", equinix_errors.FormatFabricError(err) + } + return serviceToken, string(serviceToken.GetState()), nil + }, + Timeout: tieout, + Delay: 30 * time.Second, + MinTimeout: 30 * time.Second, + } + + _, err := stateConf.WaitForStateContext(ctx) + + return err +} + +func WaitForDeletion(uuid string, meta interface{}, d *schema.ResourceData, ctx context.Context, timeout time.Duration) error { + log.Printf("Waiting for service token to be deleted, uuid %s", uuid) + stateConf := &retry.StateChangeConf{ + Pending: []string{ + string(fabricv4.SERVICETOKENSTATE_INACTIVE), + }, + Target: []string{ + string(fabricv4.SERVICETOKENSTATE_DELETED), + }, + Refresh: func() (interface{}, string, error) { + client := meta.(*config.Config).NewFabricClientForSDK(d) + serviceToken, body, err := client.ServiceTokensApi.GetServiceTokenByUuid(ctx, uuid).Execute() + if err != nil { + if body.StatusCode >= 400 && body.StatusCode <= 499 { + // Already deleted resource + return serviceToken, string(fabricv4.SERVICETOKENSTATE_DELETED), nil + } + return "", "", equinix_errors.FormatFabricError(err) + } + return serviceToken, string(serviceToken.GetState()), nil + }, + Timeout: timeout, + Delay: 30 * time.Second, + MinTimeout: 30 * time.Second, + } + + _, err := stateConf.WaitForStateContext(ctx) + + return err +} diff --git a/internal/resources/fabric/service_token/resource_schema.go b/internal/resources/fabric/service_token/resource_schema.go new file mode 100644 index 000000000..0b9b5f39e --- /dev/null +++ b/internal/resources/fabric/service_token/resource_schema.go @@ -0,0 +1,424 @@ +package service_token + +import ( + equinix_fabric_schema "github.com/equinix/terraform-provider-equinix/internal/fabric/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceSchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"VC_TOKEN", "EPL_TOKEN"}, false), + Description: "Service Token Type; VC_TOKEN,EPL_TOKEN", + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + Description: "Equinix-assigned service token identifier", + }, + "href": { + Type: schema.TypeString, + Computed: true, + Description: "An absolute URL that is the subject of the link's context.", + }, + "issuer_side": { + Type: schema.TypeString, + Computed: true, + Description: "Information about token side; ASIDE, ZSIDE", + }, + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Name of the Service Token", + }, + "description": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Optional Description to the Service Token you will be creating", + }, + "expiration_date_time": { + Type: schema.TypeString, + Required: true, + Description: "Expiration date and time of the service token; 2020-11-06T07:00:00Z", + }, + "service_token_connection": { + Type: schema.TypeSet, + Required: true, + Description: "Service Token Connection Type Information", + Elem: serviceTokenConnectionSch(), + }, + "state": { + Type: schema.TypeString, + Computed: true, + Description: "Service token state; ACTIVE, INACTIVE, EXPIRED, DELETED", + }, + "notifications": { + Type: schema.TypeSet, + Required: true, + Description: "Preferences for notifications on Service Token configuration or status changes", + Elem: &schema.Resource{ + Schema: equinix_fabric_schema.NotificationSch(), + }, + }, + "account": { + Type: schema.TypeSet, + Computed: true, + Description: "Customer account information that is associated with this service token", + Elem: &schema.Resource{ + Schema: equinix_fabric_schema.AccountSch(), + }, + }, + "change_log": { + Type: schema.TypeSet, + Computed: true, + Description: "Captures connection lifecycle change information", + Elem: &schema.Resource{ + Schema: equinix_fabric_schema.ChangeLogSch(), + }, + }, + "project": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Description: "Project information", + MaxItems: 1, + Elem: &schema.Resource{ + Schema: equinix_fabric_schema.ProjectSch(), + }, + }, + } +} + +func serviceTokenConnectionSch() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Required: true, + Description: "Type of Connection supported by Service Token you will create; EVPL_VC, EVPLAN_VC, EPLAN_VC, IPWAN_VC", + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + Description: "Equinix-assigned connection identifier", + }, + "allow_remote_connection": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + Description: "Authorization to connect remotely", + }, + "allow_custom_bandwidth": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + Description: "Allow custom bandwidth value", + }, + "bandwidth_limit": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + ValidateFunc: validation.IntBetween(1, 100000), + Description: "Connection bandwidth limit in Mbps", + }, + "supported_bandwidths": { + Type: schema.TypeList, + Required: true, + Description: "List of permitted bandwidths", + Elem: &schema.Schema{ + Type: schema.TypeInt, + }, + }, + "a_side": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Description: "A-Side Connection link protocol,virtual device or network configuration", + Elem: serviceTokenAccessPointSch(), + }, + "z_side": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Description: "Z-Side Connection link protocol,virtual device or network configuration", + Elem: serviceTokenAccessPointSch(), + }, + }, + } +} + +func serviceTokenAccessPointSch() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "access_point_selectors": { + Type: schema.TypeSet, + Required: true, + Description: "List of criteria for selecting network access points with optimal efficiency, security, compatibility, and availability", + Elem: accessPointSelectorsSch(), + }, + }, + } +} + +func accessPointSelectorsSch() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Type of Access point; COLO, VD, NETWORK", + }, + "port": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Description: "Port Configuration", + MaxItems: 1, + Elem: portSch(), + }, + "link_protocol": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Description: "Link protocol Configuration", + MaxItems: 1, + Elem: linkProtocolSch(), + }, + "virtual_device": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Description: "Virtual Device Configuration", + MaxItems: 1, + Elem: virtualDeviceSch(), + }, + "interface": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Description: "Virtual Device Interface Configuration", + MaxItems: 1, + Elem: interfaceSch(), + }, + "network": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Description: "Network Configuration", + MaxItems: 1, + Elem: networkSch(), + }, + }, + } +} + +func portSch() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "href": { + Type: schema.TypeString, + Computed: true, + Description: "Unique Resource Identifier", + }, + "uuid": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Equinix-assigned Port identifier", + }, + "type": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: "Type of Port", + }, + "cvp_id": { + Type: schema.TypeInt, + Computed: true, + Optional: true, + Description: "Customer virtual port Id", + }, + "bandwidth": { + Type: schema.TypeInt, + Computed: true, + Optional: true, + Description: "Port Bandwidth", + }, + "port_name": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: "Port Name", + }, + "encapsulation_protocol_type": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: "Port Encapsulation", + }, + "account_name": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: "Account Name", + }, + "priority": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: "Port Priority", + }, + "location": { + Type: schema.TypeSet, + Computed: true, + Optional: true, + Description: "Port Location", + Elem: &schema.Resource{ + Schema: equinix_fabric_schema.LocationSch(), + }, + }, + }, + } +} + +func virtualDeviceSch() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "href": { + Type: schema.TypeString, + Computed: true, + Description: "Unique Resource Identifier", + }, + "uuid": { + Type: schema.TypeString, + Required: true, + Description: "Equinix-assigned Virtual Device identifier", + }, + "type": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Virtual Device type", + }, + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Customer-assigned Virtual Device Name", + }, + "cluster": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Virtual Device Cluster Information", + }, + }, + } +} + +func linkProtocolSch() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Type of the link protocol - UNTAGGED, DOT1Q, QINQ, EVPN_VXLAN", + ValidateFunc: validation.StringInSlice([]string{"UNTAGGED", "DOT1Q", "QINQ", "EVPN_VXLAN"}, true), + }, + "vlan_tag": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "Vlan Tag information, vlanTag value specified for DOT1Q connections", + }, + "vlan_s_tag": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "Vlan Provider Tag information, vlanSTag value specified for QINQ connections", + }, + "vlan_c_tag": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "Vlan Customer Tag information, vlanCTag value specified for QINQ connections", + }, + }, + } +} + +func interfaceSch() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "uuid": { + Type: schema.TypeString, + Computed: true, + Description: "Equinix-assigned interface identifier", + }, + "id": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "id", + }, + "type": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Interface type", + }, + }, + } +} + +func networkSch() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "uuid": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Equinix-assigned Network identifier", + }, + "href": { + Type: schema.TypeString, + Computed: true, + Description: "Unique Resource Identifier", + }, + "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{ + Schema: equinix_fabric_schema.LocationSch(), + }, + }, + }, + } +} diff --git a/internal/resources/fabric/service_token/resource_test.go b/internal/resources/fabric/service_token/resource_test.go new file mode 100644 index 000000000..c571b4230 --- /dev/null +++ b/internal/resources/fabric/service_token/resource_test.go @@ -0,0 +1,94 @@ +package service_token_test + +import ( + "context" + "fmt" + "github.com/equinix/terraform-provider-equinix/internal/acceptance" + "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/service_token" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "testing" + "time" +) + +func TestAccFabricServiceToken_PNFV(t *testing.T) { + serviceTokenName, serviceTokenUpdatedName := "Service_token_PNFV", "UP_Service_Token_PNFV" + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.TestAccPreCheck(t) }, + Providers: acceptance.TestAccProviders, + CheckDestroy: CheckServiceTokenDelete, + Steps: []resource.TestStep{ + { + Config: testAccFabricServiceTokenConfig(serviceTokenName), + 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", "zside vd token"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "expiration_date_time", "2024-11-18T06:43:49.980Z"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.supported_bandwidths.#", "3"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid", "fcf0fcec-65f6-4544-8810-ae4756fab8c4"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id", "5"), + ), + ExpectNonEmptyPlan: true, + }, + { + Config: testAccFabricServiceTokenConfig(serviceTokenUpdatedName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("equinix_fabric_service_token.test", "uuid"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "name", serviceTokenUpdatedName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccFabricServiceTokenConfig(serviceTokenName string) string { + return fmt.Sprintf( + `resource "equinix_fabric_service_token" "test"{ + type = "VC_TOKEN" + name = "%s" + description = "zside vd token" + expiration_date_time = "2024-11-18T06:43:49.980Z" + service_token_connection { + type = "EVPL_VC" + supported_bandwidths = [50, 200, 10000] + z_side { + access_point_selectors{ + type = "VD" + virtual_device{ + type = "EDGE" + uuid = "fcf0fcec-65f6-4544-8810-ae4756fab8c4" + } + interface{ + type = "NETWORK" + id = 5 + } + } + } + } + notifications { + type = "ALL" + emails = ["example@equinix.com", "test1@equinix.com"] + } + + } + `, serviceTokenName) +} + +func CheckServiceTokenDelete(s *terraform.State) error { + ctx := context.Background() + for _, rs := range s.RootModule().Resources { + if rs.Type != "equinix_fabric_service_token" { + continue + } + + err := service_token.WaitForDeletion(rs.Primary.ID, acceptance.TestAccProvider.Meta(), &schema.ResourceData{}, ctx, 10*time.Minute) + if err != nil { + return fmt.Errorf("API call failed while waiting for resource deletion") + } + } + return nil +} From 760e9c20399246103a07114dcb179e149283111c Mon Sep 17 00:00:00 2001 From: srushti-patl Date: Wed, 23 Oct 2024 13:23:59 -0700 Subject: [PATCH 17/55] fix: Updating Service Token Resource & Data source --- .../fabric/service_token/datasources_test.go | 93 +++++++++++++++++++ .../resources/fabric/service_token/models.go | 26 ++++-- .../fabric/service_token/resource_schema.go | 2 + .../fabric/service_token/resource_test.go | 25 +++-- 4 files changed, 129 insertions(+), 17 deletions(-) create mode 100644 internal/resources/fabric/service_token/datasources_test.go diff --git a/internal/resources/fabric/service_token/datasources_test.go b/internal/resources/fabric/service_token/datasources_test.go new file mode 100644 index 000000000..943c67852 --- /dev/null +++ b/internal/resources/fabric/service_token/datasources_test.go @@ -0,0 +1,93 @@ +package service_token_test + +import ( + "fmt" + "github.com/equinix/terraform-provider-equinix/internal/acceptance" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "testing" +) + +func TestAccFabricServiceTokenDataSource_PNFV(t *testing.T) { + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.TestAccPreCheck(t) }, + Providers: acceptance.TestAccProviders, + CheckDestroy: CheckServiceTokenDelete, + Steps: []resource.TestStep{ + { + Config: testAccFabricServiceTokenConfigDataSourceConfig(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.equinix_fabric_service_token.service-token", "uuid"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token", "type", "VC_TOKEN"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token", "expiration_date_time", "2024-11-18T06:43:49.980Z"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token", "service_token_connection.0.supported_bandwidths.#", "3"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.type", "EDGE"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid", "fcf0fcec-65f6-4544-8810-ae4756fab8c4"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.type", "NETWORK"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id", "5"), + resource.TestCheckResourceAttrSet("data.equinix_fabric_service_tokens.service-tokens", "data.0.uuid"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "data.0.type", "VC_TOKEN"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "data.0.expiration_date_time", "2024-11-18T06:43:49.980Z"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "data.0.service_token_connection.0.supported_bandwidths.#", "3"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.type", "EDGE"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "data.0.service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid", "fcf0fcec-65f6-4544-8810-ae4756fab8c4"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.type", "NETWORK"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "data.0.service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id", "5"), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccFabricServiceTokenConfigDataSourceConfig() string { + return fmt.Sprintf( + `resource "equinix_fabric_service_token" "test"{ + type = "VC_TOKEN" + expiration_date_time = "2024-11-18T06:43:49.980Z" + service_token_connection { + type = "EVPL_VC" + supported_bandwidths = [50, 200, 10000] + z_side { + access_point_selectors{ + type = "VD" + virtual_device{ + type = "EDGE" + uuid = "fcf0fcec-65f6-4544-8810-ae4756fab8c4" + } + interface{ + type = "NETWORK" + id = 5 + } + } + } + } + notifications { + type = "ALL" + emails = ["example@equinix.com", "test1@equinix.com"] + } + } + + data "equinix_fabric_service_token" "service-token" { + uuid = equinix_fabric_service_token.test.id + } + + data "equinix_fabric_service_tokens" "service-tokens"{ + filter { + property = "/uuid" + operator = "=" + values = [equinix_fabric_service_token.test.id] + } + filter { + property = "/state" + operator = "=" + values = ["INACTIVE"] + } + pagination { + offset = 0 + limit = 5 + total = 25 + } + } + + `) +} diff --git a/internal/resources/fabric/service_token/models.go b/internal/resources/fabric/service_token/models.go index 6fff6d489..6d38f7e42 100644 --- a/internal/resources/fabric/service_token/models.go +++ b/internal/resources/fabric/service_token/models.go @@ -282,10 +282,6 @@ func serviceTokenResponseMap(token *fabricv4.ServiceToken) map[string]interface{ 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) @@ -562,6 +558,9 @@ func connectionGoToTerraform(connection *fabricv4.ServiceTokenConnection) *schem mappedConnection["supported_bandwidths"] = interfaceBandwidths } + if connection.BandwidthLimit != nil { + mappedConnection["bandwidth_limit"] = int(connection.GetBandwidthLimit()) + } if connection.ASide != nil { accessPoint := connection.GetASide() mappedConnection["a_side"] = accessPointGoToTerraform(&accessPoint) @@ -663,14 +662,21 @@ func virtualDeviceGoToTerraform(virtualDevice *fabricv4.SimplifiedVirtualDevice) return nil } mappedVirtualDevice := make(map[string]interface{}) - mappedVirtualDevice["name"] = virtualDevice.GetName() - mappedVirtualDevice["href"] = virtualDevice.GetHref() - mappedVirtualDevice["type"] = string(virtualDevice.GetType()) - mappedVirtualDevice["uuid"] = virtualDevice.GetUuid() - if virtualDevice.Cluster != nil { + if name := virtualDevice.GetName(); name != "" { + mappedVirtualDevice["name"] = name + } + if href := virtualDevice.GetHref(); href != "" { + mappedVirtualDevice["href"] = href + } + if virtualDevice.GetType() != "" { + mappedVirtualDevice["type"] = string(virtualDevice.GetType()) + } + if uuid := virtualDevice.GetUuid(); uuid != "" { + mappedVirtualDevice["uuid"] = uuid + } + if virtualDevice.Cluster != nil && virtualDevice.GetCluster() != "" { mappedVirtualDevice["cluster"] = virtualDevice.GetCluster() } - virtualDeviceSet := schema.NewSet( schema.HashResource(virtualDeviceSch()), []interface{}{mappedVirtualDevice}, diff --git a/internal/resources/fabric/service_token/resource_schema.go b/internal/resources/fabric/service_token/resource_schema.go index 0b9b5f39e..793ced695 100644 --- a/internal/resources/fabric/service_token/resource_schema.go +++ b/internal/resources/fabric/service_token/resource_schema.go @@ -111,12 +111,14 @@ func serviceTokenConnectionSch() *schema.Resource { Type: schema.TypeBool, Optional: true, Computed: true, + Default: true, Description: "Authorization to connect remotely", }, "allow_custom_bandwidth": { Type: schema.TypeBool, Optional: true, Computed: true, + Default: true, Description: "Allow custom bandwidth value", }, "bandwidth_limit": { diff --git a/internal/resources/fabric/service_token/resource_test.go b/internal/resources/fabric/service_token/resource_test.go index c571b4230..d3d9e6183 100644 --- a/internal/resources/fabric/service_token/resource_test.go +++ b/internal/resources/fabric/service_token/resource_test.go @@ -14,30 +14,41 @@ import ( func TestAccFabricServiceToken_PNFV(t *testing.T) { serviceTokenName, serviceTokenUpdatedName := "Service_token_PNFV", "UP_Service_Token_PNFV" + serviceTokenDescription, serviceTokenUpdatedDescription := "zside vd token", "Updated zside vd token" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acceptance.TestAccPreCheck(t) }, Providers: acceptance.TestAccProviders, CheckDestroy: CheckServiceTokenDelete, Steps: []resource.TestStep{ { - Config: testAccFabricServiceTokenConfig(serviceTokenName), + Config: testAccFabricServiceTokenConfig(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", "zside vd token"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "description", serviceTokenDescription), resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "expiration_date_time", "2024-11-18T06:43:49.980Z"), resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.supported_bandwidths.#", "3"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.type", "EDGE"), resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid", "fcf0fcec-65f6-4544-8810-ae4756fab8c4"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.type", "NETWORK"), resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id", "5"), ), ExpectNonEmptyPlan: true, }, { - Config: testAccFabricServiceTokenConfig(serviceTokenUpdatedName), + Config: testAccFabricServiceTokenConfig(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", "2024-11-18T06:43:49.980Z"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.supported_bandwidths.#", "3"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.type", "EDGE"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid", "fcf0fcec-65f6-4544-8810-ae4756fab8c4"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.type", "NETWORK"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id", "5"), ), ExpectNonEmptyPlan: true, }, @@ -45,13 +56,13 @@ func TestAccFabricServiceToken_PNFV(t *testing.T) { }) } -func testAccFabricServiceTokenConfig(serviceTokenName string) string { +func testAccFabricServiceTokenConfig(serviceTokenName string, serviceTokenDescription string) string { return fmt.Sprintf( `resource "equinix_fabric_service_token" "test"{ type = "VC_TOKEN" name = "%s" - description = "zside vd token" - expiration_date_time = "2024-11-18T06:43:49.980Z" + description = "%s" + expiration_date_time = "2024-11-18T06:43:49.98Z" service_token_connection { type = "EVPL_VC" supported_bandwidths = [50, 200, 10000] @@ -75,7 +86,7 @@ func testAccFabricServiceTokenConfig(serviceTokenName string) string { } } - `, serviceTokenName) + `, serviceTokenName, serviceTokenDescription) } func CheckServiceTokenDelete(s *terraform.State) error { From 66465a098893a732901de0ae63820feec68d9893 Mon Sep 17 00:00:00 2001 From: srushti-patl Date: Wed, 16 Oct 2024 13:43:41 -0700 Subject: [PATCH 18/55] feat: Adding Fabric Service Token Resource and Data Source --- equinix/provider.go | 54 +++++++++---------- .../fabric/service_token/resource_schema.go | 2 - 2 files changed, 26 insertions(+), 30 deletions(-) diff --git a/equinix/provider.go b/equinix/provider.go index bf38b2592..cb962d277 100644 --- a/equinix/provider.go +++ b/equinix/provider.go @@ -88,8 +88,6 @@ func Provider() *schema.Provider { "equinix_fabric_routing_protocol": dataSourceRoutingProtocol(), "equinix_fabric_connection": fabric_connection.DataSource(), "equinix_fabric_connections": fabric_connection.DataSourceSearch(), - "equinix_fabric_connection_route_filter": fabric_connection_route_filter.DataSource(), - "equinix_fabric_connection_route_filters": fabric_connection_route_filter.DataSourceGetAllRules(), "equinix_fabric_cloud_router": dataSourceFabricCloudRouter(), "equinix_fabric_cloud_routers": dataSourceFabricGetCloudRouters(), "equinix_fabric_market_place_subscription": fabric_market_place_subscription.DataSourceFabricMarketplaceSubscription(), @@ -128,34 +126,34 @@ func Provider() *schema.Provider { "equinix_metal_vrf": vrf.DataSource(), }, ResourcesMap: map[string]*schema.Resource{ - "equinix_fabric_network": fabric_network.Resource(), - "equinix_fabric_cloud_router": resourceFabricCloudRouter(), - "equinix_fabric_connection": fabric_connection.Resource(), + "equinix_fabric_network": fabric_network.Resource(), + "equinix_fabric_cloud_router": resourceFabricCloudRouter(), + "equinix_fabric_connection": fabric_connection.Resource(), "equinix_fabric_connection_route_filter": fabric_connection_route_filter.Resource(), - "equinix_fabric_route_filter": fabric_route_filter.Resource(), - "equinix_fabric_route_filter_rule": fabric_route_filter_rule.Resource(), - "equinix_fabric_routing_protocol": resourceFabricRoutingProtocol(), - "equinix_fabric_service_profile": resourceFabricServiceProfile(), + "equinix_fabric_route_filter": fabric_route_filter.Resource(), + "equinix_fabric_route_filter_rule": fabric_route_filter_rule.Resource(), + "equinix_fabric_routing_protocol": resourceFabricRoutingProtocol(), + "equinix_fabric_service_profile": resourceFabricServiceProfile(), "equinix_fabric_service_token": fabric_service_token.Resource(), - "equinix_network_device": resourceNetworkDevice(), - "equinix_network_ssh_user": resourceNetworkSSHUser(), - "equinix_network_bgp": resourceNetworkBGP(), - "equinix_network_ssh_key": resourceNetworkSSHKey(), - "equinix_network_acl_template": resourceNetworkACLTemplate(), - "equinix_network_device_link": resourceNetworkDeviceLink(), - "equinix_network_file": resourceNetworkFile(), - "equinix_metal_user_api_key": resourceMetalUserAPIKey(), - "equinix_metal_project_api_key": resourceMetalProjectAPIKey(), - "equinix_metal_device": metal_device.Resource(), - "equinix_metal_device_network_type": resourceMetalDeviceNetworkType(), - "equinix_metal_port": metal_port.Resource(), - "equinix_metal_reserved_ip_block": resourceMetalReservedIPBlock(), - "equinix_metal_ip_attachment": resourceMetalIPAttachment(), - "equinix_metal_spot_market_request": resourceMetalSpotMarketRequest(), - "equinix_metal_virtual_circuit": virtual_circuit.Resource(), - "equinix_metal_vrf": vrf.Resource(), - "equinix_metal_bgp_session": resourceMetalBGPSession(), - "equinix_metal_port_vlan_attachment": resourceMetalPortVlanAttachment(), + "equinix_network_device": resourceNetworkDevice(), + "equinix_network_ssh_user": resourceNetworkSSHUser(), + "equinix_network_bgp": resourceNetworkBGP(), + "equinix_network_ssh_key": resourceNetworkSSHKey(), + "equinix_network_acl_template": resourceNetworkACLTemplate(), + "equinix_network_device_link": resourceNetworkDeviceLink(), + "equinix_network_file": resourceNetworkFile(), + "equinix_metal_user_api_key": resourceMetalUserAPIKey(), + "equinix_metal_project_api_key": resourceMetalProjectAPIKey(), + "equinix_metal_device": metal_device.Resource(), + "equinix_metal_device_network_type": resourceMetalDeviceNetworkType(), + "equinix_metal_port": metal_port.Resource(), + "equinix_metal_reserved_ip_block": resourceMetalReservedIPBlock(), + "equinix_metal_ip_attachment": resourceMetalIPAttachment(), + "equinix_metal_spot_market_request": resourceMetalSpotMarketRequest(), + "equinix_metal_virtual_circuit": virtual_circuit.Resource(), + "equinix_metal_vrf": vrf.Resource(), + "equinix_metal_bgp_session": resourceMetalBGPSession(), + "equinix_metal_port_vlan_attachment": resourceMetalPortVlanAttachment(), }, ProviderMetaSchema: map[string]*schema.Schema{ "module_name": { diff --git a/internal/resources/fabric/service_token/resource_schema.go b/internal/resources/fabric/service_token/resource_schema.go index 793ced695..0b9b5f39e 100644 --- a/internal/resources/fabric/service_token/resource_schema.go +++ b/internal/resources/fabric/service_token/resource_schema.go @@ -111,14 +111,12 @@ func serviceTokenConnectionSch() *schema.Resource { Type: schema.TypeBool, Optional: true, Computed: true, - Default: true, Description: "Authorization to connect remotely", }, "allow_custom_bandwidth": { Type: schema.TypeBool, Optional: true, Computed: true, - Default: true, Description: "Allow custom bandwidth value", }, "bandwidth_limit": { From 0f1e42a8c536c1095ab999adc121a9b22e2dda66 Mon Sep 17 00:00:00 2001 From: srushti-patl Date: Sun, 27 Oct 2024 23:09:40 -0700 Subject: [PATCH 19/55] feat: Adding doc changes for Service Token resource and data sources --- docs/data-sources/fabric_service_token.md | 350 +++++++++++++++ docs/data-sources/fabric_service_tokens.md | 405 ++++++++++++++++++ docs/resources/fabric_service_token.md | 397 +++++++++++++++++ .../data-source.tf | 35 ++ .../data-source.tf | 53 +++ .../fabric/service_token/datasources.go | 12 +- .../resources/fabric/service_token/models.go | 109 ++--- .../fabric/service_token/resource.go | 3 +- .../fabric/service_token/resource_schema.go | 8 +- .../resources/fabric_service_token.md.tmpl | 23 + 10 files changed, 1317 insertions(+), 78 deletions(-) create mode 100644 docs/data-sources/fabric_service_token.md create mode 100644 docs/data-sources/fabric_service_tokens.md create mode 100644 docs/resources/fabric_service_token.md create mode 100644 examples/data-sources/equinix_fabric_service_token/data-source.tf create mode 100644 examples/data-sources/equinix_fabric_service_tokens/data-source.tf create mode 100644 templates/resources/fabric_service_token.md.tmpl diff --git a/docs/data-sources/fabric_service_token.md b/docs/data-sources/fabric_service_token.md new file mode 100644 index 000000000..136996f62 --- /dev/null +++ b/docs/data-sources/fabric_service_token.md @@ -0,0 +1,350 @@ +--- +subcategory: "Fabric" +--- + +# equinix_fabric_service_token (Data Source) + +Fabric V4 API compatible data resource that allow user to fetch service token for a given UUID + +Additional documentation: +* Getting Started: https://docs.equinix.com/en-us/Content/Interconnection/Fabric/service%20tokens/Fabric-Service-Tokens.htm +* API: https://docs.equinix.com/en-us/Content/KnowledgeCenter/Fabric/GettingStarted/Integrating-with-Fabric-V4-APIs/ConnectUsingServiceToken.htm + +## Example Usage + +```terraform +data "equinix_fabric_service_token" "service-token" { + uuid = "" +} + +output "id" { + value = data.equinix_fabric_service_token.service-token.id +} + +output "type" { + value = data.equinix_fabric_service_token.service-token.type +} + +output "expiration_date_time" { + value = data.equinix_fabric_service_token.service-token.expiration_date_time +} + +output "supported_bandwidths" { + value = data.equinix_fabric_service_token.service-token.service_token_connection.0.supported_bandwidths +} + +output "virtual_device_type" { + value = data.equinix_fabric_service_token.service-token.service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.type +} + +output "virtual_device_uuid" { + value = data.equinix_fabric_service_token.service-token.service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid +} + +output "interface_type" { + value = data.equinix_fabric_service_token.service-token.service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.type +} + +output "interface_uuid" { + value = data.equinix_fabric_service_token.service-token.service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id +} +``` + + +## Schema + +### Required + +- `uuid` (String) Equinix-assigned service token identifier + +### Read-Only + +- `account` (Set of Object) Customer account information that is associated with this service token (see [below for nested schema](#nestedatt--account)) +- `change_log` (Set of Object) Captures connection lifecycle change information (see [below for nested schema](#nestedatt--change_log)) +- `description` (String) Optional Description to the Service Token you will be creating +- `expiration_date_time` (String) Expiration date and time of the service token; 2020-11-06T07:00:00Z +- `href` (String) An absolute URL that is the subject of the link's context. +- `id` (String) The ID of this resource. +- `issuer_side` (String) Information about token side; ASIDE, ZSIDE +- `name` (String) Name of the Service Token +- `notifications` (Set of Object) Preferences for notifications on Service Token configuration or status changes (see [below for nested schema](#nestedatt--notifications)) +- `project` (Set of Object) Project information (see [below for nested schema](#nestedatt--project)) +- `service_token_connection` (Set of Object) Service Token Connection Type Information (see [below for nested schema](#nestedatt--service_token_connection)) +- `state` (String) Service token state; ACTIVE, INACTIVE, EXPIRED, DELETED +- `type` (String) Service Token Type; VC_TOKEN,EPL_TOKEN + + +### Nested Schema for `account` + +Read-Only: + +- `account_name` (String) +- `account_number` (Number) +- `global_cust_id` (String) +- `global_org_id` (String) +- `global_organization_name` (String) +- `org_id` (Number) +- `organization_name` (String) +- `ucm_id` (String) + + + +### Nested Schema for `change_log` + +Read-Only: + +- `created_by` (String) +- `created_by_email` (String) +- `created_by_full_name` (String) +- `created_date_time` (String) +- `deleted_by` (String) +- `deleted_by_email` (String) +- `deleted_by_full_name` (String) +- `deleted_date_time` (String) +- `updated_by` (String) +- `updated_by_email` (String) +- `updated_by_full_name` (String) +- `updated_date_time` (String) + + + +### Nested Schema for `notifications` + +Read-Only: + +- `emails` (List of String) +- `send_interval` (String) +- `type` (String) + + + +### Nested Schema for `project` + +Read-Only: + +- `href` (String) +- `project_id` (String) + + + +### Nested Schema for `service_token_connection` + +Read-Only: + +- `a_side` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--a_side)) +- `allow_custom_bandwidth` (Boolean) +- `allow_remote_connection` (Boolean) +- `bandwidth_limit` (Number) +- `supported_bandwidths` (List of Number) +- `type` (String) +- `uuid` (String) +- `z_side` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--z_side)) + + +### Nested Schema for `service_token_connection.a_side` + +Read-Only: + +- `access_point_selectors` (List of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--a_side--access_point_selectors)) + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors` + +Read-Only: + +- `interface` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--a_side--access_point_selectors--interface)) +- `link_protocol` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--a_side--access_point_selectors--link_protocol)) +- `network` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--a_side--access_point_selectors--network)) +- `port` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--a_side--access_point_selectors--port)) +- `type` (String) +- `virtual_device` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--a_side--access_point_selectors--virtual_device)) + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.interface` + +Read-Only: + +- `id` (Number) +- `type` (String) +- `uuid` (String) + + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.link_protocol` + +Read-Only: + +- `type` (String) +- `vlan_c_tag` (Number) +- `vlan_s_tag` (Number) +- `vlan_tag` (Number) + + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.network` + +Read-Only: + +- `href` (String) +- `location` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--a_side--access_point_selectors--network--location)) +- `name` (String) +- `scope` (String) +- `type` (String) +- `uuid` (String) + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.network.location` + +Read-Only: + +- `ibx` (String) +- `metro_code` (String) +- `metro_name` (String) +- `region` (String) + + + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.port` + +Read-Only: + +- `account_name` (String) +- `bandwidth` (Number) +- `cvp_id` (Number) +- `encapsulation_protocol_type` (String) +- `href` (String) +- `location` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--a_side--access_point_selectors--port--location)) +- `port_name` (String) +- `priority` (String) +- `type` (String) +- `uuid` (String) + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.port.location` + +Read-Only: + +- `ibx` (String) +- `metro_code` (String) +- `metro_name` (String) +- `region` (String) + + + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.virtual_device` + +Read-Only: + +- `cluster` (String) +- `href` (String) +- `name` (String) +- `type` (String) +- `uuid` (String) + + + + + +### Nested Schema for `service_token_connection.z_side` + +Read-Only: + +- `access_point_selectors` (List of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--z_side--access_point_selectors)) + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors` + +Read-Only: + +- `interface` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--z_side--access_point_selectors--interface)) +- `link_protocol` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--z_side--access_point_selectors--link_protocol)) +- `network` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--z_side--access_point_selectors--network)) +- `port` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--z_side--access_point_selectors--port)) +- `type` (String) +- `virtual_device` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--z_side--access_point_selectors--virtual_device)) + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.interface` + +Read-Only: + +- `id` (Number) +- `type` (String) +- `uuid` (String) + + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.link_protocol` + +Read-Only: + +- `type` (String) +- `vlan_c_tag` (Number) +- `vlan_s_tag` (Number) +- `vlan_tag` (Number) + + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.network` + +Read-Only: + +- `href` (String) +- `location` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--z_side--access_point_selectors--network--location)) +- `name` (String) +- `scope` (String) +- `type` (String) +- `uuid` (String) + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.network.location` + +Read-Only: + +- `ibx` (String) +- `metro_code` (String) +- `metro_name` (String) +- `region` (String) + + + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.port` + +Read-Only: + +- `account_name` (String) +- `bandwidth` (Number) +- `cvp_id` (Number) +- `encapsulation_protocol_type` (String) +- `href` (String) +- `location` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--z_side--access_point_selectors--port--location)) +- `port_name` (String) +- `priority` (String) +- `type` (String) +- `uuid` (String) + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.port.location` + +Read-Only: + +- `ibx` (String) +- `metro_code` (String) +- `metro_name` (String) +- `region` (String) + + + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.virtual_device` + +Read-Only: + +- `cluster` (String) +- `href` (String) +- `name` (String) +- `type` (String) +- `uuid` (String) diff --git a/docs/data-sources/fabric_service_tokens.md b/docs/data-sources/fabric_service_tokens.md new file mode 100644 index 000000000..2379afcfc --- /dev/null +++ b/docs/data-sources/fabric_service_tokens.md @@ -0,0 +1,405 @@ +--- +subcategory: "Fabric" +--- + +# equinix_fabric_service_tokens (Data Source) + +Fabric V4 API compatible data resource that allow user to fetch service token for a given search data set + +Additional documentation: +* Getting Started: https://docs.equinix.com/en-us/Content/Interconnection/Fabric/service%20tokens/Fabric-Service-Tokens.htm +* API: https://docs.equinix.com/en-us/Content/KnowledgeCenter/Fabric/GettingStarted/Integrating-with-Fabric-V4-APIs/ConnectUsingServiceToken.htm + +## Example Usage + +```terraform +data "equinix_fabric_service_tokens" "service-tokens" { + filter { + property = "/type" + operator = "=" + values = "EVPL_VC" + } + filter { + property = "/state" + operator = "=" + values = ["INACTIVE"] + } + pagination { + offset = 0 + limit = 5 + total = 25 + } +} + +output "number_of_returned_service_tokens" { + value = length(data.equinix_fabric_service_tokens.service-tokens.data) +} + +output "first_service_token_id" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.id +} + +output "first_service_token_type" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.type +} + +output "first_service_token_expiration_date_time" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.expiration_date_time +} + +output "first_service_token_supported_bandwidths" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.service_token_connection.0.supported_bandwidths +} + +output "first_service_token_virtual_device_type" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.type +} + +output "first_service_token_virtual_device_uuid" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid +} + +output "first_service_token_interface_type" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.type +} + +output "first_service_token_interface_uuid" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id +} +``` + + +## Schema + +### Required + +- `filter` (Block List, Min: 1, Max: 10) Filters for the Data Source Search Request. Maximum of 8 total filters. (see [below for nested schema](#nestedblock--filter)) + +### Optional + +- `pagination` (Block Set, Max: 1) Pagination details for the Data Source Search Request (see [below for nested schema](#nestedblock--pagination)) + +### Read-Only + +- `data` (List of Object) List of Route Filters (see [below for nested schema](#nestedatt--data)) +- `id` (String) The ID of this resource. + + +### Nested Schema for `filter` + +Required: + +- `operator` (String) Possible operators to use on the filter property. Can be one of the following: [ "=", "!=", "[NOT] LIKE", "[NOT] IN", "ILIKE" ] +- `property` (String) The API response property which you want to filter your request on. Can be one of the following: "/type", "/name", "/project/projectId", "/uuid", "/state" +- `values` (List of String) The values that you want to apply the property+operator combination to in order to filter your data search + + + +### Nested Schema for `pagination` + +Optional: + +- `limit` (Number) Number of elements to be requested per page. Number must be between 1 and 100. Default is 20 +- `offset` (Number) The page offset for the pagination request. Index of the first element. Default is 0. +- `total` (Number) Total number of elements returned. + +Read-Only: + +- `next` (String) URL relative to the last item in the response. +- `previous` (String) URL relative to the first item in the response. + + + +### Nested Schema for `data` + +Read-Only: + +- `account` (Set of Object) (see [below for nested schema](#nestedobjatt--data--account)) +- `change_log` (Set of Object) (see [below for nested schema](#nestedobjatt--data--change_log)) +- `description` (String) +- `expiration_date_time` (String) +- `href` (String) +- `issuer_side` (String) +- `name` (String) +- `notifications` (Set of Object) (see [below for nested schema](#nestedobjatt--data--notifications)) +- `project` (Set of Object) (see [below for nested schema](#nestedobjatt--data--project)) +- `service_token_connection` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection)) +- `state` (String) +- `type` (String) +- `uuid` (String) + + +### Nested Schema for `data.account` + +Read-Only: + +- `account_name` (String) +- `account_number` (Number) +- `global_cust_id` (String) +- `global_org_id` (String) +- `global_organization_name` (String) +- `org_id` (Number) +- `organization_name` (String) +- `ucm_id` (String) + + + +### Nested Schema for `data.change_log` + +Read-Only: + +- `created_by` (String) +- `created_by_email` (String) +- `created_by_full_name` (String) +- `created_date_time` (String) +- `deleted_by` (String) +- `deleted_by_email` (String) +- `deleted_by_full_name` (String) +- `deleted_date_time` (String) +- `updated_by` (String) +- `updated_by_email` (String) +- `updated_by_full_name` (String) +- `updated_date_time` (String) + + + +### Nested Schema for `data.notifications` + +Read-Only: + +- `emails` (List of String) +- `send_interval` (String) +- `type` (String) + + + +### Nested Schema for `data.project` + +Read-Only: + +- `href` (String) +- `project_id` (String) + + + +### Nested Schema for `data.service_token_connection` + +Read-Only: + +- `a_side` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--a_side)) +- `allow_custom_bandwidth` (Boolean) +- `allow_remote_connection` (Boolean) +- `bandwidth_limit` (Number) +- `supported_bandwidths` (List of Number) +- `type` (String) +- `uuid` (String) +- `z_side` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--z_side)) + + +### Nested Schema for `data.service_token_connection.a_side` + +Read-Only: + +- `access_point_selectors` (List of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--a_side--access_point_selectors)) + + +### Nested Schema for `data.service_token_connection.a_side.access_point_selectors` + +Read-Only: + +- `interface` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--a_side--access_point_selectors--interface)) +- `link_protocol` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--a_side--access_point_selectors--link_protocol)) +- `network` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--a_side--access_point_selectors--network)) +- `port` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--a_side--access_point_selectors--port)) +- `type` (String) +- `virtual_device` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--a_side--access_point_selectors--virtual_device)) + + +### Nested Schema for `data.service_token_connection.a_side.access_point_selectors.interface` + +Read-Only: + +- `id` (Number) +- `type` (String) +- `uuid` (String) + + + +### Nested Schema for `data.service_token_connection.a_side.access_point_selectors.link_protocol` + +Read-Only: + +- `type` (String) +- `vlan_c_tag` (Number) +- `vlan_s_tag` (Number) +- `vlan_tag` (Number) + + + +### Nested Schema for `data.service_token_connection.a_side.access_point_selectors.network` + +Read-Only: + +- `href` (String) +- `location` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--a_side--access_point_selectors--network--location)) +- `name` (String) +- `scope` (String) +- `type` (String) +- `uuid` (String) + + +### Nested Schema for `data.service_token_connection.a_side.access_point_selectors.network.location` + +Read-Only: + +- `ibx` (String) +- `metro_code` (String) +- `metro_name` (String) +- `region` (String) + + + + +### Nested Schema for `data.service_token_connection.a_side.access_point_selectors.port` + +Read-Only: + +- `account_name` (String) +- `bandwidth` (Number) +- `cvp_id` (Number) +- `encapsulation_protocol_type` (String) +- `href` (String) +- `location` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--a_side--access_point_selectors--port--location)) +- `port_name` (String) +- `priority` (String) +- `type` (String) +- `uuid` (String) + + +### Nested Schema for `data.service_token_connection.a_side.access_point_selectors.port.location` + +Read-Only: + +- `ibx` (String) +- `metro_code` (String) +- `metro_name` (String) +- `region` (String) + + + + +### Nested Schema for `data.service_token_connection.a_side.access_point_selectors.virtual_device` + +Read-Only: + +- `cluster` (String) +- `href` (String) +- `name` (String) +- `type` (String) +- `uuid` (String) + + + + + +### Nested Schema for `data.service_token_connection.z_side` + +Read-Only: + +- `access_point_selectors` (List of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--z_side--access_point_selectors)) + + +### Nested Schema for `data.service_token_connection.z_side.access_point_selectors` + +Read-Only: + +- `interface` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--z_side--access_point_selectors--interface)) +- `link_protocol` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--z_side--access_point_selectors--link_protocol)) +- `network` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--z_side--access_point_selectors--network)) +- `port` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--z_side--access_point_selectors--port)) +- `type` (String) +- `virtual_device` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--z_side--access_point_selectors--virtual_device)) + + +### Nested Schema for `data.service_token_connection.z_side.access_point_selectors.interface` + +Read-Only: + +- `id` (Number) +- `type` (String) +- `uuid` (String) + + + +### Nested Schema for `data.service_token_connection.z_side.access_point_selectors.link_protocol` + +Read-Only: + +- `type` (String) +- `vlan_c_tag` (Number) +- `vlan_s_tag` (Number) +- `vlan_tag` (Number) + + + +### Nested Schema for `data.service_token_connection.z_side.access_point_selectors.network` + +Read-Only: + +- `href` (String) +- `location` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--z_side--access_point_selectors--network--location)) +- `name` (String) +- `scope` (String) +- `type` (String) +- `uuid` (String) + + +### Nested Schema for `data.service_token_connection.z_side.access_point_selectors.network.location` + +Read-Only: + +- `ibx` (String) +- `metro_code` (String) +- `metro_name` (String) +- `region` (String) + + + + +### Nested Schema for `data.service_token_connection.z_side.access_point_selectors.port` + +Read-Only: + +- `account_name` (String) +- `bandwidth` (Number) +- `cvp_id` (Number) +- `encapsulation_protocol_type` (String) +- `href` (String) +- `location` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--z_side--access_point_selectors--port--location)) +- `port_name` (String) +- `priority` (String) +- `type` (String) +- `uuid` (String) + + +### Nested Schema for `data.service_token_connection.z_side.access_point_selectors.port.location` + +Read-Only: + +- `ibx` (String) +- `metro_code` (String) +- `metro_name` (String) +- `region` (String) + + + + +### Nested Schema for `data.service_token_connection.z_side.access_point_selectors.virtual_device` + +Read-Only: + +- `cluster` (String) +- `href` (String) +- `name` (String) +- `type` (String) +- `uuid` (String) diff --git a/docs/resources/fabric_service_token.md b/docs/resources/fabric_service_token.md new file mode 100644 index 000000000..fdbec61bf --- /dev/null +++ b/docs/resources/fabric_service_token.md @@ -0,0 +1,397 @@ +--- +subcategory: "Fabric" +--- + +# equinix_fabric_service_token (Resource) + +Fabric V4 API compatible resource allows creation and management of [Equinix Fabric Service Token](https://docs.equinix.com/en-us/Content/Interconnection/Fabric/service%20tokens/Fabric-Service-Tokens.htm). + +Additional documentation: +* Getting Started: https://docs.equinix.com/en-us/Content/Interconnection/Fabric/service%20tokens/Fabric-Service-Tokens.htm +* API: https://docs.equinix.com/en-us/Content/KnowledgeCenter/Fabric/GettingStarted/Integrating-with-Fabric-V4-APIs/ConnectUsingServiceToken.htm + +## Example Usage + +Zside Virtual Device Service Token +```terraform +resource "equinix_fabric_service_token" "test" { + type = "VC_TOKEN" + expiration_date_time = "2025-01-18T06:43:49.980Z" + service_token_connection { + type = "EVPL_VC" + supported_bandwidths = [50, 200, 10000] + z_side { + access_point_selectors { + type = "VD" + virtual_device { + type = "EDGE" + uuid = " +## Schema + +### Required + +- `expiration_date_time` (String) Expiration date and time of the service token; 2020-11-06T07:00:00Z +- `notifications` (Block Set, Min: 1) Preferences for notifications on Service Token configuration or status changes (see [below for nested schema](#nestedblock--notifications)) +- `service_token_connection` (Block Set, Min: 1) Service Token Connection Type Information (see [below for nested schema](#nestedblock--service_token_connection)) +- `type` (String) Service Token Type; VC_TOKEN,EPL_TOKEN + +### Optional + +- `description` (String) Optional Description to the Service Token you will be creating +- `name` (String) Name of the Service Token +- `project` (Block Set, Max: 1) Project information (see [below for nested schema](#nestedblock--project)) +- `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts)) + +### Read-Only + +- `account` (Set of Object) Customer account information that is associated with this service token (see [below for nested schema](#nestedatt--account)) +- `change_log` (Set of Object) Captures connection lifecycle change information (see [below for nested schema](#nestedatt--change_log)) +- `href` (String) An absolute URL that is the subject of the link's context. +- `id` (String) The ID of this resource. +- `issuer_side` (String) Information about token side; ASIDE, ZSIDE +- `state` (String) Service token state; ACTIVE, INACTIVE, EXPIRED, DELETED +- `uuid` (String) Equinix-assigned service token identifier + + +### Nested Schema for `notifications` + +Required: + +- `emails` (List of String) Array of contact emails +- `type` (String) Notification Type - ALL,CONNECTION_APPROVAL,SALES_REP_NOTIFICATIONS, NOTIFICATIONS + +Optional: + +- `send_interval` (String) Send interval + + + +### Nested Schema for `service_token_connection` + +Required: + +- `supported_bandwidths` (List of Number) List of permitted bandwidths' For Port +- `type` (String) Type of Connection supported by Service Token you will create; EVPL_VC, EVPLAN_VC, EPLAN_VC, IPWAN_VC + +Optional: + +- `a_side` (Block Set) A-Side Connection link protocol,virtual device or network configuration (see [below for nested schema](#nestedblock--service_token_connection--a_side)) +- `allow_custom_bandwidth` (Boolean) Allow custom bandwidth value +- `allow_remote_connection` (Boolean) Authorization to connect remotely +- `bandwidth_limit` (Number) Connection bandwidth limit in Mbps +- `z_side` (Block Set) Z-Side Connection link protocol,virtual device or network configuration (see [below for nested schema](#nestedblock--service_token_connection--z_side)) + +Read-Only: + +- `uuid` (String) Equinix-assigned connection identifier + + +### Nested Schema for `service_token_connection.a_side` + +Required: + +- `access_point_selectors` (Block List, Min: 1) List of criteria for selecting network access points with optimal efficiency, security, compatibility, and availability (see [below for nested schema](#nestedblock--service_token_connection--a_side--access_point_selectors)) + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors` + +Optional: + +- `interface` (Block Set, Max: 1) Virtual Device Interface Configuration (see [below for nested schema](#nestedblock--service_token_connection--a_side--access_point_selectors--interface)) +- `link_protocol` (Block Set, Max: 1) Link protocol Configuration (see [below for nested schema](#nestedblock--service_token_connection--a_side--access_point_selectors--link_protocol)) +- `network` (Block Set, Max: 1) Network Configuration (see [below for nested schema](#nestedblock--service_token_connection--a_side--access_point_selectors--network)) +- `port` (Block Set, Max: 1) Port Configuration (see [below for nested schema](#nestedblock--service_token_connection--a_side--access_point_selectors--port)) +- `type` (String) Type of Access point; COLO, VD, NETWORK +- `virtual_device` (Block Set, Max: 1) Virtual Device Configuration (see [below for nested schema](#nestedblock--service_token_connection--a_side--access_point_selectors--virtual_device)) + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.interface` + +Optional: + +- `id` (Number) id +- `type` (String) Interface type + +Read-Only: + +- `uuid` (String) Equinix-assigned interface identifier + + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.link_protocol` + +Optional: + +- `type` (String) Type of the link protocol - UNTAGGED, DOT1Q, QINQ, EVPN_VXLAN +- `vlan_c_tag` (Number) Vlan Customer Tag information, vlanCTag value specified for QINQ connections +- `vlan_s_tag` (Number) Vlan Provider Tag information, vlanSTag value specified for QINQ connections +- `vlan_tag` (Number) Vlan Tag information, vlanTag value specified for DOT1Q connections + + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.network` + +Optional: + +- `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 + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.network.location` + +Optional: + +- `ibx` (String) IBX Code +- `metro_code` (String) Access point metro code +- `metro_name` (String) Access point metro name +- `region` (String) Access point region + + + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.port` + +Optional: + +- `account_name` (String) Account Name +- `bandwidth` (Number) Port Bandwidth +- `cvp_id` (Number) Customer virtual port Id +- `encapsulation_protocol_type` (String) Port Encapsulation +- `location` (Block Set) Port Location (see [below for nested schema](#nestedblock--service_token_connection--a_side--access_point_selectors--port--location)) +- `port_name` (String) Port Name +- `priority` (String) Port Priority +- `type` (String) Type of Port +- `uuid` (String) Equinix-assigned Port identifier + +Read-Only: + +- `href` (String) Unique Resource Identifier + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.port.location` + +Optional: + +- `ibx` (String) IBX Code +- `metro_code` (String) Access point metro code +- `metro_name` (String) Access point metro name +- `region` (String) Access point region + + + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.virtual_device` + +Required: + +- `uuid` (String) Equinix-assigned Virtual Device identifier + +Optional: + +- `cluster` (String) Virtual Device Cluster Information +- `name` (String) Customer-assigned Virtual Device Name +- `type` (String) Virtual Device type + +Read-Only: + +- `href` (String) Unique Resource Identifier + + + + + +### Nested Schema for `service_token_connection.z_side` + +Required: + +- `access_point_selectors` (Block List, Min: 1) List of criteria for selecting network access points with optimal efficiency, security, compatibility, and availability (see [below for nested schema](#nestedblock--service_token_connection--z_side--access_point_selectors)) + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors` + +Optional: + +- `interface` (Block Set, Max: 1) Virtual Device Interface Configuration (see [below for nested schema](#nestedblock--service_token_connection--z_side--access_point_selectors--interface)) +- `link_protocol` (Block Set, Max: 1) Link protocol Configuration (see [below for nested schema](#nestedblock--service_token_connection--z_side--access_point_selectors--link_protocol)) +- `network` (Block Set, Max: 1) Network Configuration (see [below for nested schema](#nestedblock--service_token_connection--z_side--access_point_selectors--network)) +- `port` (Block Set, Max: 1) Port Configuration (see [below for nested schema](#nestedblock--service_token_connection--z_side--access_point_selectors--port)) +- `type` (String) Type of Access point; COLO, VD, NETWORK +- `virtual_device` (Block Set, Max: 1) Virtual Device Configuration (see [below for nested schema](#nestedblock--service_token_connection--z_side--access_point_selectors--virtual_device)) + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.interface` + +Optional: + +- `id` (Number) id +- `type` (String) Interface type + +Read-Only: + +- `uuid` (String) Equinix-assigned interface identifier + + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.link_protocol` + +Optional: + +- `type` (String) Type of the link protocol - UNTAGGED, DOT1Q, QINQ, EVPN_VXLAN +- `vlan_c_tag` (Number) Vlan Customer Tag information, vlanCTag value specified for QINQ connections +- `vlan_s_tag` (Number) Vlan Provider Tag information, vlanSTag value specified for QINQ connections +- `vlan_tag` (Number) Vlan Tag information, vlanTag value specified for DOT1Q connections + + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.network` + +Optional: + +- `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 + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.network.location` + +Optional: + +- `ibx` (String) IBX Code +- `metro_code` (String) Access point metro code +- `metro_name` (String) Access point metro name +- `region` (String) Access point region + + + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.port` + +Optional: + +- `account_name` (String) Account Name +- `bandwidth` (Number) Port Bandwidth +- `cvp_id` (Number) Customer virtual port Id +- `encapsulation_protocol_type` (String) Port Encapsulation +- `location` (Block Set) Port Location (see [below for nested schema](#nestedblock--service_token_connection--z_side--access_point_selectors--port--location)) +- `port_name` (String) Port Name +- `priority` (String) Port Priority +- `type` (String) Type of Port +- `uuid` (String) Equinix-assigned Port identifier + +Read-Only: + +- `href` (String) Unique Resource Identifier + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.port.location` + +Optional: + +- `ibx` (String) IBX Code +- `metro_code` (String) Access point metro code +- `metro_name` (String) Access point metro name +- `region` (String) Access point region + + + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.virtual_device` + +Required: + +- `uuid` (String) Equinix-assigned Virtual Device identifier + +Optional: + +- `cluster` (String) Virtual Device Cluster Information +- `name` (String) Customer-assigned Virtual Device Name +- `type` (String) Virtual Device type + +Read-Only: + +- `href` (String) Unique Resource Identifier + + + + + + +### Nested Schema for `project` + +Optional: + +- `project_id` (String) Project Id + +Read-Only: + +- `href` (String) Unique Resource URL + + + +### Nested Schema for `timeouts` + +Optional: + +- `create` (String) +- `delete` (String) +- `read` (String) +- `update` (String) + + + +### Nested Schema for `account` + +Read-Only: + +- `account_name` (String) +- `account_number` (Number) +- `global_cust_id` (String) +- `global_org_id` (String) +- `global_organization_name` (String) +- `org_id` (Number) +- `organization_name` (String) +- `ucm_id` (String) + + + +### Nested Schema for `change_log` + +Read-Only: + +- `created_by` (String) +- `created_by_email` (String) +- `created_by_full_name` (String) +- `created_date_time` (String) +- `deleted_by` (String) +- `deleted_by_email` (String) +- `deleted_by_full_name` (String) +- `deleted_date_time` (String) +- `updated_by` (String) +- `updated_by_email` (String) +- `updated_by_full_name` (String) +- `updated_date_time` (String) \ No newline at end of file diff --git a/examples/data-sources/equinix_fabric_service_token/data-source.tf b/examples/data-sources/equinix_fabric_service_token/data-source.tf new file mode 100644 index 000000000..a234ba32e --- /dev/null +++ b/examples/data-sources/equinix_fabric_service_token/data-source.tf @@ -0,0 +1,35 @@ +data "equinix_fabric_service_token" "service-token" { + uuid = "" +} + +output "id" { + value = data.equinix_fabric_service_token.service-token.id +} + +output "type" { + value = data.equinix_fabric_service_token.service-token.type +} + +output "expiration_date_time" { + value = data.equinix_fabric_service_token.service-token.expiration_date_time +} + +output "supported_bandwidths" { + value = data.equinix_fabric_service_token.service-token.service_token_connection.0.supported_bandwidths +} + +output "virtual_device_type" { + value = data.equinix_fabric_service_token.service-token.service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.type +} + +output "virtual_device_uuid" { + value = data.equinix_fabric_service_token.service-token.service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid +} + +output "interface_type" { + value = data.equinix_fabric_service_token.service-token.service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.type +} + +output "interface_uuid" { + value = data.equinix_fabric_service_token.service-token.service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id +} \ No newline at end of file diff --git a/examples/data-sources/equinix_fabric_service_tokens/data-source.tf b/examples/data-sources/equinix_fabric_service_tokens/data-source.tf new file mode 100644 index 000000000..45d8a76f7 --- /dev/null +++ b/examples/data-sources/equinix_fabric_service_tokens/data-source.tf @@ -0,0 +1,53 @@ +data "equinix_fabric_service_tokens" "service-tokens" { + filter { + property = "/type" + operator = "=" + values = "EVPL_VC" + } + filter { + property = "/state" + operator = "=" + values = ["INACTIVE"] + } + pagination { + offset = 0 + limit = 5 + total = 25 + } +} + +output "number_of_returned_service_tokens" { + value = length(data.equinix_fabric_service_tokens.service-tokens.data) +} + +output "first_service_token_id" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.id +} + +output "first_service_token_type" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.type +} + +output "first_service_token_expiration_date_time" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.expiration_date_time +} + +output "first_service_token_supported_bandwidths" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.service_token_connection.0.supported_bandwidths +} + +output "first_service_token_virtual_device_type" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.type +} + +output "first_service_token_virtual_device_uuid" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid +} + +output "first_service_token_interface_type" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.type +} + +output "first_service_token_interface_uuid" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id +} \ No newline at end of file diff --git a/internal/resources/fabric/service_token/datasources.go b/internal/resources/fabric/service_token/datasources.go index adba0f1d6..f28b54c71 100644 --- a/internal/resources/fabric/service_token/datasources.go +++ b/internal/resources/fabric/service_token/datasources.go @@ -13,7 +13,11 @@ func DataSource() *schema.Resource { return &schema.Resource{ ReadContext: dataSourceRead, Schema: dataSourceBaseSchema(), - Description: `Fabric V4 API compatible data resource that allow user to fetch service token for a given UUID`, + Description: `Fabric V4 API compatible data resource that allow user to fetch service token for a given UUID + +Additional documentation: +* Getting Started: https://docs.equinix.com/en-us/Content/Interconnection/Fabric/service%20tokens/Fabric-Service-Tokens.htm +* API: https://docs.equinix.com/en-us/Content/KnowledgeCenter/Fabric/GettingStarted/Integrating-with-Fabric-V4-APIs/ConnectUsingServiceToken.htm`, } } @@ -27,7 +31,11 @@ func DataSourceSearch() *schema.Resource { return &schema.Resource{ ReadContext: dataSourceSearch, Schema: dataSourceSearchSchema(), - Description: `Fabric V4 API compatible data resource that allow user to fetch service token for a given search data set`, + Description: `Fabric V4 API compatible data resource that allow user to fetch service token for a given search data set + +Additional documentation: +* Getting Started: https://docs.equinix.com/en-us/Content/Interconnection/Fabric/service%20tokens/Fabric-Service-Tokens.htm +* API: https://docs.equinix.com/en-us/Content/KnowledgeCenter/Fabric/GettingStarted/Integrating-with-Fabric-V4-APIs/ConnectUsingServiceToken.htm`, } } diff --git a/internal/resources/fabric/service_token/models.go b/internal/resources/fabric/service_token/models.go index 6d38f7e42..b8b0168f3 100644 --- a/internal/resources/fabric/service_token/models.go +++ b/internal/resources/fabric/service_token/models.go @@ -8,7 +8,6 @@ import ( 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/schema" - "log" "reflect" "sort" "time" @@ -21,13 +20,11 @@ func buildCreateRequest(d *schema.ResourceData) fabricv4.ServiceToken { serviceTokenRequest.SetType(fabricv4.ServiceTokenType(typeConfig)) expirationDateTimeConfig := d.Get("expiration_date_time").(string) - log.Printf("[DEBUG] !!!! Expiration Date %v", expirationDateTimeConfig) const TimeFormat = "2006-01-02T15:04:05.000Z" expirationTime, err := time.Parse(TimeFormat, expirationDateTimeConfig) if err != nil { fmt.Print("Error Parsing expiration date time: ", err) } - log.Printf("[DEBUG] !!! Parsed expiration date %v", expirationTime) serviceTokenRequest.SetExpirationDateTime(expirationTime) connectionConfig := d.Get("service_token_connection").(*schema.Set).List() @@ -79,7 +76,6 @@ func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOpe for _, notification := range oldNotifications.(*schema.Set).List() { notificationMap := notification.(map[string]interface{}) - // Extract old emails list if it exists if emails, ok := notificationMap["emails"]; ok { oldEmailInterface := emails.([]interface{}) if len(oldEmailInterface) > 0 { @@ -92,7 +88,6 @@ func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOpe for _, notification := range newNotifications.(*schema.Set).List() { notificationMap := notification.(map[string]interface{}) - // Extract old emails list if it exists if emails, ok := notificationMap["emails"]; ok { newEmailInterface := emails.([]interface{}) if len(newEmailInterface) > 0 { @@ -102,7 +97,6 @@ func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOpe } } - log.Print("!!! DEBUG value of new email", newNotificationEmails) if !reflect.DeepEqual(oldNotificationEmails, newNotificationEmails) { patches = append(patches, fabricv4.ServiceTokenChangeOperation{ Op: "replace", @@ -112,18 +106,13 @@ func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOpe } oldServiceTokenConnection, newServiceTokenConnection := d.GetChange("service_token_connection") - log.Printf("[DEBUG] !!! old ServiceToken Connection %v", oldServiceTokenConnection) - log.Printf("[DEBUG] !!! new ServiceToken Connection %v", newServiceTokenConnection) - // Initialize variables for bandwidth limits var oldAsideBandwidthLimit, newAsideBandwidthLimit int - // Extract old bandwidth limit if oldServiceTokenConnection != nil { for _, connection := range oldServiceTokenConnection.(*schema.Set).List() { notificationMap := connection.(map[string]interface{}) - // Extract old bandwidth limit if it exists if bandwidth, ok := notificationMap["bandwidthLimit"]; ok { oldBandwidthLimit := bandwidth.([]interface{}) if len(oldBandwidthLimit) > 0 { @@ -134,12 +123,10 @@ func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOpe } } - // Extract new bandwidth limit if newServiceTokenConnection != nil { for _, connection := range newServiceTokenConnection.(*schema.Set).List() { notificationMap := connection.(map[string]interface{}) - // Extract new bandwidth limit if it exists if bandwidth, ok := notificationMap["bandwidthLimit"]; ok { newBandwidthLimit := bandwidth.([]interface{}) if len(newBandwidthLimit) > 0 { @@ -158,14 +145,12 @@ func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOpe }) } - // Get the old and new values for bandwidth limit var oldZsideBandwidth, newZsideBandwidth []int if oldServiceTokenConnection != nil { for _, connection := range oldServiceTokenConnection.(*schema.Set).List() { notificationMap := connection.(map[string]interface{}) - // Extract old bandwidth limit if it exists if bandwidth, ok := notificationMap["supported_bandwidths"]; ok { oldSupportedBandwidth := bandwidth.([]interface{}) if len(oldSupportedBandwidth) > 0 { @@ -179,7 +164,6 @@ func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOpe for _, connection := range newServiceTokenConnection.(*schema.Set).List() { notificationMap := connection.(map[string]interface{}) - // Extract new bandwidth limit if it exists if bandwidth, ok := notificationMap["supported_bandwidths"]; ok { newSupportedBandwidth := bandwidth.([]interface{}) if len(newSupportedBandwidth) > 0 { @@ -190,8 +174,6 @@ func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOpe } } - log.Print("!!! DEBUG value of new supprted bandwidth", newZsideBandwidth) - log.Printf("[DEBUG] Value of aresliceequal fucntion %v", areSlicesEqual(oldZsideBandwidth, newZsideBandwidth)) if !areSlicesEqual(oldZsideBandwidth, newZsideBandwidth) { patches = append(patches, fabricv4.ServiceTokenChangeOperation{ Op: "replace", @@ -208,13 +190,9 @@ func areSlicesEqual(a, b []int) bool { return false } - // Sort both slices sort.Ints(a) sort.Ints(b) - log.Printf("value of int a %v", a) - log.Printf("value of int b %v", b) - // Compare sorted slices for i := range a { if a[i] != b[i] { return false @@ -324,14 +302,10 @@ func connectionTerraformToGo(connectionTerraform []interface{}) fabricv4.Service supportedBandwidths := connectionMap["supported_bandwidths"].([]interface{}) if supportedBandwidths != nil { - // Create a new slice to hold the int32 values int32Bandwidths := make([]int32, len(supportedBandwidths)) - - // Convert []interface{} to []int32 for i, v := range supportedBandwidths { - int32Bandwidths[i] = int32(v.(int)) // Assign directly to the slice at index i + int32Bandwidths[i] = int32(v.(int)) } - // Set the converted []int32 to the connection connection.SetSupportedBandwidths(int32Bandwidths) } @@ -356,7 +330,7 @@ func accessPointTerraformToGo(accessPoint []interface{}) fabricv4.ServiceTokenSi var apSide fabricv4.ServiceTokenSide accessPointMap := accessPoint[0].(map[string]interface{}) - accessPointSelectors := accessPointMap["access_point_selectors"].(*schema.Set).List() + accessPointSelectors := accessPointMap["access_point_selectors"].([]interface{}) if len(accessPointSelectors) != 0 { aps := accessPointSelectorsTerraformToGo(accessPointSelectors) apSide.SetAccessPointSelectors(aps) @@ -577,56 +551,45 @@ func connectionGoToTerraform(connection *fabricv4.ServiceTokenConnection) *schem } func accessPointGoToTerraform(accessPoint *fabricv4.ServiceTokenSide) *schema.Set { - mappedAccessPoint := make(map[string]interface{}) - if accessPoint.AccessPointSelectors != nil { - accessPointSelectors := accessPoint.GetAccessPointSelectors() - - apSelectorsSet := schema.NewSet( - schema.HashResource(accessPointSelectorsSch()), - nil, - ) - for _, selector := range accessPointSelectors { - mappedSelector := accessPointSelectorsGoToTerraform(&selector) - apSelectorsSet.Add(mappedSelector) - - } - mappedAccessPoint["access_point_selectors"] = apSelectorsSet - } - - accessPointSet := schema.NewSet( - schema.HashResource(accessPointSelectorsSch()), - []interface{}{mappedAccessPoint}, + return schema.NewSet( + schema.HashResource(serviceTokenAccessPointSch()), + []interface{}{map[string]interface{}{ + "access_point_selectors": accessPointSelectorsGoToTerraform(accessPoint.GetAccessPointSelectors()), + }}, ) - return accessPointSet } -func accessPointSelectorsGoToTerraform(apSelectors *fabricv4.AccessPointSelector) map[string]interface{} { - mappedAccessPointSelectors := make(map[string]interface{}) - if apSelectors.Type != nil { - mappedAccessPointSelectors["type"] = string(apSelectors.GetType()) - } - if apSelectors.Port != nil { - port := apSelectors.GetPort() - mappedAccessPointSelectors["port"] = portGoToTerraform(&port) - } - if apSelectors.LinkProtocol != nil { - linkProtocol := apSelectors.GetLinkProtocol() - mappedAccessPointSelectors["link_protocol"] = linkedProtocolGoToTerraform(&linkProtocol) - } - if apSelectors.VirtualDevice != nil { - virtualDevice := apSelectors.GetVirtualDevice() - mappedAccessPointSelectors["virtual_device"] = virtualDeviceGoToTerraform(&virtualDevice) - } - if apSelectors.Interface != nil { - interface_ := apSelectors.GetInterface() - mappedAccessPointSelectors["interface"] = interfaceGoToTerraform(&interface_) - } - if apSelectors.Network != nil { - network := apSelectors.GetNetwork() - mappedAccessPointSelectors["network"] = networkGoToTerraform(&network) +func accessPointSelectorsGoToTerraform(apSelectors []fabricv4.AccessPointSelector) []interface{} { + mappedSelectors := make([]interface{}, len(apSelectors)) + for index, selector := range apSelectors { + mappedAccessPointSelector := make(map[string]interface{}) + if selector.Type != nil { + mappedAccessPointSelector["type"] = string(selector.GetType()) + } + if selector.Port != nil { + port := selector.GetPort() + mappedAccessPointSelector["port"] = portGoToTerraform(&port) + } + if selector.LinkProtocol != nil { + linkProtocol := selector.GetLinkProtocol() + mappedAccessPointSelector["link_protocol"] = linkedProtocolGoToTerraform(&linkProtocol) + } + if selector.VirtualDevice != nil { + virtualDevice := selector.GetVirtualDevice() + mappedAccessPointSelector["virtual_device"] = virtualDeviceGoToTerraform(&virtualDevice) + } + if selector.Interface != nil { + interface_ := selector.GetInterface() + mappedAccessPointSelector["interface"] = interfaceGoToTerraform(&interface_) + } + if selector.Network != nil { + network := selector.GetNetwork() + mappedAccessPointSelector["network"] = networkGoToTerraform(&network) + } + mappedSelectors[index] = mappedAccessPointSelector } - return mappedAccessPointSelectors + return mappedSelectors } func portGoToTerraform(port *fabricv4.SimplifiedMetadataEntity) *schema.Set { diff --git a/internal/resources/fabric/service_token/resource.go b/internal/resources/fabric/service_token/resource.go index 94301e915..14d04ebf6 100644 --- a/internal/resources/fabric/service_token/resource.go +++ b/internal/resources/fabric/service_token/resource.go @@ -98,7 +98,8 @@ func resourceDelete(ctx context.Context, d *schema.ResourceData, meta interface{ if err != nil { if genericError, ok := err.(*fabricv4.GenericOpenAPIError); ok { if fabricErrs, ok := genericError.Model().([]fabricv4.Error); ok { - if equinix_errors.HasErrorCode(fabricErrs, "") { + // EQ-3034019 = Service Token already deleted + if equinix_errors.HasErrorCode(fabricErrs, "EQ-3034019") { return diags } } diff --git a/internal/resources/fabric/service_token/resource_schema.go b/internal/resources/fabric/service_token/resource_schema.go index 0b9b5f39e..199e3cfc9 100644 --- a/internal/resources/fabric/service_token/resource_schema.go +++ b/internal/resources/fabric/service_token/resource_schema.go @@ -51,6 +51,7 @@ func resourceSchema() map[string]*schema.Schema { Required: true, Description: "Service Token Connection Type Information", Elem: serviceTokenConnectionSch(), + Set: schema.HashResource(serviceTokenConnectionSch()), }, "state": { Type: schema.TypeString, @@ -129,7 +130,7 @@ func serviceTokenConnectionSch() *schema.Resource { "supported_bandwidths": { Type: schema.TypeList, Required: true, - Description: "List of permitted bandwidths", + Description: "List of permitted bandwidths' For Port ", Elem: &schema.Schema{ Type: schema.TypeInt, }, @@ -140,6 +141,7 @@ func serviceTokenConnectionSch() *schema.Resource { Computed: true, Description: "A-Side Connection link protocol,virtual device or network configuration", Elem: serviceTokenAccessPointSch(), + Set: schema.HashResource(serviceTokenAccessPointSch()), }, "z_side": { Type: schema.TypeSet, @@ -147,6 +149,7 @@ func serviceTokenConnectionSch() *schema.Resource { Computed: true, Description: "Z-Side Connection link protocol,virtual device or network configuration", Elem: serviceTokenAccessPointSch(), + Set: schema.HashResource(serviceTokenAccessPointSch()), }, }, } @@ -156,10 +159,11 @@ func serviceTokenAccessPointSch() *schema.Resource { return &schema.Resource{ Schema: map[string]*schema.Schema{ "access_point_selectors": { - Type: schema.TypeSet, + Type: schema.TypeList, Required: true, Description: "List of criteria for selecting network access points with optimal efficiency, security, compatibility, and availability", Elem: accessPointSelectorsSch(), + Set: schema.HashResource(accessPointSelectorsSch()), }, }, } diff --git a/templates/resources/fabric_service_token.md.tmpl b/templates/resources/fabric_service_token.md.tmpl new file mode 100644 index 000000000..4d8d656f7 --- /dev/null +++ b/templates/resources/fabric_service_token.md.tmpl @@ -0,0 +1,23 @@ +--- +subcategory: "Fabric" +--- + +{{/* This template serves as a starting point for documentation generation, and can be customized with hardcoded values and/or doc gen templates. + +For example, the {{ .SchemaMarkdown }} template can be used to replace manual schema documentation if descriptions of schema attributes are added in the provider source code. */ -}} + +# equinix_fabric_service_token (Resource) + +Fabric V4 API compatible resource allows creation and management of [Equinix Fabric Service Token](https://docs.equinix.com/en-us/Content/Interconnection/Fabric/service%20tokens/Fabric-Service-Tokens.htm). + +Additional documentation: +* Getting Started: https://docs.equinix.com/en-us/Content/Interconnection/Fabric/service%20tokens/Fabric-Service-Tokens.htm +* API: https://docs.equinix.com/en-us/Content/KnowledgeCenter/Fabric/GettingStarted/Integrating-with-Fabric-V4-APIs/ConnectUsingServiceToken.htm + +## Example Usage + +Zside Virtual Device Service Token +{{tffile "examples/resources/equinix_fabric_service_token/zside_vd_service_token.tf"}} + + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file From 3623385263b9378eacb0b82efc4b830f63226e5b Mon Sep 17 00:00:00 2001 From: Charles Treatman Date: Mon, 28 Oct 2024 11:10:51 -0500 Subject: [PATCH 20/55] chore: allow teams to add/remove resources & data sources (#797) This refactors the SDKv2 and framework providers so that the data sources and resources for each Equinix service are defined in separate files. This enables each team to add or remove their own data sources and resources without requiring review from DRE. --- equinix/fabric_provider_maps.go | 48 +++++++++++ equinix/metal_provider_maps.go | 47 +++++++++++ equinix/network_edge_provider_maps.go | 27 +++++++ equinix/provider.go | 93 +++------------------- internal/provider/provider.go | 39 +++------ internal/provider/services/fabric.go | 14 ++++ internal/provider/services/metal.go | 38 +++++++++ internal/provider/services/network_edge.go | 14 ++++ 8 files changed, 214 insertions(+), 106 deletions(-) create mode 100644 equinix/fabric_provider_maps.go create mode 100644 equinix/metal_provider_maps.go create mode 100644 equinix/network_edge_provider_maps.go create mode 100644 internal/provider/services/fabric.go create mode 100644 internal/provider/services/metal.go create mode 100644 internal/provider/services/network_edge.go diff --git a/equinix/fabric_provider_maps.go b/equinix/fabric_provider_maps.go new file mode 100644 index 000000000..68d56a618 --- /dev/null +++ b/equinix/fabric_provider_maps.go @@ -0,0 +1,48 @@ +package equinix + +import ( + fabric_connection "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/connection" + fabric_connection_route_filter "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/connection_route_filter" + fabric_market_place_subscription "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/marketplace" + fabric_network "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/network" + fabric_route_filter "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/route_filter" + fabric_route_filter_rule "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/route_filter_rule" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func fabricDatasources() map[string]*schema.Resource { + return map[string]*schema.Resource{ + "equinix_fabric_routing_protocol": dataSourceRoutingProtocol(), + "equinix_fabric_connection": fabric_connection.DataSource(), + "equinix_fabric_connections": fabric_connection.DataSourceSearch(), + "equinix_fabric_connection_route_filter": fabric_connection_route_filter.DataSource(), + "equinix_fabric_connection_route_filters": fabric_connection_route_filter.DataSourceGetAllRules(), + "equinix_fabric_cloud_router": dataSourceFabricCloudRouter(), + "equinix_fabric_cloud_routers": dataSourceFabricGetCloudRouters(), + "equinix_fabric_market_place_subscription": fabric_market_place_subscription.DataSourceFabricMarketplaceSubscription(), + "equinix_fabric_network": fabric_network.DataSource(), + "equinix_fabric_networks": fabric_network.DataSourceSearch(), + "equinix_fabric_port": dataSourceFabricPort(), + "equinix_fabric_ports": dataSourceFabricGetPortsByName(), + "equinix_fabric_route_filter": fabric_route_filter.DataSource(), + "equinix_fabric_route_filters": fabric_route_filter.DataSourceSearch(), + "equinix_fabric_route_filter_rule": fabric_route_filter_rule.DataSource(), + "equinix_fabric_route_filter_rules": fabric_route_filter_rule.DataSourceGetAllRules(), + "equinix_fabric_service_profile": dataSourceFabricServiceProfileReadByUuid(), + "equinix_fabric_service_profiles": dataSourceFabricSearchServiceProfilesByName(), + } +} + +func fabricResources() map[string]*schema.Resource { + return map[string]*schema.Resource{ + "equinix_fabric_network": fabric_network.Resource(), + "equinix_fabric_cloud_router": resourceFabricCloudRouter(), + "equinix_fabric_connection": fabric_connection.Resource(), + "equinix_fabric_connection_route_filter": fabric_connection_route_filter.Resource(), + "equinix_fabric_route_filter": fabric_route_filter.Resource(), + "equinix_fabric_route_filter_rule": fabric_route_filter_rule.Resource(), + "equinix_fabric_routing_protocol": resourceFabricRoutingProtocol(), + "equinix_fabric_service_profile": resourceFabricServiceProfile(), + } +} diff --git a/equinix/metal_provider_maps.go b/equinix/metal_provider_maps.go new file mode 100644 index 000000000..04e822ad2 --- /dev/null +++ b/equinix/metal_provider_maps.go @@ -0,0 +1,47 @@ +package equinix + +import ( + metal_device "github.com/equinix/terraform-provider-equinix/internal/resources/metal/device" + metal_port "github.com/equinix/terraform-provider-equinix/internal/resources/metal/port" + "github.com/equinix/terraform-provider-equinix/internal/resources/metal/virtual_circuit" + "github.com/equinix/terraform-provider-equinix/internal/resources/metal/vrf" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func metalDatasources() map[string]*schema.Resource { + return map[string]*schema.Resource{ + "equinix_metal_hardware_reservation": dataSourceMetalHardwareReservation(), + "equinix_metal_metro": dataSourceMetalMetro(), + "equinix_metal_facility": dataSourceMetalFacility(), + "equinix_metal_ip_block_ranges": dataSourceMetalIPBlockRanges(), + "equinix_metal_precreated_ip_block": dataSourceMetalPreCreatedIPBlock(), + "equinix_metal_operating_system": dataSourceOperatingSystem(), + "equinix_metal_spot_market_price": dataSourceSpotMarketPrice(), + "equinix_metal_device": metal_device.DataSource(), + "equinix_metal_devices": metal_device.ListDataSource(), + "equinix_metal_device_bgp_neighbors": dataSourceMetalDeviceBGPNeighbors(), + "equinix_metal_plans": dataSourceMetalPlans(), + "equinix_metal_port": metal_port.DataSource(), + "equinix_metal_reserved_ip_block": dataSourceMetalReservedIPBlock(), + "equinix_metal_spot_market_request": dataSourceMetalSpotMarketRequest(), + "equinix_metal_virtual_circuit": virtual_circuit.DataSource(), + "equinix_metal_vrf": vrf.DataSource(), + } +} + +func metalResources() map[string]*schema.Resource { + return map[string]*schema.Resource{ + "equinix_metal_user_api_key": resourceMetalUserAPIKey(), + "equinix_metal_project_api_key": resourceMetalProjectAPIKey(), + "equinix_metal_device": metal_device.Resource(), + "equinix_metal_device_network_type": resourceMetalDeviceNetworkType(), + "equinix_metal_port": metal_port.Resource(), + "equinix_metal_reserved_ip_block": resourceMetalReservedIPBlock(), + "equinix_metal_ip_attachment": resourceMetalIPAttachment(), + "equinix_metal_spot_market_request": resourceMetalSpotMarketRequest(), + "equinix_metal_virtual_circuit": virtual_circuit.Resource(), + "equinix_metal_vrf": vrf.Resource(), + "equinix_metal_bgp_session": resourceMetalBGPSession(), + "equinix_metal_port_vlan_attachment": resourceMetalPortVlanAttachment(), + } +} diff --git a/equinix/network_edge_provider_maps.go b/equinix/network_edge_provider_maps.go new file mode 100644 index 000000000..3754961a3 --- /dev/null +++ b/equinix/network_edge_provider_maps.go @@ -0,0 +1,27 @@ +package equinix + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func networkEdgeDatasources() map[string]*schema.Resource { + return map[string]*schema.Resource{ + "equinix_network_account": dataSourceNetworkAccount(), + "equinix_network_device": dataSourceNetworkDevice(), + "equinix_network_device_type": dataSourceNetworkDeviceType(), + "equinix_network_device_software": dataSourceNetworkDeviceSoftware(), + "equinix_network_device_platform": dataSourceNetworkDevicePlatform(), + } +} + +func networkEdgeResources() map[string]*schema.Resource { + return map[string]*schema.Resource{ + "equinix_network_device": resourceNetworkDevice(), + "equinix_network_ssh_user": resourceNetworkSSHUser(), + "equinix_network_bgp": resourceNetworkBGP(), + "equinix_network_ssh_key": resourceNetworkSSHKey(), + "equinix_network_acl_template": resourceNetworkACLTemplate(), + "equinix_network_device_link": resourceNetworkDeviceLink(), + "equinix_network_file": resourceNetworkFile(), + } +} diff --git a/equinix/provider.go b/equinix/provider.go index fe8b02d7a..47fa527b0 100644 --- a/equinix/provider.go +++ b/equinix/provider.go @@ -3,20 +3,11 @@ package equinix import ( "context" "fmt" + "maps" "slices" "time" "github.com/equinix/terraform-provider-equinix/internal/config" - fabric_connection "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/connection" - fabric_connection_route_filter "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/connection_route_filter" - fabric_market_place_subscription "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/marketplace" - fabric_network "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/network" - fabric_route_filter "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/route_filter" - fabric_route_filter_rule "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/route_filter_rule" - metal_device "github.com/equinix/terraform-provider-equinix/internal/resources/metal/device" - metal_port "github.com/equinix/terraform-provider-equinix/internal/resources/metal/port" - "github.com/equinix/terraform-provider-equinix/internal/resources/metal/virtual_circuit" - "github.com/equinix/terraform-provider-equinix/internal/resources/metal/vrf" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" @@ -24,6 +15,16 @@ import ( // Provider returns Equinix terraform *schema.Provider func Provider() *schema.Provider { + datasources := make(map[string]*schema.Resource) + maps.Copy(datasources, fabricDatasources()) + maps.Copy(datasources, metalDatasources()) + maps.Copy(datasources, networkEdgeDatasources()) + + resources := make(map[string]*schema.Resource) + maps.Copy(resources, fabricResources()) + maps.Copy(resources, metalResources()) + maps.Copy(resources, networkEdgeResources()) + provider := &schema.Provider{ Schema: map[string]*schema.Schema{ "endpoint": { @@ -83,76 +84,8 @@ func Provider() *schema.Provider { Description: "Maximum number of seconds to wait before retrying a request.", }, }, - DataSourcesMap: map[string]*schema.Resource{ - "equinix_fabric_routing_protocol": dataSourceRoutingProtocol(), - "equinix_fabric_connection": fabric_connection.DataSource(), - "equinix_fabric_connections": fabric_connection.DataSourceSearch(), - "equinix_fabric_connection_route_filter": fabric_connection_route_filter.DataSource(), - "equinix_fabric_connection_route_filters": fabric_connection_route_filter.DataSourceGetAllRules(), - "equinix_fabric_cloud_router": dataSourceFabricCloudRouter(), - "equinix_fabric_cloud_routers": dataSourceFabricGetCloudRouters(), - "equinix_fabric_market_place_subscription": fabric_market_place_subscription.DataSourceFabricMarketplaceSubscription(), - "equinix_fabric_network": fabric_network.DataSource(), - "equinix_fabric_networks": fabric_network.DataSourceSearch(), - "equinix_fabric_port": dataSourceFabricPort(), - "equinix_fabric_ports": dataSourceFabricGetPortsByName(), - "equinix_fabric_route_filter": fabric_route_filter.DataSource(), - "equinix_fabric_route_filters": fabric_route_filter.DataSourceSearch(), - "equinix_fabric_route_filter_rule": fabric_route_filter_rule.DataSource(), - "equinix_fabric_route_filter_rules": fabric_route_filter_rule.DataSourceGetAllRules(), - "equinix_fabric_service_profile": dataSourceFabricServiceProfileReadByUuid(), - "equinix_fabric_service_profiles": dataSourceFabricSearchServiceProfilesByName(), - "equinix_network_account": dataSourceNetworkAccount(), - "equinix_network_device": dataSourceNetworkDevice(), - "equinix_network_device_type": dataSourceNetworkDeviceType(), - "equinix_network_device_software": dataSourceNetworkDeviceSoftware(), - "equinix_network_device_platform": dataSourceNetworkDevicePlatform(), - "equinix_metal_hardware_reservation": dataSourceMetalHardwareReservation(), - "equinix_metal_metro": dataSourceMetalMetro(), - "equinix_metal_facility": dataSourceMetalFacility(), - "equinix_metal_ip_block_ranges": dataSourceMetalIPBlockRanges(), - "equinix_metal_precreated_ip_block": dataSourceMetalPreCreatedIPBlock(), - "equinix_metal_operating_system": dataSourceOperatingSystem(), - "equinix_metal_spot_market_price": dataSourceSpotMarketPrice(), - "equinix_metal_device": metal_device.DataSource(), - "equinix_metal_devices": metal_device.ListDataSource(), - "equinix_metal_device_bgp_neighbors": dataSourceMetalDeviceBGPNeighbors(), - "equinix_metal_plans": dataSourceMetalPlans(), - "equinix_metal_port": metal_port.DataSource(), - "equinix_metal_reserved_ip_block": dataSourceMetalReservedIPBlock(), - "equinix_metal_spot_market_request": dataSourceMetalSpotMarketRequest(), - "equinix_metal_virtual_circuit": virtual_circuit.DataSource(), - "equinix_metal_vrf": vrf.DataSource(), - }, - ResourcesMap: map[string]*schema.Resource{ - "equinix_fabric_network": fabric_network.Resource(), - "equinix_fabric_cloud_router": resourceFabricCloudRouter(), - "equinix_fabric_connection": fabric_connection.Resource(), - "equinix_fabric_connection_route_filter": fabric_connection_route_filter.Resource(), - "equinix_fabric_route_filter": fabric_route_filter.Resource(), - "equinix_fabric_route_filter_rule": fabric_route_filter_rule.Resource(), - "equinix_fabric_routing_protocol": resourceFabricRoutingProtocol(), - "equinix_fabric_service_profile": resourceFabricServiceProfile(), - "equinix_network_device": resourceNetworkDevice(), - "equinix_network_ssh_user": resourceNetworkSSHUser(), - "equinix_network_bgp": resourceNetworkBGP(), - "equinix_network_ssh_key": resourceNetworkSSHKey(), - "equinix_network_acl_template": resourceNetworkACLTemplate(), - "equinix_network_device_link": resourceNetworkDeviceLink(), - "equinix_network_file": resourceNetworkFile(), - "equinix_metal_user_api_key": resourceMetalUserAPIKey(), - "equinix_metal_project_api_key": resourceMetalProjectAPIKey(), - "equinix_metal_device": metal_device.Resource(), - "equinix_metal_device_network_type": resourceMetalDeviceNetworkType(), - "equinix_metal_port": metal_port.Resource(), - "equinix_metal_reserved_ip_block": resourceMetalReservedIPBlock(), - "equinix_metal_ip_attachment": resourceMetalIPAttachment(), - "equinix_metal_spot_market_request": resourceMetalSpotMarketRequest(), - "equinix_metal_virtual_circuit": virtual_circuit.Resource(), - "equinix_metal_vrf": vrf.Resource(), - "equinix_metal_bgp_session": resourceMetalBGPSession(), - "equinix_metal_port_vlan_attachment": resourceMetalPortVlanAttachment(), - }, + DataSourcesMap: datasources, + ResourcesMap: resources, ProviderMetaSchema: map[string]*schema.Schema{ "module_name": { Type: schema.TypeString, diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 4e0d18653..45881fc6b 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -5,14 +5,7 @@ import ( "fmt" "github.com/equinix/terraform-provider-equinix/internal/config" - metalconnection "github.com/equinix/terraform-provider-equinix/internal/resources/metal/connection" - metalgateway "github.com/equinix/terraform-provider-equinix/internal/resources/metal/gateway" - metalorganization "github.com/equinix/terraform-provider-equinix/internal/resources/metal/organization" - metalorganizationmember "github.com/equinix/terraform-provider-equinix/internal/resources/metal/organization_member" - metalproject "github.com/equinix/terraform-provider-equinix/internal/resources/metal/project" - metalprojectsshkey "github.com/equinix/terraform-provider-equinix/internal/resources/metal/project_ssh_key" - metalsshkey "github.com/equinix/terraform-provider-equinix/internal/resources/metal/ssh_key" - "github.com/equinix/terraform-provider-equinix/internal/resources/metal/vlan" + "github.com/equinix/terraform-provider-equinix/internal/provider/services" equinix_validation "github.com/equinix/terraform-provider-equinix/internal/validation" "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" "github.com/hashicorp/terraform-plugin-framework/datasource" @@ -113,25 +106,19 @@ func (p *FrameworkProvider) MetaSchema( } func (p *FrameworkProvider) Resources(ctx context.Context) []func() resource.Resource { - return []func() resource.Resource{ - metalgateway.NewResource, - metalproject.NewResource, - metalprojectsshkey.NewResource, - metalsshkey.NewResource, - metalconnection.NewResource, - metalorganization.NewResource, - metalorganizationmember.NewResource, - vlan.NewResource, - } + resources := []func() resource.Resource{} + resources = append(resources, services.FabricResources()...) + resources = append(resources, services.MetalResources()...) + resources = append(resources, services.NetworkEdgeResources()...) + + return resources } func (p *FrameworkProvider) DataSources(ctx context.Context) []func() datasource.DataSource { - return []func() datasource.DataSource{ - metalgateway.NewDataSource, - metalproject.NewDataSource, - metalprojectsshkey.NewDataSource, - metalconnection.NewDataSource, - metalorganization.NewDataSource, - vlan.NewDataSource, - } + datasources := []func() datasource.DataSource{} + datasources = append(datasources, services.FabricDatasources()...) + datasources = append(datasources, services.MetalDatasources()...) + datasources = append(datasources, services.NetworkEdgeDatasources()...) + + return datasources } diff --git a/internal/provider/services/fabric.go b/internal/provider/services/fabric.go new file mode 100644 index 000000000..f545396bf --- /dev/null +++ b/internal/provider/services/fabric.go @@ -0,0 +1,14 @@ +package services + +import ( + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +func FabricResources() []func() resource.Resource { + return []func() resource.Resource{} +} + +func FabricDatasources() []func() datasource.DataSource { + return []func() datasource.DataSource{} +} diff --git a/internal/provider/services/metal.go b/internal/provider/services/metal.go new file mode 100644 index 000000000..26d7ddca7 --- /dev/null +++ b/internal/provider/services/metal.go @@ -0,0 +1,38 @@ +package services + +import ( + metalconnection "github.com/equinix/terraform-provider-equinix/internal/resources/metal/connection" + metalgateway "github.com/equinix/terraform-provider-equinix/internal/resources/metal/gateway" + metalorganization "github.com/equinix/terraform-provider-equinix/internal/resources/metal/organization" + metalorganizationmember "github.com/equinix/terraform-provider-equinix/internal/resources/metal/organization_member" + metalproject "github.com/equinix/terraform-provider-equinix/internal/resources/metal/project" + metalprojectsshkey "github.com/equinix/terraform-provider-equinix/internal/resources/metal/project_ssh_key" + metalsshkey "github.com/equinix/terraform-provider-equinix/internal/resources/metal/ssh_key" + "github.com/equinix/terraform-provider-equinix/internal/resources/metal/vlan" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +func MetalResources() []func() resource.Resource { + return []func() resource.Resource{ + metalgateway.NewResource, + metalproject.NewResource, + metalprojectsshkey.NewResource, + metalsshkey.NewResource, + metalconnection.NewResource, + metalorganization.NewResource, + metalorganizationmember.NewResource, + vlan.NewResource, + } +} + +func MetalDatasources() []func() datasource.DataSource { + return []func() datasource.DataSource{ + metalgateway.NewDataSource, + metalproject.NewDataSource, + metalprojectsshkey.NewDataSource, + metalconnection.NewDataSource, + metalorganization.NewDataSource, + vlan.NewDataSource, + } +} diff --git a/internal/provider/services/network_edge.go b/internal/provider/services/network_edge.go new file mode 100644 index 000000000..946cefb27 --- /dev/null +++ b/internal/provider/services/network_edge.go @@ -0,0 +1,14 @@ +package services + +import ( + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +func NetworkEdgeResources() []func() resource.Resource { + return []func() resource.Resource{} +} + +func NetworkEdgeDatasources() []func() datasource.DataSource { + return []func() datasource.DataSource{} +} From e1d041808672eb665b3ac914165b0cf5ead26744 Mon Sep 17 00:00:00 2001 From: Charles Treatman Date: Mon, 28 Oct 2024 11:10:51 -0500 Subject: [PATCH 21/55] chore: allow teams to add/remove resources & data sources (#797) This refactors the SDKv2 and framework providers so that the data sources and resources for each Equinix service are defined in separate files. This enables each team to add or remove their own data sources and resources without requiring review from DRE. --- equinix/fabric_provider_maps.go | 48 +++++++++++ equinix/metal_provider_maps.go | 47 +++++++++++ equinix/network_edge_provider_maps.go | 27 ++++++ equinix/provider.go | 95 +++------------------- internal/provider/provider.go | 39 +++------ internal/provider/services/fabric.go | 14 ++++ internal/provider/services/metal.go | 38 +++++++++ internal/provider/services/network_edge.go | 14 ++++ 8 files changed, 214 insertions(+), 108 deletions(-) create mode 100644 equinix/fabric_provider_maps.go create mode 100644 equinix/metal_provider_maps.go create mode 100644 equinix/network_edge_provider_maps.go create mode 100644 internal/provider/services/fabric.go create mode 100644 internal/provider/services/metal.go create mode 100644 internal/provider/services/network_edge.go diff --git a/equinix/fabric_provider_maps.go b/equinix/fabric_provider_maps.go new file mode 100644 index 000000000..68d56a618 --- /dev/null +++ b/equinix/fabric_provider_maps.go @@ -0,0 +1,48 @@ +package equinix + +import ( + fabric_connection "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/connection" + fabric_connection_route_filter "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/connection_route_filter" + fabric_market_place_subscription "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/marketplace" + fabric_network "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/network" + fabric_route_filter "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/route_filter" + fabric_route_filter_rule "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/route_filter_rule" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func fabricDatasources() map[string]*schema.Resource { + return map[string]*schema.Resource{ + "equinix_fabric_routing_protocol": dataSourceRoutingProtocol(), + "equinix_fabric_connection": fabric_connection.DataSource(), + "equinix_fabric_connections": fabric_connection.DataSourceSearch(), + "equinix_fabric_connection_route_filter": fabric_connection_route_filter.DataSource(), + "equinix_fabric_connection_route_filters": fabric_connection_route_filter.DataSourceGetAllRules(), + "equinix_fabric_cloud_router": dataSourceFabricCloudRouter(), + "equinix_fabric_cloud_routers": dataSourceFabricGetCloudRouters(), + "equinix_fabric_market_place_subscription": fabric_market_place_subscription.DataSourceFabricMarketplaceSubscription(), + "equinix_fabric_network": fabric_network.DataSource(), + "equinix_fabric_networks": fabric_network.DataSourceSearch(), + "equinix_fabric_port": dataSourceFabricPort(), + "equinix_fabric_ports": dataSourceFabricGetPortsByName(), + "equinix_fabric_route_filter": fabric_route_filter.DataSource(), + "equinix_fabric_route_filters": fabric_route_filter.DataSourceSearch(), + "equinix_fabric_route_filter_rule": fabric_route_filter_rule.DataSource(), + "equinix_fabric_route_filter_rules": fabric_route_filter_rule.DataSourceGetAllRules(), + "equinix_fabric_service_profile": dataSourceFabricServiceProfileReadByUuid(), + "equinix_fabric_service_profiles": dataSourceFabricSearchServiceProfilesByName(), + } +} + +func fabricResources() map[string]*schema.Resource { + return map[string]*schema.Resource{ + "equinix_fabric_network": fabric_network.Resource(), + "equinix_fabric_cloud_router": resourceFabricCloudRouter(), + "equinix_fabric_connection": fabric_connection.Resource(), + "equinix_fabric_connection_route_filter": fabric_connection_route_filter.Resource(), + "equinix_fabric_route_filter": fabric_route_filter.Resource(), + "equinix_fabric_route_filter_rule": fabric_route_filter_rule.Resource(), + "equinix_fabric_routing_protocol": resourceFabricRoutingProtocol(), + "equinix_fabric_service_profile": resourceFabricServiceProfile(), + } +} diff --git a/equinix/metal_provider_maps.go b/equinix/metal_provider_maps.go new file mode 100644 index 000000000..04e822ad2 --- /dev/null +++ b/equinix/metal_provider_maps.go @@ -0,0 +1,47 @@ +package equinix + +import ( + metal_device "github.com/equinix/terraform-provider-equinix/internal/resources/metal/device" + metal_port "github.com/equinix/terraform-provider-equinix/internal/resources/metal/port" + "github.com/equinix/terraform-provider-equinix/internal/resources/metal/virtual_circuit" + "github.com/equinix/terraform-provider-equinix/internal/resources/metal/vrf" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func metalDatasources() map[string]*schema.Resource { + return map[string]*schema.Resource{ + "equinix_metal_hardware_reservation": dataSourceMetalHardwareReservation(), + "equinix_metal_metro": dataSourceMetalMetro(), + "equinix_metal_facility": dataSourceMetalFacility(), + "equinix_metal_ip_block_ranges": dataSourceMetalIPBlockRanges(), + "equinix_metal_precreated_ip_block": dataSourceMetalPreCreatedIPBlock(), + "equinix_metal_operating_system": dataSourceOperatingSystem(), + "equinix_metal_spot_market_price": dataSourceSpotMarketPrice(), + "equinix_metal_device": metal_device.DataSource(), + "equinix_metal_devices": metal_device.ListDataSource(), + "equinix_metal_device_bgp_neighbors": dataSourceMetalDeviceBGPNeighbors(), + "equinix_metal_plans": dataSourceMetalPlans(), + "equinix_metal_port": metal_port.DataSource(), + "equinix_metal_reserved_ip_block": dataSourceMetalReservedIPBlock(), + "equinix_metal_spot_market_request": dataSourceMetalSpotMarketRequest(), + "equinix_metal_virtual_circuit": virtual_circuit.DataSource(), + "equinix_metal_vrf": vrf.DataSource(), + } +} + +func metalResources() map[string]*schema.Resource { + return map[string]*schema.Resource{ + "equinix_metal_user_api_key": resourceMetalUserAPIKey(), + "equinix_metal_project_api_key": resourceMetalProjectAPIKey(), + "equinix_metal_device": metal_device.Resource(), + "equinix_metal_device_network_type": resourceMetalDeviceNetworkType(), + "equinix_metal_port": metal_port.Resource(), + "equinix_metal_reserved_ip_block": resourceMetalReservedIPBlock(), + "equinix_metal_ip_attachment": resourceMetalIPAttachment(), + "equinix_metal_spot_market_request": resourceMetalSpotMarketRequest(), + "equinix_metal_virtual_circuit": virtual_circuit.Resource(), + "equinix_metal_vrf": vrf.Resource(), + "equinix_metal_bgp_session": resourceMetalBGPSession(), + "equinix_metal_port_vlan_attachment": resourceMetalPortVlanAttachment(), + } +} diff --git a/equinix/network_edge_provider_maps.go b/equinix/network_edge_provider_maps.go new file mode 100644 index 000000000..3754961a3 --- /dev/null +++ b/equinix/network_edge_provider_maps.go @@ -0,0 +1,27 @@ +package equinix + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func networkEdgeDatasources() map[string]*schema.Resource { + return map[string]*schema.Resource{ + "equinix_network_account": dataSourceNetworkAccount(), + "equinix_network_device": dataSourceNetworkDevice(), + "equinix_network_device_type": dataSourceNetworkDeviceType(), + "equinix_network_device_software": dataSourceNetworkDeviceSoftware(), + "equinix_network_device_platform": dataSourceNetworkDevicePlatform(), + } +} + +func networkEdgeResources() map[string]*schema.Resource { + return map[string]*schema.Resource{ + "equinix_network_device": resourceNetworkDevice(), + "equinix_network_ssh_user": resourceNetworkSSHUser(), + "equinix_network_bgp": resourceNetworkBGP(), + "equinix_network_ssh_key": resourceNetworkSSHKey(), + "equinix_network_acl_template": resourceNetworkACLTemplate(), + "equinix_network_device_link": resourceNetworkDeviceLink(), + "equinix_network_file": resourceNetworkFile(), + } +} diff --git a/equinix/provider.go b/equinix/provider.go index cb962d277..47fa527b0 100644 --- a/equinix/provider.go +++ b/equinix/provider.go @@ -3,21 +3,11 @@ package equinix import ( "context" "fmt" + "maps" "slices" "time" "github.com/equinix/terraform-provider-equinix/internal/config" - fabric_connection "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/connection" - fabric_connection_route_filter "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/connection_route_filter" - fabric_market_place_subscription "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/marketplace" - fabric_network "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/network" - fabric_route_filter "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/route_filter" - fabric_route_filter_rule "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/route_filter_rule" - fabric_service_token "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/service_token" - metal_device "github.com/equinix/terraform-provider-equinix/internal/resources/metal/device" - metal_port "github.com/equinix/terraform-provider-equinix/internal/resources/metal/port" - "github.com/equinix/terraform-provider-equinix/internal/resources/metal/virtual_circuit" - "github.com/equinix/terraform-provider-equinix/internal/resources/metal/vrf" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" @@ -25,6 +15,16 @@ import ( // Provider returns Equinix terraform *schema.Provider func Provider() *schema.Provider { + datasources := make(map[string]*schema.Resource) + maps.Copy(datasources, fabricDatasources()) + maps.Copy(datasources, metalDatasources()) + maps.Copy(datasources, networkEdgeDatasources()) + + resources := make(map[string]*schema.Resource) + maps.Copy(resources, fabricResources()) + maps.Copy(resources, metalResources()) + maps.Copy(resources, networkEdgeResources()) + provider := &schema.Provider{ Schema: map[string]*schema.Schema{ "endpoint": { @@ -84,77 +84,8 @@ func Provider() *schema.Provider { Description: "Maximum number of seconds to wait before retrying a request.", }, }, - DataSourcesMap: map[string]*schema.Resource{ - "equinix_fabric_routing_protocol": dataSourceRoutingProtocol(), - "equinix_fabric_connection": fabric_connection.DataSource(), - "equinix_fabric_connections": fabric_connection.DataSourceSearch(), - "equinix_fabric_cloud_router": dataSourceFabricCloudRouter(), - "equinix_fabric_cloud_routers": dataSourceFabricGetCloudRouters(), - "equinix_fabric_market_place_subscription": fabric_market_place_subscription.DataSourceFabricMarketplaceSubscription(), - "equinix_fabric_network": fabric_network.DataSource(), - "equinix_fabric_networks": fabric_network.DataSourceSearch(), - "equinix_fabric_port": dataSourceFabricPort(), - "equinix_fabric_ports": dataSourceFabricGetPortsByName(), - "equinix_fabric_route_filter": fabric_route_filter.DataSource(), - "equinix_fabric_route_filters": fabric_route_filter.DataSourceSearch(), - "equinix_fabric_route_filter_rule": fabric_route_filter_rule.DataSource(), - "equinix_fabric_route_filter_rules": fabric_route_filter_rule.DataSourceGetAllRules(), - "equinix_fabric_service_profile": dataSourceFabricServiceProfileReadByUuid(), - "equinix_fabric_service_profiles": dataSourceFabricSearchServiceProfilesByName(), - "equinix_fabric_service_token": fabric_service_token.DataSource(), - "equinix_fabric_service_tokens": fabric_service_token.DataSourceSearch(), - "equinix_network_account": dataSourceNetworkAccount(), - "equinix_network_device": dataSourceNetworkDevice(), - "equinix_network_device_type": dataSourceNetworkDeviceType(), - "equinix_network_device_software": dataSourceNetworkDeviceSoftware(), - "equinix_network_device_platform": dataSourceNetworkDevicePlatform(), - "equinix_metal_hardware_reservation": dataSourceMetalHardwareReservation(), - "equinix_metal_metro": dataSourceMetalMetro(), - "equinix_metal_facility": dataSourceMetalFacility(), - "equinix_metal_ip_block_ranges": dataSourceMetalIPBlockRanges(), - "equinix_metal_precreated_ip_block": dataSourceMetalPreCreatedIPBlock(), - "equinix_metal_operating_system": dataSourceOperatingSystem(), - "equinix_metal_spot_market_price": dataSourceSpotMarketPrice(), - "equinix_metal_device": metal_device.DataSource(), - "equinix_metal_devices": metal_device.ListDataSource(), - "equinix_metal_device_bgp_neighbors": dataSourceMetalDeviceBGPNeighbors(), - "equinix_metal_plans": dataSourceMetalPlans(), - "equinix_metal_port": metal_port.DataSource(), - "equinix_metal_reserved_ip_block": dataSourceMetalReservedIPBlock(), - "equinix_metal_spot_market_request": dataSourceMetalSpotMarketRequest(), - "equinix_metal_virtual_circuit": virtual_circuit.DataSource(), - "equinix_metal_vrf": vrf.DataSource(), - }, - ResourcesMap: map[string]*schema.Resource{ - "equinix_fabric_network": fabric_network.Resource(), - "equinix_fabric_cloud_router": resourceFabricCloudRouter(), - "equinix_fabric_connection": fabric_connection.Resource(), - "equinix_fabric_connection_route_filter": fabric_connection_route_filter.Resource(), - "equinix_fabric_route_filter": fabric_route_filter.Resource(), - "equinix_fabric_route_filter_rule": fabric_route_filter_rule.Resource(), - "equinix_fabric_routing_protocol": resourceFabricRoutingProtocol(), - "equinix_fabric_service_profile": resourceFabricServiceProfile(), - "equinix_fabric_service_token": fabric_service_token.Resource(), - "equinix_network_device": resourceNetworkDevice(), - "equinix_network_ssh_user": resourceNetworkSSHUser(), - "equinix_network_bgp": resourceNetworkBGP(), - "equinix_network_ssh_key": resourceNetworkSSHKey(), - "equinix_network_acl_template": resourceNetworkACLTemplate(), - "equinix_network_device_link": resourceNetworkDeviceLink(), - "equinix_network_file": resourceNetworkFile(), - "equinix_metal_user_api_key": resourceMetalUserAPIKey(), - "equinix_metal_project_api_key": resourceMetalProjectAPIKey(), - "equinix_metal_device": metal_device.Resource(), - "equinix_metal_device_network_type": resourceMetalDeviceNetworkType(), - "equinix_metal_port": metal_port.Resource(), - "equinix_metal_reserved_ip_block": resourceMetalReservedIPBlock(), - "equinix_metal_ip_attachment": resourceMetalIPAttachment(), - "equinix_metal_spot_market_request": resourceMetalSpotMarketRequest(), - "equinix_metal_virtual_circuit": virtual_circuit.Resource(), - "equinix_metal_vrf": vrf.Resource(), - "equinix_metal_bgp_session": resourceMetalBGPSession(), - "equinix_metal_port_vlan_attachment": resourceMetalPortVlanAttachment(), - }, + DataSourcesMap: datasources, + ResourcesMap: resources, ProviderMetaSchema: map[string]*schema.Schema{ "module_name": { Type: schema.TypeString, diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 4e0d18653..45881fc6b 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -5,14 +5,7 @@ import ( "fmt" "github.com/equinix/terraform-provider-equinix/internal/config" - metalconnection "github.com/equinix/terraform-provider-equinix/internal/resources/metal/connection" - metalgateway "github.com/equinix/terraform-provider-equinix/internal/resources/metal/gateway" - metalorganization "github.com/equinix/terraform-provider-equinix/internal/resources/metal/organization" - metalorganizationmember "github.com/equinix/terraform-provider-equinix/internal/resources/metal/organization_member" - metalproject "github.com/equinix/terraform-provider-equinix/internal/resources/metal/project" - metalprojectsshkey "github.com/equinix/terraform-provider-equinix/internal/resources/metal/project_ssh_key" - metalsshkey "github.com/equinix/terraform-provider-equinix/internal/resources/metal/ssh_key" - "github.com/equinix/terraform-provider-equinix/internal/resources/metal/vlan" + "github.com/equinix/terraform-provider-equinix/internal/provider/services" equinix_validation "github.com/equinix/terraform-provider-equinix/internal/validation" "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" "github.com/hashicorp/terraform-plugin-framework/datasource" @@ -113,25 +106,19 @@ func (p *FrameworkProvider) MetaSchema( } func (p *FrameworkProvider) Resources(ctx context.Context) []func() resource.Resource { - return []func() resource.Resource{ - metalgateway.NewResource, - metalproject.NewResource, - metalprojectsshkey.NewResource, - metalsshkey.NewResource, - metalconnection.NewResource, - metalorganization.NewResource, - metalorganizationmember.NewResource, - vlan.NewResource, - } + resources := []func() resource.Resource{} + resources = append(resources, services.FabricResources()...) + resources = append(resources, services.MetalResources()...) + resources = append(resources, services.NetworkEdgeResources()...) + + return resources } func (p *FrameworkProvider) DataSources(ctx context.Context) []func() datasource.DataSource { - return []func() datasource.DataSource{ - metalgateway.NewDataSource, - metalproject.NewDataSource, - metalprojectsshkey.NewDataSource, - metalconnection.NewDataSource, - metalorganization.NewDataSource, - vlan.NewDataSource, - } + datasources := []func() datasource.DataSource{} + datasources = append(datasources, services.FabricDatasources()...) + datasources = append(datasources, services.MetalDatasources()...) + datasources = append(datasources, services.NetworkEdgeDatasources()...) + + return datasources } diff --git a/internal/provider/services/fabric.go b/internal/provider/services/fabric.go new file mode 100644 index 000000000..f545396bf --- /dev/null +++ b/internal/provider/services/fabric.go @@ -0,0 +1,14 @@ +package services + +import ( + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +func FabricResources() []func() resource.Resource { + return []func() resource.Resource{} +} + +func FabricDatasources() []func() datasource.DataSource { + return []func() datasource.DataSource{} +} diff --git a/internal/provider/services/metal.go b/internal/provider/services/metal.go new file mode 100644 index 000000000..26d7ddca7 --- /dev/null +++ b/internal/provider/services/metal.go @@ -0,0 +1,38 @@ +package services + +import ( + metalconnection "github.com/equinix/terraform-provider-equinix/internal/resources/metal/connection" + metalgateway "github.com/equinix/terraform-provider-equinix/internal/resources/metal/gateway" + metalorganization "github.com/equinix/terraform-provider-equinix/internal/resources/metal/organization" + metalorganizationmember "github.com/equinix/terraform-provider-equinix/internal/resources/metal/organization_member" + metalproject "github.com/equinix/terraform-provider-equinix/internal/resources/metal/project" + metalprojectsshkey "github.com/equinix/terraform-provider-equinix/internal/resources/metal/project_ssh_key" + metalsshkey "github.com/equinix/terraform-provider-equinix/internal/resources/metal/ssh_key" + "github.com/equinix/terraform-provider-equinix/internal/resources/metal/vlan" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +func MetalResources() []func() resource.Resource { + return []func() resource.Resource{ + metalgateway.NewResource, + metalproject.NewResource, + metalprojectsshkey.NewResource, + metalsshkey.NewResource, + metalconnection.NewResource, + metalorganization.NewResource, + metalorganizationmember.NewResource, + vlan.NewResource, + } +} + +func MetalDatasources() []func() datasource.DataSource { + return []func() datasource.DataSource{ + metalgateway.NewDataSource, + metalproject.NewDataSource, + metalprojectsshkey.NewDataSource, + metalconnection.NewDataSource, + metalorganization.NewDataSource, + vlan.NewDataSource, + } +} diff --git a/internal/provider/services/network_edge.go b/internal/provider/services/network_edge.go new file mode 100644 index 000000000..946cefb27 --- /dev/null +++ b/internal/provider/services/network_edge.go @@ -0,0 +1,14 @@ +package services + +import ( + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +func NetworkEdgeResources() []func() resource.Resource { + return []func() resource.Resource{} +} + +func NetworkEdgeDatasources() []func() datasource.DataSource { + return []func() datasource.DataSource{} +} From 7678db7b12e633b1c9cb1d4e8abcfa930cef0c1b Mon Sep 17 00:00:00 2001 From: srushti-patl Date: Wed, 23 Oct 2024 13:23:59 -0700 Subject: [PATCH 22/55] fix: Updating Service Token Resource & Data source --- internal/resources/fabric/service_token/resource_schema.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/resources/fabric/service_token/resource_schema.go b/internal/resources/fabric/service_token/resource_schema.go index 199e3cfc9..d3fd6ff1b 100644 --- a/internal/resources/fabric/service_token/resource_schema.go +++ b/internal/resources/fabric/service_token/resource_schema.go @@ -112,12 +112,14 @@ func serviceTokenConnectionSch() *schema.Resource { Type: schema.TypeBool, Optional: true, Computed: true, + Default: true, Description: "Authorization to connect remotely", }, "allow_custom_bandwidth": { Type: schema.TypeBool, Optional: true, Computed: true, + Default: true, Description: "Allow custom bandwidth value", }, "bandwidth_limit": { From 1e18a1ec34461c6b19109d5d96bc42de65077bc3 Mon Sep 17 00:00:00 2001 From: thogarty <139183873+thogarty@users.noreply.github.com> Date: Mon, 14 Oct 2024 12:27:50 -0700 Subject: [PATCH 23/55] feat: fabric resource connection_route_filter (#795) * Add equinix_fabric_connection_route_filter resource to attach route filter policies to fabric cloud router connections * Add data source for retrieving connection_route_filters by connection and route filter uuids * Add data source to get all route filters for a given connection uuid * Add docs with make docs * Add acceptance tests for resource and data sources Local tests passing: image --- equinix/provider.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/equinix/provider.go b/equinix/provider.go index 47fa527b0..d661ca35a 100644 --- a/equinix/provider.go +++ b/equinix/provider.go @@ -8,6 +8,16 @@ import ( "time" "github.com/equinix/terraform-provider-equinix/internal/config" + fabric_connection "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/connection" + fabric_market_place_subscription "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/marketplace" + fabric_network "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/network" + fabric_route_filter "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/route_filter" + fabric_route_filter_rule "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/route_filter_rule" + fabric_service_token "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/service_token" + metal_device "github.com/equinix/terraform-provider-equinix/internal/resources/metal/device" + metal_port "github.com/equinix/terraform-provider-equinix/internal/resources/metal/port" + "github.com/equinix/terraform-provider-equinix/internal/resources/metal/virtual_circuit" + "github.com/equinix/terraform-provider-equinix/internal/resources/metal/vrf" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" From 133f02730993baf8871ff26b90436d0ffa1de486 Mon Sep 17 00:00:00 2001 From: kpdhulipala <84343462+kpdhulipala@users.noreply.github.com> Date: Mon, 21 Oct 2024 12:01:53 -0700 Subject: [PATCH 24/55] feat: Update docs for New device type - "Aviatrix Transit Edge" under Aviatrix Vendor (#801) feat: Update docs for New device type - "Aviatrix Transit Edge" under Aviatrix Vendor --- templates/resources/network_device.md.tmpl | 1 - 1 file changed, 1 deletion(-) diff --git a/templates/resources/network_device.md.tmpl b/templates/resources/network_device.md.tmpl index cd8bc862e..5b157c7ee 100644 --- a/templates/resources/network_device.md.tmpl +++ b/templates/resources/network_device.md.tmpl @@ -57,7 +57,6 @@ The following arguments are supported: * `package_code` - (Required) Device software package code. * `version` - (Required) Device software software version. * `core_count` - (Required) Number of CPU cores used by device. (**NOTE: Use this field to resize your device. When resizing your HA devices, primary device will be upgraded first. If the upgrade failed, device will be automatically rolled back to the previous state with original core number.**) -* `tier` - (Optional, conflicts with `throughput`,`throughput_unit` ) Select bandwidth tier for your own license, i.e., `0` or `1` or `2` or `3`. Tiers applicable only for C8000V Autonomous or C8000V SDWAN (controller) device types. If not provided, tier is defaulted to '2'. * `term_length` - (Required) Device term length. * `self_managed` - (Optional) Boolean value that determines device management mode, i.e., `self-managed` or `Equinix-managed` (default). * `byol` - (Optional) Boolean value that determines device licensing mode, i.e., `bring your own license` or `subscription` (default). From 83f3bd165b0d62b8779535ef79ab10db7170365c Mon Sep 17 00:00:00 2001 From: kpdhulipala <84343462+kpdhulipala@users.noreply.github.com> Date: Wed, 23 Oct 2024 07:54:36 -0700 Subject: [PATCH 25/55] feat: Add optional attribute tier in Create Virtual Device request for C8000V and C8000V SDWAN (#771) feat: Add optional attribute tier in Create Virtual Device request for C8000V and C8000V SDWAN --- templates/resources/network_device.md.tmpl | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/resources/network_device.md.tmpl b/templates/resources/network_device.md.tmpl index 5b157c7ee..cd8bc862e 100644 --- a/templates/resources/network_device.md.tmpl +++ b/templates/resources/network_device.md.tmpl @@ -57,6 +57,7 @@ The following arguments are supported: * `package_code` - (Required) Device software package code. * `version` - (Required) Device software software version. * `core_count` - (Required) Number of CPU cores used by device. (**NOTE: Use this field to resize your device. When resizing your HA devices, primary device will be upgraded first. If the upgrade failed, device will be automatically rolled back to the previous state with original core number.**) +* `tier` - (Optional, conflicts with `throughput`,`throughput_unit` ) Select bandwidth tier for your own license, i.e., `0` or `1` or `2` or `3`. Tiers applicable only for C8000V Autonomous or C8000V SDWAN (controller) device types. If not provided, tier is defaulted to '2'. * `term_length` - (Required) Device term length. * `self_managed` - (Optional) Boolean value that determines device management mode, i.e., `self-managed` or `Equinix-managed` (default). * `byol` - (Optional) Boolean value that determines device licensing mode, i.e., `bring your own license` or `subscription` (default). From ac17fead2ef1de9cfc80e597b3e30cd356076333 Mon Sep 17 00:00:00 2001 From: srushti-patl Date: Wed, 16 Oct 2024 13:43:41 -0700 Subject: [PATCH 26/55] feat: Adding Fabric Service Token Resource and Data Source --- internal/resources/fabric/service_token/resource_schema.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/resources/fabric/service_token/resource_schema.go b/internal/resources/fabric/service_token/resource_schema.go index d3fd6ff1b..199e3cfc9 100644 --- a/internal/resources/fabric/service_token/resource_schema.go +++ b/internal/resources/fabric/service_token/resource_schema.go @@ -112,14 +112,12 @@ func serviceTokenConnectionSch() *schema.Resource { Type: schema.TypeBool, Optional: true, Computed: true, - Default: true, Description: "Authorization to connect remotely", }, "allow_custom_bandwidth": { Type: schema.TypeBool, Optional: true, Computed: true, - Default: true, Description: "Allow custom bandwidth value", }, "bandwidth_limit": { From 2945cebb72a4badf0cdd34904713c2152a1b9276 Mon Sep 17 00:00:00 2001 From: srushti-patl Date: Wed, 16 Oct 2024 13:43:41 -0700 Subject: [PATCH 27/55] feat: Adding Fabric Service Token Resource and Data Source --- .../fabric/service_token/datasources.go | 49 ++ .../service_token/datasources_schema.go | 175 +++++ .../resources/fabric/service_token/models.go | 728 ++++++++++++++++++ .../fabric/service_token/resource.go | 169 ++++ .../fabric/service_token/resource_schema.go | 424 ++++++++++ .../fabric/service_token/resource_test.go | 94 +++ 6 files changed, 1639 insertions(+) create mode 100644 internal/resources/fabric/service_token/datasources.go create mode 100644 internal/resources/fabric/service_token/datasources_schema.go create mode 100644 internal/resources/fabric/service_token/models.go create mode 100644 internal/resources/fabric/service_token/resource.go create mode 100644 internal/resources/fabric/service_token/resource_schema.go create mode 100644 internal/resources/fabric/service_token/resource_test.go diff --git a/internal/resources/fabric/service_token/datasources.go b/internal/resources/fabric/service_token/datasources.go new file mode 100644 index 000000000..adba0f1d6 --- /dev/null +++ b/internal/resources/fabric/service_token/datasources.go @@ -0,0 +1,49 @@ +package service_token + +import ( + "context" + "fmt" + "github.com/equinix/terraform-provider-equinix/internal/config" + equinix_errors "github.com/equinix/terraform-provider-equinix/internal/errors" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func DataSource() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceRead, + Schema: dataSourceBaseSchema(), + Description: `Fabric V4 API compatible data resource that allow user to fetch service token for a given UUID`, + } +} + +func dataSourceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + uuid, _ := d.Get("uuid").(string) + d.SetId(uuid) + return resourceRead(ctx, d, meta) +} + +func DataSourceSearch() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceSearch, + Schema: dataSourceSearchSchema(), + Description: `Fabric V4 API compatible data resource that allow user to fetch service token for a given search data set`, + } +} + +func dataSourceSearch(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*config.Config).NewFabricClientForSDK(d) + searchRequest := buildSearchRequest(d) + + serviceTokens, _, err := client.ServiceTokensApi.SearchServiceTokens(ctx).ServiceTokenSearchRequest(searchRequest).Execute() + if err != nil { + return diag.FromErr(equinix_errors.FormatFabricError(err)) + } + + if len(serviceTokens.Data) < 1 { + return diag.FromErr(fmt.Errorf("no records are found for the route filter search criteria provided - %d , please change the search criteria", len(serviceTokens.Data))) + } + + d.SetId(serviceTokens.Data[0].GetUuid()) + return setServiceTokensData(d, serviceTokens) +} diff --git a/internal/resources/fabric/service_token/datasources_schema.go b/internal/resources/fabric/service_token/datasources_schema.go new file mode 100644 index 000000000..046112cee --- /dev/null +++ b/internal/resources/fabric/service_token/datasources_schema.go @@ -0,0 +1,175 @@ +package service_token + +import ( + equinix_fabric_schema "github.com/equinix/terraform-provider-equinix/internal/fabric/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func dataSourceBaseSchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Computed: true, + Description: "Service Token Type; VC_TOKEN,EPL_TOKEN", + }, + "uuid": { + Type: schema.TypeString, + Required: true, + Description: "Equinix-assigned service token identifier", + }, + "href": { + Type: schema.TypeString, + Computed: true, + Description: "An absolute URL that is the subject of the link's context.", + }, + "issuer_side": { + Type: schema.TypeString, + Computed: true, + Description: "Information about token side; ASIDE, ZSIDE", + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: "Name of the Service Token", + }, + "description": { + Type: schema.TypeString, + Computed: true, + Description: "Optional Description to the Service Token you will be creating", + }, + "expiration_date_time": { + Type: schema.TypeString, + Computed: true, + Description: "Expiration date and time of the service token; 2020-11-06T07:00:00Z", + }, + "service_token_connection": { + Type: schema.TypeSet, + Computed: true, + Description: "Service Token Connection Type Information", + Elem: serviceTokenConnectionSch(), + }, + "state": { + Type: schema.TypeString, + Computed: true, + Description: "Service token state; ACTIVE, INACTIVE, EXPIRED, DELETED", + }, + "notifications": { + Type: schema.TypeSet, + Computed: true, + Description: "Preferences for notifications on Service Token configuration or status changes", + Elem: &schema.Resource{ + Schema: equinix_fabric_schema.NotificationSch(), + }, + }, + "account": { + Type: schema.TypeSet, + Computed: true, + Description: "Customer account information that is associated with this service token", + Elem: &schema.Resource{ + Schema: equinix_fabric_schema.AccountSch(), + }, + }, + "change_log": { + Type: schema.TypeSet, + Computed: true, + Description: "Captures connection lifecycle change information", + Elem: &schema.Resource{ + Schema: equinix_fabric_schema.ChangeLogSch(), + }, + }, + "project": { + Type: schema.TypeSet, + Computed: true, + Description: "Project information", + Elem: &schema.Resource{ + Schema: equinix_fabric_schema.ProjectSch(), + }, + }, + } +} + +func paginationSchema() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "offset": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "The page offset for the pagination request. Index of the first element. Default is 0.", + }, + "limit": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "Number of elements to be requested per page. Number must be between 1 and 100. Default is 20", + }, + "total": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "Total number of elements returned.", + }, + "next": { + Type: schema.TypeString, + Computed: true, + Description: "URL relative to the last item in the response.", + }, + "previous": { + Type: schema.TypeString, + Computed: true, + Description: "URL relative to the first item in the response.", + }, + }, + } +} + +func dataSourceSearchSchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "data": { + Type: schema.TypeList, + Computed: true, + Description: "List of Route Filters", + Elem: &schema.Resource{ + Schema: dataSourceBaseSchema(), + }, + }, + "filter": { + Type: schema.TypeList, + Required: true, + Description: "Filters for the Data Source Search Request. Maximum of 8 total filters.", + MaxItems: 10, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "property": { + Type: schema.TypeString, + Required: true, + Description: "The API response property which you want to filter your request on. Can be one of the following: \"/type\", \"/name\", \"/project/projectId\", \"/uuid\", \"/state\"", + ValidateFunc: validation.StringInSlice([]string{"/uuid", "/state", "/name", "/project/projectId"}, true), + }, + "operator": { + Type: schema.TypeString, + Required: true, + Description: "Possible operators to use on the filter property. Can be one of the following: [ \"=\", \"!=\", \"[NOT] LIKE\", \"[NOT] IN\", \"ILIKE\" ]", + ValidateFunc: validation.StringInSlice([]string{"=", "!=", "[NOT] LIKE", "[NOT] IN", "ILIKE"}, true), + }, + "values": { + Type: schema.TypeList, + Required: true, + Description: "The values that you want to apply the property+operator combination to in order to filter your data search", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "pagination": { + Type: schema.TypeSet, + Optional: true, + Description: "Pagination details for the Data Source Search Request", + MaxItems: 1, + Elem: paginationSchema(), + }, + } +} diff --git a/internal/resources/fabric/service_token/models.go b/internal/resources/fabric/service_token/models.go new file mode 100644 index 000000000..6fff6d489 --- /dev/null +++ b/internal/resources/fabric/service_token/models.go @@ -0,0 +1,728 @@ +package service_token + +import ( + "fmt" + "github.com/equinix/equinix-sdk-go/services/fabricv4" + "github.com/equinix/terraform-provider-equinix/internal/converters" + equinix_fabric_schema "github.com/equinix/terraform-provider-equinix/internal/fabric/schema" + 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/schema" + "log" + "reflect" + "sort" + "time" +) + +func buildCreateRequest(d *schema.ResourceData) fabricv4.ServiceToken { + serviceTokenRequest := fabricv4.ServiceToken{} + + typeConfig := d.Get("type").(string) + serviceTokenRequest.SetType(fabricv4.ServiceTokenType(typeConfig)) + + expirationDateTimeConfig := d.Get("expiration_date_time").(string) + log.Printf("[DEBUG] !!!! Expiration Date %v", expirationDateTimeConfig) + const TimeFormat = "2006-01-02T15:04:05.000Z" + expirationTime, err := time.Parse(TimeFormat, expirationDateTimeConfig) + if err != nil { + fmt.Print("Error Parsing expiration date time: ", err) + } + log.Printf("[DEBUG] !!! Parsed expiration date %v", expirationTime) + serviceTokenRequest.SetExpirationDateTime(expirationTime) + + connectionConfig := d.Get("service_token_connection").(*schema.Set).List() + connection := connectionTerraformToGo(connectionConfig) + serviceTokenRequest.SetConnection(connection) + + notificationsConfig := d.Get("notifications").(*schema.Set).List() + notifications := equinix_fabric_schema.NotificationsTerraformToGo(notificationsConfig) + serviceTokenRequest.SetNotifications(notifications) + + return serviceTokenRequest + +} + +func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOperation { + patches := make([]fabricv4.ServiceTokenChangeOperation, 0) + oldName, newName := d.GetChange("name") + if oldName.(string) != newName.(string) { + 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{ + Op: "replace", + Path: "/description", + Value: newDescription.(string), + }) + } + + oldExpirationDate, newExpirationDate := d.GetChange("expiration_date_time") + if oldExpirationDate.(string) != newExpirationDate.(string) { + patches = append(patches, fabricv4.ServiceTokenChangeOperation{ + Op: "replace", + Path: "/expirationDateTime", + Value: newExpirationDate.(string), + }) + } + + oldNotifications, newNotifications := d.GetChange("notifications") + + var oldNotificationEmails, newNotificationEmails []string + + if oldNotifications != nil { + for _, notification := range oldNotifications.(*schema.Set).List() { + notificationMap := notification.(map[string]interface{}) + + // Extract old emails list if it exists + if emails, ok := notificationMap["emails"]; ok { + oldEmailInterface := emails.([]interface{}) + if len(oldEmailInterface) > 0 { + oldNotificationEmails = converters.IfArrToStringArr(oldEmailInterface) + } + } + } + } + if newNotifications != nil { + for _, notification := range newNotifications.(*schema.Set).List() { + notificationMap := notification.(map[string]interface{}) + + // Extract old emails list if it exists + if emails, ok := notificationMap["emails"]; ok { + newEmailInterface := emails.([]interface{}) + if len(newEmailInterface) > 0 { + newNotificationEmails = converters.IfArrToStringArr(newEmailInterface) + } + } + } + } + + log.Print("!!! DEBUG value of new email", newNotificationEmails) + if !reflect.DeepEqual(oldNotificationEmails, newNotificationEmails) { + patches = append(patches, fabricv4.ServiceTokenChangeOperation{ + Op: "replace", + Path: "/notifications/emails", + Value: newNotificationEmails, + }) + } + + oldServiceTokenConnection, newServiceTokenConnection := d.GetChange("service_token_connection") + log.Printf("[DEBUG] !!! old ServiceToken Connection %v", oldServiceTokenConnection) + log.Printf("[DEBUG] !!! new ServiceToken Connection %v", newServiceTokenConnection) + + // Initialize variables for bandwidth limits + var oldAsideBandwidthLimit, newAsideBandwidthLimit int + + // Extract old bandwidth limit + if oldServiceTokenConnection != nil { + for _, connection := range oldServiceTokenConnection.(*schema.Set).List() { + notificationMap := connection.(map[string]interface{}) + + // Extract old bandwidth limit if it exists + if bandwidth, ok := notificationMap["bandwidthLimit"]; ok { + oldBandwidthLimit := bandwidth.([]interface{}) + if len(oldBandwidthLimit) > 0 { + oldAsideBandwidthLimits := converters.IfArrToIntArr(oldBandwidthLimit) + oldAsideBandwidthLimit = oldAsideBandwidthLimits[0] + } + } + } + } + + // Extract new bandwidth limit + if newServiceTokenConnection != nil { + for _, connection := range newServiceTokenConnection.(*schema.Set).List() { + notificationMap := connection.(map[string]interface{}) + + // Extract new bandwidth limit if it exists + if bandwidth, ok := notificationMap["bandwidthLimit"]; ok { + newBandwidthLimit := bandwidth.([]interface{}) + if len(newBandwidthLimit) > 0 { + newAsideBandwidthLimits := converters.IfArrToIntArr(newBandwidthLimit) + newAsideBandwidthLimit = newAsideBandwidthLimits[0] + } + } + } + } + + if oldAsideBandwidthLimit != newAsideBandwidthLimit { + patches = append(patches, fabricv4.ServiceTokenChangeOperation{ + Op: "replace", + Path: "/connection/bandwidthLimit", + Value: newAsideBandwidthLimit, + }) + } + + // Get the old and new values for bandwidth limit + var oldZsideBandwidth, newZsideBandwidth []int + + if oldServiceTokenConnection != nil { + for _, connection := range oldServiceTokenConnection.(*schema.Set).List() { + notificationMap := connection.(map[string]interface{}) + + // Extract old bandwidth limit if it exists + if bandwidth, ok := notificationMap["supported_bandwidths"]; ok { + oldSupportedBandwidth := bandwidth.([]interface{}) + if len(oldSupportedBandwidth) > 0 { + oldZsideBandwidth = converters.IfArrToIntArr(oldSupportedBandwidth) + } + } + } + } + + if newServiceTokenConnection != nil { + for _, connection := range newServiceTokenConnection.(*schema.Set).List() { + notificationMap := connection.(map[string]interface{}) + + // Extract new bandwidth limit if it exists + if bandwidth, ok := notificationMap["supported_bandwidths"]; ok { + newSupportedBandwidth := bandwidth.([]interface{}) + if len(newSupportedBandwidth) > 0 { + newZsideBandwidth = converters.IfArrToIntArr(newSupportedBandwidth) + + } + } + } + } + + log.Print("!!! DEBUG value of new supprted bandwidth", newZsideBandwidth) + log.Printf("[DEBUG] Value of aresliceequal fucntion %v", areSlicesEqual(oldZsideBandwidth, newZsideBandwidth)) + if !areSlicesEqual(oldZsideBandwidth, newZsideBandwidth) { + patches = append(patches, fabricv4.ServiceTokenChangeOperation{ + Op: "replace", + Path: "/connection/supportedBandwidths", + Value: newZsideBandwidth, + }) + } + + return patches +} + +func areSlicesEqual(a, b []int) bool { + if len(a) != len(b) { + return false + } + + // Sort both slices + sort.Ints(a) + sort.Ints(b) + + log.Printf("value of int a %v", a) + log.Printf("value of int b %v", b) + // Compare sorted slices + for i := range a { + if a[i] != b[i] { + return false + } + } + + return true +} + +func buildSearchRequest(d *schema.ResourceData) fabricv4.ServiceTokenSearchRequest { + searchRequest := fabricv4.ServiceTokenSearchRequest{} + + schemaFilters := d.Get("filter").([]interface{}) + filter := filtersTerraformToGo(schemaFilters) + searchRequest.SetFilter(filter) + + if schemaPagination, ok := d.GetOk("pagination"); ok { + pagination := paginationTerraformToGo(schemaPagination.(*schema.Set).List()) + searchRequest.SetPagination(pagination) + } + + return searchRequest +} +func setServiceTokenMap(d *schema.ResourceData, serviceToken *fabricv4.ServiceToken) diag.Diagnostics { + diags := diag.Diagnostics{} + serviceTokenMap := serviceTokenResponseMap(serviceToken) + err := equinix_schema.SetMap(d, serviceTokenMap) + if err != nil { + return diag.FromErr(err) + } + return diags +} + +func setServiceTokensData(d *schema.ResourceData, routeFilters *fabricv4.ServiceTokens) diag.Diagnostics { + diags := diag.Diagnostics{} + mappedRouteFilters := make([]map[string]interface{}, len(routeFilters.Data)) + pagination := routeFilters.GetPagination() + if routeFilters.Data != nil { + for index, routeFilter := range routeFilters.Data { + mappedRouteFilters[index] = serviceTokenResponseMap(&routeFilter) + } + } else { + mappedRouteFilters = nil + } + err := equinix_schema.SetMap(d, map[string]interface{}{ + "data": mappedRouteFilters, + "pagination": paginationGoToTerraform(&pagination), + }) + if err != nil { + return diag.FromErr(err) + } + return diags +} + +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() + 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) + } + if token.Changelog != nil { + changelog := token.GetChangelog() + serviceToken["change_log"] = equinix_fabric_schema.ChangeLogGoToTerraform(&changelog) + } + if token.Project != nil { + project := token.GetProject() + serviceToken["project"] = equinix_fabric_schema.ProjectGoToTerraform(&project) + } + + return serviceToken +} + +func connectionTerraformToGo(connectionTerraform []interface{}) fabricv4.ServiceTokenConnection { + if connectionTerraform == nil || len(connectionTerraform) == 0 { + return fabricv4.ServiceTokenConnection{} + } + + var connection fabricv4.ServiceTokenConnection + + connectionMap := connectionTerraform[0].(map[string]interface{}) + + typeVal := connectionMap["type"].(string) + connection.SetType(fabricv4.ServiceTokenConnectionType(typeVal)) + + uuid := connectionMap["uuid"].(string) + connection.SetUuid(uuid) + + allowRemoteConnection := connectionMap["allow_remote_connection"].(bool) + connection.SetAllowRemoteConnection(allowRemoteConnection) + + allowCustomBandwidth := connectionMap["allow_custom_bandwidth"].(bool) + connection.SetAllowCustomBandwidth(allowCustomBandwidth) + + bandwidthLimit := connectionMap["bandwidth_limit"].(int) + connection.SetBandwidthLimit(int32(bandwidthLimit)) + + supportedBandwidths := connectionMap["supported_bandwidths"].([]interface{}) + if supportedBandwidths != nil { + // Create a new slice to hold the int32 values + int32Bandwidths := make([]int32, len(supportedBandwidths)) + + // Convert []interface{} to []int32 + for i, v := range supportedBandwidths { + int32Bandwidths[i] = int32(v.(int)) // Assign directly to the slice at index i + } + // Set the converted []int32 to the connection + connection.SetSupportedBandwidths(int32Bandwidths) + } + + asideRequest := connectionMap["a_side"].(*schema.Set).List() + zsideRequest := connectionMap["z_side"].(*schema.Set).List() + if len(asideRequest) != 0 { + aside := accessPointTerraformToGo(asideRequest) + connection.SetASide(aside) + } + if len(zsideRequest) != 0 { + zside := accessPointTerraformToGo(zsideRequest) + connection.SetZSide(zside) + } + return connection +} + +func accessPointTerraformToGo(accessPoint []interface{}) fabricv4.ServiceTokenSide { + if accessPoint == nil || len(accessPoint) == 0 { + return fabricv4.ServiceTokenSide{} + } + + var apSide fabricv4.ServiceTokenSide + + accessPointMap := accessPoint[0].(map[string]interface{}) + accessPointSelectors := accessPointMap["access_point_selectors"].(*schema.Set).List() + if len(accessPointSelectors) != 0 { + aps := accessPointSelectorsTerraformToGo(accessPointSelectors) + apSide.SetAccessPointSelectors(aps) + } + return apSide +} + +func accessPointSelectorsTerraformToGo(accessPointSelectors []interface{}) []fabricv4.AccessPointSelector { + if accessPointSelectors == nil || len(accessPointSelectors) == 0 { + return []fabricv4.AccessPointSelector{} + } + + var apSelectors fabricv4.AccessPointSelector + + apSelectorsMap := accessPointSelectors[0].(map[string]interface{}) + typeVal := apSelectorsMap["type"].(string) + apSelectors.SetType(fabricv4.AccessPointSelectorType(typeVal)) + portList := apSelectorsMap["port"].(*schema.Set).List() + linkProtocolList := apSelectorsMap["link_protocol"].(*schema.Set).List() + virtualDeviceList := apSelectorsMap["virtual_device"].(*schema.Set).List() + interfaceList := apSelectorsMap["interface"].(*schema.Set).List() + networkList := apSelectorsMap["network"].(*schema.Set).List() + + if len(portList) != 0 { + port := portTerraformToGo(portList) + apSelectors.SetPort(port) + } + + if len(linkProtocolList) != 0 { + linkProtocol := linkProtocolTerraformToGo(linkProtocolList) + apSelectors.SetLinkProtocol(linkProtocol) + } + + if len(virtualDeviceList) != 0 { + virtualDevice := virtualDeviceTerraformToGo(virtualDeviceList) + apSelectors.SetVirtualDevice(virtualDevice) + } + + if len(interfaceList) != 0 { + interface_ := interfaceTerraformToGo(interfaceList) + apSelectors.SetInterface(interface_) + } + + if len(networkList) != 0 { + network := networkTerraformToGo(networkList) + apSelectors.SetNetwork(network) + } + + return []fabricv4.AccessPointSelector{apSelectors} +} + +func portTerraformToGo(portList []interface{}) fabricv4.SimplifiedMetadataEntity { + if portList == nil || len(portList) == 0 { + return fabricv4.SimplifiedMetadataEntity{} + } + var port fabricv4.SimplifiedMetadataEntity + portListMap := portList[0].(map[string]interface{}) + uuid := portListMap["uuid"].(string) + port.SetUuid(uuid) + + return port +} + +func linkProtocolTerraformToGo(linkProtocolList []interface{}) fabricv4.SimplifiedLinkProtocol { + if linkProtocolList == nil || len(linkProtocolList) == 0 { + return fabricv4.SimplifiedLinkProtocol{} + } + var linkProtocol fabricv4.SimplifiedLinkProtocol + lpMap := linkProtocolList[0].(map[string]interface{}) + lpType := lpMap["type"].(string) + lpVlanSTag := int32(lpMap["vlan_s_tag"].(int)) + lpVlanTag := int32(lpMap["vlan_tag"].(int)) + lpVlanCTag := int32(lpMap["vlan_c_tag"].(int)) + + linkProtocol.SetType(fabricv4.LinkProtocolType(lpType)) + if lpVlanSTag != 0 { + linkProtocol.SetVlanSTag(lpVlanSTag) + } + if lpVlanTag != 0 { + linkProtocol.SetVlanTag(lpVlanTag) + } + if lpVlanCTag != 0 { + linkProtocol.SetVlanCTag(lpVlanCTag) + } + + return linkProtocol +} + +func virtualDeviceTerraformToGo(virtualDeviceList []interface{}) fabricv4.SimplifiedVirtualDevice { + if virtualDeviceList == nil || len(virtualDeviceList) == 0 { + return fabricv4.SimplifiedVirtualDevice{} + } + + var virtualDevice fabricv4.SimplifiedVirtualDevice + virtualDeviceMap := virtualDeviceList[0].(map[string]interface{}) + href := virtualDeviceMap["href"].(string) + type_ := virtualDeviceMap["type"].(string) + uuid := virtualDeviceMap["uuid"].(string) + name := virtualDeviceMap["name"].(string) + cluster := virtualDeviceMap["cluster"].(string) + virtualDevice.SetHref(href) + virtualDevice.SetType(fabricv4.SimplifiedVirtualDeviceType(type_)) + virtualDevice.SetUuid(uuid) + virtualDevice.SetName(name) + virtualDevice.SetCluster(cluster) + + return virtualDevice +} + +func interfaceTerraformToGo(interfaceList []interface{}) fabricv4.VirtualDeviceInterface { + if interfaceList == nil || len(interfaceList) == 0 { + return fabricv4.VirtualDeviceInterface{} + } + + var interface_ fabricv4.VirtualDeviceInterface + interfaceMap := interfaceList[0].(map[string]interface{}) + uuid := interfaceMap["uuid"].(string) + type_ := interfaceMap["type"].(string) + id := interfaceMap["id"].(int) + interface_.SetUuid(uuid) + interface_.SetType(fabricv4.VirtualDeviceInterfaceType(type_)) + interface_.SetId(int32(id)) + + return interface_ +} + +func networkTerraformToGo(networkList []interface{}) fabricv4.SimplifiedTokenNetwork { + if networkList == nil || len(networkList) == 0 { + return fabricv4.SimplifiedTokenNetwork{} + } + var network fabricv4.SimplifiedTokenNetwork + networkListMap := networkList[0].(map[string]interface{}) + uuid := networkListMap["uuid"].(string) + type_ := networkListMap["type"].(string) + network.SetUuid(uuid) + network.SetType(fabricv4.SimplifiedTokenNetworkType(type_)) + return network +} + +func filtersTerraformToGo(tokens []interface{}) fabricv4.ServiceTokenSearchExpression { + if tokens == nil { + return fabricv4.ServiceTokenSearchExpression{} + } + + searchTokensList := make([]fabricv4.ServiceTokenSearchExpression, 0) + + for _, filter := range tokens { + filterMap := filter.(map[string]interface{}) + filterItem := fabricv4.ServiceTokenSearchExpression{} + if property, ok := filterMap["property"]; ok { + filterItem.SetProperty(fabricv4.ServiceTokenSearchFieldName(property.(string))) + } + if operator, ok := filterMap["operator"]; ok { + filterItem.SetOperator(fabricv4.ServiceTokenSearchExpressionOperator(operator.(string))) + } + if values, ok := filterMap["values"]; ok { + stringValues := converters.IfArrToStringArr(values.([]interface{})) + filterItem.SetValues(stringValues) + } + searchTokensList = append(searchTokensList, filterItem) + } + + searchTokens := fabricv4.ServiceTokenSearchExpression{} + searchTokens.SetAnd(searchTokensList) + + return searchTokens +} + +func paginationTerraformToGo(pagination []interface{}) fabricv4.PaginationRequest { + if pagination == nil { + return fabricv4.PaginationRequest{} + } + paginationRequest := fabricv4.PaginationRequest{} + for _, page := range pagination { + pageMap := page.(map[string]interface{}) + if offset, ok := pageMap["offset"]; ok { + paginationRequest.SetOffset(int32(offset.(int))) + } + if limit, ok := pageMap["limit"]; ok { + paginationRequest.SetLimit(int32(limit.(int))) + } + } + + return paginationRequest +} + +func connectionGoToTerraform(connection *fabricv4.ServiceTokenConnection) *schema.Set { + mappedConnection := make(map[string]interface{}) + mappedConnection["type"] = string(connection.GetType()) + mappedConnection["allow_remote_connection"] = connection.GetAllowRemoteConnection() + mappedConnection["allow_custom_bandwidth"] = connection.GetAllowCustomBandwidth() + if connection.SupportedBandwidths != nil { + supportedBandwidths := connection.GetSupportedBandwidths() + interfaceBandwidths := make([]interface{}, len(supportedBandwidths)) + + for i, v := range supportedBandwidths { + interfaceBandwidths[i] = int(v) // Convert each int32 to interface{} + } + + mappedConnection["supported_bandwidths"] = interfaceBandwidths + } + if connection.ASide != nil { + accessPoint := connection.GetASide() + mappedConnection["a_side"] = accessPointGoToTerraform(&accessPoint) + } + if connection.ZSide != nil { + accessPoint := connection.GetZSide() + mappedConnection["z_side"] = accessPointGoToTerraform(&accessPoint) + } + connectionSet := schema.NewSet( + schema.HashResource(serviceTokenConnectionSch()), + []interface{}{mappedConnection}, + ) + return connectionSet +} + +func accessPointGoToTerraform(accessPoint *fabricv4.ServiceTokenSide) *schema.Set { + mappedAccessPoint := make(map[string]interface{}) + if accessPoint.AccessPointSelectors != nil { + accessPointSelectors := accessPoint.GetAccessPointSelectors() + + apSelectorsSet := schema.NewSet( + schema.HashResource(accessPointSelectorsSch()), + nil, + ) + for _, selector := range accessPointSelectors { + mappedSelector := accessPointSelectorsGoToTerraform(&selector) + apSelectorsSet.Add(mappedSelector) + + } + mappedAccessPoint["access_point_selectors"] = apSelectorsSet + } + + accessPointSet := schema.NewSet( + schema.HashResource(accessPointSelectorsSch()), + []interface{}{mappedAccessPoint}, + ) + return accessPointSet +} + +func accessPointSelectorsGoToTerraform(apSelectors *fabricv4.AccessPointSelector) map[string]interface{} { + mappedAccessPointSelectors := make(map[string]interface{}) + if apSelectors.Type != nil { + mappedAccessPointSelectors["type"] = string(apSelectors.GetType()) + } + if apSelectors.Port != nil { + port := apSelectors.GetPort() + mappedAccessPointSelectors["port"] = portGoToTerraform(&port) + } + if apSelectors.LinkProtocol != nil { + linkProtocol := apSelectors.GetLinkProtocol() + mappedAccessPointSelectors["link_protocol"] = linkedProtocolGoToTerraform(&linkProtocol) + } + if apSelectors.VirtualDevice != nil { + virtualDevice := apSelectors.GetVirtualDevice() + mappedAccessPointSelectors["virtual_device"] = virtualDeviceGoToTerraform(&virtualDevice) + } + if apSelectors.Interface != nil { + interface_ := apSelectors.GetInterface() + mappedAccessPointSelectors["interface"] = interfaceGoToTerraform(&interface_) + } + if apSelectors.Network != nil { + network := apSelectors.GetNetwork() + mappedAccessPointSelectors["network"] = networkGoToTerraform(&network) + } + + return mappedAccessPointSelectors +} + +func portGoToTerraform(port *fabricv4.SimplifiedMetadataEntity) *schema.Set { + mappedPort := make(map[string]interface{}) + mappedPort["href"] = port.GetHref() + mappedPort["type"] = port.GetType() + mappedPort["uuid"] = port.GetUuid() + + portSet := schema.NewSet( + schema.HashResource(portSch()), + []interface{}{mappedPort}, + ) + return portSet +} + +func linkedProtocolGoToTerraform(linkedProtocol *fabricv4.SimplifiedLinkProtocol) *schema.Set { + + mappedLinkedProtocol := make(map[string]interface{}) + mappedLinkedProtocol["type"] = string(linkedProtocol.GetType()) + mappedLinkedProtocol["vlan_tag"] = int(linkedProtocol.GetVlanTag()) + mappedLinkedProtocol["vlan_s_tag"] = int(linkedProtocol.GetVlanSTag()) + mappedLinkedProtocol["vlan_c_tag"] = int(linkedProtocol.GetVlanCTag()) + + linkedProtocolSet := schema.NewSet( + schema.HashResource(linkProtocolSch()), + []interface{}{mappedLinkedProtocol}, + ) + return linkedProtocolSet +} + +func virtualDeviceGoToTerraform(virtualDevice *fabricv4.SimplifiedVirtualDevice) *schema.Set { + if virtualDevice == nil { + return nil + } + mappedVirtualDevice := make(map[string]interface{}) + mappedVirtualDevice["name"] = virtualDevice.GetName() + mappedVirtualDevice["href"] = virtualDevice.GetHref() + mappedVirtualDevice["type"] = string(virtualDevice.GetType()) + mappedVirtualDevice["uuid"] = virtualDevice.GetUuid() + if virtualDevice.Cluster != nil { + mappedVirtualDevice["cluster"] = virtualDevice.GetCluster() + } + + virtualDeviceSet := schema.NewSet( + schema.HashResource(virtualDeviceSch()), + []interface{}{mappedVirtualDevice}, + ) + return virtualDeviceSet +} + +func interfaceGoToTerraform(mInterface *fabricv4.VirtualDeviceInterface) *schema.Set { + if mInterface == nil { + return nil + } + mappedMInterface := make(map[string]interface{}) + mappedMInterface["id"] = int(mInterface.GetId()) + mappedMInterface["type"] = string(mInterface.GetType()) + mappedMInterface["uuid"] = mInterface.GetUuid() + + mInterfaceSet := schema.NewSet( + schema.HashResource(interfaceSch()), + []interface{}{mappedMInterface}, + ) + return mInterfaceSet +} + +func networkGoToTerraform(network *fabricv4.SimplifiedTokenNetwork) *schema.Set { + if network == nil { + return nil + } + + mappedNetwork := make(map[string]interface{}) + mappedNetwork["uuid"] = network.GetUuid() + mappedNetwork["href"] = network.GetHref() + mappedNetwork["type"] = string(network.GetType()) + + return schema.NewSet( + schema.HashResource(networkSch()), + []interface{}{mappedNetwork}, + ) +} + +func paginationGoToTerraform(pagination *fabricv4.Pagination) *schema.Set { + if pagination == nil { + return nil + } + mappedPagination := make(map[string]interface{}) + mappedPagination["offset"] = int(pagination.GetOffset()) + mappedPagination["limit"] = int(pagination.GetLimit()) + mappedPagination["total"] = int(pagination.GetTotal()) + mappedPagination["next"] = pagination.GetNext() + mappedPagination["previous"] = pagination.GetPrevious() + + return schema.NewSet( + schema.HashResource(paginationSchema()), + []interface{}{mappedPagination}, + ) +} diff --git a/internal/resources/fabric/service_token/resource.go b/internal/resources/fabric/service_token/resource.go new file mode 100644 index 000000000..94301e915 --- /dev/null +++ b/internal/resources/fabric/service_token/resource.go @@ -0,0 +1,169 @@ +package service_token + +import ( + "context" + "github.com/equinix/equinix-sdk-go/services/fabricv4" + "github.com/equinix/terraform-provider-equinix/internal/config" + equinix_errors "github.com/equinix/terraform-provider-equinix/internal/errors" + equinix_fabric_schema "github.com/equinix/terraform-provider-equinix/internal/fabric/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "log" + "strings" + "time" +) + +func Resource() *schema.Resource { + return &schema.Resource{ + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(10 * time.Minute), + Update: schema.DefaultTimeout(10 * time.Minute), + Delete: schema.DefaultTimeout(10 * time.Minute), + Read: schema.DefaultTimeout(10 * time.Minute), + }, + ReadContext: resourceRead, + CreateContext: resourceCreate, + UpdateContext: resourceUpdate, + DeleteContext: resourceDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + Schema: resourceSchema(), + Description: `Fabric V4 API compatible resource allows creation and management of Equinix Fabric Service Token`, + } +} + +func resourceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*config.Config).NewFabricClientForSDK(d) + serviceToken, _, err := client.ServiceTokensApi.GetServiceTokenByUuid(ctx, d.Id()).Execute() + if err != nil { + log.Printf("[WARN] Service Token %s not found , error %s", d.Id(), err) + if !strings.Contains(err.Error(), "500") { + d.SetId("") + } + return diag.FromErr(equinix_errors.FormatFabricError(err)) + } + d.SetId(serviceToken.GetUuid()) + return setServiceTokenMap(d, serviceToken) +} + +func resourceCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*config.Config).NewFabricClientForSDK(d) + createRequest := buildCreateRequest(d) + + start := time.Now() + serviceToken, _, err := client.ServiceTokensApi.CreateServiceToken(ctx).ServiceToken(createRequest).Execute() + if err != nil { + return diag.FromErr(equinix_errors.FormatFabricError(err)) + } + d.SetId(serviceToken.GetUuid()) + notificationsMap := equinix_fabric_schema.NotificationsGoToTerraform(createRequest.GetNotifications()) + if err = d.Set("notifications", notificationsMap); err != nil { + return diag.Errorf("error setting notifications config to state: %s", err) + } + + createTimeout := d.Timeout(schema.TimeoutCreate) - 30*time.Second - time.Since(start) + 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) + } + + return resourceRead(ctx, d, meta) +} + +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() + if err != nil { + return diag.FromErr(equinix_errors.FormatFabricError(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) + } + + return setServiceTokenMap(d, serviceToken) +} + +func resourceDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + diags := diag.Diagnostics{} + client := meta.(*config.Config).NewFabricClientForSDK(d) + + start := time.Now() + _, _, err := client.ServiceTokensApi.DeleteServiceTokenByUuid(ctx, d.Id()).Execute() + if err != nil { + if genericError, ok := err.(*fabricv4.GenericOpenAPIError); ok { + if fabricErrs, ok := genericError.Model().([]fabricv4.Error); ok { + if equinix_errors.HasErrorCode(fabricErrs, "") { + return diags + } + } + } + return diag.FromErr(equinix_errors.FormatFabricError(err)) + } + + deleteTimeout := d.Timeout(schema.TimeoutDelete) - 30*time.Second - time.Since(start) + if err = WaitForDeletion(d.Id(), meta, d, ctx, deleteTimeout); err != nil { + return diag.Errorf("error waiting for service token (%s) to be deleted: %s", d.Id(), err) + } + return diags +} + +func waitForStability(uuid string, meta interface{}, d *schema.ResourceData, ctx context.Context, tieout time.Duration) error { + log.Printf("Waiting for service token to be created, uuid %s", uuid) + stateConf := &retry.StateChangeConf{ + Target: []string{ + string(fabricv4.SERVICETOKENSTATE_INACTIVE), + }, + Refresh: func() (interface{}, string, error) { + client := meta.(*config.Config).NewFabricClientForSDK(d) + serviceToken, _, err := client.ServiceTokensApi.GetServiceTokenByUuid(ctx, uuid).Execute() + if err != nil { + return "", "", equinix_errors.FormatFabricError(err) + } + return serviceToken, string(serviceToken.GetState()), nil + }, + Timeout: tieout, + Delay: 30 * time.Second, + MinTimeout: 30 * time.Second, + } + + _, err := stateConf.WaitForStateContext(ctx) + + return err +} + +func WaitForDeletion(uuid string, meta interface{}, d *schema.ResourceData, ctx context.Context, timeout time.Duration) error { + log.Printf("Waiting for service token to be deleted, uuid %s", uuid) + stateConf := &retry.StateChangeConf{ + Pending: []string{ + string(fabricv4.SERVICETOKENSTATE_INACTIVE), + }, + Target: []string{ + string(fabricv4.SERVICETOKENSTATE_DELETED), + }, + Refresh: func() (interface{}, string, error) { + client := meta.(*config.Config).NewFabricClientForSDK(d) + serviceToken, body, err := client.ServiceTokensApi.GetServiceTokenByUuid(ctx, uuid).Execute() + if err != nil { + if body.StatusCode >= 400 && body.StatusCode <= 499 { + // Already deleted resource + return serviceToken, string(fabricv4.SERVICETOKENSTATE_DELETED), nil + } + return "", "", equinix_errors.FormatFabricError(err) + } + return serviceToken, string(serviceToken.GetState()), nil + }, + Timeout: timeout, + Delay: 30 * time.Second, + MinTimeout: 30 * time.Second, + } + + _, err := stateConf.WaitForStateContext(ctx) + + return err +} diff --git a/internal/resources/fabric/service_token/resource_schema.go b/internal/resources/fabric/service_token/resource_schema.go new file mode 100644 index 000000000..0b9b5f39e --- /dev/null +++ b/internal/resources/fabric/service_token/resource_schema.go @@ -0,0 +1,424 @@ +package service_token + +import ( + equinix_fabric_schema "github.com/equinix/terraform-provider-equinix/internal/fabric/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceSchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"VC_TOKEN", "EPL_TOKEN"}, false), + Description: "Service Token Type; VC_TOKEN,EPL_TOKEN", + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + Description: "Equinix-assigned service token identifier", + }, + "href": { + Type: schema.TypeString, + Computed: true, + Description: "An absolute URL that is the subject of the link's context.", + }, + "issuer_side": { + Type: schema.TypeString, + Computed: true, + Description: "Information about token side; ASIDE, ZSIDE", + }, + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Name of the Service Token", + }, + "description": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Optional Description to the Service Token you will be creating", + }, + "expiration_date_time": { + Type: schema.TypeString, + Required: true, + Description: "Expiration date and time of the service token; 2020-11-06T07:00:00Z", + }, + "service_token_connection": { + Type: schema.TypeSet, + Required: true, + Description: "Service Token Connection Type Information", + Elem: serviceTokenConnectionSch(), + }, + "state": { + Type: schema.TypeString, + Computed: true, + Description: "Service token state; ACTIVE, INACTIVE, EXPIRED, DELETED", + }, + "notifications": { + Type: schema.TypeSet, + Required: true, + Description: "Preferences for notifications on Service Token configuration or status changes", + Elem: &schema.Resource{ + Schema: equinix_fabric_schema.NotificationSch(), + }, + }, + "account": { + Type: schema.TypeSet, + Computed: true, + Description: "Customer account information that is associated with this service token", + Elem: &schema.Resource{ + Schema: equinix_fabric_schema.AccountSch(), + }, + }, + "change_log": { + Type: schema.TypeSet, + Computed: true, + Description: "Captures connection lifecycle change information", + Elem: &schema.Resource{ + Schema: equinix_fabric_schema.ChangeLogSch(), + }, + }, + "project": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Description: "Project information", + MaxItems: 1, + Elem: &schema.Resource{ + Schema: equinix_fabric_schema.ProjectSch(), + }, + }, + } +} + +func serviceTokenConnectionSch() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Required: true, + Description: "Type of Connection supported by Service Token you will create; EVPL_VC, EVPLAN_VC, EPLAN_VC, IPWAN_VC", + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + Description: "Equinix-assigned connection identifier", + }, + "allow_remote_connection": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + Description: "Authorization to connect remotely", + }, + "allow_custom_bandwidth": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + Description: "Allow custom bandwidth value", + }, + "bandwidth_limit": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + ValidateFunc: validation.IntBetween(1, 100000), + Description: "Connection bandwidth limit in Mbps", + }, + "supported_bandwidths": { + Type: schema.TypeList, + Required: true, + Description: "List of permitted bandwidths", + Elem: &schema.Schema{ + Type: schema.TypeInt, + }, + }, + "a_side": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Description: "A-Side Connection link protocol,virtual device or network configuration", + Elem: serviceTokenAccessPointSch(), + }, + "z_side": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Description: "Z-Side Connection link protocol,virtual device or network configuration", + Elem: serviceTokenAccessPointSch(), + }, + }, + } +} + +func serviceTokenAccessPointSch() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "access_point_selectors": { + Type: schema.TypeSet, + Required: true, + Description: "List of criteria for selecting network access points with optimal efficiency, security, compatibility, and availability", + Elem: accessPointSelectorsSch(), + }, + }, + } +} + +func accessPointSelectorsSch() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Type of Access point; COLO, VD, NETWORK", + }, + "port": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Description: "Port Configuration", + MaxItems: 1, + Elem: portSch(), + }, + "link_protocol": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Description: "Link protocol Configuration", + MaxItems: 1, + Elem: linkProtocolSch(), + }, + "virtual_device": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Description: "Virtual Device Configuration", + MaxItems: 1, + Elem: virtualDeviceSch(), + }, + "interface": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Description: "Virtual Device Interface Configuration", + MaxItems: 1, + Elem: interfaceSch(), + }, + "network": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Description: "Network Configuration", + MaxItems: 1, + Elem: networkSch(), + }, + }, + } +} + +func portSch() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "href": { + Type: schema.TypeString, + Computed: true, + Description: "Unique Resource Identifier", + }, + "uuid": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Equinix-assigned Port identifier", + }, + "type": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: "Type of Port", + }, + "cvp_id": { + Type: schema.TypeInt, + Computed: true, + Optional: true, + Description: "Customer virtual port Id", + }, + "bandwidth": { + Type: schema.TypeInt, + Computed: true, + Optional: true, + Description: "Port Bandwidth", + }, + "port_name": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: "Port Name", + }, + "encapsulation_protocol_type": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: "Port Encapsulation", + }, + "account_name": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: "Account Name", + }, + "priority": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: "Port Priority", + }, + "location": { + Type: schema.TypeSet, + Computed: true, + Optional: true, + Description: "Port Location", + Elem: &schema.Resource{ + Schema: equinix_fabric_schema.LocationSch(), + }, + }, + }, + } +} + +func virtualDeviceSch() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "href": { + Type: schema.TypeString, + Computed: true, + Description: "Unique Resource Identifier", + }, + "uuid": { + Type: schema.TypeString, + Required: true, + Description: "Equinix-assigned Virtual Device identifier", + }, + "type": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Virtual Device type", + }, + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Customer-assigned Virtual Device Name", + }, + "cluster": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Virtual Device Cluster Information", + }, + }, + } +} + +func linkProtocolSch() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Type of the link protocol - UNTAGGED, DOT1Q, QINQ, EVPN_VXLAN", + ValidateFunc: validation.StringInSlice([]string{"UNTAGGED", "DOT1Q", "QINQ", "EVPN_VXLAN"}, true), + }, + "vlan_tag": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "Vlan Tag information, vlanTag value specified for DOT1Q connections", + }, + "vlan_s_tag": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "Vlan Provider Tag information, vlanSTag value specified for QINQ connections", + }, + "vlan_c_tag": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "Vlan Customer Tag information, vlanCTag value specified for QINQ connections", + }, + }, + } +} + +func interfaceSch() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "uuid": { + Type: schema.TypeString, + Computed: true, + Description: "Equinix-assigned interface identifier", + }, + "id": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "id", + }, + "type": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Interface type", + }, + }, + } +} + +func networkSch() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "uuid": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Equinix-assigned Network identifier", + }, + "href": { + Type: schema.TypeString, + Computed: true, + Description: "Unique Resource Identifier", + }, + "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{ + Schema: equinix_fabric_schema.LocationSch(), + }, + }, + }, + } +} diff --git a/internal/resources/fabric/service_token/resource_test.go b/internal/resources/fabric/service_token/resource_test.go new file mode 100644 index 000000000..c571b4230 --- /dev/null +++ b/internal/resources/fabric/service_token/resource_test.go @@ -0,0 +1,94 @@ +package service_token_test + +import ( + "context" + "fmt" + "github.com/equinix/terraform-provider-equinix/internal/acceptance" + "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/service_token" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "testing" + "time" +) + +func TestAccFabricServiceToken_PNFV(t *testing.T) { + serviceTokenName, serviceTokenUpdatedName := "Service_token_PNFV", "UP_Service_Token_PNFV" + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.TestAccPreCheck(t) }, + Providers: acceptance.TestAccProviders, + CheckDestroy: CheckServiceTokenDelete, + Steps: []resource.TestStep{ + { + Config: testAccFabricServiceTokenConfig(serviceTokenName), + 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", "zside vd token"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "expiration_date_time", "2024-11-18T06:43:49.980Z"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.supported_bandwidths.#", "3"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid", "fcf0fcec-65f6-4544-8810-ae4756fab8c4"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id", "5"), + ), + ExpectNonEmptyPlan: true, + }, + { + Config: testAccFabricServiceTokenConfig(serviceTokenUpdatedName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("equinix_fabric_service_token.test", "uuid"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "name", serviceTokenUpdatedName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccFabricServiceTokenConfig(serviceTokenName string) string { + return fmt.Sprintf( + `resource "equinix_fabric_service_token" "test"{ + type = "VC_TOKEN" + name = "%s" + description = "zside vd token" + expiration_date_time = "2024-11-18T06:43:49.980Z" + service_token_connection { + type = "EVPL_VC" + supported_bandwidths = [50, 200, 10000] + z_side { + access_point_selectors{ + type = "VD" + virtual_device{ + type = "EDGE" + uuid = "fcf0fcec-65f6-4544-8810-ae4756fab8c4" + } + interface{ + type = "NETWORK" + id = 5 + } + } + } + } + notifications { + type = "ALL" + emails = ["example@equinix.com", "test1@equinix.com"] + } + + } + `, serviceTokenName) +} + +func CheckServiceTokenDelete(s *terraform.State) error { + ctx := context.Background() + for _, rs := range s.RootModule().Resources { + if rs.Type != "equinix_fabric_service_token" { + continue + } + + err := service_token.WaitForDeletion(rs.Primary.ID, acceptance.TestAccProvider.Meta(), &schema.ResourceData{}, ctx, 10*time.Minute) + if err != nil { + return fmt.Errorf("API call failed while waiting for resource deletion") + } + } + return nil +} From 7cf4a23bf8b4c5a6b646f712d4a0a35c2e1af6be Mon Sep 17 00:00:00 2001 From: srushti-patl Date: Wed, 23 Oct 2024 13:23:59 -0700 Subject: [PATCH 28/55] fix: Updating Service Token Resource & Data source --- .../fabric/service_token/datasources_test.go | 93 +++++++++++++++++++ .../resources/fabric/service_token/models.go | 26 ++++-- .../fabric/service_token/resource_schema.go | 2 + .../fabric/service_token/resource_test.go | 25 +++-- 4 files changed, 129 insertions(+), 17 deletions(-) create mode 100644 internal/resources/fabric/service_token/datasources_test.go diff --git a/internal/resources/fabric/service_token/datasources_test.go b/internal/resources/fabric/service_token/datasources_test.go new file mode 100644 index 000000000..943c67852 --- /dev/null +++ b/internal/resources/fabric/service_token/datasources_test.go @@ -0,0 +1,93 @@ +package service_token_test + +import ( + "fmt" + "github.com/equinix/terraform-provider-equinix/internal/acceptance" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "testing" +) + +func TestAccFabricServiceTokenDataSource_PNFV(t *testing.T) { + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.TestAccPreCheck(t) }, + Providers: acceptance.TestAccProviders, + CheckDestroy: CheckServiceTokenDelete, + Steps: []resource.TestStep{ + { + Config: testAccFabricServiceTokenConfigDataSourceConfig(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.equinix_fabric_service_token.service-token", "uuid"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token", "type", "VC_TOKEN"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token", "expiration_date_time", "2024-11-18T06:43:49.980Z"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token", "service_token_connection.0.supported_bandwidths.#", "3"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.type", "EDGE"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid", "fcf0fcec-65f6-4544-8810-ae4756fab8c4"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.type", "NETWORK"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id", "5"), + resource.TestCheckResourceAttrSet("data.equinix_fabric_service_tokens.service-tokens", "data.0.uuid"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "data.0.type", "VC_TOKEN"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "data.0.expiration_date_time", "2024-11-18T06:43:49.980Z"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "data.0.service_token_connection.0.supported_bandwidths.#", "3"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.type", "EDGE"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "data.0.service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid", "fcf0fcec-65f6-4544-8810-ae4756fab8c4"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.type", "NETWORK"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "data.0.service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id", "5"), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccFabricServiceTokenConfigDataSourceConfig() string { + return fmt.Sprintf( + `resource "equinix_fabric_service_token" "test"{ + type = "VC_TOKEN" + expiration_date_time = "2024-11-18T06:43:49.980Z" + service_token_connection { + type = "EVPL_VC" + supported_bandwidths = [50, 200, 10000] + z_side { + access_point_selectors{ + type = "VD" + virtual_device{ + type = "EDGE" + uuid = "fcf0fcec-65f6-4544-8810-ae4756fab8c4" + } + interface{ + type = "NETWORK" + id = 5 + } + } + } + } + notifications { + type = "ALL" + emails = ["example@equinix.com", "test1@equinix.com"] + } + } + + data "equinix_fabric_service_token" "service-token" { + uuid = equinix_fabric_service_token.test.id + } + + data "equinix_fabric_service_tokens" "service-tokens"{ + filter { + property = "/uuid" + operator = "=" + values = [equinix_fabric_service_token.test.id] + } + filter { + property = "/state" + operator = "=" + values = ["INACTIVE"] + } + pagination { + offset = 0 + limit = 5 + total = 25 + } + } + + `) +} diff --git a/internal/resources/fabric/service_token/models.go b/internal/resources/fabric/service_token/models.go index 6fff6d489..6d38f7e42 100644 --- a/internal/resources/fabric/service_token/models.go +++ b/internal/resources/fabric/service_token/models.go @@ -282,10 +282,6 @@ func serviceTokenResponseMap(token *fabricv4.ServiceToken) map[string]interface{ 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) @@ -562,6 +558,9 @@ func connectionGoToTerraform(connection *fabricv4.ServiceTokenConnection) *schem mappedConnection["supported_bandwidths"] = interfaceBandwidths } + if connection.BandwidthLimit != nil { + mappedConnection["bandwidth_limit"] = int(connection.GetBandwidthLimit()) + } if connection.ASide != nil { accessPoint := connection.GetASide() mappedConnection["a_side"] = accessPointGoToTerraform(&accessPoint) @@ -663,14 +662,21 @@ func virtualDeviceGoToTerraform(virtualDevice *fabricv4.SimplifiedVirtualDevice) return nil } mappedVirtualDevice := make(map[string]interface{}) - mappedVirtualDevice["name"] = virtualDevice.GetName() - mappedVirtualDevice["href"] = virtualDevice.GetHref() - mappedVirtualDevice["type"] = string(virtualDevice.GetType()) - mappedVirtualDevice["uuid"] = virtualDevice.GetUuid() - if virtualDevice.Cluster != nil { + if name := virtualDevice.GetName(); name != "" { + mappedVirtualDevice["name"] = name + } + if href := virtualDevice.GetHref(); href != "" { + mappedVirtualDevice["href"] = href + } + if virtualDevice.GetType() != "" { + mappedVirtualDevice["type"] = string(virtualDevice.GetType()) + } + if uuid := virtualDevice.GetUuid(); uuid != "" { + mappedVirtualDevice["uuid"] = uuid + } + if virtualDevice.Cluster != nil && virtualDevice.GetCluster() != "" { mappedVirtualDevice["cluster"] = virtualDevice.GetCluster() } - virtualDeviceSet := schema.NewSet( schema.HashResource(virtualDeviceSch()), []interface{}{mappedVirtualDevice}, diff --git a/internal/resources/fabric/service_token/resource_schema.go b/internal/resources/fabric/service_token/resource_schema.go index 0b9b5f39e..793ced695 100644 --- a/internal/resources/fabric/service_token/resource_schema.go +++ b/internal/resources/fabric/service_token/resource_schema.go @@ -111,12 +111,14 @@ func serviceTokenConnectionSch() *schema.Resource { Type: schema.TypeBool, Optional: true, Computed: true, + Default: true, Description: "Authorization to connect remotely", }, "allow_custom_bandwidth": { Type: schema.TypeBool, Optional: true, Computed: true, + Default: true, Description: "Allow custom bandwidth value", }, "bandwidth_limit": { diff --git a/internal/resources/fabric/service_token/resource_test.go b/internal/resources/fabric/service_token/resource_test.go index c571b4230..d3d9e6183 100644 --- a/internal/resources/fabric/service_token/resource_test.go +++ b/internal/resources/fabric/service_token/resource_test.go @@ -14,30 +14,41 @@ import ( func TestAccFabricServiceToken_PNFV(t *testing.T) { serviceTokenName, serviceTokenUpdatedName := "Service_token_PNFV", "UP_Service_Token_PNFV" + serviceTokenDescription, serviceTokenUpdatedDescription := "zside vd token", "Updated zside vd token" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acceptance.TestAccPreCheck(t) }, Providers: acceptance.TestAccProviders, CheckDestroy: CheckServiceTokenDelete, Steps: []resource.TestStep{ { - Config: testAccFabricServiceTokenConfig(serviceTokenName), + Config: testAccFabricServiceTokenConfig(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", "zside vd token"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "description", serviceTokenDescription), resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "expiration_date_time", "2024-11-18T06:43:49.980Z"), resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.supported_bandwidths.#", "3"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.type", "EDGE"), resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid", "fcf0fcec-65f6-4544-8810-ae4756fab8c4"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.type", "NETWORK"), resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id", "5"), ), ExpectNonEmptyPlan: true, }, { - Config: testAccFabricServiceTokenConfig(serviceTokenUpdatedName), + Config: testAccFabricServiceTokenConfig(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", "2024-11-18T06:43:49.980Z"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.supported_bandwidths.#", "3"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.type", "EDGE"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid", "fcf0fcec-65f6-4544-8810-ae4756fab8c4"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.type", "NETWORK"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id", "5"), ), ExpectNonEmptyPlan: true, }, @@ -45,13 +56,13 @@ func TestAccFabricServiceToken_PNFV(t *testing.T) { }) } -func testAccFabricServiceTokenConfig(serviceTokenName string) string { +func testAccFabricServiceTokenConfig(serviceTokenName string, serviceTokenDescription string) string { return fmt.Sprintf( `resource "equinix_fabric_service_token" "test"{ type = "VC_TOKEN" name = "%s" - description = "zside vd token" - expiration_date_time = "2024-11-18T06:43:49.980Z" + description = "%s" + expiration_date_time = "2024-11-18T06:43:49.98Z" service_token_connection { type = "EVPL_VC" supported_bandwidths = [50, 200, 10000] @@ -75,7 +86,7 @@ func testAccFabricServiceTokenConfig(serviceTokenName string) string { } } - `, serviceTokenName) + `, serviceTokenName, serviceTokenDescription) } func CheckServiceTokenDelete(s *terraform.State) error { From c5d28f53b62bb90d70e18ff3f514ae2193de0627 Mon Sep 17 00:00:00 2001 From: srushti-patl Date: Wed, 16 Oct 2024 13:43:41 -0700 Subject: [PATCH 29/55] feat: Adding Fabric Service Token Resource and Data Source --- equinix/provider.go | 11 +++++++++++ .../resources/fabric/service_token/resource_schema.go | 2 -- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/equinix/provider.go b/equinix/provider.go index 47fa527b0..7b3f0d270 100644 --- a/equinix/provider.go +++ b/equinix/provider.go @@ -8,6 +8,17 @@ import ( "time" "github.com/equinix/terraform-provider-equinix/internal/config" + fabric_connection "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/connection" + fabric_connection_route_filter "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/connection_route_filter" + fabric_market_place_subscription "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/marketplace" + fabric_network "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/network" + fabric_route_filter "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/route_filter" + fabric_route_filter_rule "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/route_filter_rule" + fabric_service_token "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/service_token" + metal_device "github.com/equinix/terraform-provider-equinix/internal/resources/metal/device" + metal_port "github.com/equinix/terraform-provider-equinix/internal/resources/metal/port" + "github.com/equinix/terraform-provider-equinix/internal/resources/metal/virtual_circuit" + "github.com/equinix/terraform-provider-equinix/internal/resources/metal/vrf" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" diff --git a/internal/resources/fabric/service_token/resource_schema.go b/internal/resources/fabric/service_token/resource_schema.go index 793ced695..0b9b5f39e 100644 --- a/internal/resources/fabric/service_token/resource_schema.go +++ b/internal/resources/fabric/service_token/resource_schema.go @@ -111,14 +111,12 @@ func serviceTokenConnectionSch() *schema.Resource { Type: schema.TypeBool, Optional: true, Computed: true, - Default: true, Description: "Authorization to connect remotely", }, "allow_custom_bandwidth": { Type: schema.TypeBool, Optional: true, Computed: true, - Default: true, Description: "Allow custom bandwidth value", }, "bandwidth_limit": { From 96c34c2d80731b198e516056855a0fa9a21058b5 Mon Sep 17 00:00:00 2001 From: srushti-patl Date: Sun, 27 Oct 2024 23:09:40 -0700 Subject: [PATCH 30/55] feat: Adding doc changes for Service Token resource and data sources --- docs/data-sources/fabric_service_token.md | 350 +++++++++++++++ docs/data-sources/fabric_service_tokens.md | 405 ++++++++++++++++++ docs/resources/fabric_service_token.md | 397 +++++++++++++++++ .../data-source.tf | 35 ++ .../data-source.tf | 53 +++ .../fabric/service_token/datasources.go | 12 +- .../resources/fabric/service_token/models.go | 109 ++--- .../fabric/service_token/resource.go | 3 +- .../fabric/service_token/resource_schema.go | 8 +- .../resources/fabric_service_token.md.tmpl | 23 + 10 files changed, 1317 insertions(+), 78 deletions(-) create mode 100644 docs/data-sources/fabric_service_token.md create mode 100644 docs/data-sources/fabric_service_tokens.md create mode 100644 docs/resources/fabric_service_token.md create mode 100644 examples/data-sources/equinix_fabric_service_token/data-source.tf create mode 100644 examples/data-sources/equinix_fabric_service_tokens/data-source.tf create mode 100644 templates/resources/fabric_service_token.md.tmpl diff --git a/docs/data-sources/fabric_service_token.md b/docs/data-sources/fabric_service_token.md new file mode 100644 index 000000000..136996f62 --- /dev/null +++ b/docs/data-sources/fabric_service_token.md @@ -0,0 +1,350 @@ +--- +subcategory: "Fabric" +--- + +# equinix_fabric_service_token (Data Source) + +Fabric V4 API compatible data resource that allow user to fetch service token for a given UUID + +Additional documentation: +* Getting Started: https://docs.equinix.com/en-us/Content/Interconnection/Fabric/service%20tokens/Fabric-Service-Tokens.htm +* API: https://docs.equinix.com/en-us/Content/KnowledgeCenter/Fabric/GettingStarted/Integrating-with-Fabric-V4-APIs/ConnectUsingServiceToken.htm + +## Example Usage + +```terraform +data "equinix_fabric_service_token" "service-token" { + uuid = "" +} + +output "id" { + value = data.equinix_fabric_service_token.service-token.id +} + +output "type" { + value = data.equinix_fabric_service_token.service-token.type +} + +output "expiration_date_time" { + value = data.equinix_fabric_service_token.service-token.expiration_date_time +} + +output "supported_bandwidths" { + value = data.equinix_fabric_service_token.service-token.service_token_connection.0.supported_bandwidths +} + +output "virtual_device_type" { + value = data.equinix_fabric_service_token.service-token.service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.type +} + +output "virtual_device_uuid" { + value = data.equinix_fabric_service_token.service-token.service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid +} + +output "interface_type" { + value = data.equinix_fabric_service_token.service-token.service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.type +} + +output "interface_uuid" { + value = data.equinix_fabric_service_token.service-token.service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id +} +``` + + +## Schema + +### Required + +- `uuid` (String) Equinix-assigned service token identifier + +### Read-Only + +- `account` (Set of Object) Customer account information that is associated with this service token (see [below for nested schema](#nestedatt--account)) +- `change_log` (Set of Object) Captures connection lifecycle change information (see [below for nested schema](#nestedatt--change_log)) +- `description` (String) Optional Description to the Service Token you will be creating +- `expiration_date_time` (String) Expiration date and time of the service token; 2020-11-06T07:00:00Z +- `href` (String) An absolute URL that is the subject of the link's context. +- `id` (String) The ID of this resource. +- `issuer_side` (String) Information about token side; ASIDE, ZSIDE +- `name` (String) Name of the Service Token +- `notifications` (Set of Object) Preferences for notifications on Service Token configuration or status changes (see [below for nested schema](#nestedatt--notifications)) +- `project` (Set of Object) Project information (see [below for nested schema](#nestedatt--project)) +- `service_token_connection` (Set of Object) Service Token Connection Type Information (see [below for nested schema](#nestedatt--service_token_connection)) +- `state` (String) Service token state; ACTIVE, INACTIVE, EXPIRED, DELETED +- `type` (String) Service Token Type; VC_TOKEN,EPL_TOKEN + + +### Nested Schema for `account` + +Read-Only: + +- `account_name` (String) +- `account_number` (Number) +- `global_cust_id` (String) +- `global_org_id` (String) +- `global_organization_name` (String) +- `org_id` (Number) +- `organization_name` (String) +- `ucm_id` (String) + + + +### Nested Schema for `change_log` + +Read-Only: + +- `created_by` (String) +- `created_by_email` (String) +- `created_by_full_name` (String) +- `created_date_time` (String) +- `deleted_by` (String) +- `deleted_by_email` (String) +- `deleted_by_full_name` (String) +- `deleted_date_time` (String) +- `updated_by` (String) +- `updated_by_email` (String) +- `updated_by_full_name` (String) +- `updated_date_time` (String) + + + +### Nested Schema for `notifications` + +Read-Only: + +- `emails` (List of String) +- `send_interval` (String) +- `type` (String) + + + +### Nested Schema for `project` + +Read-Only: + +- `href` (String) +- `project_id` (String) + + + +### Nested Schema for `service_token_connection` + +Read-Only: + +- `a_side` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--a_side)) +- `allow_custom_bandwidth` (Boolean) +- `allow_remote_connection` (Boolean) +- `bandwidth_limit` (Number) +- `supported_bandwidths` (List of Number) +- `type` (String) +- `uuid` (String) +- `z_side` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--z_side)) + + +### Nested Schema for `service_token_connection.a_side` + +Read-Only: + +- `access_point_selectors` (List of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--a_side--access_point_selectors)) + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors` + +Read-Only: + +- `interface` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--a_side--access_point_selectors--interface)) +- `link_protocol` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--a_side--access_point_selectors--link_protocol)) +- `network` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--a_side--access_point_selectors--network)) +- `port` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--a_side--access_point_selectors--port)) +- `type` (String) +- `virtual_device` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--a_side--access_point_selectors--virtual_device)) + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.interface` + +Read-Only: + +- `id` (Number) +- `type` (String) +- `uuid` (String) + + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.link_protocol` + +Read-Only: + +- `type` (String) +- `vlan_c_tag` (Number) +- `vlan_s_tag` (Number) +- `vlan_tag` (Number) + + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.network` + +Read-Only: + +- `href` (String) +- `location` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--a_side--access_point_selectors--network--location)) +- `name` (String) +- `scope` (String) +- `type` (String) +- `uuid` (String) + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.network.location` + +Read-Only: + +- `ibx` (String) +- `metro_code` (String) +- `metro_name` (String) +- `region` (String) + + + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.port` + +Read-Only: + +- `account_name` (String) +- `bandwidth` (Number) +- `cvp_id` (Number) +- `encapsulation_protocol_type` (String) +- `href` (String) +- `location` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--a_side--access_point_selectors--port--location)) +- `port_name` (String) +- `priority` (String) +- `type` (String) +- `uuid` (String) + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.port.location` + +Read-Only: + +- `ibx` (String) +- `metro_code` (String) +- `metro_name` (String) +- `region` (String) + + + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.virtual_device` + +Read-Only: + +- `cluster` (String) +- `href` (String) +- `name` (String) +- `type` (String) +- `uuid` (String) + + + + + +### Nested Schema for `service_token_connection.z_side` + +Read-Only: + +- `access_point_selectors` (List of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--z_side--access_point_selectors)) + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors` + +Read-Only: + +- `interface` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--z_side--access_point_selectors--interface)) +- `link_protocol` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--z_side--access_point_selectors--link_protocol)) +- `network` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--z_side--access_point_selectors--network)) +- `port` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--z_side--access_point_selectors--port)) +- `type` (String) +- `virtual_device` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--z_side--access_point_selectors--virtual_device)) + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.interface` + +Read-Only: + +- `id` (Number) +- `type` (String) +- `uuid` (String) + + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.link_protocol` + +Read-Only: + +- `type` (String) +- `vlan_c_tag` (Number) +- `vlan_s_tag` (Number) +- `vlan_tag` (Number) + + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.network` + +Read-Only: + +- `href` (String) +- `location` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--z_side--access_point_selectors--network--location)) +- `name` (String) +- `scope` (String) +- `type` (String) +- `uuid` (String) + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.network.location` + +Read-Only: + +- `ibx` (String) +- `metro_code` (String) +- `metro_name` (String) +- `region` (String) + + + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.port` + +Read-Only: + +- `account_name` (String) +- `bandwidth` (Number) +- `cvp_id` (Number) +- `encapsulation_protocol_type` (String) +- `href` (String) +- `location` (Set of Object) (see [below for nested schema](#nestedobjatt--service_token_connection--z_side--access_point_selectors--port--location)) +- `port_name` (String) +- `priority` (String) +- `type` (String) +- `uuid` (String) + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.port.location` + +Read-Only: + +- `ibx` (String) +- `metro_code` (String) +- `metro_name` (String) +- `region` (String) + + + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.virtual_device` + +Read-Only: + +- `cluster` (String) +- `href` (String) +- `name` (String) +- `type` (String) +- `uuid` (String) diff --git a/docs/data-sources/fabric_service_tokens.md b/docs/data-sources/fabric_service_tokens.md new file mode 100644 index 000000000..2379afcfc --- /dev/null +++ b/docs/data-sources/fabric_service_tokens.md @@ -0,0 +1,405 @@ +--- +subcategory: "Fabric" +--- + +# equinix_fabric_service_tokens (Data Source) + +Fabric V4 API compatible data resource that allow user to fetch service token for a given search data set + +Additional documentation: +* Getting Started: https://docs.equinix.com/en-us/Content/Interconnection/Fabric/service%20tokens/Fabric-Service-Tokens.htm +* API: https://docs.equinix.com/en-us/Content/KnowledgeCenter/Fabric/GettingStarted/Integrating-with-Fabric-V4-APIs/ConnectUsingServiceToken.htm + +## Example Usage + +```terraform +data "equinix_fabric_service_tokens" "service-tokens" { + filter { + property = "/type" + operator = "=" + values = "EVPL_VC" + } + filter { + property = "/state" + operator = "=" + values = ["INACTIVE"] + } + pagination { + offset = 0 + limit = 5 + total = 25 + } +} + +output "number_of_returned_service_tokens" { + value = length(data.equinix_fabric_service_tokens.service-tokens.data) +} + +output "first_service_token_id" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.id +} + +output "first_service_token_type" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.type +} + +output "first_service_token_expiration_date_time" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.expiration_date_time +} + +output "first_service_token_supported_bandwidths" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.service_token_connection.0.supported_bandwidths +} + +output "first_service_token_virtual_device_type" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.type +} + +output "first_service_token_virtual_device_uuid" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid +} + +output "first_service_token_interface_type" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.type +} + +output "first_service_token_interface_uuid" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id +} +``` + + +## Schema + +### Required + +- `filter` (Block List, Min: 1, Max: 10) Filters for the Data Source Search Request. Maximum of 8 total filters. (see [below for nested schema](#nestedblock--filter)) + +### Optional + +- `pagination` (Block Set, Max: 1) Pagination details for the Data Source Search Request (see [below for nested schema](#nestedblock--pagination)) + +### Read-Only + +- `data` (List of Object) List of Route Filters (see [below for nested schema](#nestedatt--data)) +- `id` (String) The ID of this resource. + + +### Nested Schema for `filter` + +Required: + +- `operator` (String) Possible operators to use on the filter property. Can be one of the following: [ "=", "!=", "[NOT] LIKE", "[NOT] IN", "ILIKE" ] +- `property` (String) The API response property which you want to filter your request on. Can be one of the following: "/type", "/name", "/project/projectId", "/uuid", "/state" +- `values` (List of String) The values that you want to apply the property+operator combination to in order to filter your data search + + + +### Nested Schema for `pagination` + +Optional: + +- `limit` (Number) Number of elements to be requested per page. Number must be between 1 and 100. Default is 20 +- `offset` (Number) The page offset for the pagination request. Index of the first element. Default is 0. +- `total` (Number) Total number of elements returned. + +Read-Only: + +- `next` (String) URL relative to the last item in the response. +- `previous` (String) URL relative to the first item in the response. + + + +### Nested Schema for `data` + +Read-Only: + +- `account` (Set of Object) (see [below for nested schema](#nestedobjatt--data--account)) +- `change_log` (Set of Object) (see [below for nested schema](#nestedobjatt--data--change_log)) +- `description` (String) +- `expiration_date_time` (String) +- `href` (String) +- `issuer_side` (String) +- `name` (String) +- `notifications` (Set of Object) (see [below for nested schema](#nestedobjatt--data--notifications)) +- `project` (Set of Object) (see [below for nested schema](#nestedobjatt--data--project)) +- `service_token_connection` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection)) +- `state` (String) +- `type` (String) +- `uuid` (String) + + +### Nested Schema for `data.account` + +Read-Only: + +- `account_name` (String) +- `account_number` (Number) +- `global_cust_id` (String) +- `global_org_id` (String) +- `global_organization_name` (String) +- `org_id` (Number) +- `organization_name` (String) +- `ucm_id` (String) + + + +### Nested Schema for `data.change_log` + +Read-Only: + +- `created_by` (String) +- `created_by_email` (String) +- `created_by_full_name` (String) +- `created_date_time` (String) +- `deleted_by` (String) +- `deleted_by_email` (String) +- `deleted_by_full_name` (String) +- `deleted_date_time` (String) +- `updated_by` (String) +- `updated_by_email` (String) +- `updated_by_full_name` (String) +- `updated_date_time` (String) + + + +### Nested Schema for `data.notifications` + +Read-Only: + +- `emails` (List of String) +- `send_interval` (String) +- `type` (String) + + + +### Nested Schema for `data.project` + +Read-Only: + +- `href` (String) +- `project_id` (String) + + + +### Nested Schema for `data.service_token_connection` + +Read-Only: + +- `a_side` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--a_side)) +- `allow_custom_bandwidth` (Boolean) +- `allow_remote_connection` (Boolean) +- `bandwidth_limit` (Number) +- `supported_bandwidths` (List of Number) +- `type` (String) +- `uuid` (String) +- `z_side` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--z_side)) + + +### Nested Schema for `data.service_token_connection.a_side` + +Read-Only: + +- `access_point_selectors` (List of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--a_side--access_point_selectors)) + + +### Nested Schema for `data.service_token_connection.a_side.access_point_selectors` + +Read-Only: + +- `interface` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--a_side--access_point_selectors--interface)) +- `link_protocol` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--a_side--access_point_selectors--link_protocol)) +- `network` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--a_side--access_point_selectors--network)) +- `port` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--a_side--access_point_selectors--port)) +- `type` (String) +- `virtual_device` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--a_side--access_point_selectors--virtual_device)) + + +### Nested Schema for `data.service_token_connection.a_side.access_point_selectors.interface` + +Read-Only: + +- `id` (Number) +- `type` (String) +- `uuid` (String) + + + +### Nested Schema for `data.service_token_connection.a_side.access_point_selectors.link_protocol` + +Read-Only: + +- `type` (String) +- `vlan_c_tag` (Number) +- `vlan_s_tag` (Number) +- `vlan_tag` (Number) + + + +### Nested Schema for `data.service_token_connection.a_side.access_point_selectors.network` + +Read-Only: + +- `href` (String) +- `location` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--a_side--access_point_selectors--network--location)) +- `name` (String) +- `scope` (String) +- `type` (String) +- `uuid` (String) + + +### Nested Schema for `data.service_token_connection.a_side.access_point_selectors.network.location` + +Read-Only: + +- `ibx` (String) +- `metro_code` (String) +- `metro_name` (String) +- `region` (String) + + + + +### Nested Schema for `data.service_token_connection.a_side.access_point_selectors.port` + +Read-Only: + +- `account_name` (String) +- `bandwidth` (Number) +- `cvp_id` (Number) +- `encapsulation_protocol_type` (String) +- `href` (String) +- `location` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--a_side--access_point_selectors--port--location)) +- `port_name` (String) +- `priority` (String) +- `type` (String) +- `uuid` (String) + + +### Nested Schema for `data.service_token_connection.a_side.access_point_selectors.port.location` + +Read-Only: + +- `ibx` (String) +- `metro_code` (String) +- `metro_name` (String) +- `region` (String) + + + + +### Nested Schema for `data.service_token_connection.a_side.access_point_selectors.virtual_device` + +Read-Only: + +- `cluster` (String) +- `href` (String) +- `name` (String) +- `type` (String) +- `uuid` (String) + + + + + +### Nested Schema for `data.service_token_connection.z_side` + +Read-Only: + +- `access_point_selectors` (List of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--z_side--access_point_selectors)) + + +### Nested Schema for `data.service_token_connection.z_side.access_point_selectors` + +Read-Only: + +- `interface` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--z_side--access_point_selectors--interface)) +- `link_protocol` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--z_side--access_point_selectors--link_protocol)) +- `network` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--z_side--access_point_selectors--network)) +- `port` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--z_side--access_point_selectors--port)) +- `type` (String) +- `virtual_device` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--z_side--access_point_selectors--virtual_device)) + + +### Nested Schema for `data.service_token_connection.z_side.access_point_selectors.interface` + +Read-Only: + +- `id` (Number) +- `type` (String) +- `uuid` (String) + + + +### Nested Schema for `data.service_token_connection.z_side.access_point_selectors.link_protocol` + +Read-Only: + +- `type` (String) +- `vlan_c_tag` (Number) +- `vlan_s_tag` (Number) +- `vlan_tag` (Number) + + + +### Nested Schema for `data.service_token_connection.z_side.access_point_selectors.network` + +Read-Only: + +- `href` (String) +- `location` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--z_side--access_point_selectors--network--location)) +- `name` (String) +- `scope` (String) +- `type` (String) +- `uuid` (String) + + +### Nested Schema for `data.service_token_connection.z_side.access_point_selectors.network.location` + +Read-Only: + +- `ibx` (String) +- `metro_code` (String) +- `metro_name` (String) +- `region` (String) + + + + +### Nested Schema for `data.service_token_connection.z_side.access_point_selectors.port` + +Read-Only: + +- `account_name` (String) +- `bandwidth` (Number) +- `cvp_id` (Number) +- `encapsulation_protocol_type` (String) +- `href` (String) +- `location` (Set of Object) (see [below for nested schema](#nestedobjatt--data--service_token_connection--z_side--access_point_selectors--port--location)) +- `port_name` (String) +- `priority` (String) +- `type` (String) +- `uuid` (String) + + +### Nested Schema for `data.service_token_connection.z_side.access_point_selectors.port.location` + +Read-Only: + +- `ibx` (String) +- `metro_code` (String) +- `metro_name` (String) +- `region` (String) + + + + +### Nested Schema for `data.service_token_connection.z_side.access_point_selectors.virtual_device` + +Read-Only: + +- `cluster` (String) +- `href` (String) +- `name` (String) +- `type` (String) +- `uuid` (String) diff --git a/docs/resources/fabric_service_token.md b/docs/resources/fabric_service_token.md new file mode 100644 index 000000000..fdbec61bf --- /dev/null +++ b/docs/resources/fabric_service_token.md @@ -0,0 +1,397 @@ +--- +subcategory: "Fabric" +--- + +# equinix_fabric_service_token (Resource) + +Fabric V4 API compatible resource allows creation and management of [Equinix Fabric Service Token](https://docs.equinix.com/en-us/Content/Interconnection/Fabric/service%20tokens/Fabric-Service-Tokens.htm). + +Additional documentation: +* Getting Started: https://docs.equinix.com/en-us/Content/Interconnection/Fabric/service%20tokens/Fabric-Service-Tokens.htm +* API: https://docs.equinix.com/en-us/Content/KnowledgeCenter/Fabric/GettingStarted/Integrating-with-Fabric-V4-APIs/ConnectUsingServiceToken.htm + +## Example Usage + +Zside Virtual Device Service Token +```terraform +resource "equinix_fabric_service_token" "test" { + type = "VC_TOKEN" + expiration_date_time = "2025-01-18T06:43:49.980Z" + service_token_connection { + type = "EVPL_VC" + supported_bandwidths = [50, 200, 10000] + z_side { + access_point_selectors { + type = "VD" + virtual_device { + type = "EDGE" + uuid = " +## Schema + +### Required + +- `expiration_date_time` (String) Expiration date and time of the service token; 2020-11-06T07:00:00Z +- `notifications` (Block Set, Min: 1) Preferences for notifications on Service Token configuration or status changes (see [below for nested schema](#nestedblock--notifications)) +- `service_token_connection` (Block Set, Min: 1) Service Token Connection Type Information (see [below for nested schema](#nestedblock--service_token_connection)) +- `type` (String) Service Token Type; VC_TOKEN,EPL_TOKEN + +### Optional + +- `description` (String) Optional Description to the Service Token you will be creating +- `name` (String) Name of the Service Token +- `project` (Block Set, Max: 1) Project information (see [below for nested schema](#nestedblock--project)) +- `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts)) + +### Read-Only + +- `account` (Set of Object) Customer account information that is associated with this service token (see [below for nested schema](#nestedatt--account)) +- `change_log` (Set of Object) Captures connection lifecycle change information (see [below for nested schema](#nestedatt--change_log)) +- `href` (String) An absolute URL that is the subject of the link's context. +- `id` (String) The ID of this resource. +- `issuer_side` (String) Information about token side; ASIDE, ZSIDE +- `state` (String) Service token state; ACTIVE, INACTIVE, EXPIRED, DELETED +- `uuid` (String) Equinix-assigned service token identifier + + +### Nested Schema for `notifications` + +Required: + +- `emails` (List of String) Array of contact emails +- `type` (String) Notification Type - ALL,CONNECTION_APPROVAL,SALES_REP_NOTIFICATIONS, NOTIFICATIONS + +Optional: + +- `send_interval` (String) Send interval + + + +### Nested Schema for `service_token_connection` + +Required: + +- `supported_bandwidths` (List of Number) List of permitted bandwidths' For Port +- `type` (String) Type of Connection supported by Service Token you will create; EVPL_VC, EVPLAN_VC, EPLAN_VC, IPWAN_VC + +Optional: + +- `a_side` (Block Set) A-Side Connection link protocol,virtual device or network configuration (see [below for nested schema](#nestedblock--service_token_connection--a_side)) +- `allow_custom_bandwidth` (Boolean) Allow custom bandwidth value +- `allow_remote_connection` (Boolean) Authorization to connect remotely +- `bandwidth_limit` (Number) Connection bandwidth limit in Mbps +- `z_side` (Block Set) Z-Side Connection link protocol,virtual device or network configuration (see [below for nested schema](#nestedblock--service_token_connection--z_side)) + +Read-Only: + +- `uuid` (String) Equinix-assigned connection identifier + + +### Nested Schema for `service_token_connection.a_side` + +Required: + +- `access_point_selectors` (Block List, Min: 1) List of criteria for selecting network access points with optimal efficiency, security, compatibility, and availability (see [below for nested schema](#nestedblock--service_token_connection--a_side--access_point_selectors)) + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors` + +Optional: + +- `interface` (Block Set, Max: 1) Virtual Device Interface Configuration (see [below for nested schema](#nestedblock--service_token_connection--a_side--access_point_selectors--interface)) +- `link_protocol` (Block Set, Max: 1) Link protocol Configuration (see [below for nested schema](#nestedblock--service_token_connection--a_side--access_point_selectors--link_protocol)) +- `network` (Block Set, Max: 1) Network Configuration (see [below for nested schema](#nestedblock--service_token_connection--a_side--access_point_selectors--network)) +- `port` (Block Set, Max: 1) Port Configuration (see [below for nested schema](#nestedblock--service_token_connection--a_side--access_point_selectors--port)) +- `type` (String) Type of Access point; COLO, VD, NETWORK +- `virtual_device` (Block Set, Max: 1) Virtual Device Configuration (see [below for nested schema](#nestedblock--service_token_connection--a_side--access_point_selectors--virtual_device)) + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.interface` + +Optional: + +- `id` (Number) id +- `type` (String) Interface type + +Read-Only: + +- `uuid` (String) Equinix-assigned interface identifier + + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.link_protocol` + +Optional: + +- `type` (String) Type of the link protocol - UNTAGGED, DOT1Q, QINQ, EVPN_VXLAN +- `vlan_c_tag` (Number) Vlan Customer Tag information, vlanCTag value specified for QINQ connections +- `vlan_s_tag` (Number) Vlan Provider Tag information, vlanSTag value specified for QINQ connections +- `vlan_tag` (Number) Vlan Tag information, vlanTag value specified for DOT1Q connections + + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.network` + +Optional: + +- `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 + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.network.location` + +Optional: + +- `ibx` (String) IBX Code +- `metro_code` (String) Access point metro code +- `metro_name` (String) Access point metro name +- `region` (String) Access point region + + + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.port` + +Optional: + +- `account_name` (String) Account Name +- `bandwidth` (Number) Port Bandwidth +- `cvp_id` (Number) Customer virtual port Id +- `encapsulation_protocol_type` (String) Port Encapsulation +- `location` (Block Set) Port Location (see [below for nested schema](#nestedblock--service_token_connection--a_side--access_point_selectors--port--location)) +- `port_name` (String) Port Name +- `priority` (String) Port Priority +- `type` (String) Type of Port +- `uuid` (String) Equinix-assigned Port identifier + +Read-Only: + +- `href` (String) Unique Resource Identifier + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.port.location` + +Optional: + +- `ibx` (String) IBX Code +- `metro_code` (String) Access point metro code +- `metro_name` (String) Access point metro name +- `region` (String) Access point region + + + + +### Nested Schema for `service_token_connection.a_side.access_point_selectors.virtual_device` + +Required: + +- `uuid` (String) Equinix-assigned Virtual Device identifier + +Optional: + +- `cluster` (String) Virtual Device Cluster Information +- `name` (String) Customer-assigned Virtual Device Name +- `type` (String) Virtual Device type + +Read-Only: + +- `href` (String) Unique Resource Identifier + + + + + +### Nested Schema for `service_token_connection.z_side` + +Required: + +- `access_point_selectors` (Block List, Min: 1) List of criteria for selecting network access points with optimal efficiency, security, compatibility, and availability (see [below for nested schema](#nestedblock--service_token_connection--z_side--access_point_selectors)) + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors` + +Optional: + +- `interface` (Block Set, Max: 1) Virtual Device Interface Configuration (see [below for nested schema](#nestedblock--service_token_connection--z_side--access_point_selectors--interface)) +- `link_protocol` (Block Set, Max: 1) Link protocol Configuration (see [below for nested schema](#nestedblock--service_token_connection--z_side--access_point_selectors--link_protocol)) +- `network` (Block Set, Max: 1) Network Configuration (see [below for nested schema](#nestedblock--service_token_connection--z_side--access_point_selectors--network)) +- `port` (Block Set, Max: 1) Port Configuration (see [below for nested schema](#nestedblock--service_token_connection--z_side--access_point_selectors--port)) +- `type` (String) Type of Access point; COLO, VD, NETWORK +- `virtual_device` (Block Set, Max: 1) Virtual Device Configuration (see [below for nested schema](#nestedblock--service_token_connection--z_side--access_point_selectors--virtual_device)) + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.interface` + +Optional: + +- `id` (Number) id +- `type` (String) Interface type + +Read-Only: + +- `uuid` (String) Equinix-assigned interface identifier + + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.link_protocol` + +Optional: + +- `type` (String) Type of the link protocol - UNTAGGED, DOT1Q, QINQ, EVPN_VXLAN +- `vlan_c_tag` (Number) Vlan Customer Tag information, vlanCTag value specified for QINQ connections +- `vlan_s_tag` (Number) Vlan Provider Tag information, vlanSTag value specified for QINQ connections +- `vlan_tag` (Number) Vlan Tag information, vlanTag value specified for DOT1Q connections + + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.network` + +Optional: + +- `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 + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.network.location` + +Optional: + +- `ibx` (String) IBX Code +- `metro_code` (String) Access point metro code +- `metro_name` (String) Access point metro name +- `region` (String) Access point region + + + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.port` + +Optional: + +- `account_name` (String) Account Name +- `bandwidth` (Number) Port Bandwidth +- `cvp_id` (Number) Customer virtual port Id +- `encapsulation_protocol_type` (String) Port Encapsulation +- `location` (Block Set) Port Location (see [below for nested schema](#nestedblock--service_token_connection--z_side--access_point_selectors--port--location)) +- `port_name` (String) Port Name +- `priority` (String) Port Priority +- `type` (String) Type of Port +- `uuid` (String) Equinix-assigned Port identifier + +Read-Only: + +- `href` (String) Unique Resource Identifier + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.port.location` + +Optional: + +- `ibx` (String) IBX Code +- `metro_code` (String) Access point metro code +- `metro_name` (String) Access point metro name +- `region` (String) Access point region + + + + +### Nested Schema for `service_token_connection.z_side.access_point_selectors.virtual_device` + +Required: + +- `uuid` (String) Equinix-assigned Virtual Device identifier + +Optional: + +- `cluster` (String) Virtual Device Cluster Information +- `name` (String) Customer-assigned Virtual Device Name +- `type` (String) Virtual Device type + +Read-Only: + +- `href` (String) Unique Resource Identifier + + + + + + +### Nested Schema for `project` + +Optional: + +- `project_id` (String) Project Id + +Read-Only: + +- `href` (String) Unique Resource URL + + + +### Nested Schema for `timeouts` + +Optional: + +- `create` (String) +- `delete` (String) +- `read` (String) +- `update` (String) + + + +### Nested Schema for `account` + +Read-Only: + +- `account_name` (String) +- `account_number` (Number) +- `global_cust_id` (String) +- `global_org_id` (String) +- `global_organization_name` (String) +- `org_id` (Number) +- `organization_name` (String) +- `ucm_id` (String) + + + +### Nested Schema for `change_log` + +Read-Only: + +- `created_by` (String) +- `created_by_email` (String) +- `created_by_full_name` (String) +- `created_date_time` (String) +- `deleted_by` (String) +- `deleted_by_email` (String) +- `deleted_by_full_name` (String) +- `deleted_date_time` (String) +- `updated_by` (String) +- `updated_by_email` (String) +- `updated_by_full_name` (String) +- `updated_date_time` (String) \ No newline at end of file diff --git a/examples/data-sources/equinix_fabric_service_token/data-source.tf b/examples/data-sources/equinix_fabric_service_token/data-source.tf new file mode 100644 index 000000000..a234ba32e --- /dev/null +++ b/examples/data-sources/equinix_fabric_service_token/data-source.tf @@ -0,0 +1,35 @@ +data "equinix_fabric_service_token" "service-token" { + uuid = "" +} + +output "id" { + value = data.equinix_fabric_service_token.service-token.id +} + +output "type" { + value = data.equinix_fabric_service_token.service-token.type +} + +output "expiration_date_time" { + value = data.equinix_fabric_service_token.service-token.expiration_date_time +} + +output "supported_bandwidths" { + value = data.equinix_fabric_service_token.service-token.service_token_connection.0.supported_bandwidths +} + +output "virtual_device_type" { + value = data.equinix_fabric_service_token.service-token.service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.type +} + +output "virtual_device_uuid" { + value = data.equinix_fabric_service_token.service-token.service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid +} + +output "interface_type" { + value = data.equinix_fabric_service_token.service-token.service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.type +} + +output "interface_uuid" { + value = data.equinix_fabric_service_token.service-token.service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id +} \ No newline at end of file diff --git a/examples/data-sources/equinix_fabric_service_tokens/data-source.tf b/examples/data-sources/equinix_fabric_service_tokens/data-source.tf new file mode 100644 index 000000000..45d8a76f7 --- /dev/null +++ b/examples/data-sources/equinix_fabric_service_tokens/data-source.tf @@ -0,0 +1,53 @@ +data "equinix_fabric_service_tokens" "service-tokens" { + filter { + property = "/type" + operator = "=" + values = "EVPL_VC" + } + filter { + property = "/state" + operator = "=" + values = ["INACTIVE"] + } + pagination { + offset = 0 + limit = 5 + total = 25 + } +} + +output "number_of_returned_service_tokens" { + value = length(data.equinix_fabric_service_tokens.service-tokens.data) +} + +output "first_service_token_id" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.id +} + +output "first_service_token_type" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.type +} + +output "first_service_token_expiration_date_time" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.expiration_date_time +} + +output "first_service_token_supported_bandwidths" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.service_token_connection.0.supported_bandwidths +} + +output "first_service_token_virtual_device_type" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.type +} + +output "first_service_token_virtual_device_uuid" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid +} + +output "first_service_token_interface_type" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.type +} + +output "first_service_token_interface_uuid" { + value = data.equinix_fabric_service_tokens.service-tokens.data.0.service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id +} \ No newline at end of file diff --git a/internal/resources/fabric/service_token/datasources.go b/internal/resources/fabric/service_token/datasources.go index adba0f1d6..f28b54c71 100644 --- a/internal/resources/fabric/service_token/datasources.go +++ b/internal/resources/fabric/service_token/datasources.go @@ -13,7 +13,11 @@ func DataSource() *schema.Resource { return &schema.Resource{ ReadContext: dataSourceRead, Schema: dataSourceBaseSchema(), - Description: `Fabric V4 API compatible data resource that allow user to fetch service token for a given UUID`, + Description: `Fabric V4 API compatible data resource that allow user to fetch service token for a given UUID + +Additional documentation: +* Getting Started: https://docs.equinix.com/en-us/Content/Interconnection/Fabric/service%20tokens/Fabric-Service-Tokens.htm +* API: https://docs.equinix.com/en-us/Content/KnowledgeCenter/Fabric/GettingStarted/Integrating-with-Fabric-V4-APIs/ConnectUsingServiceToken.htm`, } } @@ -27,7 +31,11 @@ func DataSourceSearch() *schema.Resource { return &schema.Resource{ ReadContext: dataSourceSearch, Schema: dataSourceSearchSchema(), - Description: `Fabric V4 API compatible data resource that allow user to fetch service token for a given search data set`, + Description: `Fabric V4 API compatible data resource that allow user to fetch service token for a given search data set + +Additional documentation: +* Getting Started: https://docs.equinix.com/en-us/Content/Interconnection/Fabric/service%20tokens/Fabric-Service-Tokens.htm +* API: https://docs.equinix.com/en-us/Content/KnowledgeCenter/Fabric/GettingStarted/Integrating-with-Fabric-V4-APIs/ConnectUsingServiceToken.htm`, } } diff --git a/internal/resources/fabric/service_token/models.go b/internal/resources/fabric/service_token/models.go index 6d38f7e42..b8b0168f3 100644 --- a/internal/resources/fabric/service_token/models.go +++ b/internal/resources/fabric/service_token/models.go @@ -8,7 +8,6 @@ import ( 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/schema" - "log" "reflect" "sort" "time" @@ -21,13 +20,11 @@ func buildCreateRequest(d *schema.ResourceData) fabricv4.ServiceToken { serviceTokenRequest.SetType(fabricv4.ServiceTokenType(typeConfig)) expirationDateTimeConfig := d.Get("expiration_date_time").(string) - log.Printf("[DEBUG] !!!! Expiration Date %v", expirationDateTimeConfig) const TimeFormat = "2006-01-02T15:04:05.000Z" expirationTime, err := time.Parse(TimeFormat, expirationDateTimeConfig) if err != nil { fmt.Print("Error Parsing expiration date time: ", err) } - log.Printf("[DEBUG] !!! Parsed expiration date %v", expirationTime) serviceTokenRequest.SetExpirationDateTime(expirationTime) connectionConfig := d.Get("service_token_connection").(*schema.Set).List() @@ -79,7 +76,6 @@ func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOpe for _, notification := range oldNotifications.(*schema.Set).List() { notificationMap := notification.(map[string]interface{}) - // Extract old emails list if it exists if emails, ok := notificationMap["emails"]; ok { oldEmailInterface := emails.([]interface{}) if len(oldEmailInterface) > 0 { @@ -92,7 +88,6 @@ func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOpe for _, notification := range newNotifications.(*schema.Set).List() { notificationMap := notification.(map[string]interface{}) - // Extract old emails list if it exists if emails, ok := notificationMap["emails"]; ok { newEmailInterface := emails.([]interface{}) if len(newEmailInterface) > 0 { @@ -102,7 +97,6 @@ func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOpe } } - log.Print("!!! DEBUG value of new email", newNotificationEmails) if !reflect.DeepEqual(oldNotificationEmails, newNotificationEmails) { patches = append(patches, fabricv4.ServiceTokenChangeOperation{ Op: "replace", @@ -112,18 +106,13 @@ func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOpe } oldServiceTokenConnection, newServiceTokenConnection := d.GetChange("service_token_connection") - log.Printf("[DEBUG] !!! old ServiceToken Connection %v", oldServiceTokenConnection) - log.Printf("[DEBUG] !!! new ServiceToken Connection %v", newServiceTokenConnection) - // Initialize variables for bandwidth limits var oldAsideBandwidthLimit, newAsideBandwidthLimit int - // Extract old bandwidth limit if oldServiceTokenConnection != nil { for _, connection := range oldServiceTokenConnection.(*schema.Set).List() { notificationMap := connection.(map[string]interface{}) - // Extract old bandwidth limit if it exists if bandwidth, ok := notificationMap["bandwidthLimit"]; ok { oldBandwidthLimit := bandwidth.([]interface{}) if len(oldBandwidthLimit) > 0 { @@ -134,12 +123,10 @@ func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOpe } } - // Extract new bandwidth limit if newServiceTokenConnection != nil { for _, connection := range newServiceTokenConnection.(*schema.Set).List() { notificationMap := connection.(map[string]interface{}) - // Extract new bandwidth limit if it exists if bandwidth, ok := notificationMap["bandwidthLimit"]; ok { newBandwidthLimit := bandwidth.([]interface{}) if len(newBandwidthLimit) > 0 { @@ -158,14 +145,12 @@ func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOpe }) } - // Get the old and new values for bandwidth limit var oldZsideBandwidth, newZsideBandwidth []int if oldServiceTokenConnection != nil { for _, connection := range oldServiceTokenConnection.(*schema.Set).List() { notificationMap := connection.(map[string]interface{}) - // Extract old bandwidth limit if it exists if bandwidth, ok := notificationMap["supported_bandwidths"]; ok { oldSupportedBandwidth := bandwidth.([]interface{}) if len(oldSupportedBandwidth) > 0 { @@ -179,7 +164,6 @@ func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOpe for _, connection := range newServiceTokenConnection.(*schema.Set).List() { notificationMap := connection.(map[string]interface{}) - // Extract new bandwidth limit if it exists if bandwidth, ok := notificationMap["supported_bandwidths"]; ok { newSupportedBandwidth := bandwidth.([]interface{}) if len(newSupportedBandwidth) > 0 { @@ -190,8 +174,6 @@ func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOpe } } - log.Print("!!! DEBUG value of new supprted bandwidth", newZsideBandwidth) - log.Printf("[DEBUG] Value of aresliceequal fucntion %v", areSlicesEqual(oldZsideBandwidth, newZsideBandwidth)) if !areSlicesEqual(oldZsideBandwidth, newZsideBandwidth) { patches = append(patches, fabricv4.ServiceTokenChangeOperation{ Op: "replace", @@ -208,13 +190,9 @@ func areSlicesEqual(a, b []int) bool { return false } - // Sort both slices sort.Ints(a) sort.Ints(b) - log.Printf("value of int a %v", a) - log.Printf("value of int b %v", b) - // Compare sorted slices for i := range a { if a[i] != b[i] { return false @@ -324,14 +302,10 @@ func connectionTerraformToGo(connectionTerraform []interface{}) fabricv4.Service supportedBandwidths := connectionMap["supported_bandwidths"].([]interface{}) if supportedBandwidths != nil { - // Create a new slice to hold the int32 values int32Bandwidths := make([]int32, len(supportedBandwidths)) - - // Convert []interface{} to []int32 for i, v := range supportedBandwidths { - int32Bandwidths[i] = int32(v.(int)) // Assign directly to the slice at index i + int32Bandwidths[i] = int32(v.(int)) } - // Set the converted []int32 to the connection connection.SetSupportedBandwidths(int32Bandwidths) } @@ -356,7 +330,7 @@ func accessPointTerraformToGo(accessPoint []interface{}) fabricv4.ServiceTokenSi var apSide fabricv4.ServiceTokenSide accessPointMap := accessPoint[0].(map[string]interface{}) - accessPointSelectors := accessPointMap["access_point_selectors"].(*schema.Set).List() + accessPointSelectors := accessPointMap["access_point_selectors"].([]interface{}) if len(accessPointSelectors) != 0 { aps := accessPointSelectorsTerraformToGo(accessPointSelectors) apSide.SetAccessPointSelectors(aps) @@ -577,56 +551,45 @@ func connectionGoToTerraform(connection *fabricv4.ServiceTokenConnection) *schem } func accessPointGoToTerraform(accessPoint *fabricv4.ServiceTokenSide) *schema.Set { - mappedAccessPoint := make(map[string]interface{}) - if accessPoint.AccessPointSelectors != nil { - accessPointSelectors := accessPoint.GetAccessPointSelectors() - - apSelectorsSet := schema.NewSet( - schema.HashResource(accessPointSelectorsSch()), - nil, - ) - for _, selector := range accessPointSelectors { - mappedSelector := accessPointSelectorsGoToTerraform(&selector) - apSelectorsSet.Add(mappedSelector) - - } - mappedAccessPoint["access_point_selectors"] = apSelectorsSet - } - - accessPointSet := schema.NewSet( - schema.HashResource(accessPointSelectorsSch()), - []interface{}{mappedAccessPoint}, + return schema.NewSet( + schema.HashResource(serviceTokenAccessPointSch()), + []interface{}{map[string]interface{}{ + "access_point_selectors": accessPointSelectorsGoToTerraform(accessPoint.GetAccessPointSelectors()), + }}, ) - return accessPointSet } -func accessPointSelectorsGoToTerraform(apSelectors *fabricv4.AccessPointSelector) map[string]interface{} { - mappedAccessPointSelectors := make(map[string]interface{}) - if apSelectors.Type != nil { - mappedAccessPointSelectors["type"] = string(apSelectors.GetType()) - } - if apSelectors.Port != nil { - port := apSelectors.GetPort() - mappedAccessPointSelectors["port"] = portGoToTerraform(&port) - } - if apSelectors.LinkProtocol != nil { - linkProtocol := apSelectors.GetLinkProtocol() - mappedAccessPointSelectors["link_protocol"] = linkedProtocolGoToTerraform(&linkProtocol) - } - if apSelectors.VirtualDevice != nil { - virtualDevice := apSelectors.GetVirtualDevice() - mappedAccessPointSelectors["virtual_device"] = virtualDeviceGoToTerraform(&virtualDevice) - } - if apSelectors.Interface != nil { - interface_ := apSelectors.GetInterface() - mappedAccessPointSelectors["interface"] = interfaceGoToTerraform(&interface_) - } - if apSelectors.Network != nil { - network := apSelectors.GetNetwork() - mappedAccessPointSelectors["network"] = networkGoToTerraform(&network) +func accessPointSelectorsGoToTerraform(apSelectors []fabricv4.AccessPointSelector) []interface{} { + mappedSelectors := make([]interface{}, len(apSelectors)) + for index, selector := range apSelectors { + mappedAccessPointSelector := make(map[string]interface{}) + if selector.Type != nil { + mappedAccessPointSelector["type"] = string(selector.GetType()) + } + if selector.Port != nil { + port := selector.GetPort() + mappedAccessPointSelector["port"] = portGoToTerraform(&port) + } + if selector.LinkProtocol != nil { + linkProtocol := selector.GetLinkProtocol() + mappedAccessPointSelector["link_protocol"] = linkedProtocolGoToTerraform(&linkProtocol) + } + if selector.VirtualDevice != nil { + virtualDevice := selector.GetVirtualDevice() + mappedAccessPointSelector["virtual_device"] = virtualDeviceGoToTerraform(&virtualDevice) + } + if selector.Interface != nil { + interface_ := selector.GetInterface() + mappedAccessPointSelector["interface"] = interfaceGoToTerraform(&interface_) + } + if selector.Network != nil { + network := selector.GetNetwork() + mappedAccessPointSelector["network"] = networkGoToTerraform(&network) + } + mappedSelectors[index] = mappedAccessPointSelector } - return mappedAccessPointSelectors + return mappedSelectors } func portGoToTerraform(port *fabricv4.SimplifiedMetadataEntity) *schema.Set { diff --git a/internal/resources/fabric/service_token/resource.go b/internal/resources/fabric/service_token/resource.go index 94301e915..14d04ebf6 100644 --- a/internal/resources/fabric/service_token/resource.go +++ b/internal/resources/fabric/service_token/resource.go @@ -98,7 +98,8 @@ func resourceDelete(ctx context.Context, d *schema.ResourceData, meta interface{ if err != nil { if genericError, ok := err.(*fabricv4.GenericOpenAPIError); ok { if fabricErrs, ok := genericError.Model().([]fabricv4.Error); ok { - if equinix_errors.HasErrorCode(fabricErrs, "") { + // EQ-3034019 = Service Token already deleted + if equinix_errors.HasErrorCode(fabricErrs, "EQ-3034019") { return diags } } diff --git a/internal/resources/fabric/service_token/resource_schema.go b/internal/resources/fabric/service_token/resource_schema.go index 0b9b5f39e..199e3cfc9 100644 --- a/internal/resources/fabric/service_token/resource_schema.go +++ b/internal/resources/fabric/service_token/resource_schema.go @@ -51,6 +51,7 @@ func resourceSchema() map[string]*schema.Schema { Required: true, Description: "Service Token Connection Type Information", Elem: serviceTokenConnectionSch(), + Set: schema.HashResource(serviceTokenConnectionSch()), }, "state": { Type: schema.TypeString, @@ -129,7 +130,7 @@ func serviceTokenConnectionSch() *schema.Resource { "supported_bandwidths": { Type: schema.TypeList, Required: true, - Description: "List of permitted bandwidths", + Description: "List of permitted bandwidths' For Port ", Elem: &schema.Schema{ Type: schema.TypeInt, }, @@ -140,6 +141,7 @@ func serviceTokenConnectionSch() *schema.Resource { Computed: true, Description: "A-Side Connection link protocol,virtual device or network configuration", Elem: serviceTokenAccessPointSch(), + Set: schema.HashResource(serviceTokenAccessPointSch()), }, "z_side": { Type: schema.TypeSet, @@ -147,6 +149,7 @@ func serviceTokenConnectionSch() *schema.Resource { Computed: true, Description: "Z-Side Connection link protocol,virtual device or network configuration", Elem: serviceTokenAccessPointSch(), + Set: schema.HashResource(serviceTokenAccessPointSch()), }, }, } @@ -156,10 +159,11 @@ func serviceTokenAccessPointSch() *schema.Resource { return &schema.Resource{ Schema: map[string]*schema.Schema{ "access_point_selectors": { - Type: schema.TypeSet, + Type: schema.TypeList, Required: true, Description: "List of criteria for selecting network access points with optimal efficiency, security, compatibility, and availability", Elem: accessPointSelectorsSch(), + Set: schema.HashResource(accessPointSelectorsSch()), }, }, } diff --git a/templates/resources/fabric_service_token.md.tmpl b/templates/resources/fabric_service_token.md.tmpl new file mode 100644 index 000000000..4d8d656f7 --- /dev/null +++ b/templates/resources/fabric_service_token.md.tmpl @@ -0,0 +1,23 @@ +--- +subcategory: "Fabric" +--- + +{{/* This template serves as a starting point for documentation generation, and can be customized with hardcoded values and/or doc gen templates. + +For example, the {{ .SchemaMarkdown }} template can be used to replace manual schema documentation if descriptions of schema attributes are added in the provider source code. */ -}} + +# equinix_fabric_service_token (Resource) + +Fabric V4 API compatible resource allows creation and management of [Equinix Fabric Service Token](https://docs.equinix.com/en-us/Content/Interconnection/Fabric/service%20tokens/Fabric-Service-Tokens.htm). + +Additional documentation: +* Getting Started: https://docs.equinix.com/en-us/Content/Interconnection/Fabric/service%20tokens/Fabric-Service-Tokens.htm +* API: https://docs.equinix.com/en-us/Content/KnowledgeCenter/Fabric/GettingStarted/Integrating-with-Fabric-V4-APIs/ConnectUsingServiceToken.htm + +## Example Usage + +Zside Virtual Device Service Token +{{tffile "examples/resources/equinix_fabric_service_token/zside_vd_service_token.tf"}} + + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file From 4e223642c64092bb8516b6fecfb8448caa8b744f Mon Sep 17 00:00:00 2001 From: srushti-patl Date: Wed, 23 Oct 2024 13:23:59 -0700 Subject: [PATCH 31/55] fix: Updating Service Token Resource & Data source --- internal/resources/fabric/service_token/resource_schema.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/resources/fabric/service_token/resource_schema.go b/internal/resources/fabric/service_token/resource_schema.go index 199e3cfc9..d3fd6ff1b 100644 --- a/internal/resources/fabric/service_token/resource_schema.go +++ b/internal/resources/fabric/service_token/resource_schema.go @@ -112,12 +112,14 @@ func serviceTokenConnectionSch() *schema.Resource { Type: schema.TypeBool, Optional: true, Computed: true, + Default: true, Description: "Authorization to connect remotely", }, "allow_custom_bandwidth": { Type: schema.TypeBool, Optional: true, Computed: true, + Default: true, Description: "Allow custom bandwidth value", }, "bandwidth_limit": { From 74452bc486fe4d4589981d647fe0dc5b4be2e1a0 Mon Sep 17 00:00:00 2001 From: srushti-patl Date: Wed, 16 Oct 2024 13:43:41 -0700 Subject: [PATCH 32/55] feat: Adding Fabric Service Token Resource and Data Source --- internal/resources/fabric/service_token/resource_schema.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/resources/fabric/service_token/resource_schema.go b/internal/resources/fabric/service_token/resource_schema.go index d3fd6ff1b..199e3cfc9 100644 --- a/internal/resources/fabric/service_token/resource_schema.go +++ b/internal/resources/fabric/service_token/resource_schema.go @@ -112,14 +112,12 @@ func serviceTokenConnectionSch() *schema.Resource { Type: schema.TypeBool, Optional: true, Computed: true, - Default: true, Description: "Authorization to connect remotely", }, "allow_custom_bandwidth": { Type: schema.TypeBool, Optional: true, Computed: true, - Default: true, Description: "Allow custom bandwidth value", }, "bandwidth_limit": { From db7320bce7b5741c1013ca4bf404f5d0362777e6 Mon Sep 17 00:00:00 2001 From: Charles Treatman Date: Mon, 28 Oct 2024 11:10:51 -0500 Subject: [PATCH 33/55] chore: allow teams to add/remove resources & data sources (#797) This refactors the SDKv2 and framework providers so that the data sources and resources for each Equinix service are defined in separate files. This enables each team to add or remove their own data sources and resources without requiring review from DRE. --- equinix/provider.go | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/equinix/provider.go b/equinix/provider.go index 7b3f0d270..47fa527b0 100644 --- a/equinix/provider.go +++ b/equinix/provider.go @@ -8,17 +8,6 @@ import ( "time" "github.com/equinix/terraform-provider-equinix/internal/config" - fabric_connection "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/connection" - fabric_connection_route_filter "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/connection_route_filter" - fabric_market_place_subscription "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/marketplace" - fabric_network "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/network" - fabric_route_filter "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/route_filter" - fabric_route_filter_rule "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/route_filter_rule" - fabric_service_token "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/service_token" - metal_device "github.com/equinix/terraform-provider-equinix/internal/resources/metal/device" - metal_port "github.com/equinix/terraform-provider-equinix/internal/resources/metal/port" - "github.com/equinix/terraform-provider-equinix/internal/resources/metal/virtual_circuit" - "github.com/equinix/terraform-provider-equinix/internal/resources/metal/vrf" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" From 19caaf158f7589e234686754fafa8e8895f1ebff Mon Sep 17 00:00:00 2001 From: srushti-patl Date: Wed, 23 Oct 2024 13:23:59 -0700 Subject: [PATCH 34/55] fix: Updating Service Token Resource & Data source --- internal/resources/fabric/service_token/resource_schema.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/resources/fabric/service_token/resource_schema.go b/internal/resources/fabric/service_token/resource_schema.go index 199e3cfc9..d3fd6ff1b 100644 --- a/internal/resources/fabric/service_token/resource_schema.go +++ b/internal/resources/fabric/service_token/resource_schema.go @@ -112,12 +112,14 @@ func serviceTokenConnectionSch() *schema.Resource { Type: schema.TypeBool, Optional: true, Computed: true, + Default: true, Description: "Authorization to connect remotely", }, "allow_custom_bandwidth": { Type: schema.TypeBool, Optional: true, Computed: true, + Default: true, Description: "Allow custom bandwidth value", }, "bandwidth_limit": { From 6b61a435e8230fa8106d84ea86060ac3245ab615 Mon Sep 17 00:00:00 2001 From: thogarty <139183873+thogarty@users.noreply.github.com> Date: Mon, 14 Oct 2024 12:27:50 -0700 Subject: [PATCH 35/55] feat: fabric resource connection_route_filter (#795) * Add equinix_fabric_connection_route_filter resource to attach route filter policies to fabric cloud router connections * Add data source for retrieving connection_route_filters by connection and route filter uuids * Add data source to get all route filters for a given connection uuid * Add docs with make docs * Add acceptance tests for resource and data sources Local tests passing: image --- equinix/provider.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/equinix/provider.go b/equinix/provider.go index 47fa527b0..d661ca35a 100644 --- a/equinix/provider.go +++ b/equinix/provider.go @@ -8,6 +8,16 @@ import ( "time" "github.com/equinix/terraform-provider-equinix/internal/config" + fabric_connection "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/connection" + fabric_market_place_subscription "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/marketplace" + fabric_network "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/network" + fabric_route_filter "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/route_filter" + fabric_route_filter_rule "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/route_filter_rule" + fabric_service_token "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/service_token" + metal_device "github.com/equinix/terraform-provider-equinix/internal/resources/metal/device" + metal_port "github.com/equinix/terraform-provider-equinix/internal/resources/metal/port" + "github.com/equinix/terraform-provider-equinix/internal/resources/metal/virtual_circuit" + "github.com/equinix/terraform-provider-equinix/internal/resources/metal/vrf" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" From b780ddfd060ef6cba4ac116ad3c2890a13dbea9f Mon Sep 17 00:00:00 2001 From: kpdhulipala <84343462+kpdhulipala@users.noreply.github.com> Date: Mon, 21 Oct 2024 12:01:53 -0700 Subject: [PATCH 36/55] feat: Update docs for New device type - "Aviatrix Transit Edge" under Aviatrix Vendor (#801) feat: Update docs for New device type - "Aviatrix Transit Edge" under Aviatrix Vendor --- templates/resources/network_device.md.tmpl | 1 - 1 file changed, 1 deletion(-) diff --git a/templates/resources/network_device.md.tmpl b/templates/resources/network_device.md.tmpl index cd8bc862e..5b157c7ee 100644 --- a/templates/resources/network_device.md.tmpl +++ b/templates/resources/network_device.md.tmpl @@ -57,7 +57,6 @@ The following arguments are supported: * `package_code` - (Required) Device software package code. * `version` - (Required) Device software software version. * `core_count` - (Required) Number of CPU cores used by device. (**NOTE: Use this field to resize your device. When resizing your HA devices, primary device will be upgraded first. If the upgrade failed, device will be automatically rolled back to the previous state with original core number.**) -* `tier` - (Optional, conflicts with `throughput`,`throughput_unit` ) Select bandwidth tier for your own license, i.e., `0` or `1` or `2` or `3`. Tiers applicable only for C8000V Autonomous or C8000V SDWAN (controller) device types. If not provided, tier is defaulted to '2'. * `term_length` - (Required) Device term length. * `self_managed` - (Optional) Boolean value that determines device management mode, i.e., `self-managed` or `Equinix-managed` (default). * `byol` - (Optional) Boolean value that determines device licensing mode, i.e., `bring your own license` or `subscription` (default). From 4f91dcbc7b9f358454aefbd1a3c41476b979030d Mon Sep 17 00:00:00 2001 From: kpdhulipala <84343462+kpdhulipala@users.noreply.github.com> Date: Wed, 23 Oct 2024 07:54:36 -0700 Subject: [PATCH 37/55] feat: Add optional attribute tier in Create Virtual Device request for C8000V and C8000V SDWAN (#771) feat: Add optional attribute tier in Create Virtual Device request for C8000V and C8000V SDWAN --- templates/resources/network_device.md.tmpl | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/resources/network_device.md.tmpl b/templates/resources/network_device.md.tmpl index 5b157c7ee..cd8bc862e 100644 --- a/templates/resources/network_device.md.tmpl +++ b/templates/resources/network_device.md.tmpl @@ -57,6 +57,7 @@ The following arguments are supported: * `package_code` - (Required) Device software package code. * `version` - (Required) Device software software version. * `core_count` - (Required) Number of CPU cores used by device. (**NOTE: Use this field to resize your device. When resizing your HA devices, primary device will be upgraded first. If the upgrade failed, device will be automatically rolled back to the previous state with original core number.**) +* `tier` - (Optional, conflicts with `throughput`,`throughput_unit` ) Select bandwidth tier for your own license, i.e., `0` or `1` or `2` or `3`. Tiers applicable only for C8000V Autonomous or C8000V SDWAN (controller) device types. If not provided, tier is defaulted to '2'. * `term_length` - (Required) Device term length. * `self_managed` - (Optional) Boolean value that determines device management mode, i.e., `self-managed` or `Equinix-managed` (default). * `byol` - (Optional) Boolean value that determines device licensing mode, i.e., `bring your own license` or `subscription` (default). From 9a70727b788de05ad27d8458f5ce1c918f95f74c Mon Sep 17 00:00:00 2001 From: srushti-patl Date: Wed, 16 Oct 2024 13:43:41 -0700 Subject: [PATCH 38/55] feat: Adding Fabric Service Token Resource and Data Source --- internal/resources/fabric/service_token/resource_schema.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/resources/fabric/service_token/resource_schema.go b/internal/resources/fabric/service_token/resource_schema.go index d3fd6ff1b..199e3cfc9 100644 --- a/internal/resources/fabric/service_token/resource_schema.go +++ b/internal/resources/fabric/service_token/resource_schema.go @@ -112,14 +112,12 @@ func serviceTokenConnectionSch() *schema.Resource { Type: schema.TypeBool, Optional: true, Computed: true, - Default: true, Description: "Authorization to connect remotely", }, "allow_custom_bandwidth": { Type: schema.TypeBool, Optional: true, Computed: true, - Default: true, Description: "Allow custom bandwidth value", }, "bandwidth_limit": { From 190bbd4ecebf13a8911419c4c4464a5b7fbfc29b Mon Sep 17 00:00:00 2001 From: srushti-patl Date: Mon, 28 Oct 2024 11:10:39 -0700 Subject: [PATCH 39/55] fix: Adding service token resource and data source to fabric provider maps --- equinix/fabric_provider_maps.go | 4 ++++ equinix/provider.go | 10 ---------- .../equinix_fabric_service_token/data-source.tf | 2 +- .../equinix_fabric_service_tokens/data-source.tf | 2 +- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/equinix/fabric_provider_maps.go b/equinix/fabric_provider_maps.go index 68d56a618..f1e8a6a80 100644 --- a/equinix/fabric_provider_maps.go +++ b/equinix/fabric_provider_maps.go @@ -7,6 +7,7 @@ import ( fabric_network "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/network" fabric_route_filter "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/route_filter" fabric_route_filter_rule "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/route_filter_rule" + fabric_service_token "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/service_token" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -31,6 +32,8 @@ func fabricDatasources() map[string]*schema.Resource { "equinix_fabric_route_filter_rules": fabric_route_filter_rule.DataSourceGetAllRules(), "equinix_fabric_service_profile": dataSourceFabricServiceProfileReadByUuid(), "equinix_fabric_service_profiles": dataSourceFabricSearchServiceProfilesByName(), + "equinix_fabric_service_token": fabric_service_token.DataSource(), + "equinix_fabric_service_tokens": fabric_service_token.DataSourceSearch(), } } @@ -44,5 +47,6 @@ func fabricResources() map[string]*schema.Resource { "equinix_fabric_route_filter_rule": fabric_route_filter_rule.Resource(), "equinix_fabric_routing_protocol": resourceFabricRoutingProtocol(), "equinix_fabric_service_profile": resourceFabricServiceProfile(), + "equinix_fabric_service_token": fabric_service_token.Resource(), } } diff --git a/equinix/provider.go b/equinix/provider.go index d661ca35a..47fa527b0 100644 --- a/equinix/provider.go +++ b/equinix/provider.go @@ -8,16 +8,6 @@ import ( "time" "github.com/equinix/terraform-provider-equinix/internal/config" - fabric_connection "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/connection" - fabric_market_place_subscription "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/marketplace" - fabric_network "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/network" - fabric_route_filter "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/route_filter" - fabric_route_filter_rule "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/route_filter_rule" - fabric_service_token "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/service_token" - metal_device "github.com/equinix/terraform-provider-equinix/internal/resources/metal/device" - metal_port "github.com/equinix/terraform-provider-equinix/internal/resources/metal/port" - "github.com/equinix/terraform-provider-equinix/internal/resources/metal/virtual_circuit" - "github.com/equinix/terraform-provider-equinix/internal/resources/metal/vrf" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" diff --git a/examples/data-sources/equinix_fabric_service_token/data-source.tf b/examples/data-sources/equinix_fabric_service_token/data-source.tf index a234ba32e..c1289b4b9 100644 --- a/examples/data-sources/equinix_fabric_service_token/data-source.tf +++ b/examples/data-sources/equinix_fabric_service_token/data-source.tf @@ -32,4 +32,4 @@ output "interface_type" { output "interface_uuid" { value = data.equinix_fabric_service_token.service-token.service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id -} \ No newline at end of file +} diff --git a/examples/data-sources/equinix_fabric_service_tokens/data-source.tf b/examples/data-sources/equinix_fabric_service_tokens/data-source.tf index 45d8a76f7..775550b96 100644 --- a/examples/data-sources/equinix_fabric_service_tokens/data-source.tf +++ b/examples/data-sources/equinix_fabric_service_tokens/data-source.tf @@ -50,4 +50,4 @@ output "first_service_token_interface_type" { output "first_service_token_interface_uuid" { value = data.equinix_fabric_service_tokens.service-tokens.data.0.service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id -} \ No newline at end of file +} From a89f32f407fc5d3a1a23d46afdb91a30c28566f8 Mon Sep 17 00:00:00 2001 From: Charles Treatman Date: Tue, 29 Oct 2024 12:05:39 -0500 Subject: [PATCH 40/55] chore: remove unused function (#807) --- equinix/provider_test.go | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/equinix/provider_test.go b/equinix/provider_test.go index a539aca3d..768ae1e3e 100644 --- a/equinix/provider_test.go +++ b/equinix/provider_test.go @@ -119,24 +119,6 @@ func TestProvider_isEmpty(t *testing.T) { } } -func TestProvider_setSchemaValueIfNotEmpty(t *testing.T) { - // given - key := "test" - s := map[string]*schema.Schema{ - key: { - Type: schema.TypeString, - Optional: true, - }, - } - var b *int = nil - d := schema.TestResourceDataRaw(t, s, make(map[string]interface{})) - // when - setSchemaValueIfNotEmpty(key, b, d) - // then - _, ok := d.GetOk(key) - assert.False(t, ok, "Key was not set") -} - // Deprecated test moved to internal/comparissons/comparisons_test.go func TestProvider_slicesMatch(t *testing.T) { // given @@ -250,10 +232,3 @@ func copyMap(source map[string]interface{}) map[string]interface{} { } return target } - -func setSchemaValueIfNotEmpty(key string, value interface{}, d *schema.ResourceData) error { - if !isEmpty(value) { - return d.Set(key, value) - } - return nil -} From fe0d4f809d47dcb588e163a36ffd884d81e316a4 Mon Sep 17 00:00:00 2001 From: Charles Treatman Date: Wed, 30 Oct 2024 09:42:51 -0500 Subject: [PATCH 41/55] chore: prevent new Go files from being added to equinix package (#808) To ensure that we continue to make progress on #106, this updates the `Validate PR` GitHub Actions Workflow to check if new Go files have been added to the `equinix/` directory. The workflow only checks for [files with `"added"` status](https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#list-pull-requests-files) that are directly under the `equinix/` directory. This leaves room for renaming/copying files and for adding new packages in `equinix//`. --- .github/workflows/validate_pr.yml | 65 ++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/.github/workflows/validate_pr.yml b/.github/workflows/validate_pr.yml index a10a0e2ed..4cbd74a83 100644 --- a/.github/workflows/validate_pr.yml +++ b/.github/workflows/validate_pr.yml @@ -11,7 +11,7 @@ permissions: pull-requests: write jobs: - main: + check-pr-title: name: Validate PR title runs-on: ubuntu-latest steps: @@ -43,3 +43,66 @@ jobs: with: header: pr-title-lint-error delete: true + + enforce-packages: + name: Prevent new files in `equinix` package + runs-on: ubuntu-latest + steps: + - uses: actions/github-script@v7 + id: check_added_files + with: + result-encoding: string + retries: 3 + script: | + const files = await github.paginate(github.rest.pulls.listFiles,{ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number + }) + + const blockedFiles = [] + + for (const file of files) { + if (file.status === "added" && file.filename.match(/equinix\/[^\/]*\.go/)) { + blockedFiles.push("- " + file.filename) + } + } + + var errorMessage = "" + + if (blockedFiles.length > 0) { + errorMessage = `The following files were added to the \`equinix\` package and must be moved somewhere else: + ${blockedFiles.join("\n")} + ` + core.setFailed(errorMessage) + } + + return errorMessage + + - uses: marocchino/sticky-pull-request-comment@v2 + # When the previous steps fails, the workflow would stop. By adding this + # condition you can continue the execution with the populated error message. + if: always() && (steps.check_added_files.outputs.result != '') + with: + header: files-added-to-equinix-error + message: | + We are actively working to reduce the amount of code in the `equinix` + package to avoid unintentional code sharing. + + New files should be added in an isolated package instead of adding + more code to the `equinix` package. You may need to refactor and/or + temporarily duplicate existing code in order to move your new code + to an isolated package. + + Details: + + ``` + ${{ steps.check_added_files.outputs.result }} + ``` + + # Delete a previous comment when the issue has been resolved + - if: steps.check_added_files.outputs.result == '' + uses: marocchino/sticky-pull-request-comment@v2 + with: + header: files-added-to-equinix-error + delete: true From 013c1ae44b284c0b5b291df1b81486a420557c80 Mon Sep 17 00:00:00 2001 From: srushti-patl Date: Wed, 30 Oct 2024 09:43:03 -0700 Subject: [PATCH 42/55] fix: Adding CustomizeDiff function --- .../resources/fabric/service_token/models.go | 16 ++-- .../fabric/service_token/resource.go | 89 ++++++++++++++++++- .../fabric/service_token/resource_schema.go | 31 ++++++- 3 files changed, 126 insertions(+), 10 deletions(-) diff --git a/internal/resources/fabric/service_token/models.go b/internal/resources/fabric/service_token/models.go index b8b0168f3..dbf71fa1c 100644 --- a/internal/resources/fabric/service_token/models.go +++ b/internal/resources/fabric/service_token/models.go @@ -111,9 +111,9 @@ func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOpe if oldServiceTokenConnection != nil { for _, connection := range oldServiceTokenConnection.(*schema.Set).List() { - notificationMap := connection.(map[string]interface{}) + oldBandwidthLimitMap := connection.(map[string]interface{}) - if bandwidth, ok := notificationMap["bandwidthLimit"]; ok { + if bandwidth, ok := oldBandwidthLimitMap["bandwidthLimit"]; ok { oldBandwidthLimit := bandwidth.([]interface{}) if len(oldBandwidthLimit) > 0 { oldAsideBandwidthLimits := converters.IfArrToIntArr(oldBandwidthLimit) @@ -125,9 +125,9 @@ func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOpe if newServiceTokenConnection != nil { for _, connection := range newServiceTokenConnection.(*schema.Set).List() { - notificationMap := connection.(map[string]interface{}) + newBandwidthLimitMap := connection.(map[string]interface{}) - if bandwidth, ok := notificationMap["bandwidthLimit"]; ok { + if bandwidth, ok := newBandwidthLimitMap["bandwidthLimit"]; ok { newBandwidthLimit := bandwidth.([]interface{}) if len(newBandwidthLimit) > 0 { newAsideBandwidthLimits := converters.IfArrToIntArr(newBandwidthLimit) @@ -149,9 +149,9 @@ func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOpe if oldServiceTokenConnection != nil { for _, connection := range oldServiceTokenConnection.(*schema.Set).List() { - notificationMap := connection.(map[string]interface{}) + olSupportedBandwidthMap := connection.(map[string]interface{}) - if bandwidth, ok := notificationMap["supported_bandwidths"]; ok { + if bandwidth, ok := olSupportedBandwidthMap["supported_bandwidths"]; ok { oldSupportedBandwidth := bandwidth.([]interface{}) if len(oldSupportedBandwidth) > 0 { oldZsideBandwidth = converters.IfArrToIntArr(oldSupportedBandwidth) @@ -162,9 +162,9 @@ func buildUpdateRequest(d *schema.ResourceData) []fabricv4.ServiceTokenChangeOpe if newServiceTokenConnection != nil { for _, connection := range newServiceTokenConnection.(*schema.Set).List() { - notificationMap := connection.(map[string]interface{}) + newSupportedBandwidthMap := connection.(map[string]interface{}) - if bandwidth, ok := notificationMap["supported_bandwidths"]; ok { + if bandwidth, ok := newSupportedBandwidthMap["supported_bandwidths"]; ok { newSupportedBandwidth := bandwidth.([]interface{}) if len(newSupportedBandwidth) > 0 { newZsideBandwidth = converters.IfArrToIntArr(newSupportedBandwidth) diff --git a/internal/resources/fabric/service_token/resource.go b/internal/resources/fabric/service_token/resource.go index 14d04ebf6..fc0c68e9d 100644 --- a/internal/resources/fabric/service_token/resource.go +++ b/internal/resources/fabric/service_token/resource.go @@ -2,14 +2,18 @@ package service_token import ( "context" + "fmt" "github.com/equinix/equinix-sdk-go/services/fabricv4" "github.com/equinix/terraform-provider-equinix/internal/config" equinix_errors "github.com/equinix/terraform-provider-equinix/internal/errors" equinix_fabric_schema "github.com/equinix/terraform-provider-equinix/internal/fabric/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "log" + "reflect" + "sort" "strings" "time" ) @@ -29,11 +33,94 @@ func Resource() *schema.Resource { Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, - Schema: resourceSchema(), + Schema: resourceSchema(), + CustomizeDiff: customdiff.All( + customdiff.ValidateChange("service_token_connection", func(ctx context.Context, oldValue, newValue, meta interface{}) error { + var oldZsideBandwidth, newZsideBandwidth []int + log.Printf("service token old %v", oldValue) + log.Printf("service token new %v", newValue) + + for _, connection := range oldValue.(*schema.Set).List() { + olSupportedBandwidthMap := connection.(map[string]interface{}) + + if bandwidth, ok := olSupportedBandwidthMap["supported_bandwidths"]; ok { + oldSupportedBandwidth := bandwidth.([]interface{}) + log.Printf("old value %v", oldSupportedBandwidth) + if len(oldSupportedBandwidth) > 0 { + sort.Ints(oldZsideBandwidth) + + } + } + } + + for _, connection := range newValue.(*schema.Set).List() { + newSupportedBandwidthMap := connection.(map[string]interface{}) + + if bandwidth, ok := newSupportedBandwidthMap["supported_bandwidths"]; ok { + newSupportedBandwidth := bandwidth.([]interface{}) + log.Printf("new value %v", newSupportedBandwidth) + if len(newSupportedBandwidth) > 0 { + sort.Ints(newZsideBandwidth) + + } + } + } + log.Printf("!! old value %v", oldZsideBandwidth) + log.Printf("!! old value %v", oldZsideBandwidth) + + if reflect.DeepEqual(oldZsideBandwidth, newZsideBandwidth) { + return nil // No diff if equivalent after sorting + } + + return fmt.Errorf("supported_bandwidths values are different: %v", newZsideBandwidth) + + }), + ), Description: `Fabric V4 API compatible resource allows creation and management of Equinix Fabric Service Token`, } } +func extractIntValues(value interface{}) []int { + var intSlice []int + + switch v := value.(type) { + case *schema.Set: + for _, item := range v.List() { + if val, ok := item.(int); ok { + intSlice = append(intSlice, val) + } + } + case []interface{}: + for _, item := range v { + if val, ok := item.(int); ok { + intSlice = append(intSlice, val) + } + } + } + + return intSlice +} + +//func convertToIntSlice(input interface{}) ([]int, bool) { +// if input == nil { +// return []int{}, true +// } +// +// // Type assert to a slice of interfaces, if applicable +// if slice, ok := input.([]interface{}); ok { +// intSlice := make([]int, 0, len(slice)) +// for _, v := range slice { +// if intVal, ok := v.(int); ok { +// intSlice = append(intSlice, intVal) +// } else { +// return []int{}, false // Conversion failed +// } +// } +// return intSlice, true +// } +// return []int{}, false // Not a valid slice type +//} + func resourceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*config.Config).NewFabricClientForSDK(d) serviceToken, _, err := client.ServiceTokensApi.GetServiceTokenByUuid(ctx, d.Id()).Execute() diff --git a/internal/resources/fabric/service_token/resource_schema.go b/internal/resources/fabric/service_token/resource_schema.go index 199e3cfc9..9725302bf 100644 --- a/internal/resources/fabric/service_token/resource_schema.go +++ b/internal/resources/fabric/service_token/resource_schema.go @@ -4,6 +4,8 @@ import ( equinix_fabric_schema "github.com/equinix/terraform-provider-equinix/internal/fabric/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "strconv" + "strings" ) func resourceSchema() map[string]*schema.Schema { @@ -134,6 +136,20 @@ func serviceTokenConnectionSch() *schema.Resource { Elem: &schema.Schema{ Type: schema.TypeInt, }, + //DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + // oldValues := convertStringToIntSlice(old) + // newValues := convertStringToIntSlice(new) + // + // // Sort both slices for consistent comparison + // sort.Ints(oldValues) + // sort.Ints(newValues) + // + // log.Printf("!!! old value %v", oldValues) + // log.Printf("!!! new Value %v", newValues) + // + // // Suppress diff if sorted slices are identical + // return reflect.DeepEqual(oldValues, newValues) + //}, }, "a_side": { Type: schema.TypeSet, @@ -154,6 +170,20 @@ func serviceTokenConnectionSch() *schema.Resource { }, } } +func convertStringToIntSlice(value string) []int { + // Split the comma-separated string + strValues := strings.Split(value, ",") + intSlice := make([]int, 0, len(strValues)) + + for _, str := range strValues { + // Trim spaces and convert each to int + num, err := strconv.Atoi(strings.TrimSpace(str)) + if err == nil { + intSlice = append(intSlice, num) + } + } + return intSlice +} func serviceTokenAccessPointSch() *schema.Resource { return &schema.Resource{ @@ -163,7 +193,6 @@ func serviceTokenAccessPointSch() *schema.Resource { Required: true, Description: "List of criteria for selecting network access points with optimal efficiency, security, compatibility, and availability", Elem: accessPointSelectorsSch(), - Set: schema.HashResource(accessPointSelectorsSch()), }, }, } From 8881b77fcb2a54fd0a0e80542554d13e63b2b325 Mon Sep 17 00:00:00 2001 From: srushti-patl Date: Thu, 31 Oct 2024 12:50:45 -0700 Subject: [PATCH 43/55] fix: Resolving lint errors! --- docs/data-sources/fabric_service_tokens.md | 4 +- docs/resources/fabric_service_token.md | 22 ++--- .../zside_vd_service_token.tf | 20 ++++ .../fabric/service_token/datasources.go | 1 + .../fabric/service_token/datasources_test.go | 50 +++++----- .../fabric/service_token/resource.go | 96 +------------------ .../fabric/service_token/resource_schema.go | 39 +------- .../fabric/service_token/resource_test.go | 40 ++++---- 8 files changed, 92 insertions(+), 180 deletions(-) create mode 100644 examples/resources/equinix_fabric_service_token/zside_vd_service_token.tf diff --git a/docs/data-sources/fabric_service_tokens.md b/docs/data-sources/fabric_service_tokens.md index 2379afcfc..732a0cd96 100644 --- a/docs/data-sources/fabric_service_tokens.md +++ b/docs/data-sources/fabric_service_tokens.md @@ -73,7 +73,7 @@ output "first_service_token_interface_uuid" { ### Required -- `filter` (Block List, Min: 1, Max: 10) Filters for the Data Source Search Request. Maximum of 8 total filters. (see [below for nested schema](#nestedblock--filter)) +- `filter` (Block List, Min: 1, Max: 10) Filters for the Data Source Search Request (see [below for nested schema](#nestedblock--filter)) ### Optional @@ -81,7 +81,7 @@ output "first_service_token_interface_uuid" { ### Read-Only -- `data` (List of Object) List of Route Filters (see [below for nested schema](#nestedatt--data)) +- `data` (List of Object) List of Service Tokens (see [below for nested schema](#nestedatt--data)) - `id` (String) The ID of this resource. diff --git a/docs/resources/fabric_service_token.md b/docs/resources/fabric_service_token.md index fdbec61bf..687368767 100644 --- a/docs/resources/fabric_service_token.md +++ b/docs/resources/fabric_service_token.md @@ -16,7 +16,7 @@ Zside Virtual Device Service Token ```terraform resource "equinix_fabric_service_token" "test" { type = "VC_TOKEN" - expiration_date_time = "2025-01-18T06:43:49.980Z" + expiration_date_time = "2025-01-18T06:43:49.986Z" service_token_connection { type = "EVPL_VC" supported_bandwidths = [50, 200, 10000] @@ -25,7 +25,7 @@ resource "equinix_fabric_service_token" "test" { type = "VD" virtual_device { type = "EDGE" - uuid = " ### Nested Schema for `service_token_connection.a_side.access_point_selectors.interface` -Optional: +Required: -- `id` (Number) id - `type` (String) Interface type Read-Only: +- `id` (Number) id - `uuid` (String) Equinix-assigned interface identifier @@ -207,13 +207,13 @@ Required: Optional: -- `cluster` (String) Virtual Device Cluster Information -- `name` (String) Customer-assigned Virtual Device Name - `type` (String) Virtual Device type Read-Only: +- `cluster` (String) Virtual Device Cluster Information - `href` (String) Unique Resource Identifier +- `name` (String) Customer-assigned Virtual Device Name @@ -240,13 +240,13 @@ Optional: ### Nested Schema for `service_token_connection.z_side.access_point_selectors.interface` -Optional: +Required: -- `id` (Number) id - `type` (String) Interface type Read-Only: +- `id` (Number) id - `uuid` (String) Equinix-assigned interface identifier @@ -328,13 +328,13 @@ Required: Optional: -- `cluster` (String) Virtual Device Cluster Information -- `name` (String) Customer-assigned Virtual Device Name - `type` (String) Virtual Device type Read-Only: +- `cluster` (String) Virtual Device Cluster Information - `href` (String) Unique Resource Identifier +- `name` (String) Customer-assigned Virtual Device Name 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 new file mode 100644 index 000000000..15eaf72d6 --- /dev/null +++ b/examples/resources/equinix_fabric_service_token/zside_vd_service_token.tf @@ -0,0 +1,20 @@ +resource "equinix_fabric_service_token" "test" { + type = "VC_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 = "VD" + virtual_device { + type = "EDGE" + uuid = "" + } + interface { + type = "NETWORK" + } + } + } + } +} diff --git a/internal/resources/fabric/service_token/datasources.go b/internal/resources/fabric/service_token/datasources.go index f28b54c71..92241fcd5 100644 --- a/internal/resources/fabric/service_token/datasources.go +++ b/internal/resources/fabric/service_token/datasources.go @@ -3,6 +3,7 @@ package service_token import ( "context" "fmt" + "github.com/equinix/terraform-provider-equinix/internal/config" equinix_errors "github.com/equinix/terraform-provider-equinix/internal/errors" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" diff --git a/internal/resources/fabric/service_token/datasources_test.go b/internal/resources/fabric/service_token/datasources_test.go index 943c67852..df9986b0a 100644 --- a/internal/resources/fabric/service_token/datasources_test.go +++ b/internal/resources/fabric/service_token/datasources_test.go @@ -2,48 +2,55 @@ package service_token_test import ( "fmt" + "testing" + "github.com/equinix/terraform-provider-equinix/internal/acceptance" + "github.com/equinix/terraform-provider-equinix/internal/fabric/testing_helpers" "github.com/hashicorp/terraform-plugin-testing/helper/resource" - "testing" ) func TestAccFabricServiceTokenDataSource_PNFV(t *testing.T) { + connectionTestData := testing_helpers.GetFabricEnvConnectionTestData(t) + var virtualDevice string + if len(connectionTestData) > 0 { + virtualDevice = connectionTestData["pnfv"]["virtualDevice"] + } resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acceptance.TestAccPreCheck(t) }, Providers: acceptance.TestAccProviders, CheckDestroy: CheckServiceTokenDelete, Steps: []resource.TestStep{ { - Config: testAccFabricServiceTokenConfigDataSourceConfig(), + Config: testAccFabricServiceTokenConfigDataSourceConfig(virtualDevice), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrSet("data.equinix_fabric_service_token.service-token", "uuid"), - resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token", "type", "VC_TOKEN"), - resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token", "expiration_date_time", "2024-11-18T06:43:49.980Z"), - resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token", "service_token_connection.0.supported_bandwidths.#", "3"), - resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.type", "EDGE"), - resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid", "fcf0fcec-65f6-4544-8810-ae4756fab8c4"), - resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.type", "NETWORK"), - resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id", "5"), + resource.TestCheckResourceAttrSet("data.equinix_fabric_service_token.service-token-for-zside-virtual-device", "uuid"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token-for-zside-virtual-device", "type", "VC_TOKEN"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token-for-zside-virtual-device", "expiration_date_time", "2025-01-18T06:43:49.981Z"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token-for-zside-virtual-device", "service_token_connection.0.supported_bandwidths.#", "3"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token-for-zside-virtual-device", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.type", "EDGE"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token-for-zside-virtual-device", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid", virtualDevice), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token-for-zside-virtual-device", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.type", "NETWORK"), + resource.TestCheckResourceAttrSet("data.equinix_fabric_service_token.service-token-for-zside-virtual-device", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id"), resource.TestCheckResourceAttrSet("data.equinix_fabric_service_tokens.service-tokens", "data.0.uuid"), resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "data.0.type", "VC_TOKEN"), - resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "data.0.expiration_date_time", "2024-11-18T06:43:49.980Z"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "data.0.expiration_date_time", "2025-01-18T06:43:49.981Z"), resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "data.0.service_token_connection.0.supported_bandwidths.#", "3"), - resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.type", "EDGE"), - resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "data.0.service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid", "fcf0fcec-65f6-4544-8810-ae4756fab8c4"), - resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.type", "NETWORK"), - resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "data.0.service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id", "5"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "data.0.service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.type", "EDGE"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "data.0.service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid", virtualDevice), + resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "data.0.service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.type", "NETWORK"), + resource.TestCheckResourceAttrSet("data.equinix_fabric_service_tokens.service-tokens", "data.0.service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id"), ), - ExpectNonEmptyPlan: true, + ExpectNonEmptyPlan: false, }, }, }) } -func testAccFabricServiceTokenConfigDataSourceConfig() string { +func testAccFabricServiceTokenConfigDataSourceConfig(virtualDeviceUuid string) string { return fmt.Sprintf( `resource "equinix_fabric_service_token" "test"{ type = "VC_TOKEN" - expiration_date_time = "2024-11-18T06:43:49.980Z" + expiration_date_time = "2025-01-18T06:43:49.981Z" service_token_connection { type = "EVPL_VC" supported_bandwidths = [50, 200, 10000] @@ -52,11 +59,10 @@ func testAccFabricServiceTokenConfigDataSourceConfig() string { type = "VD" virtual_device{ type = "EDGE" - uuid = "fcf0fcec-65f6-4544-8810-ae4756fab8c4" + uuid = "%s" } interface{ type = "NETWORK" - id = 5 } } } @@ -67,7 +73,7 @@ func testAccFabricServiceTokenConfigDataSourceConfig() string { } } - data "equinix_fabric_service_token" "service-token" { + data "equinix_fabric_service_token" "service-token-for-zside-virtual-device" { uuid = equinix_fabric_service_token.test.id } @@ -89,5 +95,5 @@ func testAccFabricServiceTokenConfigDataSourceConfig() string { } } - `) + `, virtualDeviceUuid) } diff --git a/internal/resources/fabric/service_token/resource.go b/internal/resources/fabric/service_token/resource.go index fc0c68e9d..ec8f57cff 100644 --- a/internal/resources/fabric/service_token/resource.go +++ b/internal/resources/fabric/service_token/resource.go @@ -2,20 +2,17 @@ package service_token import ( "context" - "fmt" + "log" + "strings" + "time" + "github.com/equinix/equinix-sdk-go/services/fabricv4" "github.com/equinix/terraform-provider-equinix/internal/config" equinix_errors "github.com/equinix/terraform-provider-equinix/internal/errors" equinix_fabric_schema "github.com/equinix/terraform-provider-equinix/internal/fabric/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "log" - "reflect" - "sort" - "strings" - "time" ) func Resource() *schema.Resource { @@ -33,94 +30,11 @@ func Resource() *schema.Resource { Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, - Schema: resourceSchema(), - CustomizeDiff: customdiff.All( - customdiff.ValidateChange("service_token_connection", func(ctx context.Context, oldValue, newValue, meta interface{}) error { - var oldZsideBandwidth, newZsideBandwidth []int - log.Printf("service token old %v", oldValue) - log.Printf("service token new %v", newValue) - - for _, connection := range oldValue.(*schema.Set).List() { - olSupportedBandwidthMap := connection.(map[string]interface{}) - - if bandwidth, ok := olSupportedBandwidthMap["supported_bandwidths"]; ok { - oldSupportedBandwidth := bandwidth.([]interface{}) - log.Printf("old value %v", oldSupportedBandwidth) - if len(oldSupportedBandwidth) > 0 { - sort.Ints(oldZsideBandwidth) - - } - } - } - - for _, connection := range newValue.(*schema.Set).List() { - newSupportedBandwidthMap := connection.(map[string]interface{}) - - if bandwidth, ok := newSupportedBandwidthMap["supported_bandwidths"]; ok { - newSupportedBandwidth := bandwidth.([]interface{}) - log.Printf("new value %v", newSupportedBandwidth) - if len(newSupportedBandwidth) > 0 { - sort.Ints(newZsideBandwidth) - - } - } - } - log.Printf("!! old value %v", oldZsideBandwidth) - log.Printf("!! old value %v", oldZsideBandwidth) - - if reflect.DeepEqual(oldZsideBandwidth, newZsideBandwidth) { - return nil // No diff if equivalent after sorting - } - - return fmt.Errorf("supported_bandwidths values are different: %v", newZsideBandwidth) - - }), - ), + Schema: resourceSchema(), Description: `Fabric V4 API compatible resource allows creation and management of Equinix Fabric Service Token`, } } -func extractIntValues(value interface{}) []int { - var intSlice []int - - switch v := value.(type) { - case *schema.Set: - for _, item := range v.List() { - if val, ok := item.(int); ok { - intSlice = append(intSlice, val) - } - } - case []interface{}: - for _, item := range v { - if val, ok := item.(int); ok { - intSlice = append(intSlice, val) - } - } - } - - return intSlice -} - -//func convertToIntSlice(input interface{}) ([]int, bool) { -// if input == nil { -// return []int{}, true -// } -// -// // Type assert to a slice of interfaces, if applicable -// if slice, ok := input.([]interface{}); ok { -// intSlice := make([]int, 0, len(slice)) -// for _, v := range slice { -// if intVal, ok := v.(int); ok { -// intSlice = append(intSlice, intVal) -// } else { -// return []int{}, false // Conversion failed -// } -// } -// return intSlice, true -// } -// return []int{}, false // Not a valid slice type -//} - func resourceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*config.Config).NewFabricClientForSDK(d) serviceToken, _, err := client.ServiceTokensApi.GetServiceTokenByUuid(ctx, d.Id()).Execute() diff --git a/internal/resources/fabric/service_token/resource_schema.go b/internal/resources/fabric/service_token/resource_schema.go index 9725302bf..ab2a363a5 100644 --- a/internal/resources/fabric/service_token/resource_schema.go +++ b/internal/resources/fabric/service_token/resource_schema.go @@ -4,8 +4,6 @@ import ( equinix_fabric_schema "github.com/equinix/terraform-provider-equinix/internal/fabric/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" - "strconv" - "strings" ) func resourceSchema() map[string]*schema.Schema { @@ -132,24 +130,10 @@ func serviceTokenConnectionSch() *schema.Resource { "supported_bandwidths": { Type: schema.TypeList, Required: true, - Description: "List of permitted bandwidths' For Port ", + Description: "List of permitted bandwidths'; For Port-based Service Tokens, the maximum allowable bandwidth is 50 Gbps, while for Virtual Device-based Service Tokens, it is limited to 10 Gbps", Elem: &schema.Schema{ Type: schema.TypeInt, }, - //DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - // oldValues := convertStringToIntSlice(old) - // newValues := convertStringToIntSlice(new) - // - // // Sort both slices for consistent comparison - // sort.Ints(oldValues) - // sort.Ints(newValues) - // - // log.Printf("!!! old value %v", oldValues) - // log.Printf("!!! new Value %v", newValues) - // - // // Suppress diff if sorted slices are identical - // return reflect.DeepEqual(oldValues, newValues) - //}, }, "a_side": { Type: schema.TypeSet, @@ -170,20 +154,6 @@ func serviceTokenConnectionSch() *schema.Resource { }, } } -func convertStringToIntSlice(value string) []int { - // Split the comma-separated string - strValues := strings.Split(value, ",") - intSlice := make([]int, 0, len(strValues)) - - for _, str := range strValues { - // Trim spaces and convert each to int - num, err := strconv.Atoi(strings.TrimSpace(str)) - if err == nil { - intSlice = append(intSlice, num) - } - } - return intSlice -} func serviceTokenAccessPointSch() *schema.Resource { return &schema.Resource{ @@ -226,7 +196,6 @@ func accessPointSelectorsSch() *schema.Resource { "virtual_device": { Type: schema.TypeSet, Optional: true, - Computed: true, Description: "Virtual Device Configuration", MaxItems: 1, Elem: virtualDeviceSch(), @@ -341,13 +310,11 @@ func virtualDeviceSch() *schema.Resource { }, "name": { Type: schema.TypeString, - Optional: true, Computed: true, Description: "Customer-assigned Virtual Device Name", }, "cluster": { Type: schema.TypeString, - Optional: true, Computed: true, Description: "Virtual Device Cluster Information", }, @@ -397,14 +364,12 @@ func interfaceSch() *schema.Resource { }, "id": { Type: schema.TypeInt, - Optional: true, Computed: true, Description: "id", }, "type": { Type: schema.TypeString, - Optional: true, - Computed: true, + Required: true, Description: "Interface type", }, }, diff --git a/internal/resources/fabric/service_token/resource_test.go b/internal/resources/fabric/service_token/resource_test.go index d3d9e6183..9790d8ba7 100644 --- a/internal/resources/fabric/service_token/resource_test.go +++ b/internal/resources/fabric/service_token/resource_test.go @@ -3,16 +3,23 @@ package service_token_test import ( "context" "fmt" + "testing" + "time" + "github.com/equinix/terraform-provider-equinix/internal/acceptance" + "github.com/equinix/terraform-provider-equinix/internal/fabric/testing_helpers" "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/service_token" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" - "testing" - "time" ) func TestAccFabricServiceToken_PNFV(t *testing.T) { + connectionTestData := testing_helpers.GetFabricEnvConnectionTestData(t) + var virtualDevice string + if len(connectionTestData) > 0 { + virtualDevice = connectionTestData["pnfv"]["virtualDevice"] + } serviceTokenName, serviceTokenUpdatedName := "Service_token_PNFV", "UP_Service_Token_PNFV" serviceTokenDescription, serviceTokenUpdatedDescription := "zside vd token", "Updated zside vd token" resource.ParallelTest(t, resource.TestCase{ @@ -21,48 +28,48 @@ func TestAccFabricServiceToken_PNFV(t *testing.T) { CheckDestroy: CheckServiceTokenDelete, Steps: []resource.TestStep{ { - Config: testAccFabricServiceTokenConfig(serviceTokenName, serviceTokenDescription), + Config: testAccFabricServiceTokenConfig(serviceTokenName, serviceTokenDescription, virtualDevice), 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", "2024-11-18T06:43:49.980Z"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "expiration_date_time", "2025-01-18T06:43:49.981Z"), resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.supported_bandwidths.#", "3"), resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.type", "EDGE"), - resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid", "fcf0fcec-65f6-4544-8810-ae4756fab8c4"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid", virtualDevice), resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.type", "NETWORK"), - resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id", "5"), + resource.TestCheckResourceAttrSet("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id"), ), - ExpectNonEmptyPlan: true, + ExpectNonEmptyPlan: false, }, { - Config: testAccFabricServiceTokenConfig(serviceTokenUpdatedName, serviceTokenUpdatedDescription), + Config: testAccFabricServiceTokenConfig(serviceTokenUpdatedName, serviceTokenUpdatedDescription, virtualDevice), 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", "2024-11-18T06:43:49.980Z"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "expiration_date_time", "2025-01-18T06:43:49.981Z"), resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.supported_bandwidths.#", "3"), resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.type", "EDGE"), - resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid", "fcf0fcec-65f6-4544-8810-ae4756fab8c4"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid", virtualDevice), resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.type", "NETWORK"), - resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id", "5"), + resource.TestCheckResourceAttrSet("equinix_fabric_service_token.test", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id"), ), - ExpectNonEmptyPlan: true, + ExpectNonEmptyPlan: false, }, }, }) } -func testAccFabricServiceTokenConfig(serviceTokenName string, serviceTokenDescription string) string { +func testAccFabricServiceTokenConfig(serviceTokenName string, serviceTokenDescription string, virtualDeviceUuid string) string { return fmt.Sprintf( `resource "equinix_fabric_service_token" "test"{ type = "VC_TOKEN" name = "%s" description = "%s" - expiration_date_time = "2024-11-18T06:43:49.98Z" + expiration_date_time = "2025-01-18T06:43:49.981Z" service_token_connection { type = "EVPL_VC" supported_bandwidths = [50, 200, 10000] @@ -71,11 +78,10 @@ func testAccFabricServiceTokenConfig(serviceTokenName string, serviceTokenDescri type = "VD" virtual_device{ type = "EDGE" - uuid = "fcf0fcec-65f6-4544-8810-ae4756fab8c4" + uuid = "%s" } interface{ type = "NETWORK" - id = 5 } } } @@ -86,7 +92,7 @@ func testAccFabricServiceTokenConfig(serviceTokenName string, serviceTokenDescri } } - `, serviceTokenName, serviceTokenDescription) + `, serviceTokenName, serviceTokenDescription, virtualDeviceUuid) } func CheckServiceTokenDelete(s *terraform.State) error { From a07c062efbca07dace588b4221acc9c3ed4bb067 Mon Sep 17 00:00:00 2001 From: srushti-patl Date: Thu, 31 Oct 2024 13:42:14 -0700 Subject: [PATCH 44/55] fix: Updating model method to fix terraform plan update issue --- .../service_token/datasources_schema.go | 108 ++++-------------- .../fabric/service_token/datasources_test.go | 18 +-- .../resources/fabric/service_token/models.go | 30 +++-- 3 files changed, 53 insertions(+), 103 deletions(-) diff --git a/internal/resources/fabric/service_token/datasources_schema.go b/internal/resources/fabric/service_token/datasources_schema.go index 046112cee..3ea4dffaa 100644 --- a/internal/resources/fabric/service_token/datasources_schema.go +++ b/internal/resources/fabric/service_token/datasources_schema.go @@ -1,92 +1,34 @@ package service_token import ( - equinix_fabric_schema "github.com/equinix/terraform-provider-equinix/internal/fabric/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) func dataSourceBaseSchema() map[string]*schema.Schema { - return map[string]*schema.Schema{ - "type": { - Type: schema.TypeString, - Computed: true, - Description: "Service Token Type; VC_TOKEN,EPL_TOKEN", - }, - "uuid": { - Type: schema.TypeString, - Required: true, - Description: "Equinix-assigned service token identifier", - }, - "href": { - Type: schema.TypeString, - Computed: true, - Description: "An absolute URL that is the subject of the link's context.", - }, - "issuer_side": { - Type: schema.TypeString, - Computed: true, - Description: "Information about token side; ASIDE, ZSIDE", - }, - "name": { - Type: schema.TypeString, - Computed: true, - Description: "Name of the Service Token", - }, - "description": { - Type: schema.TypeString, - Computed: true, - Description: "Optional Description to the Service Token you will be creating", - }, - "expiration_date_time": { - Type: schema.TypeString, - Computed: true, - Description: "Expiration date and time of the service token; 2020-11-06T07:00:00Z", - }, - "service_token_connection": { - Type: schema.TypeSet, - Computed: true, - Description: "Service Token Connection Type Information", - Elem: serviceTokenConnectionSch(), - }, - "state": { - Type: schema.TypeString, - Computed: true, - Description: "Service token state; ACTIVE, INACTIVE, EXPIRED, DELETED", - }, - "notifications": { - Type: schema.TypeSet, - Computed: true, - Description: "Preferences for notifications on Service Token configuration or status changes", - Elem: &schema.Resource{ - Schema: equinix_fabric_schema.NotificationSch(), - }, - }, - "account": { - Type: schema.TypeSet, - Computed: true, - Description: "Customer account information that is associated with this service token", - Elem: &schema.Resource{ - Schema: equinix_fabric_schema.AccountSch(), - }, - }, - "change_log": { - Type: schema.TypeSet, - Computed: true, - Description: "Captures connection lifecycle change information", - Elem: &schema.Resource{ - Schema: equinix_fabric_schema.ChangeLogSch(), - }, - }, - "project": { - Type: schema.TypeSet, - Computed: true, - Description: "Project information", - Elem: &schema.Resource{ - Schema: equinix_fabric_schema.ProjectSch(), - }, - }, + sch := resourceSchema() + 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 dataSourceBaseSchemaUpdated() map[string]*schema.Schema { + sch := dataSourceBaseSchema() + sch["uuid"].Computed = true + sch["uuid"].Optional = false + sch["uuid"].Required = false + return sch } func paginationSchema() *schema.Resource { @@ -129,15 +71,15 @@ func dataSourceSearchSchema() map[string]*schema.Schema { "data": { Type: schema.TypeList, Computed: true, - Description: "List of Route Filters", + Description: "List of Service Tokens", Elem: &schema.Resource{ - Schema: dataSourceBaseSchema(), + Schema: dataSourceBaseSchemaUpdated(), }, }, "filter": { Type: schema.TypeList, Required: true, - Description: "Filters for the Data Source Search Request. Maximum of 8 total filters.", + Description: "Filters for the Data Source Search Request", MaxItems: 10, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ diff --git a/internal/resources/fabric/service_token/datasources_test.go b/internal/resources/fabric/service_token/datasources_test.go index df9986b0a..f075af1e6 100644 --- a/internal/resources/fabric/service_token/datasources_test.go +++ b/internal/resources/fabric/service_token/datasources_test.go @@ -23,14 +23,14 @@ func TestAccFabricServiceTokenDataSource_PNFV(t *testing.T) { { Config: testAccFabricServiceTokenConfigDataSourceConfig(virtualDevice), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrSet("data.equinix_fabric_service_token.service-token-for-zside-virtual-device", "uuid"), - resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token-for-zside-virtual-device", "type", "VC_TOKEN"), - resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token-for-zside-virtual-device", "expiration_date_time", "2025-01-18T06:43:49.981Z"), - resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token-for-zside-virtual-device", "service_token_connection.0.supported_bandwidths.#", "3"), - resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token-for-zside-virtual-device", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.type", "EDGE"), - resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token-for-zside-virtual-device", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid", virtualDevice), - resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token-for-zside-virtual-device", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.type", "NETWORK"), - resource.TestCheckResourceAttrSet("data.equinix_fabric_service_token.service-token-for-zside-virtual-device", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id"), + resource.TestCheckResourceAttrSet("data.equinix_fabric_service_token.service-token-for-zside-virtual-device-for-zside-virtual-device", "uuid"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token-for-zside-virtual-device-for-zside-virtual-device", "type", "VC_TOKEN"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token-for-zside-virtual-device-for-zside-virtual-device", "expiration_date_time", "2025-01-18T06:43:49.981Z"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token-for-zside-virtual-device-for-zside-virtual-device", "service_token_connection.0.supported_bandwidths.#", "3"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token-for-zside-virtual-device-for-zside-virtual-device", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.type", "EDGE"), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token-for-zside-virtual-device-for-zside-virtual-device", "service_token_connection.0.z_side.0.access_point_selectors.0.virtual_device.0.uuid", virtualDevice), + resource.TestCheckResourceAttr("data.equinix_fabric_service_token.service-token-for-zside-virtual-device-for-zside-virtual-device", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.type", "NETWORK"), + resource.TestCheckResourceAttrSet("data.equinix_fabric_service_token.service-token-for-zside-virtual-device-for-zside-virtual-device", "service_token_connection.0.z_side.0.access_point_selectors.0.interface.0.id"), resource.TestCheckResourceAttrSet("data.equinix_fabric_service_tokens.service-tokens", "data.0.uuid"), resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "data.0.type", "VC_TOKEN"), resource.TestCheckResourceAttr("data.equinix_fabric_service_tokens.service-tokens", "data.0.expiration_date_time", "2025-01-18T06:43:49.981Z"), @@ -73,7 +73,7 @@ func testAccFabricServiceTokenConfigDataSourceConfig(virtualDeviceUuid string) s } } - data "equinix_fabric_service_token" "service-token-for-zside-virtual-device" { + data "equinix_fabric_service_token" "service-token-for-zside-virtual-device-for-zside-virtual-device" { uuid = equinix_fabric_service_token.test.id } diff --git a/internal/resources/fabric/service_token/models.go b/internal/resources/fabric/service_token/models.go index dbf71fa1c..0a3a58c24 100644 --- a/internal/resources/fabric/service_token/models.go +++ b/internal/resources/fabric/service_token/models.go @@ -2,15 +2,16 @@ package service_token import ( "fmt" + "reflect" + "sort" + "time" + "github.com/equinix/equinix-sdk-go/services/fabricv4" "github.com/equinix/terraform-provider-equinix/internal/converters" equinix_fabric_schema "github.com/equinix/terraform-provider-equinix/internal/fabric/schema" 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/schema" - "reflect" - "sort" - "time" ) func buildCreateRequest(d *schema.ResourceData) fabricv4.ServiceToken { @@ -277,7 +278,7 @@ func serviceTokenResponseMap(token *fabricv4.ServiceToken) map[string]interface{ } func connectionTerraformToGo(connectionTerraform []interface{}) fabricv4.ServiceTokenConnection { - if connectionTerraform == nil || len(connectionTerraform) == 0 { + if len(connectionTerraform) == 0 { return fabricv4.ServiceTokenConnection{} } @@ -306,6 +307,9 @@ func connectionTerraformToGo(connectionTerraform []interface{}) fabricv4.Service for i, v := range supportedBandwidths { int32Bandwidths[i] = int32(v.(int)) } + sort.Slice(int32Bandwidths, func(i, j int) bool { + return int32Bandwidths[i] < int32Bandwidths[j] + }) connection.SetSupportedBandwidths(int32Bandwidths) } @@ -323,7 +327,7 @@ func connectionTerraformToGo(connectionTerraform []interface{}) fabricv4.Service } func accessPointTerraformToGo(accessPoint []interface{}) fabricv4.ServiceTokenSide { - if accessPoint == nil || len(accessPoint) == 0 { + if len(accessPoint) == 0 { return fabricv4.ServiceTokenSide{} } @@ -339,7 +343,7 @@ func accessPointTerraformToGo(accessPoint []interface{}) fabricv4.ServiceTokenSi } func accessPointSelectorsTerraformToGo(accessPointSelectors []interface{}) []fabricv4.AccessPointSelector { - if accessPointSelectors == nil || len(accessPointSelectors) == 0 { + if len(accessPointSelectors) == 0 { return []fabricv4.AccessPointSelector{} } @@ -383,7 +387,7 @@ func accessPointSelectorsTerraformToGo(accessPointSelectors []interface{}) []fab } func portTerraformToGo(portList []interface{}) fabricv4.SimplifiedMetadataEntity { - if portList == nil || len(portList) == 0 { + if len(portList) == 0 { return fabricv4.SimplifiedMetadataEntity{} } var port fabricv4.SimplifiedMetadataEntity @@ -395,7 +399,7 @@ func portTerraformToGo(portList []interface{}) fabricv4.SimplifiedMetadataEntity } func linkProtocolTerraformToGo(linkProtocolList []interface{}) fabricv4.SimplifiedLinkProtocol { - if linkProtocolList == nil || len(linkProtocolList) == 0 { + if len(linkProtocolList) == 0 { return fabricv4.SimplifiedLinkProtocol{} } var linkProtocol fabricv4.SimplifiedLinkProtocol @@ -420,7 +424,7 @@ func linkProtocolTerraformToGo(linkProtocolList []interface{}) fabricv4.Simplifi } func virtualDeviceTerraformToGo(virtualDeviceList []interface{}) fabricv4.SimplifiedVirtualDevice { - if virtualDeviceList == nil || len(virtualDeviceList) == 0 { + if len(virtualDeviceList) == 0 { return fabricv4.SimplifiedVirtualDevice{} } @@ -441,7 +445,7 @@ func virtualDeviceTerraformToGo(virtualDeviceList []interface{}) fabricv4.Simpli } func interfaceTerraformToGo(interfaceList []interface{}) fabricv4.VirtualDeviceInterface { - if interfaceList == nil || len(interfaceList) == 0 { + if len(interfaceList) == 0 { return fabricv4.VirtualDeviceInterface{} } @@ -458,7 +462,7 @@ func interfaceTerraformToGo(interfaceList []interface{}) fabricv4.VirtualDeviceI } func networkTerraformToGo(networkList []interface{}) fabricv4.SimplifiedTokenNetwork { - if networkList == nil || len(networkList) == 0 { + if len(networkList) == 0 { return fabricv4.SimplifiedTokenNetwork{} } var network fabricv4.SimplifiedTokenNetwork @@ -530,6 +534,10 @@ func connectionGoToTerraform(connection *fabricv4.ServiceTokenConnection) *schem interfaceBandwidths[i] = int(v) // Convert each int32 to interface{} } + sort.Slice(interfaceBandwidths, func(i, j int) bool { + return interfaceBandwidths[i].(int) < interfaceBandwidths[j].(int) + }) + mappedConnection["supported_bandwidths"] = interfaceBandwidths } if connection.BandwidthLimit != nil { From 89008f36fae874fd6fb2fd8f47265b0ba34bb494 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 1 Nov 2024 15:54:59 -0500 Subject: [PATCH 45/55] fix(deps): update module golang.org/x/oauth2 to v0.23.0 (#769) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | golang.org/x/oauth2 | `v0.22.0` -> `v0.23.0` | [![age](https://developer.mend.io/api/mc/badges/age/go/golang.org%2fx%2foauth2/v0.23.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/go/golang.org%2fx%2foauth2/v0.23.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/go/golang.org%2fx%2foauth2/v0.22.0/v0.23.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/golang.org%2fx%2foauth2/v0.22.0/v0.23.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/equinix/terraform-provider-equinix). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 99eb76abc..6088d1e6c 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/packethost/packngo v0.31.0 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.9.0 - golang.org/x/oauth2 v0.22.0 + golang.org/x/oauth2 v0.23.0 ) require ( diff --git a/go.sum b/go.sum index 9ebfb0925..06f05aaea 100644 --- a/go.sum +++ b/go.sum @@ -41,8 +41,6 @@ github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/equinix/equinix-sdk-go v0.46.0 h1:ldQo4GtXNr+0XsThQJf/pUdx5wcLFe9QpLFtAwonqH8= github.com/equinix/equinix-sdk-go v0.46.0/go.mod h1:hEb3XLaedz7xhl/dpPIS6eOIiXNPeqNiVoyDrT6paIg= -github.com/equinix/ne-go v1.17.0 h1:+wZq0GNognpiTHTsBXtATOCphTFvnowF046NzQXj0n0= -github.com/equinix/ne-go v1.17.0/go.mod h1:eHkkxM4nbTB7DZ9X9zGnwfYnxIJWIsU3aHA+FAoZ1EI= github.com/equinix/ne-go v1.18.0 h1:5az4ai39y1XLNOq3+qQVT9wFG7BmaQfj941MNqqouhk= github.com/equinix/ne-go v1.18.0/go.mod h1:eHkkxM4nbTB7DZ9X9zGnwfYnxIJWIsU3aHA+FAoZ1EI= github.com/equinix/oauth2-go v1.0.0 h1:fHtAPGq82PdgtK5vEThs8Vwz6f7D/8SX4tE3NJu+KcU= @@ -266,8 +264,8 @@ golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= -golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= From dcc2cbd5968bc3a12e5c63ccda747f931970348d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 12:19:42 -0600 Subject: [PATCH 46/55] fix(deps): update module github.com/hashicorp/terraform-plugin-testing to v1.10.0 (#753) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [github.com/hashicorp/terraform-plugin-testing](https://redirect.github.com/hashicorp/terraform-plugin-testing) | `v1.9.0` -> `v1.10.0` | [![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fhashicorp%2fterraform-plugin-testing/v1.10.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/go/github.com%2fhashicorp%2fterraform-plugin-testing/v1.10.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/go/github.com%2fhashicorp%2fterraform-plugin-testing/v1.9.0/v1.10.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fhashicorp%2fterraform-plugin-testing/v1.9.0/v1.10.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
hashicorp/terraform-plugin-testing (github.com/hashicorp/terraform-plugin-testing) ### [`v1.10.0`](https://redirect.github.com/hashicorp/terraform-plugin-testing/releases/tag/v1.10.0) [Compare Source](https://redirect.github.com/hashicorp/terraform-plugin-testing/compare/v1.9.0...v1.10.0) NOTES: - compare: The `compare` package is considered experimental and may be altered or removed in a subsequent release ([#​330](https://redirect.github.com/hashicorp/terraform-plugin-testing/issues/330)) - statecheck: `CompareValue`, `CompareValueCollection`, and `CompareValuePairs` state checks are considered experimental and may be altered or removed in a subsequent release. ([#​330](https://redirect.github.com/hashicorp/terraform-plugin-testing/issues/330)) FEATURES: - compare: Introduced new `compare` package, which contains interfaces and implementations for value comparisons in state checks. ([#​330](https://redirect.github.com/hashicorp/terraform-plugin-testing/issues/330)) - statecheck: Added `CompareValue` state check, which compares sequential values of the specified attribute at the given managed resource, or data source, using the supplied value comparer. ([#​330](https://redirect.github.com/hashicorp/terraform-plugin-testing/issues/330)) - statecheck: Added `CompareValueCollection` state check, which compares each item in the specified collection (e.g., list, set) attribute, with the second specified attribute at the given managed resources, or data sources, using the supplied value comparer. ([#​330](https://redirect.github.com/hashicorp/terraform-plugin-testing/issues/330)) - statecheck: Added `CompareValuePairs` state check, which compares the specified attributes at the given managed resources, or data sources, using the supplied value comparer. ([#​330](https://redirect.github.com/hashicorp/terraform-plugin-testing/issues/330))
--- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/equinix/terraform-provider-equinix). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- go.mod | 16 ++++++++-------- go.sum | 36 ++++++++++++++++++------------------ 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/go.mod b/go.mod index 6088d1e6c..442565ca2 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/hashicorp/terraform-plugin-mux v0.16.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0 - github.com/hashicorp/terraform-plugin-testing v1.9.0 + github.com/hashicorp/terraform-plugin-testing v1.10.0 github.com/packethost/packngo v0.31.0 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.9.0 @@ -53,7 +53,7 @@ require ( github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/hashicorp/go-plugin v1.6.0 // indirect github.com/hashicorp/go-version v1.7.0 // indirect - github.com/hashicorp/hc-install v0.7.0 // indirect + github.com/hashicorp/hc-install v0.8.0 // indirect github.com/hashicorp/hcl/v2 v2.21.0 // indirect github.com/hashicorp/logutils v1.0.0 // indirect github.com/hashicorp/terraform-exec v0.21.0 // indirect @@ -81,15 +81,15 @@ require ( github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/yuin/goldmark v1.7.1 // indirect github.com/yuin/goldmark-meta v1.1.0 // indirect - github.com/zclconf/go-cty v1.14.4 // indirect + github.com/zclconf/go-cty v1.15.0 // indirect go.abhg.dev/goldmark/frontmatter v0.2.0 // indirect - golang.org/x/crypto v0.25.0 // indirect + golang.org/x/crypto v0.26.0 // indirect golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc // indirect - golang.org/x/mod v0.17.0 // indirect + golang.org/x/mod v0.19.0 // indirect golang.org/x/net v0.25.0 // indirect - golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.22.0 // indirect - golang.org/x/text v0.16.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.23.0 // indirect + golang.org/x/text v0.17.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect diff --git a/go.sum b/go.sum index 06f05aaea..b6183b99a 100644 --- a/go.sum +++ b/go.sum @@ -104,8 +104,8 @@ github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/C github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/hc-install v0.7.0 h1:Uu9edVqjKQxxuD28mR5TikkKDd/p55S8vzPC1659aBk= -github.com/hashicorp/hc-install v0.7.0/go.mod h1:ELmmzZlGnEcqoUMKUuykHaPCIR1sYLYX+KSggWSKZuA= +github.com/hashicorp/hc-install v0.8.0 h1:LdpZeXkZYMQhoKPCecJHlKvUkQFixN/nvyR1CdfOLjI= +github.com/hashicorp/hc-install v0.8.0/go.mod h1:+MwJYjDfCruSD/udvBmRB22Nlkwwkwf5sAB6uTIhSaU= github.com/hashicorp/hcl/v2 v2.21.0 h1:lve4q/o/2rqwYOgUg3y3V2YPyD1/zkCLGjIV74Jit14= github.com/hashicorp/hcl/v2 v2.21.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA= github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= @@ -130,8 +130,8 @@ github.com/hashicorp/terraform-plugin-mux v0.16.0 h1:RCzXHGDYwUwwqfYYWJKBFaS3fQs github.com/hashicorp/terraform-plugin-mux v0.16.0/go.mod h1:PF79mAsPc8CpusXPfEVa4X8PtkB+ngWoiUClMrNZlYo= github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0 h1:kJiWGx2kiQVo97Y5IOGR4EMcZ8DtMswHhUuFibsCQQE= github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0/go.mod h1:sl/UoabMc37HA6ICVMmGO+/0wofkVIRxf+BMb/dnoIg= -github.com/hashicorp/terraform-plugin-testing v1.9.0 h1:xOsQRqqlHKXpFq6etTxih3ubdK3HVDtfE1IY7Rpd37o= -github.com/hashicorp/terraform-plugin-testing v1.9.0/go.mod h1:fhhVx/8+XNJZTD5o3b4stfZ6+q7z9+lIWigIYdT6/44= +github.com/hashicorp/terraform-plugin-testing v1.10.0 h1:2+tmRNhvnfE4Bs8rB6v58S/VpqzGC6RCh9Y8ujdn+aw= +github.com/hashicorp/terraform-plugin-testing v1.10.0/go.mod h1:iWRW3+loP33WMch2P/TEyCxxct/ZEcCGMquSLSCVsrc= github.com/hashicorp/terraform-registry-address v0.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTVcLZRu7JseiXNRHbOAyoTI= github.com/hashicorp/terraform-registry-address v0.2.3/go.mod h1:lFHA76T8jfQteVfT7caREqguFrW3c4MFSPhZB7HHgUM= github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ= @@ -236,8 +236,8 @@ github.com/yuin/goldmark v1.7.1 h1:3bajkSilaCbjdKVsKdZjZCLBNPL9pYzrCakKaf4U49U= github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc= github.com/yuin/goldmark-meta v1.1.0/go.mod h1:U4spWENafuA7Zyg+Lj5RqK/MF+ovMYtBvXi1lBb2VP0= -github.com/zclconf/go-cty v1.14.4 h1:uXXczd9QDGsgu0i/QFR/hzI5NYCHLf6NQw/atrbnhq8= -github.com/zclconf/go-cty v1.14.4/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +github.com/zclconf/go-cty v1.15.0 h1:tTCRWxsexYUmtt/wVxgDClUe+uQusuI443uL6e+5sXQ= +github.com/zclconf/go-cty v1.15.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= go.abhg.dev/goldmark/frontmatter v0.2.0 h1:P8kPG0YkL12+aYk2yU3xHv4tcXzeVnN+gU0tJ5JnxRw= @@ -246,13 +246,13 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20200420201142-3c4aac89819a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= -golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcHCgR0s52IfwutMfEbdM= golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= +golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -270,8 +270,8 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -287,21 +287,21 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= +golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= -golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= +golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= +golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= From 05baabb21d096b1cfa2949a686deae2bfd20e077 Mon Sep 17 00:00:00 2001 From: Charles Treatman Date: Mon, 4 Nov 2024 12:03:48 -0600 Subject: [PATCH 47/55] chore: upgrade go to 1.23 --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 442565ca2..01a1e8e22 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/equinix/terraform-provider-equinix -go 1.22 +go 1.23 require ( github.com/equinix/equinix-sdk-go v0.46.0 From 48c65ac7599cfa8d722788dd75e3911fc4cb974e Mon Sep 17 00:00:00 2001 From: srushti-patl Date: Tue, 5 Nov 2024 15:01:25 -0800 Subject: [PATCH 48/55] feat: Updating Service Token Resource for Aside Port Service Token --- docs/resources/fabric_service_token.md | 94 +++++++++++++------ .../aside_colo_service_token.tf | 25 +++++ .../zside_vd_service_token.tf | 5 + .../resources/fabric/service_token/models.go | 79 +++++++++++++++- .../fabric/service_token/resource_schema.go | 13 +-- .../fabric/service_token/resource_test.go | 87 ++++++++++++++++- .../resources/fabric_service_token.md.tmpl | 3 + 7 files changed, 261 insertions(+), 45 deletions(-) create mode 100644 examples/resources/equinix_fabric_service_token/aside_colo_service_token.tf diff --git a/docs/resources/fabric_service_token.md b/docs/resources/fabric_service_token.md index 687368767..d3c7caf46 100644 --- a/docs/resources/fabric_service_token.md +++ b/docs/resources/fabric_service_token.md @@ -12,10 +12,40 @@ Additional documentation: ## Example Usage +Aside Port Service Token +```terraform +resource "equinix_fabric_service_token" "test" { + type = "VC_TOKEN" + description = "Aside COLO Service Token" + expiration_date_time = "2025-01-18T06:43:49.981Z" + service_token_connection { + type = "EVPL_VC" + bandwidth_limit = 1000 + a_side { + access_point_selectors{ + type = "COLO" + port { + uuid = "" + } + link_protocol { + type = "DOT1Q" + vlan_tag = "2987" + } + } + } + } + notifications { + type = "ALL" + emails = ["example@equinix.com", "test1@equinix.com"] + } +} +``` + Zside Virtual Device Service Token ```terraform resource "equinix_fabric_service_token" "test" { type = "VC_TOKEN" + description = "Zside VD Service Token" expiration_date_time = "2025-01-18T06:43:49.986Z" service_token_connection { type = "EVPL_VC" @@ -33,6 +63,10 @@ resource "equinix_fabric_service_token" "test" { } } } + notifications { + type = "ALL" + emails = ["example@equinix.com"] + } } ``` @@ -82,7 +116,6 @@ Optional: Required: -- `supported_bandwidths` (List of Number) List of permitted bandwidths'; For Port-based Service Tokens, the maximum allowable bandwidth is 50 Gbps, while for Virtual Device-based Service Tokens, it is limited to 10 Gbps - `type` (String) Type of Connection supported by Service Token you will create; EVPL_VC, EVPLAN_VC, EPLAN_VC, IPWAN_VC Optional: @@ -91,6 +124,7 @@ Optional: - `allow_custom_bandwidth` (Boolean) Allow custom bandwidth value - `allow_remote_connection` (Boolean) Authorization to connect remotely - `bandwidth_limit` (Number) Connection bandwidth limit in Mbps +- `supported_bandwidths` (List of Number) List of permitted bandwidths'; For Port-based Service Tokens, the maximum allowable bandwidth is 50 Gbps, while for Virtual Device-based Service Tokens, it is limited to 10 Gbps - `z_side` (Block Set) Z-Side Connection link protocol,virtual device or network configuration (see [below for nested schema](#nestedblock--service_token_connection--z_side)) Read-Only: @@ -170,31 +204,34 @@ Optional: ### Nested Schema for `service_token_connection.a_side.access_point_selectors.port` +Required: + +- `uuid` (String) Equinix-assigned Port identifier + Optional: +- `type` (String) Type of Port + +Read-Only: + - `account_name` (String) Account Name - `bandwidth` (Number) Port Bandwidth - `cvp_id` (Number) Customer virtual port Id - `encapsulation_protocol_type` (String) Port Encapsulation -- `location` (Block Set) Port Location (see [below for nested schema](#nestedblock--service_token_connection--a_side--access_point_selectors--port--location)) +- `href` (String) Unique Resource Identifier +- `location` (Set of Object) Port Location (see [below for nested schema](#nestedatt--service_token_connection--a_side--access_point_selectors--port--location)) - `port_name` (String) Port Name - `priority` (String) Port Priority -- `type` (String) Type of Port -- `uuid` (String) Equinix-assigned Port identifier -Read-Only: - -- `href` (String) Unique Resource Identifier - - + ### Nested Schema for `service_token_connection.a_side.access_point_selectors.port.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) @@ -291,31 +328,34 @@ Optional: ### Nested Schema for `service_token_connection.z_side.access_point_selectors.port` +Required: + +- `uuid` (String) Equinix-assigned Port identifier + Optional: +- `type` (String) Type of Port + +Read-Only: + - `account_name` (String) Account Name - `bandwidth` (Number) Port Bandwidth - `cvp_id` (Number) Customer virtual port Id - `encapsulation_protocol_type` (String) Port Encapsulation -- `location` (Block Set) Port Location (see [below for nested schema](#nestedblock--service_token_connection--z_side--access_point_selectors--port--location)) +- `href` (String) Unique Resource Identifier +- `location` (Set of Object) Port Location (see [below for nested schema](#nestedatt--service_token_connection--z_side--access_point_selectors--port--location)) - `port_name` (String) Port Name - `priority` (String) Port Priority -- `type` (String) Type of Port -- `uuid` (String) Equinix-assigned Port identifier - -Read-Only: - -- `href` (String) Unique Resource Identifier - + ### Nested Schema for `service_token_connection.z_side.access_point_selectors.port.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 new file mode 100644 index 000000000..b65b98be7 --- /dev/null +++ b/examples/resources/equinix_fabric_service_token/aside_colo_service_token.tf @@ -0,0 +1,25 @@ +resource "equinix_fabric_service_token" "test" { + type = "VC_TOKEN" + description = "Aside COLO Service Token" + expiration_date_time = "2025-01-18T06:43:49.981Z" + service_token_connection { + type = "EVPL_VC" + bandwidth_limit = 1000 + a_side { + access_point_selectors{ + type = "COLO" + port { + uuid = "" + } + link_protocol { + type = "DOT1Q" + vlan_tag = "2987" + } + } + } + } + notifications { + type = "ALL" + emails = ["example@equinix.com", "test1@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 15eaf72d6..f3f0b8b36 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,5 +1,6 @@ resource "equinix_fabric_service_token" "test" { type = "VC_TOKEN" + description = "Zside VD Service Token" expiration_date_time = "2025-01-18T06:43:49.986Z" service_token_connection { type = "EVPL_VC" @@ -17,4 +18,8 @@ resource "equinix_fabric_service_token" "test" { } } } + notifications { + type = "ALL" + emails = ["example@equinix.com"] + } } diff --git a/internal/resources/fabric/service_token/models.go b/internal/resources/fabric/service_token/models.go index 0a3a58c24..2e37c57b5 100644 --- a/internal/resources/fabric/service_token/models.go +++ b/internal/resources/fabric/service_token/models.go @@ -393,7 +393,47 @@ func portTerraformToGo(portList []interface{}) fabricv4.SimplifiedMetadataEntity var port fabricv4.SimplifiedMetadataEntity portListMap := portList[0].(map[string]interface{}) uuid := portListMap["uuid"].(string) - port.SetUuid(uuid) + href := portListMap["href"].(string) + type_ := portListMap["type"].(string) + cvpId := portListMap["cvp_id"].(int) + bandwidth := portListMap["bandwidth"].(int) + portName := portListMap["port_name"].(string) + encapsulationProtocolType := portListMap["encapsulation_protocol_type"].(string) + accountName := portListMap["account_name"].(string) + priority := portListMap["priority"].(string) + locationList := portListMap["location"].(*schema.Set).List() + + if uuid != "" { + port.SetUuid(uuid) + } + if href != "" { + port.SetHref(href) + } + if type_ != "" { + port.SetType(type_) + } + if cvpId != 0 { + port.SetCvpId(int32(cvpId)) + } + if bandwidth != 0 { + port.SetBandwidth(float32(bandwidth)) + } + if portName != "" { + port.SetPortName(portName) + } + if encapsulationProtocolType != "" { + port.SetEncapsulationProtocolType(encapsulationProtocolType) + } + if accountName != "" { + port.SetAccountName(accountName) + } + if priority != "" { + port.SetPriority(priority) + } + if len(locationList) != 0 { + location := equinix_fabric_schema.LocationTerraformToGo(locationList) + port.SetLocation(location) + } return port } @@ -601,10 +641,41 @@ func accessPointSelectorsGoToTerraform(apSelectors []fabricv4.AccessPointSelecto } func portGoToTerraform(port *fabricv4.SimplifiedMetadataEntity) *schema.Set { + if port == nil { + return nil + } mappedPort := make(map[string]interface{}) - mappedPort["href"] = port.GetHref() - mappedPort["type"] = port.GetType() - mappedPort["uuid"] = port.GetUuid() + if href := port.GetHref(); href != "" { + mappedPort["href"] = href + } + if uuid := port.GetUuid(); uuid != "" { + mappedPort["uuid"] = uuid + } + if port.GetType() != "" { + mappedPort["type"] = port.GetType() + } + if cvpId := port.GetCvpId(); cvpId != 0 { + mappedPort["cvp_id"] = port.GetCvpId() + } + if bandwidth := port.GetBandwidth(); bandwidth != 0 { + mappedPort["bandwidth"] = port.GetBandwidth() + } + if portName := port.GetPortName(); portName != "" { + mappedPort["port_name"] = port.GetPortName() + } + if encapsulationProtocolType := port.GetEncapsulationProtocolType(); encapsulationProtocolType != "" { + mappedPort["encapsulation_protocol_type"] = port.GetEncapsulationProtocolType() + } + if accountName := port.GetAccountName(); accountName != "" { + mappedPort["account_name"] = port.GetAccountName() + } + if priority := port.GetPriority(); priority != "" { + mappedPort["priority"] = port.GetPriority() + } + if port.Location != nil { + location := port.GetLocation() + mappedPort["location"] = equinix_fabric_schema.LocationGoToTerraform(&location) + } portSet := schema.NewSet( schema.HashResource(portSch()), diff --git a/internal/resources/fabric/service_token/resource_schema.go b/internal/resources/fabric/service_token/resource_schema.go index ab2a363a5..fe4bf0b02 100644 --- a/internal/resources/fabric/service_token/resource_schema.go +++ b/internal/resources/fabric/service_token/resource_schema.go @@ -129,7 +129,8 @@ func serviceTokenConnectionSch() *schema.Resource { }, "supported_bandwidths": { Type: schema.TypeList, - Required: true, + Optional: true, + Computed: true, Description: "List of permitted bandwidths'; For Port-based Service Tokens, the maximum allowable bandwidth is 50 Gbps, while for Virtual Device-based Service Tokens, it is limited to 10 Gbps", Elem: &schema.Schema{ Type: schema.TypeInt, @@ -230,8 +231,7 @@ func portSch() *schema.Resource { }, "uuid": { Type: schema.TypeString, - Optional: true, - Computed: true, + Required: true, Description: "Equinix-assigned Port identifier", }, "type": { @@ -243,43 +243,36 @@ func portSch() *schema.Resource { "cvp_id": { Type: schema.TypeInt, Computed: true, - Optional: true, Description: "Customer virtual port Id", }, "bandwidth": { Type: schema.TypeInt, Computed: true, - Optional: true, Description: "Port Bandwidth", }, "port_name": { Type: schema.TypeString, Computed: true, - Optional: true, Description: "Port Name", }, "encapsulation_protocol_type": { Type: schema.TypeString, Computed: true, - Optional: true, Description: "Port Encapsulation", }, "account_name": { Type: schema.TypeString, Computed: true, - Optional: true, Description: "Account Name", }, "priority": { Type: schema.TypeString, Computed: true, - Optional: true, Description: "Port Priority", }, "location": { Type: schema.TypeSet, Computed: true, - Optional: true, Description: "Port Location", Elem: &schema.Resource{ Schema: equinix_fabric_schema.LocationSch(), diff --git a/internal/resources/fabric/service_token/resource_test.go b/internal/resources/fabric/service_token/resource_test.go index 9790d8ba7..077f1cba8 100644 --- a/internal/resources/fabric/service_token/resource_test.go +++ b/internal/resources/fabric/service_token/resource_test.go @@ -14,7 +14,7 @@ import ( "github.com/hashicorp/terraform-plugin-testing/terraform" ) -func TestAccFabricServiceToken_PNFV(t *testing.T) { +func TestAccFabricZsideVirtualDeviceServiceToken_PNFV(t *testing.T) { connectionTestData := testing_helpers.GetFabricEnvConnectionTestData(t) var virtualDevice string if len(connectionTestData) > 0 { @@ -28,7 +28,7 @@ func TestAccFabricServiceToken_PNFV(t *testing.T) { CheckDestroy: CheckServiceTokenDelete, Steps: []resource.TestStep{ { - Config: testAccFabricServiceTokenConfig(serviceTokenName, serviceTokenDescription, virtualDevice), + Config: testAccFabricZsideVirtualDeviceServiceTokenConfig(serviceTokenName, serviceTokenDescription, virtualDevice), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet("equinix_fabric_service_token.test", "uuid"), resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "name", serviceTokenName), @@ -44,7 +44,7 @@ func TestAccFabricServiceToken_PNFV(t *testing.T) { ExpectNonEmptyPlan: false, }, { - Config: testAccFabricServiceTokenConfig(serviceTokenUpdatedName, serviceTokenUpdatedDescription, virtualDevice), + Config: testAccFabricZsideVirtualDeviceServiceTokenConfig(serviceTokenUpdatedName, serviceTokenUpdatedDescription, virtualDevice), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet("equinix_fabric_service_token.test", "uuid"), resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "name", serviceTokenUpdatedName), @@ -63,7 +63,54 @@ func TestAccFabricServiceToken_PNFV(t *testing.T) { }) } -func testAccFabricServiceTokenConfig(serviceTokenName string, serviceTokenDescription string, virtualDeviceUuid string) string { +func TestAccFabricAsidePortServiceToken_PNFV(t *testing.T) { + ports := testing_helpers.GetFabricEnvPorts(t) + var portUuid string + if len(ports) > 0 { + portUuid = ports["ppds"]["dot1q"][0].GetUuid() + } + serviceTokenName, serviceTokenUpdatedName := "token_port_PNFV", "UP_Token_port_PNFV" + serviceTokenDescription, serviceTokenUpdatedDescription := "aside port token", "Updated aside port token" + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.TestAccPreCheck(t) }, + Providers: acceptance.TestAccProviders, + CheckDestroy: CheckServiceTokenDelete, + Steps: []resource.TestStep{ + { + Config: testAccFabricAsidePortServiceTokenConfig(serviceTokenName, serviceTokenDescription, portUuid), + 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-01-18T06:43:49.981Z"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.bandwidth_limit", "1000"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.a_side.0.access_point_selectors.0.port.0.uuid", portUuid), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.a_side.0.access_point_selectors.0.link_protocol.0.type", "DOT1Q"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.a_side.0.access_point_selectors.0.link_protocol.0.vlan_tag", "2987"), + ), + ExpectNonEmptyPlan: false, + }, + { + Config: testAccFabricAsidePortServiceTokenConfig(serviceTokenUpdatedName, serviceTokenUpdatedDescription, portUuid), + 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-01-18T06:43:49.981Z"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.bandwidth_limit", "1000"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.a_side.0.access_point_selectors.0.port.0.uuid", portUuid), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.a_side.0.access_point_selectors.0.link_protocol.0.type", "DOT1Q"), + resource.TestCheckResourceAttr("equinix_fabric_service_token.test", "service_token_connection.0.a_side.0.access_point_selectors.0.link_protocol.0.vlan_tag", "2987"), + ), + ExpectNonEmptyPlan: false, + }, + }, + }) +} + +func testAccFabricZsideVirtualDeviceServiceTokenConfig(serviceTokenName string, serviceTokenDescription string, virtualDeviceUuid string) string { return fmt.Sprintf( `resource "equinix_fabric_service_token" "test"{ type = "VC_TOKEN" @@ -95,6 +142,38 @@ func testAccFabricServiceTokenConfig(serviceTokenName string, serviceTokenDescri `, serviceTokenName, serviceTokenDescription, virtualDeviceUuid) } +func testAccFabricAsidePortServiceTokenConfig(serviceTokenName string, serviceTokenDescription string, portUuid string) string { + return fmt.Sprintf( + `resource "equinix_fabric_service_token" "test"{ + type = "VC_TOKEN" + name = "%s" + description = "%s" + expiration_date_time = "2025-01-18T06:43:49.981Z" + service_token_connection { + type = "EVPL_VC" + bandwidth_limit = 1000 + a_side { + access_point_selectors{ + type = "COLO" + port { + uuid = "%s" + } + link_protocol { + type = "DOT1Q" + vlan_tag = "2987" + } + } + } + } + notifications { + type = "ALL" + emails = ["example@equinix.com", "test1@equinix.com"] + } + + } + `, serviceTokenName, serviceTokenDescription, portUuid) +} + func CheckServiceTokenDelete(s *terraform.State) error { ctx := context.Background() for _, rs := range s.RootModule().Resources { diff --git a/templates/resources/fabric_service_token.md.tmpl b/templates/resources/fabric_service_token.md.tmpl index 4d8d656f7..937d6ae6f 100644 --- a/templates/resources/fabric_service_token.md.tmpl +++ b/templates/resources/fabric_service_token.md.tmpl @@ -16,6 +16,9 @@ Additional documentation: ## Example Usage +Aside Port Service Token +{{tffile "examples/resources/equinix_fabric_service_token/aside_colo_service_token.tf"}} + Zside Virtual Device Service Token {{tffile "examples/resources/equinix_fabric_service_token/zside_vd_service_token.tf"}} From f697114e400e795521d7698d19419b95359d9295 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 7 Nov 2024 09:15:07 -0600 Subject: [PATCH 49/55] fix(deps): update module github.com/hashicorp/terraform-plugin-docs to v0.20.0 (#814) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [github.com/hashicorp/terraform-plugin-docs](https://redirect.github.com/hashicorp/terraform-plugin-docs) | `v0.19.4` -> `v0.20.0` | [![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fhashicorp%2fterraform-plugin-docs/v0.20.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/go/github.com%2fhashicorp%2fterraform-plugin-docs/v0.20.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/go/github.com%2fhashicorp%2fterraform-plugin-docs/v0.19.4/v0.20.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fhashicorp%2fterraform-plugin-docs/v0.19.4/v0.20.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
hashicorp/terraform-plugin-docs (github.com/hashicorp/terraform-plugin-docs) ### [`v0.20.0`](https://redirect.github.com/hashicorp/terraform-plugin-docs/releases/tag/v0.20.0) [Compare Source](https://redirect.github.com/hashicorp/terraform-plugin-docs/compare/v0.19.4...v0.20.0) NOTES: - all: This Go module has been updated to Go 1.22 per the [Go support policy](https://go.dev/doc/devel/release#policy). It is recommended to review the [Go 1.22 release notes](https://go.dev/doc/go1.22) before upgrading. Any consumers building on earlier Go versions may experience errors. ([#​400](https://redirect.github.com/hashicorp/terraform-plugin-docs/issues/400)) FEATURES: - generate: Add support for ephemeral resources ([#​415](https://redirect.github.com/hashicorp/terraform-plugin-docs/issues/415)) - migrate: Add support for ephemeral resources ([#​415](https://redirect.github.com/hashicorp/terraform-plugin-docs/issues/415)) - validate: Add support for ephemeral resources ([#​415](https://redirect.github.com/hashicorp/terraform-plugin-docs/issues/415)) BUG FIXES: - validate: File extension check now runs on `index.*` files instead of just `index.md` files. ([#​413](https://redirect.github.com/hashicorp/terraform-plugin-docs/issues/413)) - validate: File extension check now specifies the correct valid extensions in the error message. ([#​413](https://redirect.github.com/hashicorp/terraform-plugin-docs/issues/413)) - validate: Front matter check now runs with the correct options on legacy index files. ([#​413](https://redirect.github.com/hashicorp/terraform-plugin-docs/issues/413))
--- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/equinix/terraform-provider-equinix). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- go.mod | 18 +++++++++--------- go.sum | 40 ++++++++++++++++++++-------------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/go.mod b/go.mod index 01a1e8e22..822d7debd 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-retryablehttp v0.7.7 github.com/hashicorp/go-uuid v1.0.3 - github.com/hashicorp/terraform-plugin-docs v0.19.4 + github.com/hashicorp/terraform-plugin-docs v0.20.0 github.com/hashicorp/terraform-plugin-framework v1.10.0 github.com/hashicorp/terraform-plugin-framework-timeouts v0.4.1 github.com/hashicorp/terraform-plugin-framework-validators v0.13.0 @@ -38,7 +38,7 @@ require ( github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/armon/go-radix v1.0.0 // indirect github.com/bgentry/speakeasy v0.1.0 // indirect - github.com/bmatcuk/doublestar/v4 v4.6.1 // indirect + github.com/bmatcuk/doublestar/v4 v4.7.1 // indirect github.com/cloudflare/circl v1.3.7 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fatih/color v1.16.0 // indirect @@ -53,11 +53,11 @@ require ( github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/hashicorp/go-plugin v1.6.0 // indirect github.com/hashicorp/go-version v1.7.0 // indirect - github.com/hashicorp/hc-install v0.8.0 // indirect + github.com/hashicorp/hc-install v0.9.0 // indirect github.com/hashicorp/hcl/v2 v2.21.0 // indirect github.com/hashicorp/logutils v1.0.0 // indirect github.com/hashicorp/terraform-exec v0.21.0 // indirect - github.com/hashicorp/terraform-json v0.22.1 // indirect + github.com/hashicorp/terraform-json v0.23.0 // indirect github.com/hashicorp/terraform-registry-address v0.2.3 // indirect github.com/hashicorp/terraform-svchost v0.1.1 // indirect github.com/hashicorp/yamux v0.1.1 // indirect @@ -79,18 +79,18 @@ require ( github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect - github.com/yuin/goldmark v1.7.1 // indirect + github.com/yuin/goldmark v1.7.7 // indirect github.com/yuin/goldmark-meta v1.1.0 // indirect github.com/zclconf/go-cty v1.15.0 // indirect go.abhg.dev/goldmark/frontmatter v0.2.0 // indirect golang.org/x/crypto v0.26.0 // indirect golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc // indirect - golang.org/x/mod v0.19.0 // indirect - golang.org/x/net v0.25.0 // indirect + golang.org/x/mod v0.21.0 // indirect + golang.org/x/net v0.26.0 // indirect golang.org/x/sync v0.8.0 // indirect golang.org/x/sys v0.23.0 // indirect - golang.org/x/text v0.17.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + golang.org/x/text v0.19.0 // indirect + golang.org/x/tools v0.22.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect google.golang.org/grpc v1.63.2 // indirect diff --git a/go.sum b/go.sum index b6183b99a..d7d518089 100644 --- a/go.sum +++ b/go.sum @@ -24,8 +24,8 @@ github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I= -github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= +github.com/bmatcuk/doublestar/v4 v4.7.1 h1:fdDeAqgT47acgwd9bd9HxJRDmc9UAmPpc+2m0CXv75Q= +github.com/bmatcuk/doublestar/v4 v4.7.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= @@ -104,18 +104,18 @@ github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/C github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/hc-install v0.8.0 h1:LdpZeXkZYMQhoKPCecJHlKvUkQFixN/nvyR1CdfOLjI= -github.com/hashicorp/hc-install v0.8.0/go.mod h1:+MwJYjDfCruSD/udvBmRB22Nlkwwkwf5sAB6uTIhSaU= +github.com/hashicorp/hc-install v0.9.0 h1:2dIk8LcvANwtv3QZLckxcjyF5w8KVtiMxu6G6eLhghE= +github.com/hashicorp/hc-install v0.9.0/go.mod h1:+6vOP+mf3tuGgMApVYtmsnDoKWMDcFXeTxCACYZ8SFg= github.com/hashicorp/hcl/v2 v2.21.0 h1:lve4q/o/2rqwYOgUg3y3V2YPyD1/zkCLGjIV74Jit14= github.com/hashicorp/hcl/v2 v2.21.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA= github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/terraform-exec v0.21.0 h1:uNkLAe95ey5Uux6KJdua6+cv8asgILFVWkd/RG0D2XQ= github.com/hashicorp/terraform-exec v0.21.0/go.mod h1:1PPeMYou+KDUSSeRE9szMZ/oHf4fYUmB923Wzbq1ICg= -github.com/hashicorp/terraform-json v0.22.1 h1:xft84GZR0QzjPVWs4lRUwvTcPnegqlyS7orfb5Ltvec= -github.com/hashicorp/terraform-json v0.22.1/go.mod h1:JbWSQCLFSXFFhg42T7l9iJwdGXBYV8fmmD6o/ML4p3A= -github.com/hashicorp/terraform-plugin-docs v0.19.4 h1:G3Bgo7J22OMtegIgn8Cd/CaSeyEljqjH3G39w28JK4c= -github.com/hashicorp/terraform-plugin-docs v0.19.4/go.mod h1:4pLASsatTmRynVzsjEhbXZ6s7xBlUw/2Kt0zfrq8HxA= +github.com/hashicorp/terraform-json v0.23.0 h1:sniCkExU4iKtTADReHzACkk8fnpQXrdD2xoR+lppBkI= +github.com/hashicorp/terraform-json v0.23.0/go.mod h1:MHdXbBAbSg0GvzuWazEGKAn/cyNfIB7mN6y7KJN6y2c= +github.com/hashicorp/terraform-plugin-docs v0.20.0 h1:ox7rm1FN0dVZaJBUzkVVh10R1r3+FeMQWL0QopQ9d7o= +github.com/hashicorp/terraform-plugin-docs v0.20.0/go.mod h1:A/+4SVMdAkQYtIBtaxV0H7AU862TxVZk/hhKaMDQB6Y= github.com/hashicorp/terraform-plugin-framework v1.10.0 h1:xXhICE2Fns1RYZxEQebwkB2+kXouLC932Li9qelozrc= github.com/hashicorp/terraform-plugin-framework v1.10.0/go.mod h1:qBXLDn69kM97NNVi/MQ9qgd1uWWsVftGSnygYG1tImM= github.com/hashicorp/terraform-plugin-framework-timeouts v0.4.1 h1:gm5b1kHgFFhaKFhm4h2TgvMUlNzFAtUqlcOWnWPm+9E= @@ -196,8 +196,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= @@ -232,8 +232,8 @@ github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/goldmark v1.7.1 h1:3bajkSilaCbjdKVsKdZjZCLBNPL9pYzrCakKaf4U49U= -github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= +github.com/yuin/goldmark v1.7.7 h1:5m9rrB1sW3JUMToKFQfb+FGt1U7r57IHu5GrYrG2nqU= +github.com/yuin/goldmark v1.7.7/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc= github.com/yuin/goldmark-meta v1.1.0/go.mod h1:U4spWENafuA7Zyg+Lj5RqK/MF+ovMYtBvXi1lBb2VP0= github.com/zclconf/go-cty v1.15.0 h1:tTCRWxsexYUmtt/wVxgDClUe+uQusuI443uL6e+5sXQ= @@ -251,8 +251,8 @@ golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn5 golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcHCgR0s52IfwutMfEbdM= golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= -golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -261,8 +261,8 @@ golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= @@ -300,13 +300,13 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= From 36d0bacc1aec39994bbb75de2bc1964ea3094c71 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 7 Nov 2024 09:30:38 -0600 Subject: [PATCH 50/55] fix(deps): update module github.com/hashicorp/terraform-plugin-go to v0.25.0 (#778) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [github.com/hashicorp/terraform-plugin-go](https://redirect.github.com/hashicorp/terraform-plugin-go) | `v0.23.0` -> `v0.25.0` | [![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fhashicorp%2fterraform-plugin-go/v0.25.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/go/github.com%2fhashicorp%2fterraform-plugin-go/v0.25.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/go/github.com%2fhashicorp%2fterraform-plugin-go/v0.23.0/v0.25.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fhashicorp%2fterraform-plugin-go/v0.23.0/v0.25.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
hashicorp/terraform-plugin-go (github.com/hashicorp/terraform-plugin-go) ### [`v0.25.0`](https://redirect.github.com/hashicorp/terraform-plugin-go/releases/tag/v0.25.0) [Compare Source](https://redirect.github.com/hashicorp/terraform-plugin-go/compare/v0.24.0...v0.25.0) NOTES: - tfprotov5+tfprotov6: An upcoming release will require the `EphemeralResourceServer` implementation as part of `ProviderServer`. ([#​441](https://redirect.github.com/hashicorp/terraform-plugin-go/issues/441)) FEATURES: - tfprotov5+tfprotov6: Upgraded protocols and added types to support the new ephemeral resource type ([#​441](https://redirect.github.com/hashicorp/terraform-plugin-go/issues/441)) ### [`v0.24.0`](https://redirect.github.com/hashicorp/terraform-plugin-go/releases/tag/v0.24.0) [Compare Source](https://redirect.github.com/hashicorp/terraform-plugin-go/compare/v0.23.0...v0.24.0) BREAKING CHANGES: - tfprotov5+tfprotov6: Removed temporary `ResourceServerWithMoveResourceState` interface type. Use `ResourceServer` instead. ([#​408](https://redirect.github.com/hashicorp/terraform-plugin-go/issues/408)) NOTES: - all: If using terraform-plugin-mux, it must be upgraded to v0.16.0 or later to prevent compilation errors ([#​408](https://redirect.github.com/hashicorp/terraform-plugin-go/issues/408)) - all: This Go module has been updated to Go 1.22 per the [Go support policy](https://go.dev/doc/devel/release#policy). It is recommended to review the [Go 1.22 release notes](https://go.dev/doc/go1.22) before upgrading. Any consumers building on earlier Go versions may experience errors. ([#​428](https://redirect.github.com/hashicorp/terraform-plugin-go/issues/428)) BUG FIXES: - logging: Add existing `MoveResourceState` server capability to v5 and v6 logging ([#​432](https://redirect.github.com/hashicorp/terraform-plugin-go/issues/432))
--- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/equinix/terraform-provider-equinix). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- go.mod | 14 +++++++------- go.sum | 28 ++++++++++++++-------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/go.mod b/go.mod index 822d7debd..840ed6fb3 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/hashicorp/terraform-plugin-framework v1.10.0 github.com/hashicorp/terraform-plugin-framework-timeouts v0.4.1 github.com/hashicorp/terraform-plugin-framework-validators v0.13.0 - github.com/hashicorp/terraform-plugin-go v0.23.0 + github.com/hashicorp/terraform-plugin-go v0.25.0 github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/hashicorp/terraform-plugin-mux v0.16.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0 @@ -51,7 +51,7 @@ require ( github.com/hashicorp/go-checkpoint v0.5.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect - github.com/hashicorp/go-plugin v1.6.0 // indirect + github.com/hashicorp/go-plugin v1.6.2 // indirect github.com/hashicorp/go-version v1.7.0 // indirect github.com/hashicorp/hc-install v0.9.0 // indirect github.com/hashicorp/hcl/v2 v2.21.0 // indirect @@ -86,15 +86,15 @@ require ( golang.org/x/crypto v0.26.0 // indirect golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc // indirect golang.org/x/mod v0.21.0 // indirect - golang.org/x/net v0.26.0 // indirect + golang.org/x/net v0.28.0 // indirect golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.23.0 // indirect + golang.org/x/sys v0.24.0 // indirect golang.org/x/text v0.19.0 // indirect golang.org/x/tools v0.22.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect - google.golang.org/grpc v1.63.2 // indirect - google.golang.org/protobuf v1.34.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect + google.golang.org/grpc v1.67.1 // indirect + google.golang.org/protobuf v1.35.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index d7d518089..1f51c4581 100644 --- a/go.sum +++ b/go.sum @@ -95,8 +95,8 @@ github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVH github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-plugin v1.6.0 h1:wgd4KxHJTVGGqWBq4QPB1i5BZNEx9BR8+OFmHDmTk8A= -github.com/hashicorp/go-plugin v1.6.0/go.mod h1:lBS5MtSSBZk0SHc66KACcjjlU6WzEVP/8pwz68aMkCI= +github.com/hashicorp/go-plugin v1.6.2 h1:zdGAEd0V1lCaU0u+MxWQhtSDQmahpkwOun8U8EiRVog= +github.com/hashicorp/go-plugin v1.6.2/go.mod h1:CkgLQ5CZqNmdL9U9JzM532t8ZiYQ35+pj3b1FD37R0Q= github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= @@ -122,8 +122,8 @@ github.com/hashicorp/terraform-plugin-framework-timeouts v0.4.1 h1:gm5b1kHgFFhaK github.com/hashicorp/terraform-plugin-framework-timeouts v0.4.1/go.mod h1:MsjL1sQ9L7wGwzJ5RjcI6FzEMdyoBnw+XK8ZnOvQOLY= github.com/hashicorp/terraform-plugin-framework-validators v0.13.0 h1:bxZfGo9DIUoLLtHMElsu+zwqI4IsMZQBRRy4iLzZJ8E= github.com/hashicorp/terraform-plugin-framework-validators v0.13.0/go.mod h1:wGeI02gEhj9nPANU62F2jCaHjXulejm/X+af4PdZaNo= -github.com/hashicorp/terraform-plugin-go v0.23.0 h1:AALVuU1gD1kPb48aPQUjug9Ir/125t+AAurhqphJ2Co= -github.com/hashicorp/terraform-plugin-go v0.23.0/go.mod h1:1E3Cr9h2vMlahWMbsSEcNrOCxovCZhOOIXjFHbjc/lQ= +github.com/hashicorp/terraform-plugin-go v0.25.0 h1:oi13cx7xXA6QciMcpcFi/rwA974rdTxjqEhXJjbAyks= +github.com/hashicorp/terraform-plugin-go v0.25.0/go.mod h1:+SYagMYadJP86Kvn+TGeV+ofr/R3g4/If0O5sO96MVw= github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow= github.com/hashicorp/terraform-plugin-mux v0.16.0 h1:RCzXHGDYwUwwqfYYWJKBFaS3fQsWn/ZECEiW7p2023I= @@ -261,8 +261,8 @@ golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= @@ -287,8 +287,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= -golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -313,14 +313,14 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= -google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= -google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.34.0 h1:Qo/qEd2RZPCf2nKuorzksSknv0d3ERwp1vFG38gSmH4= -google.golang.org/protobuf v1.34.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 4ba08d54e44578a34c2cebddbd5963277976f21f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 7 Nov 2024 10:32:05 -0600 Subject: [PATCH 51/55] fix(deps): update module github.com/hashicorp/terraform-plugin-sdk/v2 to v2.35.0 (#811) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [github.com/hashicorp/terraform-plugin-sdk/v2](https://redirect.github.com/hashicorp/terraform-plugin-sdk) | `v2.34.0` -> `v2.35.0` | [![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fhashicorp%2fterraform-plugin-sdk%2fv2/v2.35.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/go/github.com%2fhashicorp%2fterraform-plugin-sdk%2fv2/v2.35.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/go/github.com%2fhashicorp%2fterraform-plugin-sdk%2fv2/v2.34.0/v2.35.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fhashicorp%2fterraform-plugin-sdk%2fv2/v2.34.0/v2.35.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
hashicorp/terraform-plugin-sdk (github.com/hashicorp/terraform-plugin-sdk/v2) ### [`v2.35.0`](https://redirect.github.com/hashicorp/terraform-plugin-sdk/releases/tag/v2.35.0) [Compare Source](https://redirect.github.com/hashicorp/terraform-plugin-sdk/compare/v2.34.0...v2.35.0) NOTES: - all: This Go module has been updated to Go 1.22 per the [Go support policy](https://go.dev/doc/devel/release#policy). It is recommended to review the [Go 1.22 release notes](https://go.dev/doc/go1.22) before upgrading. Any consumers building on earlier Go versions may experience errors. ([#​1373](https://redirect.github.com/hashicorp/terraform-plugin-sdk/issues/1373)) - helper/schema: While this Go module will not receive support for ephemeral resource types, the provider server is updated to handle the new operations, which will be required to prevent errors when updating `terraform-plugin-framework` or `terraform-plugin-mux` in the future. ([#​1390](https://redirect.github.com/hashicorp/terraform-plugin-sdk/issues/1390))
--- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/equinix/terraform-provider-equinix). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- go.mod | 8 ++++---- go.sum | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/go.mod b/go.mod index 840ed6fb3..68c9437c4 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/hashicorp/terraform-plugin-go v0.25.0 github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/hashicorp/terraform-plugin-mux v0.16.0 - github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0 + github.com/hashicorp/terraform-plugin-sdk/v2 v2.35.0 github.com/hashicorp/terraform-plugin-testing v1.10.0 github.com/packethost/packngo v0.31.0 github.com/pkg/errors v0.9.1 @@ -54,7 +54,7 @@ require ( github.com/hashicorp/go-plugin v1.6.2 // indirect github.com/hashicorp/go-version v1.7.0 // indirect github.com/hashicorp/hc-install v0.9.0 // indirect - github.com/hashicorp/hcl/v2 v2.21.0 // indirect + github.com/hashicorp/hcl/v2 v2.22.0 // indirect github.com/hashicorp/logutils v1.0.0 // indirect github.com/hashicorp/terraform-exec v0.21.0 // indirect github.com/hashicorp/terraform-json v0.23.0 // indirect @@ -83,12 +83,12 @@ require ( github.com/yuin/goldmark-meta v1.1.0 // indirect github.com/zclconf/go-cty v1.15.0 // indirect go.abhg.dev/goldmark/frontmatter v0.2.0 // indirect - golang.org/x/crypto v0.26.0 // indirect + golang.org/x/crypto v0.28.0 // indirect golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc // indirect golang.org/x/mod v0.21.0 // indirect golang.org/x/net v0.28.0 // indirect golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.24.0 // indirect + golang.org/x/sys v0.26.0 // indirect golang.org/x/text v0.19.0 // indirect golang.org/x/tools v0.22.0 // indirect google.golang.org/appengine v1.6.8 // indirect diff --git a/go.sum b/go.sum index 1f51c4581..19945e86b 100644 --- a/go.sum +++ b/go.sum @@ -106,8 +106,8 @@ github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKe github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/hc-install v0.9.0 h1:2dIk8LcvANwtv3QZLckxcjyF5w8KVtiMxu6G6eLhghE= github.com/hashicorp/hc-install v0.9.0/go.mod h1:+6vOP+mf3tuGgMApVYtmsnDoKWMDcFXeTxCACYZ8SFg= -github.com/hashicorp/hcl/v2 v2.21.0 h1:lve4q/o/2rqwYOgUg3y3V2YPyD1/zkCLGjIV74Jit14= -github.com/hashicorp/hcl/v2 v2.21.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA= +github.com/hashicorp/hcl/v2 v2.22.0 h1:hkZ3nCtqeJsDhPRFz5EA9iwcG1hNWGePOTw6oyul12M= +github.com/hashicorp/hcl/v2 v2.22.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA= github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/terraform-exec v0.21.0 h1:uNkLAe95ey5Uux6KJdua6+cv8asgILFVWkd/RG0D2XQ= @@ -128,8 +128,8 @@ github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9T github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow= github.com/hashicorp/terraform-plugin-mux v0.16.0 h1:RCzXHGDYwUwwqfYYWJKBFaS3fQsWn/ZECEiW7p2023I= github.com/hashicorp/terraform-plugin-mux v0.16.0/go.mod h1:PF79mAsPc8CpusXPfEVa4X8PtkB+ngWoiUClMrNZlYo= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0 h1:kJiWGx2kiQVo97Y5IOGR4EMcZ8DtMswHhUuFibsCQQE= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0/go.mod h1:sl/UoabMc37HA6ICVMmGO+/0wofkVIRxf+BMb/dnoIg= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.35.0 h1:wyKCCtn6pBBL46c1uIIBNUOWlNfYXfXpVo16iDyLp8Y= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.35.0/go.mod h1:B0Al8NyYVr8Mp/KLwssKXG1RqnTk7FySqSn4fRuLNgw= github.com/hashicorp/terraform-plugin-testing v1.10.0 h1:2+tmRNhvnfE4Bs8rB6v58S/VpqzGC6RCh9Y8ujdn+aw= github.com/hashicorp/terraform-plugin-testing v1.10.0/go.mod h1:iWRW3+loP33WMch2P/TEyCxxct/ZEcCGMquSLSCVsrc= github.com/hashicorp/terraform-registry-address v0.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTVcLZRu7JseiXNRHbOAyoTI= @@ -246,8 +246,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20200420201142-3c4aac89819a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcHCgR0s52IfwutMfEbdM= golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -287,13 +287,13 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= -golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= From 6a02652d1ceb56d7f2ce25107a52c968e44a4924 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 7 Nov 2024 10:38:38 -0600 Subject: [PATCH 52/55] fix(deps): update module github.com/hashicorp/terraform-plugin-mux to v0.17.0 (#809) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [github.com/hashicorp/terraform-plugin-mux](https://redirect.github.com/hashicorp/terraform-plugin-mux) | `v0.16.0` -> `v0.17.0` | [![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fhashicorp%2fterraform-plugin-mux/v0.17.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/go/github.com%2fhashicorp%2fterraform-plugin-mux/v0.17.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/go/github.com%2fhashicorp%2fterraform-plugin-mux/v0.16.0/v0.17.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fhashicorp%2fterraform-plugin-mux/v0.16.0/v0.17.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
hashicorp/terraform-plugin-mux (github.com/hashicorp/terraform-plugin-mux) ### [`v0.17.0`](https://redirect.github.com/hashicorp/terraform-plugin-mux/releases/tag/v0.17.0) [Compare Source](https://redirect.github.com/hashicorp/terraform-plugin-mux/compare/v0.16.0...v0.17.0) NOTES: - all: This Go module has been updated to Go 1.22 per the [Go support policy](https://go.dev/doc/devel/release#policy). It is recommended to review the [Go 1.22 release notes](https://go.dev/doc/go1.22) before upgrading. Any consumers building on earlier Go versions may experience errors. ([#​250](https://redirect.github.com/hashicorp/terraform-plugin-mux/issues/250)) FEATURES: - all: Upgrade protocol versions to support ephemeral resource types ([#​257](https://redirect.github.com/hashicorp/terraform-plugin-mux/issues/257))
--- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/equinix/terraform-provider-equinix). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 68c9437c4..04f0f2c52 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/hashicorp/terraform-plugin-framework-validators v0.13.0 github.com/hashicorp/terraform-plugin-go v0.25.0 github.com/hashicorp/terraform-plugin-log v0.9.0 - github.com/hashicorp/terraform-plugin-mux v0.16.0 + github.com/hashicorp/terraform-plugin-mux v0.17.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.35.0 github.com/hashicorp/terraform-plugin-testing v1.10.0 github.com/packethost/packngo v0.31.0 diff --git a/go.sum b/go.sum index 19945e86b..b5d781591 100644 --- a/go.sum +++ b/go.sum @@ -126,8 +126,8 @@ github.com/hashicorp/terraform-plugin-go v0.25.0 h1:oi13cx7xXA6QciMcpcFi/rwA974r github.com/hashicorp/terraform-plugin-go v0.25.0/go.mod h1:+SYagMYadJP86Kvn+TGeV+ofr/R3g4/If0O5sO96MVw= github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow= -github.com/hashicorp/terraform-plugin-mux v0.16.0 h1:RCzXHGDYwUwwqfYYWJKBFaS3fQsWn/ZECEiW7p2023I= -github.com/hashicorp/terraform-plugin-mux v0.16.0/go.mod h1:PF79mAsPc8CpusXPfEVa4X8PtkB+ngWoiUClMrNZlYo= +github.com/hashicorp/terraform-plugin-mux v0.17.0 h1:/J3vv3Ps2ISkbLPiZOLspFcIZ0v5ycUXCEQScudGCCw= +github.com/hashicorp/terraform-plugin-mux v0.17.0/go.mod h1:yWuM9U1Jg8DryNfvCp+lH70WcYv6D8aooQxxxIzFDsE= github.com/hashicorp/terraform-plugin-sdk/v2 v2.35.0 h1:wyKCCtn6pBBL46c1uIIBNUOWlNfYXfXpVo16iDyLp8Y= github.com/hashicorp/terraform-plugin-sdk/v2 v2.35.0/go.mod h1:B0Al8NyYVr8Mp/KLwssKXG1RqnTk7FySqSn4fRuLNgw= github.com/hashicorp/terraform-plugin-testing v1.10.0 h1:2+tmRNhvnfE4Bs8rB6v58S/VpqzGC6RCh9Y8ujdn+aw= From b2cd11445fa9ad7ea4c8ba09bfc6b530530df301 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 7 Nov 2024 12:18:24 -0600 Subject: [PATCH 53/55] fix(deps): update module github.com/hashicorp/terraform-plugin-framework to v1.13.0 (#751) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [github.com/hashicorp/terraform-plugin-framework](https://redirect.github.com/hashicorp/terraform-plugin-framework) | `v1.10.0` -> `v1.13.0` | [![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fhashicorp%2fterraform-plugin-framework/v1.13.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/go/github.com%2fhashicorp%2fterraform-plugin-framework/v1.13.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/go/github.com%2fhashicorp%2fterraform-plugin-framework/v1.10.0/v1.13.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fhashicorp%2fterraform-plugin-framework/v1.10.0/v1.13.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
hashicorp/terraform-plugin-framework (github.com/hashicorp/terraform-plugin-framework) ### [`v1.13.0`](https://redirect.github.com/hashicorp/terraform-plugin-framework/releases/tag/v1.13.0) [Compare Source](https://redirect.github.com/hashicorp/terraform-plugin-framework/compare/v1.12.0...v1.13.0) NOTES: - Ephemeral resource support is in technical preview and offered without compatibility promises until Terraform 1.10 is generally available. ([#​1050](https://redirect.github.com/hashicorp/terraform-plugin-framework/issues/1050)) FEATURES: - ephemeral: New package for implementing ephemeral resources ([#​1050](https://redirect.github.com/hashicorp/terraform-plugin-framework/issues/1050)) - ephemeral/schema: New package for implementing ephemeral resource schemas ([#​1050](https://redirect.github.com/hashicorp/terraform-plugin-framework/issues/1050)) ENHANCEMENTS: - provider: Added `ProviderWithEphemeralResources` interface for implementing ephemeral resources ([#​1050](https://redirect.github.com/hashicorp/terraform-plugin-framework/issues/1050)) - tfsdk: Added `EphemeralResultData` struct for representing ephemeral values produced by a provider, such as from an ephemeral resource ([#​1050](https://redirect.github.com/hashicorp/terraform-plugin-framework/issues/1050)) - provider: Added `EphemeralResourceData` to `ConfigureResponse`, to pass provider-defined data to `ephemeral.EphemeralResource` implementations ([#​1050](https://redirect.github.com/hashicorp/terraform-plugin-framework/issues/1050)) ### [`v1.12.0`](https://redirect.github.com/hashicorp/terraform-plugin-framework/releases/tag/v1.12.0) [Compare Source](https://redirect.github.com/hashicorp/terraform-plugin-framework/compare/v1.11.0...v1.12.0) NOTES: - all: This Go module has been updated to Go 1.22 per the [Go support policy](https://go.dev/doc/devel/release#policy). It is recommended to review the [Go 1.22 release notes](https://go.dev/doc/go1.22) before upgrading. Any consumers building on earlier Go versions may experience errors ([#​1033](https://redirect.github.com/hashicorp/terraform-plugin-framework/issues/1033)) BUG FIXES: - providerserver: Fixed bug that prevented `moved` operation support between resource types for framework-only providers. ([#​1039](https://redirect.github.com/hashicorp/terraform-plugin-framework/issues/1039)) ### [`v1.11.0`](https://redirect.github.com/hashicorp/terraform-plugin-framework/releases/tag/v1.11.0) [Compare Source](https://redirect.github.com/hashicorp/terraform-plugin-framework/compare/v1.10.0...v1.11.0) NOTES: - Framework reflection logic (`Config.Get`, `Plan.Get`, etc.) for structs with `tfsdk` field tags has been updated to support embedded structs that promote exported fields. For existing structs that embed unexported structs with exported fields, a tfsdk ignore tag (`tfsdk:"-"`) can be added to ignore all promoted fields. For example, the following struct will now return an error diagnostic: ```go type thingResourceModel struct { Attr1 types.String `tfsdk:"attr_1"` Attr2 types.Bool `tfsdk:"attr_2"` // Previously, this embedded struct was ignored, will now promote underlying fields embeddedModel } type embeddedModel struct { // No `tfsdk` tag ExportedField string } ``` To preserve the original behavior, a tfsdk ignore tag can be added to ignore the entire embedded struct: ```go type thingResourceModel struct { Attr1 types.String `tfsdk:"attr_1"` Attr2 types.Bool `tfsdk:"attr_2"` // This embedded struct will now be ignored embeddedModel `tfsdk:"-"` } type embeddedModel struct { ExportedField string } ``` ([#​1021](https://redirect.github.com/hashicorp/terraform-plugin-framework/issues/1021)) ENHANCEMENTS: - all: Added embedded struct support for object to struct conversions with `tfsdk` tags ([#​1021](https://redirect.github.com/hashicorp/terraform-plugin-framework/issues/1021))
--- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/equinix/terraform-provider-equinix). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 04f0f2c52..aec845b01 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/hashicorp/go-retryablehttp v0.7.7 github.com/hashicorp/go-uuid v1.0.3 github.com/hashicorp/terraform-plugin-docs v0.20.0 - github.com/hashicorp/terraform-plugin-framework v1.10.0 + github.com/hashicorp/terraform-plugin-framework v1.13.0 github.com/hashicorp/terraform-plugin-framework-timeouts v0.4.1 github.com/hashicorp/terraform-plugin-framework-validators v0.13.0 github.com/hashicorp/terraform-plugin-go v0.25.0 diff --git a/go.sum b/go.sum index b5d781591..f7517217e 100644 --- a/go.sum +++ b/go.sum @@ -116,8 +116,8 @@ github.com/hashicorp/terraform-json v0.23.0 h1:sniCkExU4iKtTADReHzACkk8fnpQXrdD2 github.com/hashicorp/terraform-json v0.23.0/go.mod h1:MHdXbBAbSg0GvzuWazEGKAn/cyNfIB7mN6y7KJN6y2c= github.com/hashicorp/terraform-plugin-docs v0.20.0 h1:ox7rm1FN0dVZaJBUzkVVh10R1r3+FeMQWL0QopQ9d7o= github.com/hashicorp/terraform-plugin-docs v0.20.0/go.mod h1:A/+4SVMdAkQYtIBtaxV0H7AU862TxVZk/hhKaMDQB6Y= -github.com/hashicorp/terraform-plugin-framework v1.10.0 h1:xXhICE2Fns1RYZxEQebwkB2+kXouLC932Li9qelozrc= -github.com/hashicorp/terraform-plugin-framework v1.10.0/go.mod h1:qBXLDn69kM97NNVi/MQ9qgd1uWWsVftGSnygYG1tImM= +github.com/hashicorp/terraform-plugin-framework v1.13.0 h1:8OTG4+oZUfKgnfTdPTJwZ532Bh2BobF4H+yBiYJ/scw= +github.com/hashicorp/terraform-plugin-framework v1.13.0/go.mod h1:j64rwMGpgM3NYXTKuxrCnyubQb/4VKldEKlcG8cvmjU= github.com/hashicorp/terraform-plugin-framework-timeouts v0.4.1 h1:gm5b1kHgFFhaKFhm4h2TgvMUlNzFAtUqlcOWnWPm+9E= github.com/hashicorp/terraform-plugin-framework-timeouts v0.4.1/go.mod h1:MsjL1sQ9L7wGwzJ5RjcI6FzEMdyoBnw+XK8ZnOvQOLY= github.com/hashicorp/terraform-plugin-framework-validators v0.13.0 h1:bxZfGo9DIUoLLtHMElsu+zwqI4IsMZQBRRy4iLzZJ8E= From d83402194acb6bded6e154917e69d3640de0a498 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 7 Nov 2024 13:32:55 -0600 Subject: [PATCH 54/55] fix(deps): update module github.com/hashicorp/terraform-plugin-framework-validators to v0.15.0 (#800) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [github.com/hashicorp/terraform-plugin-framework-validators](https://redirect.github.com/hashicorp/terraform-plugin-framework-validators) | `v0.13.0` -> `v0.15.0` | [![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fhashicorp%2fterraform-plugin-framework-validators/v0.15.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/go/github.com%2fhashicorp%2fterraform-plugin-framework-validators/v0.15.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/go/github.com%2fhashicorp%2fterraform-plugin-framework-validators/v0.13.0/v0.15.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fhashicorp%2fterraform-plugin-framework-validators/v0.13.0/v0.15.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
hashicorp/terraform-plugin-framework-validators (github.com/hashicorp/terraform-plugin-framework-validators) ### [`v0.15.0`](https://redirect.github.com/hashicorp/terraform-plugin-framework-validators/releases/tag/v0.15.0) [Compare Source](https://redirect.github.com/hashicorp/terraform-plugin-framework-validators/compare/v0.14.0...v0.15.0) FEATURES: - ephemeralvalidator: Introduce new package with declarative validators for ephemeral resource configurations ([#​242](https://redirect.github.com/hashicorp/terraform-plugin-framework-validators/issues/242)) ### [`v0.14.0`](https://redirect.github.com/hashicorp/terraform-plugin-framework-validators/releases/tag/v0.14.0) [Compare Source](https://redirect.github.com/hashicorp/terraform-plugin-framework-validators/compare/v0.13.0...v0.14.0) NOTES: - all: This Go module has been updated to Go 1.22 per the [Go support policy](https://go.dev/doc/devel/release#policy). It is recommended to review the [Go 1.22 release notes](https://go.dev/doc/go1.22) before upgrading. Any consumers building on earlier Go versions may experience errors. ([#​229](https://redirect.github.com/hashicorp/terraform-plugin-framework-validators/issues/229)) - all: Previously, creating validators with invalid data would result in a `nil` value being returned and a panic from `terraform-plugin-framework`. This has been updated to return an implementation diagnostic referencing the invalid data/validator during config validation. ([#​235](https://redirect.github.com/hashicorp/terraform-plugin-framework-validators/issues/235)) FEATURES: - boolvalidator: Added `Equals` validator ([#​232](https://redirect.github.com/hashicorp/terraform-plugin-framework-validators/issues/232)) ENHANCEMENTS: - all: Implemented parameter interfaces for all value-based validators. This allows these validators to be used with provider-defined functions. ([#​235](https://redirect.github.com/hashicorp/terraform-plugin-framework-validators/issues/235))
--- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/equinix/terraform-provider-equinix). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index aec845b01..a78da011b 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/hashicorp/terraform-plugin-docs v0.20.0 github.com/hashicorp/terraform-plugin-framework v1.13.0 github.com/hashicorp/terraform-plugin-framework-timeouts v0.4.1 - github.com/hashicorp/terraform-plugin-framework-validators v0.13.0 + github.com/hashicorp/terraform-plugin-framework-validators v0.15.0 github.com/hashicorp/terraform-plugin-go v0.25.0 github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/hashicorp/terraform-plugin-mux v0.17.0 diff --git a/go.sum b/go.sum index f7517217e..96e1ae997 100644 --- a/go.sum +++ b/go.sum @@ -120,8 +120,8 @@ github.com/hashicorp/terraform-plugin-framework v1.13.0 h1:8OTG4+oZUfKgnfTdPTJwZ github.com/hashicorp/terraform-plugin-framework v1.13.0/go.mod h1:j64rwMGpgM3NYXTKuxrCnyubQb/4VKldEKlcG8cvmjU= github.com/hashicorp/terraform-plugin-framework-timeouts v0.4.1 h1:gm5b1kHgFFhaKFhm4h2TgvMUlNzFAtUqlcOWnWPm+9E= github.com/hashicorp/terraform-plugin-framework-timeouts v0.4.1/go.mod h1:MsjL1sQ9L7wGwzJ5RjcI6FzEMdyoBnw+XK8ZnOvQOLY= -github.com/hashicorp/terraform-plugin-framework-validators v0.13.0 h1:bxZfGo9DIUoLLtHMElsu+zwqI4IsMZQBRRy4iLzZJ8E= -github.com/hashicorp/terraform-plugin-framework-validators v0.13.0/go.mod h1:wGeI02gEhj9nPANU62F2jCaHjXulejm/X+af4PdZaNo= +github.com/hashicorp/terraform-plugin-framework-validators v0.15.0 h1:RXMmu7JgpFjnI1a5QjMCBb11usrW2OtAG+iOTIj5c9Y= +github.com/hashicorp/terraform-plugin-framework-validators v0.15.0/go.mod h1:Bh89/hNmqsEWug4/XWKYBwtnw3tbz5BAy1L1OgvbIaY= github.com/hashicorp/terraform-plugin-go v0.25.0 h1:oi13cx7xXA6QciMcpcFi/rwA974rdTxjqEhXJjbAyks= github.com/hashicorp/terraform-plugin-go v0.25.0/go.mod h1:+SYagMYadJP86Kvn+TGeV+ofr/R3g4/If0O5sO96MVw= github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= From aa53a801823f03c80f20746ba0dd27ed2ef985fc Mon Sep 17 00:00:00 2001 From: srushti-patl Date: Thu, 7 Nov 2024 12:12:40 -0800 Subject: [PATCH 55/55] fix: Updating port user for acc test --- internal/resources/fabric/service_token/resource_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/resources/fabric/service_token/resource_test.go b/internal/resources/fabric/service_token/resource_test.go index 077f1cba8..be7aa44e7 100644 --- a/internal/resources/fabric/service_token/resource_test.go +++ b/internal/resources/fabric/service_token/resource_test.go @@ -67,7 +67,7 @@ func TestAccFabricAsidePortServiceToken_PNFV(t *testing.T) { ports := testing_helpers.GetFabricEnvPorts(t) var portUuid string if len(ports) > 0 { - portUuid = ports["ppds"]["dot1q"][0].GetUuid() + portUuid = ports["pnfv"]["dot1q"][0].GetUuid() } serviceTokenName, serviceTokenUpdatedName := "token_port_PNFV", "UP_Token_port_PNFV" serviceTokenDescription, serviceTokenUpdatedDescription := "aside port token", "Updated aside port token"