diff --git a/.golangci.yaml b/.golangci.yaml index 674ed5dad..bd812ea3f 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -12,4 +12,4 @@ linters: - unused issues: max-issues-per-linter: 0 - max-same-issues: 0 \ No newline at end of file + max-same-issues: 0 diff --git a/docs/data-sources/fabric_stream.md b/docs/data-sources/fabric_stream.md new file mode 100644 index 000000000..627b5adad --- /dev/null +++ b/docs/data-sources/fabric_stream.md @@ -0,0 +1,70 @@ +--- +subcategory: "Fabric" +--- + +# equinix_fabric_stream (Data Source) + +Fabric V4 API compatible data resource that allow user to fetch Equinix Fabric Stream by UUID + +Additional Documentation: +* Getting Started: https://docs.equinix.com/en-us/Content/KnowledgeCenter/Fabric/GettingStarted/Integrating-with-Fabric-V4-APIs/IntegrateWithSink.htm +* API: https://developer.equinix.com/catalog/fabricv4#tag/Streams + +## Example Usage + +```terraform +data "equinix_fabric_stream" "data_stream" { + stream_id = "" +} + +output "stream_state" { + value = data.equinix_fabric_stream.data_stream.state +} +``` + + +## Schema + +### Required + +- `stream_id` (String) The uuid of the stream this data source should retrieve + +### Read-Only + +- `assets_count` (Number) Count of the streaming assets attached to the stream resource +- `change_log` (Attributes) Details of the last change on the stream resource (see [below for nested schema](#nestedatt--change_log)) +- `description` (String) Customer-provided description of the stream resource +- `href` (String) Equinix auto generated URI to the stream resource in Equinix Portal +- `id` (String) The unique identifier of the resource +- `name` (String) Customer-provided name of the stream resource +- `project` (Attributes) Equinix Project attribute object (see [below for nested schema](#nestedatt--project)) +- `state` (String) Value representing provisioning status for the stream resource +- `stream_subscriptions_count` (Number) Count of the client subscriptions on the stream resource +- `type` (String) Equinix defined Streaming Type +- `uuid` (String) Equinix-assigned unique id for the stream resource + + +### Nested Schema for `change_log` + +Read-Only: + +- `created_by` (String) User name of creator of the stream resource +- `created_by_email` (String) Email of creator of the stream resource +- `created_by_full_name` (String) Legal name of creator of the stream resource +- `created_date_time` (String) Creation time of the stream resource +- `deleted_by` (String) User name of deleter of the stream resource +- `deleted_by_email` (String) Email of deleter of the stream resource +- `deleted_by_full_name` (String) Legal name of deleter of the stream resource +- `deleted_date_time` (String) Deletion time of the stream resource +- `updated_by` (String) User name of last updater of the stream resource +- `updated_by_email` (String) Email of last updater of the stream resource +- `updated_by_full_name` (String) Legal name of last updater of the stream resource +- `updated_date_time` (String) Last update time of the stream resource + + + +### Nested Schema for `project` + +Read-Only: + +- `project_id` (String) Equinix Subscriber-assigned project ID diff --git a/docs/data-sources/fabric_streams.md b/docs/data-sources/fabric_streams.md new file mode 100644 index 000000000..744e8be29 --- /dev/null +++ b/docs/data-sources/fabric_streams.md @@ -0,0 +1,95 @@ +--- +subcategory: "Fabric" +--- + +# equinix_fabric_streams (Data Source) + +Fabric V4 API compatible data resource that allow user to fetch Equinix Fabric Streams with pagination details + +Additional Documentation: +* Getting Started: https://docs.equinix.com/en-us/Content/KnowledgeCenter/Fabric/GettingStarted/Integrating-with-Fabric-V4-APIs/IntegrateWithSink.htm +* API: https://developer.equinix.com/catalog/fabricv4#tag/Streams + +## Example Usage + +```terraform +data "equinix_fabric_streams" "data_streams" { + pagination = { + limit = 2 + offset = 1 + } +} + +output "number_of_returned_streams" { + value = length(data.equinix_fabric_streams.data_streams.data) +} +``` + + +## Schema + +### Required + +- `pagination` (Attributes) Pagination details for the returned streams list (see [below for nested schema](#nestedatt--pagination)) + +### Read-Only + +- `data` (Attributes List) Returned list of stream objects (see [below for nested schema](#nestedatt--data)) +- `id` (String) The unique identifier of the resource + + +### Nested Schema for `pagination` + +Optional: + +- `limit` (Number) Maximum number of search results returned per page. Number must be between 1 and 100, and the default is 20 +- `offset` (Number) Index of the first item returned in the response. The default is 0 + +Read-Only: + +- `next` (String) The URL relative to the next item in the response +- `previous` (String) The URL relative to the previous item in the response +- `total` (Number) The total number of streams available to the user making the request + + + +### Nested Schema for `data` + +Read-Only: + +- `assets_count` (Number) Count of the streaming assets attached to the stream resource +- `change_log` (Attributes) Details of the last change on the stream resource (see [below for nested schema](#nestedatt--data--change_log)) +- `description` (String) Customer-provided description of the stream resource +- `href` (String) Equinix auto generated URI to the stream resource in Equinix Portal +- `name` (String) Customer-provided name of the stream resource +- `project` (Attributes) Equinix Project attribute object (see [below for nested schema](#nestedatt--data--project)) +- `state` (String) Value representing provisioning status for the stream resource +- `stream_subscriptions_count` (Number) Count of the client subscriptions on the stream resource +- `type` (String) Equinix defined Streaming Type +- `uuid` (String) Equinix-assigned unique id for the stream resource + + +### Nested Schema for `data.change_log` + +Read-Only: + +- `created_by` (String) User name of creator of the stream resource +- `created_by_email` (String) Email of creator of the stream resource +- `created_by_full_name` (String) Legal name of creator of the stream resource +- `created_date_time` (String) Creation time of the stream resource +- `deleted_by` (String) User name of deleter of the stream resource +- `deleted_by_email` (String) Email of deleter of the stream resource +- `deleted_by_full_name` (String) Legal name of deleter of the stream resource +- `deleted_date_time` (String) Deletion time of the stream resource +- `updated_by` (String) User name of last updater of the stream resource +- `updated_by_email` (String) Email of last updater of the stream resource +- `updated_by_full_name` (String) Legal name of last updater of the stream resource +- `updated_date_time` (String) Last update time of the stream resource + + + +### Nested Schema for `data.project` + +Read-Only: + +- `project_id` (String) Equinix Subscriber-assigned project ID diff --git a/docs/resources/fabric_stream.md b/docs/resources/fabric_stream.md new file mode 100644 index 000000000..4fcf9bf7a --- /dev/null +++ b/docs/resources/fabric_stream.md @@ -0,0 +1,89 @@ +--- +subcategory: "Fabric" +--- + +# equinix_fabric_stream (Resource) + +Fabric V4 API compatible resource allows creation and management of Equinix Fabric Stream + +Additional Documentation: +* Getting Started: https://docs.equinix.com/en-us/Content/KnowledgeCenter/Fabric/GettingStarted/Integrating-with-Fabric-V4-APIs/IntegrateWithSink.htm +* API: https://developer.equinix.com/catalog/fabricv4#tag/Streams + +## Example Usage + +```terraform +resource "equinix_fabric_stream" "new_stream" { + type = "TELEMETRY_STREAM" + name = "" + description = "" + project = { + project_id = " +## Schema + +### Required + +- `description` (String) Customer-provided description of the stream resource +- `name` (String) Customer-provided name of the stream resource +- `type` (String) Equinix defined Streaming Type + +### Optional + +- `project` (Attributes) Equinix Project attribute object (see [below for nested schema](#nestedatt--project)) +- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts)) + +### Read-Only + +- `assets_count` (Number) Count of the streaming assets attached to the stream resource +- `change_log` (Attributes) Details of the last change on the stream resource (see [below for nested schema](#nestedatt--change_log)) +- `href` (String) Equinix auto generated URI to the stream resource in Equinix Portal +- `id` (String) The unique identifier of the resource +- `state` (String) Value representing provisioning status for the stream resource +- `stream_subscriptions_count` (Number) Count of the client subscriptions on the stream resource +- `uuid` (String) Equinix-assigned unique id for the stream resource + + +### Nested Schema for `project` + +Required: + +- `project_id` (String) Equinix Subscriber-assigned project ID + + + +### Nested Schema for `timeouts` + +Optional: + +- `create` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). +- `delete` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). Setting a timeout for a Delete operation is only applicable if changes are saved into state before the destroy operation occurs. +- `read` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). Read operations occur during any refresh or planning operation when refresh is enabled. +- `update` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). + + + +### Nested Schema for `change_log` + +Read-Only: + +- `created_by` (String) User name of creator of the stream resource +- `created_by_email` (String) Email of creator of the stream resource +- `created_by_full_name` (String) Legal name of creator of the stream resource +- `created_date_time` (String) Creation time of the stream resource +- `deleted_by` (String) User name of deleter of the stream resource +- `deleted_by_email` (String) Email of deleter of the stream resource +- `deleted_by_full_name` (String) Legal name of deleter of the stream resource +- `deleted_date_time` (String) Deletion time of the stream resource +- `updated_by` (String) User name of last updater of the stream resource +- `updated_by_email` (String) Email of last updater of the stream resource +- `updated_by_full_name` (String) Legal name of last updater of the stream resource +- `updated_date_time` (String) Last update time of the stream resource diff --git a/equinix/data_source_fabric_cloud_routers.go b/equinix/data_source_fabric_cloud_routers.go index d9d4a5fc9..bcaec2b7c 100644 --- a/equinix/data_source_fabric_cloud_routers.go +++ b/equinix/data_source_fabric_cloud_routers.go @@ -132,7 +132,7 @@ func dataSourceFabricGetCloudRoutersRead(ctx context.Context, d *schema.Resource } func resourceFabricCloudRoutersSearch(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) cloudRouterSearchRequest := fabricv4.CloudRouterSearchRequest{} schemaFilters := d.Get("filter").([]interface{}) diff --git a/equinix/resource_fabric_cloud_router.go b/equinix/resource_fabric_cloud_router.go index de31c9774..9a621f0ef 100644 --- a/equinix/resource_fabric_cloud_router.go +++ b/equinix/resource_fabric_cloud_router.go @@ -274,7 +274,7 @@ func marketplaceSubscriptionCloudRouterTerraformToGo(marketplaceSubscriptionTerr return marketplaceSubscription } func resourceFabricCloudRouterCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) createCloudRouterRequest := fabricv4.CloudRouterPostRequest{} @@ -329,7 +329,7 @@ func resourceFabricCloudRouterCreate(ctx context.Context, d *schema.ResourceData } func resourceFabricCloudRouterRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) cloudRouter, _, err := client.CloudRoutersApi.GetCloudRouterByUuid(ctx, d.Id()).Execute() if err != nil { log.Printf("[WARN] Fabric Cloud Router %s not found , error %s", d.Id(), err) @@ -465,7 +465,7 @@ func getCloudRouterUpdateRequests(cr *fabricv4.CloudRouter, d *schema.ResourceDa } func resourceFabricCloudRouterUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) start := time.Now() updateTimeout := d.Timeout(schema.TimeoutUpdate) - 30*time.Second - time.Since(start) dbCR, err := waitUntilCloudRouterIsProvisioned(d.Id(), meta, d, ctx, updateTimeout) @@ -505,7 +505,7 @@ func waitForCloudRouterUpdateCompletion(uuid string, meta interface{}, d *schema stateConf := &retry.StateChangeConf{ Target: []string{string(fabricv4.CLOUDROUTERACCESSPOINTSTATE_PROVISIONED)}, Refresh: func() (interface{}, string, error) { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) dbCR, _, err := client.CloudRoutersApi.GetCloudRouterByUuid(ctx, uuid).Execute() if err != nil { return "", "", equinix_errors.FormatFabricError(err) @@ -536,7 +536,7 @@ func waitUntilCloudRouterIsProvisioned(uuid string, meta interface{}, d *schema. string(fabricv4.CLOUDROUTERACCESSPOINTSTATE_PROVISIONED), }, Refresh: func() (interface{}, string, error) { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) dbCR, _, err := client.CloudRoutersApi.GetCloudRouterByUuid(ctx, uuid).Execute() if err != nil { return "", "", equinix_errors.FormatFabricError(err) @@ -559,7 +559,7 @@ func waitUntilCloudRouterIsProvisioned(uuid string, meta interface{}, d *schema. func resourceFabricCloudRouterDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { diags := diag.Diagnostics{} - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) start := time.Now() _, err := client.CloudRoutersApi.DeleteCloudRouterByUuid(ctx, d.Id()).Execute() if err != nil { @@ -592,7 +592,7 @@ func WaitUntilCloudRouterDeprovisioned(uuid string, meta interface{}, d *schema. string(fabricv4.CLOUDROUTERACCESSPOINTSTATE_DEPROVISIONED), }, Refresh: func() (interface{}, string, error) { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) dbCR, _, err := client.CloudRoutersApi.GetCloudRouterByUuid(ctx, uuid).Execute() if err != nil { return "", "", equinix_errors.FormatFabricError(err) diff --git a/equinix/resource_fabric_port.go b/equinix/resource_fabric_port.go index 85146f93f..988d1e2b3 100644 --- a/equinix/resource_fabric_port.go +++ b/equinix/resource_fabric_port.go @@ -355,7 +355,7 @@ func portEncapsulationGoToTerraform(portEncapsulation *fabricv4.PortEncapsulatio } func resourceFabricPortRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) port, _, err := client.PortsApi.GetPortByUuid(ctx, d.Id()).Execute() if err != nil { log.Printf("[WARN] Port %s not found , error %s", d.Id(), err) @@ -443,7 +443,7 @@ func resourceFabricPortGetByPortName(ctx context.Context, d *schema.ResourceData } }() - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) portNameParam := d.Get("filters").(*schema.Set).List() portName := portName(portNameParam) ports, _, err := client.PortsApi.GetPorts(ctx).Name(portName).Execute() diff --git a/equinix/resource_fabric_routing_protocol.go b/equinix/resource_fabric_routing_protocol.go index 08b0ea36c..678faa3b3 100644 --- a/equinix/resource_fabric_routing_protocol.go +++ b/equinix/resource_fabric_routing_protocol.go @@ -340,7 +340,7 @@ func resourceFabricRoutingProtocol() *schema.Resource { } func resourceFabricRoutingProtocolRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) log.Printf("[WARN] Routing Protocol Connection uuid: %s", d.Get("connection_uuid").(string)) fabricRoutingProtocolData, _, err := client.RoutingProtocolsApi.GetConnectionRoutingProtocolByUuid(ctx, d.Id(), d.Get("connection_uuid").(string)).Execute() if err != nil { @@ -357,7 +357,7 @@ func resourceFabricRoutingProtocolRead(ctx context.Context, d *schema.ResourceDa } func resourceFabricRoutingProtocolCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) start := time.Now() type_ := d.Get("type").(string) @@ -384,7 +384,7 @@ func resourceFabricRoutingProtocolCreate(ctx context.Context, d *schema.Resource } func resourceFabricRoutingProtocolUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) type_ := d.Get("type").(string) @@ -422,7 +422,7 @@ func resourceFabricRoutingProtocolUpdate(ctx context.Context, d *schema.Resource func resourceFabricRoutingProtocolDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { diags := diag.Diagnostics{} start := time.Now() - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) _, _, err := client.RoutingProtocolsApi.DeleteConnectionRoutingProtocolByUuid(ctx, d.Id(), d.Get("connection_uuid").(string)).Execute() if err != nil { if genericError, ok := err.(*fabricv4.GenericOpenAPIError); ok { @@ -642,7 +642,7 @@ func waitUntilRoutingProtocolIsProvisioned(uuid string, connUuid string, meta in string(fabricv4.CONNECTIONSTATE_PROVISIONED), }, Refresh: func() (interface{}, string, error) { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) dbConn, _, err := client.RoutingProtocolsApi.GetConnectionRoutingProtocolByUuid(ctx, uuid, connUuid).Execute() if err != nil { return "", "", equinix_errors.FormatFabricError(err) @@ -680,7 +680,7 @@ func WaitUntilRoutingProtocolIsDeprovisioned(uuid string, connUuid string, meta strconv.Itoa(404), }, Refresh: func() (interface{}, string, error) { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) dbConn, resp, _ := client.RoutingProtocolsApi.GetConnectionRoutingProtocolByUuid(ctx, uuid, connUuid).Execute() // fixme: check for error code instead? // ignore error for Target @@ -701,7 +701,7 @@ func waitForRoutingProtocolUpdateCompletion(rpChangeUuid string, uuid string, co stateConf := &retry.StateChangeConf{ Target: []string{"COMPLETED"}, Refresh: func() (interface{}, string, error) { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) dbConn, _, err := client.RoutingProtocolsApi.GetConnectionRoutingProtocolsChangeByUuid(ctx, connUuid, uuid, rpChangeUuid).Execute() if err != nil { return "", "", equinix_errors.FormatFabricError(err) diff --git a/equinix/resource_fabric_service_profile.go b/equinix/resource_fabric_service_profile.go index 288b8c147..1806b4c8e 100644 --- a/equinix/resource_fabric_service_profile.go +++ b/equinix/resource_fabric_service_profile.go @@ -554,7 +554,7 @@ Additional documentation: } func resourceFabricServiceProfileRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) serviceProfile, _, err := client.ServiceProfilesApi.GetServiceProfileByUuid(ctx, d.Id()).Execute() if err != nil { if !strings.Contains(err.Error(), "500") { @@ -567,7 +567,7 @@ func resourceFabricServiceProfileRead(ctx context.Context, d *schema.ResourceDat } func resourceFabricServiceProfileCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) createRequest := getServiceProfileRequestPayload(d) sp, _, err := client.ServiceProfilesApi.CreateServiceProfile(ctx).ServiceProfileRequest(createRequest).Execute() @@ -647,7 +647,7 @@ func getServiceProfileRequestPayload(d *schema.ResourceData) fabricv4.ServicePro } func resourceFabricServiceProfileUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) uuid := d.Id() updateRequest := getServiceProfileRequestPayload(d) @@ -685,7 +685,7 @@ func waitForServiceProfileUpdateCompletion(uuid string, meta interface{}, d *sch stateConf := &retry.StateChangeConf{ Target: []string{"COMPLETED"}, Refresh: func() (interface{}, string, error) { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) dbServiceProfile, _, err := client.ServiceProfilesApi.GetServiceProfileByUuid(ctx, uuid).Execute() if err != nil { return "", "", equinix_errors.FormatFabricError(err) @@ -713,7 +713,7 @@ func waitForActiveServiceProfileAndPopulateETag(uuid string, meta interface{}, d stateConf := &retry.StateChangeConf{ Target: []string{string(fabricv4.SERVICEPROFILESTATEENUM_ACTIVE)}, Refresh: func() (interface{}, string, error) { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) dbServiceProfile, res, err := client.ServiceProfilesApi.GetServiceProfileByUuid(ctx, uuid).Execute() if err != nil { return nil, "", equinix_errors.FormatFabricError(err) @@ -745,7 +745,7 @@ func waitForActiveServiceProfileAndPopulateETag(uuid string, meta interface{}, d func resourceFabricServiceProfileDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { diags := diag.Diagnostics{} - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) uuid := d.Id() if uuid == "" { return diag.Errorf("No uuid found for Service Profile Deletion %v ", uuid) @@ -771,7 +771,7 @@ func WaitAndCheckServiceProfileDeleted(uuid string, meta interface{}, d *schema. stateConf := &retry.StateChangeConf{ Target: []string{string(fabricv4.SERVICEPROFILESTATEENUM_DELETED)}, Refresh: func() (interface{}, string, error) { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) dbConn, _, err := client.ServiceProfilesApi.GetServiceProfileByUuid(ctx, uuid).Execute() if err != nil { return "", "", equinix_errors.FormatFabricError(err) @@ -851,7 +851,7 @@ func fabricServiceProfileMap(serviceProfile *fabricv4.ServiceProfile) map[string } func resourceServiceProfilesSearchRequest(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) serviceProfilesSearchRequest := fabricv4.ServiceProfileSearchRequest{} andFilters := false diff --git a/examples/data-sources/equinix_fabric_stream/data-source.tf b/examples/data-sources/equinix_fabric_stream/data-source.tf new file mode 100644 index 000000000..1342b3dbf --- /dev/null +++ b/examples/data-sources/equinix_fabric_stream/data-source.tf @@ -0,0 +1,7 @@ +data "equinix_fabric_stream" "data_stream" { + stream_id = "" +} + +output "stream_state" { + value = data.equinix_fabric_stream.data_stream.state +} diff --git a/examples/data-sources/equinix_fabric_streams/data-source.tf b/examples/data-sources/equinix_fabric_streams/data-source.tf new file mode 100644 index 000000000..7ffb5623b --- /dev/null +++ b/examples/data-sources/equinix_fabric_streams/data-source.tf @@ -0,0 +1,10 @@ +data "equinix_fabric_streams" "data_streams" { + pagination = { + limit = 2 + offset = 1 + } +} + +output "number_of_returned_streams" { + value = length(data.equinix_fabric_streams.data_streams.data) +} diff --git a/examples/resources/equinix_fabric_stream/resource.tf b/examples/resources/equinix_fabric_stream/resource.tf new file mode 100644 index 000000000..54351db23 --- /dev/null +++ b/examples/resources/equinix_fabric_stream/resource.tf @@ -0,0 +1,12 @@ +resource "equinix_fabric_stream" "new_stream" { + type = "TELEMETRY_STREAM" + name = "" + description = "" + project = { + project_id = "= 400 && body.StatusCode <= 499 { diff --git a/internal/resources/fabric/marketplace/datasources.go b/internal/resources/fabric/marketplace/datasources.go index 820de733d..5e4a41ed0 100644 --- a/internal/resources/fabric/marketplace/datasources.go +++ b/internal/resources/fabric/marketplace/datasources.go @@ -26,7 +26,7 @@ func dataSourceFabricMarketplaceSubscriptionRead(ctx context.Context, d *schema. } func fabricMarketplaceSubscriptionRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) subscription, _, err := client.MarketplaceSubscriptionsApi.GetSubscriptionById(ctx, d.Id()).Execute() if err != nil { log.Printf("[WARN] Subscription %s not found , error %s", d.Id(), err) diff --git a/internal/resources/fabric/network/datasources.go b/internal/resources/fabric/network/datasources.go index 28f6e4fd7..28c967174 100644 --- a/internal/resources/fabric/network/datasources.go +++ b/internal/resources/fabric/network/datasources.go @@ -51,7 +51,7 @@ func dataSourceFabricNetworkSearch(ctx context.Context, d *schema.ResourceData, } func resourceFabricNetworkSearch(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) networkSearchRequest := fabricv4.NetworkSearchRequest{} schemaFilters := d.Get("filter").([]interface{}) diff --git a/internal/resources/fabric/network/resource.go b/internal/resources/fabric/network/resource.go index de13788ac..de9811d9d 100644 --- a/internal/resources/fabric/network/resource.go +++ b/internal/resources/fabric/network/resource.go @@ -42,7 +42,7 @@ Additional documentation: } func resourceFabricNetworkCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) createRequest := fabricv4.NetworkPostRequest{} createRequest.SetName(d.Get("name").(string)) @@ -81,7 +81,7 @@ func resourceFabricNetworkCreate(ctx context.Context, d *schema.ResourceData, me } func resourceFabricNetworkRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) fabricNetwork, _, err := client.NetworksApi.GetNetworkByUuid(ctx, d.Id()).Execute() if err != nil { return diag.FromErr(equinix_errors.FormatFabricError(err)) @@ -91,7 +91,7 @@ func resourceFabricNetworkRead(ctx context.Context, d *schema.ResourceData, meta } func resourceFabricNetworkUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) start := time.Now() updateTimeout := d.Timeout(schema.TimeoutUpdate) - 30*time.Second - time.Since(start) dbConn, waitUntilNetworkProvisionedErr := waitUntilFabricNetworkIsProvisioned(d.Id(), meta, d, ctx, updateTimeout) @@ -124,7 +124,7 @@ func waitForFabricNetworkUpdateCompletion(uuid string, meta interface{}, d *sche stateConf := &retry.StateChangeConf{ Target: []string{string(fabricv4.NETWORKEQUINIXSTATUS_PROVISIONED)}, Refresh: func() (interface{}, string, error) { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) dbConn, _, err := client.NetworksApi.GetNetworkByUuid(ctx, uuid).Execute() if err != nil { return "", "", equinix_errors.FormatFabricError(err) @@ -155,7 +155,7 @@ func waitUntilFabricNetworkIsProvisioned(uuid string, meta interface{}, d *schem string(fabricv4.NETWORKEQUINIXSTATUS_PROVISIONED), }, Refresh: func() (interface{}, string, error) { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) dbConn, _, err := client.NetworksApi.GetNetworkByUuid(ctx, uuid).Execute() if err != nil { return "", "", equinix_errors.FormatFabricError(err) @@ -178,7 +178,7 @@ func waitUntilFabricNetworkIsProvisioned(uuid string, meta interface{}, d *schem func resourceFabricNetworkDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { diags := diag.Diagnostics{} - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) start := time.Now() _, _, err := client.NetworksApi.DeleteNetworkByUuid(ctx, d.Id()).Execute() if err != nil { @@ -211,7 +211,7 @@ func WaitUntilFabricNetworkDeprovisioned(uuid string, meta interface{}, d *schem string(fabricv4.NETWORKEQUINIXSTATUS_DEPROVISIONED), }, Refresh: func() (interface{}, string, error) { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) dbConn, _, err := client.NetworksApi.GetNetworkByUuid(ctx, uuid).Execute() if err != nil { return "", "", equinix_errors.FormatFabricError(err) diff --git a/internal/resources/fabric/route_filter/datasources.go b/internal/resources/fabric/route_filter/datasources.go index b0602fdb2..66dbf27a0 100644 --- a/internal/resources/fabric/route_filter/datasources.go +++ b/internal/resources/fabric/route_filter/datasources.go @@ -42,7 +42,7 @@ Additional Documentation: } func dataSourceSearch(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) searchRequest := buildSearchRequest(d) routeFilters, _, err := client.RouteFiltersApi.SearchRouteFilters(ctx).RouteFiltersSearchBase(searchRequest).Execute() diff --git a/internal/resources/fabric/route_filter/resource.go b/internal/resources/fabric/route_filter/resource.go index 60e3f291f..d662280c8 100644 --- a/internal/resources/fabric/route_filter/resource.go +++ b/internal/resources/fabric/route_filter/resource.go @@ -41,7 +41,7 @@ Additional Documentation: } func resourceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) routeFilter, _, err := client.RouteFiltersApi.GetRouteFilterByUuid(ctx, d.Id()).Execute() if err != nil { log.Printf("[WARN] Route Filter %s not found , error %s", d.Id(), err) @@ -55,7 +55,7 @@ func resourceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) } func resourceCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) createRequest := buildCreateRequest(d) start := time.Now() @@ -74,7 +74,7 @@ func resourceCreate(ctx context.Context, d *schema.ResourceData, meta interface{ } func resourceUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) updateRequest := buildUpdateRequest(d) start := time.Now() @@ -93,7 +93,7 @@ func resourceUpdate(ctx context.Context, d *schema.ResourceData, meta interface{ func resourceDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { diags := diag.Diagnostics{} - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) start := time.Now() _, _, err := client.RouteFiltersApi.DeleteRouteFilterByUuid(ctx, d.Id()).Execute() @@ -127,7 +127,7 @@ func waitForStability(uuid string, meta interface{}, d *schema.ResourceData, ctx string(fabricv4.ROUTEFILTERSTATE_PROVISIONED), }, Refresh: func() (interface{}, string, error) { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) routeFilter, _, err := client.RouteFiltersApi.GetRouteFilterByUuid(ctx, uuid).Execute() if err != nil { return "", "", equinix_errors.FormatFabricError(err) @@ -155,7 +155,7 @@ func WaitForDeletion(uuid string, meta interface{}, d *schema.ResourceData, ctx string(fabricv4.ROUTEFILTERSTATE_DEPROVISIONED), }, Refresh: func() (interface{}, string, error) { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) routeFilter, body, err := client.RouteFiltersApi.GetRouteFilterByUuid(ctx, uuid).Execute() if err != nil { if body.StatusCode >= 400 && body.StatusCode <= 499 { diff --git a/internal/resources/fabric/route_filter/sweeper.go b/internal/resources/fabric/route_filter/sweeper.go index 26ab887f5..c88773b02 100644 --- a/internal/resources/fabric/route_filter/sweeper.go +++ b/internal/resources/fabric/route_filter/sweeper.go @@ -35,7 +35,7 @@ func testSweepRouteFilters(region string) error { if configLoadErr != nil { return fmt.Errorf("error loading configuration for sweeping Route Filters: %s", err) } - fabric := meta.NewFabricClientForTesting() + fabric := meta.NewFabricClientForTesting(ctx) name := fabricv4.ROUTEFILTERSSEARCHFILTERITEMPROPERTY_NAME equinixState := fabricv4.ROUTEFILTERSSEARCHFILTERITEMPROPERTY_STATE diff --git a/internal/resources/fabric/route_filter_rule/datasources.go b/internal/resources/fabric/route_filter_rule/datasources.go index 2e03110c9..9b7ba2eac 100644 --- a/internal/resources/fabric/route_filter_rule/datasources.go +++ b/internal/resources/fabric/route_filter_rule/datasources.go @@ -42,7 +42,7 @@ Additional Documentation: } func dataSourceGetAllRules(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) routeFilterId := d.Get("route_filter_id").(string) getRouteFilterRulesRequest := client.RouteFilterRulesApi.GetRouteFilterRules(ctx, routeFilterId) diff --git a/internal/resources/fabric/route_filter_rule/resource.go b/internal/resources/fabric/route_filter_rule/resource.go index d87c6cc77..fa3e6a3ac 100644 --- a/internal/resources/fabric/route_filter_rule/resource.go +++ b/internal/resources/fabric/route_filter_rule/resource.go @@ -41,7 +41,7 @@ Additional Documentation: } func resourceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) routeFilterId := d.Get("route_filter_id").(string) routeFilterRule, _, err := client.RouteFilterRulesApi.GetRouteFilterRuleByUuid(ctx, routeFilterId, d.Id()).Execute() if err != nil { @@ -56,7 +56,7 @@ func resourceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) } func resourceCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) routeFilterId := d.Get("route_filter_id").(string) createRequest := buildCreateRequest(d) @@ -80,7 +80,7 @@ func resourceCreate(ctx context.Context, d *schema.ResourceData, meta interface{ } func resourceUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) routeFilterId := d.Get("route_filter_id").(string) updateRequest := buildUpdateRequest(d) @@ -100,7 +100,7 @@ func resourceUpdate(ctx context.Context, d *schema.ResourceData, meta interface{ func resourceDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { diags := diag.Diagnostics{} - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) routeFilterId := d.Get("route_filter_id").(string) start := time.Now() @@ -135,7 +135,7 @@ func waitForStability(routeFilterId, ruleId string, meta interface{}, d *schema. string(fabricv4.ROUTEFILTERRULESTATE_PROVISIONED), }, Refresh: func() (interface{}, string, error) { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) routeFilterRule, _, err := client.RouteFilterRulesApi.GetRouteFilterRuleByUuid(ctx, routeFilterId, ruleId).Execute() if err != nil { return "", "", equinix_errors.FormatFabricError(err) @@ -163,7 +163,7 @@ func WaitForDeletion(routeFilterId, ruleId string, meta interface{}, d *schema.R string(fabricv4.ROUTEFILTERRULESTATE_DEPROVISIONED), }, Refresh: func() (interface{}, string, error) { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) routeFilterRule, body, err := client.RouteFilterRulesApi.GetRouteFilterRuleByUuid(ctx, routeFilterId, ruleId).Execute() if err != nil { if body != nil && body.StatusCode >= 400 && body.StatusCode <= 499 { diff --git a/internal/resources/fabric/service_token/datasources.go b/internal/resources/fabric/service_token/datasources.go index 92241fcd5..8932035b4 100644 --- a/internal/resources/fabric/service_token/datasources.go +++ b/internal/resources/fabric/service_token/datasources.go @@ -41,7 +41,7 @@ Additional documentation: } func dataSourceSearch(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) searchRequest := buildSearchRequest(d) serviceTokens, _, err := client.ServiceTokensApi.SearchServiceTokens(ctx).ServiceTokenSearchRequest(searchRequest).Execute() diff --git a/internal/resources/fabric/service_token/resource.go b/internal/resources/fabric/service_token/resource.go index 7bfc90b28..9c509c840 100644 --- a/internal/resources/fabric/service_token/resource.go +++ b/internal/resources/fabric/service_token/resource.go @@ -37,7 +37,7 @@ func Resource() *schema.Resource { } func resourceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, 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) @@ -51,7 +51,7 @@ func resourceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) } func resourceCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) createRequest := buildCreateRequest(d) start := time.Now() @@ -74,7 +74,7 @@ func resourceCreate(ctx context.Context, d *schema.ResourceData, meta interface{ } func resourceUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) start := time.Now() updateTimeout := d.Timeout(schema.TimeoutUpdate) - 30*time.Second - time.Since(start) dbToken, err := waitForStability(d.Id(), meta, d, ctx, updateTimeout) @@ -108,7 +108,7 @@ func resourceUpdate(ctx context.Context, d *schema.ResourceData, meta interface{ func resourceDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { diags := diag.Diagnostics{} - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) start := time.Now() _, _, err := client.ServiceTokensApi.DeleteServiceTokenByUuid(ctx, d.Id()).Execute() @@ -138,7 +138,7 @@ func waitForStability(uuid string, meta interface{}, d *schema.ResourceData, ctx string(fabricv4.SERVICETOKENSTATE_INACTIVE), }, Refresh: func() (interface{}, string, error) { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) serviceToken, _, err := client.ServiceTokensApi.GetServiceTokenByUuid(ctx, uuid).Execute() if err != nil { return "", "", equinix_errors.FormatFabricError(err) @@ -176,7 +176,7 @@ func WaitForDeletion(uuid string, meta interface{}, d *schema.ResourceData, ctx string(fabricv4.SERVICETOKENSTATE_DELETED), }, Refresh: func() (interface{}, string, error) { - client := meta.(*config.Config).NewFabricClientForSDK(d) + client := meta.(*config.Config).NewFabricClientForSDK(ctx, d) serviceToken, body, err := client.ServiceTokensApi.GetServiceTokenByUuid(ctx, uuid).Execute() if err != nil { if body.StatusCode >= 400 && body.StatusCode <= 499 { diff --git a/internal/resources/fabric/stream/datasource_all_streams.go b/internal/resources/fabric/stream/datasource_all_streams.go new file mode 100644 index 000000000..044bb00f9 --- /dev/null +++ b/internal/resources/fabric/stream/datasource_all_streams.go @@ -0,0 +1,73 @@ +package stream + +import ( + "context" + + equinix_errors "github.com/equinix/terraform-provider-equinix/internal/errors" + "github.com/equinix/terraform-provider-equinix/internal/framework" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +func NewDataSourceAllStreams() datasource.DataSource { + return &DataSourceAllStreams{ + BaseDataSource: framework.NewBaseDataSource( + framework.BaseDataSourceConfig{ + Name: "equinix_fabric_streams", + }, + ), + } +} + +type DataSourceAllStreams struct { + framework.BaseDataSource +} + +func (r *DataSourceAllStreams) Schema( + ctx context.Context, + _ datasource.SchemaRequest, + resp *datasource.SchemaResponse, +) { + resp.Schema = dataSourceAllStreamsSchema(ctx) +} + +func (r *DataSourceAllStreams) Read(ctx context.Context, request datasource.ReadRequest, response *datasource.ReadResponse) { + client := r.Meta.NewFabricClientForFramework(ctx, request.ProviderMeta) + + // Retrieve values from plan + var data DataSourceAllStreamsModel + response.Diagnostics.Append(request.Config.Get(ctx, &data)...) + if response.Diagnostics.HasError() { + return + } + + var pagination PaginationModel + diags := data.Pagination.As(ctx, &pagination, basetypes.ObjectAsOptions{}) + if diags.HasError() { + return + } + offset := pagination.Offset.ValueInt32() + limit := pagination.Limit.ValueInt32() + if limit == 0 { + limit = 20 + } + + // Use API client to get the current state of the resource + streams, _, err := client.StreamsApi.GetStreams(ctx).Limit(limit).Offset(offset).Execute() + + if err != nil { + response.State.RemoveResource(ctx) + response.Diagnostics.AddError("api error retrieving streams data", equinix_errors.FormatFabricError(err).Error()) + return + } + + // Set state to fully populated data + response.Diagnostics.Append(data.parse(ctx, streams)...) + if response.Diagnostics.HasError() { + return + } + + // Update the Terraform state + response.Diagnostics.Append(response.State.Set(ctx, &data)...) +} diff --git a/internal/resources/fabric/stream/datasource_by_streamid.go b/internal/resources/fabric/stream/datasource_by_streamid.go new file mode 100644 index 000000000..0ba6e2c6b --- /dev/null +++ b/internal/resources/fabric/stream/datasource_by_streamid.go @@ -0,0 +1,63 @@ +package stream + +import ( + "context" + + equinix_errors "github.com/equinix/terraform-provider-equinix/internal/errors" + "github.com/equinix/terraform-provider-equinix/internal/framework" + + "github.com/hashicorp/terraform-plugin-framework/datasource" +) + +func NewDataSourceByStreamID() datasource.DataSource { + return &DataSourceByStreamID{ + BaseDataSource: framework.NewBaseDataSource( + framework.BaseDataSourceConfig{ + Name: "equinix_fabric_stream", + }, + ), + } +} + +type DataSourceByStreamID struct { + framework.BaseDataSource +} + +func (r *DataSourceByStreamID) Schema( + ctx context.Context, + _ datasource.SchemaRequest, + resp *datasource.SchemaResponse, +) { + resp.Schema = dataSourceSingleStreamSchema(ctx) +} + +func (r *DataSourceByStreamID) Read(ctx context.Context, request datasource.ReadRequest, response *datasource.ReadResponse) { + client := r.Meta.NewFabricClientForFramework(ctx, request.ProviderMeta) + + // Retrieve values from plan + var data DataSourceByIDModel + response.Diagnostics.Append(request.Config.Get(ctx, &data)...) + if response.Diagnostics.HasError() { + return + } + + streamID := data.StreamID.ValueString() + + // Use API client to get the current state of the resource + stream, _, err := client.StreamsApi.GetStreamByUuid(ctx, streamID).Execute() + + if err != nil { + response.State.RemoveResource(ctx) + response.Diagnostics.AddError("api error retrieving stream data", equinix_errors.FormatFabricError(err).Error()) + return + } + + // Set state to fully populated data + response.Diagnostics.Append(data.parse(ctx, stream)...) + if response.Diagnostics.HasError() { + return + } + + // Update the Terraform state + response.Diagnostics.Append(response.State.Set(ctx, &data)...) +} diff --git a/internal/resources/fabric/stream/datasources_schema.go b/internal/resources/fabric/stream/datasources_schema.go new file mode 100644 index 000000000..9362a0522 --- /dev/null +++ b/internal/resources/fabric/stream/datasources_schema.go @@ -0,0 +1,180 @@ +package stream + +import ( + "context" + + "github.com/equinix/terraform-provider-equinix/internal/framework" + fwtypes "github.com/equinix/terraform-provider-equinix/internal/framework/types" + + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" +) + +func dataSourceAllStreamsSchema(ctx context.Context) schema.Schema { + return schema.Schema{ + Description: `Fabric V4 API compatible data resource that allow user to fetch Equinix Fabric Streams with pagination details + +Additional Documentation: +* Getting Started: https://docs.equinix.com/en-us/Content/KnowledgeCenter/Fabric/GettingStarted/Integrating-with-Fabric-V4-APIs/IntegrateWithSink.htm +* API: https://developer.equinix.com/catalog/fabricv4#tag/Streams`, + Attributes: map[string]schema.Attribute{ + "id": framework.IDAttributeDefaultDescription(), + "pagination": schema.SingleNestedAttribute{ + Description: "Pagination details for the returned streams list", + Required: true, + CustomType: fwtypes.NewObjectTypeOf[PaginationModel](ctx), + Attributes: map[string]schema.Attribute{ + "offset": schema.Int32Attribute{ + Description: "Index of the first item returned in the response. The default is 0", + Optional: true, + Computed: true, + }, + "limit": schema.Int32Attribute{ + Description: "Maximum number of search results returned per page. Number must be between 1 and 100, and the default is 20", + Optional: true, + Computed: true, + }, + "total": schema.Int32Attribute{ + Description: "The total number of streams available to the user making the request", + Computed: true, + }, + "next": schema.StringAttribute{ + Description: "The URL relative to the next item in the response", + Computed: true, + }, + "previous": schema.StringAttribute{ + Description: "The URL relative to the previous item in the response", + Computed: true, + }, + }, + }, + "data": schema.ListNestedAttribute{ + Description: "Returned list of stream objects", + Computed: true, + CustomType: fwtypes.NewListNestedObjectTypeOf[BaseStreamModel](ctx), + NestedObject: schema.NestedAttributeObject{ + Attributes: getStreamSchema(ctx), + }, + }, + }, + } +} + +func dataSourceSingleStreamSchema(ctx context.Context) schema.Schema { + baseStreamSchema := getStreamSchema(ctx) + baseStreamSchema["id"] = framework.IDAttributeDefaultDescription() + baseStreamSchema["stream_id"] = schema.StringAttribute{ + Description: "The uuid of the stream this data source should retrieve", + Required: true, + } + return schema.Schema{ + Description: `Fabric V4 API compatible data resource that allow user to fetch Equinix Fabric Stream by UUID + +Additional Documentation: +* Getting Started: https://docs.equinix.com/en-us/Content/KnowledgeCenter/Fabric/GettingStarted/Integrating-with-Fabric-V4-APIs/IntegrateWithSink.htm +* API: https://developer.equinix.com/catalog/fabricv4#tag/Streams`, + Attributes: baseStreamSchema, + } +} + +func getStreamSchema(ctx context.Context) map[string]schema.Attribute { + return map[string]schema.Attribute{ + "type": schema.StringAttribute{ + Description: "Equinix defined Streaming Type", + Computed: true, + }, + "name": schema.StringAttribute{ + Description: "Customer-provided name of the stream resource", + Computed: true, + }, + "description": schema.StringAttribute{ + Description: "Customer-provided description of the stream resource", + Computed: true, + }, + "project": schema.SingleNestedAttribute{ + Description: "Equinix Project attribute object", + Computed: true, + CustomType: fwtypes.NewObjectTypeOf[ProjectModel](ctx), + Attributes: map[string]schema.Attribute{ + "project_id": schema.StringAttribute{ + Description: "Equinix Subscriber-assigned project ID", + Computed: true, + }, + }, + }, + "href": schema.StringAttribute{ + Description: "Equinix auto generated URI to the stream resource in Equinix Portal", + Computed: true, + }, + "uuid": schema.StringAttribute{ + Description: "Equinix-assigned unique id for the stream resource", + Computed: true, + }, + "state": schema.StringAttribute{ + Description: "Value representing provisioning status for the stream resource", + Computed: true, + }, + "assets_count": schema.Int32Attribute{ + Description: "Count of the streaming assets attached to the stream resource", + Computed: true, + }, + "stream_subscriptions_count": schema.Int32Attribute{ + Description: "Count of the client subscriptions on the stream resource", + Computed: true, + }, + "change_log": schema.SingleNestedAttribute{ + Description: "Details of the last change on the stream resource", + Computed: true, + CustomType: fwtypes.NewObjectTypeOf[ChangeLogModel](ctx), + Attributes: map[string]schema.Attribute{ + "created_by": schema.StringAttribute{ + Description: "User name of creator of the stream resource", + Computed: true, + }, + "created_by_full_name": schema.StringAttribute{ + Description: "Legal name of creator of the stream resource", + Computed: true, + }, + "created_by_email": schema.StringAttribute{ + Description: "Email of creator of the stream resource", + Computed: true, + }, + "created_date_time": schema.StringAttribute{ + Description: "Creation time of the stream resource", + Computed: true, + }, + "updated_by": schema.StringAttribute{ + Description: "User name of last updater of the stream resource", + Computed: true, + }, + "updated_by_full_name": schema.StringAttribute{ + Description: "Legal name of last updater of the stream resource", + Computed: true, + }, + "updated_by_email": schema.StringAttribute{ + Description: "Email of last updater of the stream resource", + Computed: true, + }, + "updated_date_time": schema.StringAttribute{ + Description: "Last update time of the stream resource", + Computed: true, + }, + "deleted_by": schema.StringAttribute{ + Description: "User name of deleter of the stream resource", + Computed: true, + }, + "deleted_by_full_name": schema.StringAttribute{ + Description: "Legal name of deleter of the stream resource", + Computed: true, + }, + "deleted_by_email": schema.StringAttribute{ + Description: "Email of deleter of the stream resource", + Computed: true, + }, + "deleted_date_time": schema.StringAttribute{ + Description: "Deletion time of the stream resource", + Computed: true, + }, + }, + }, + } +} diff --git a/internal/resources/fabric/stream/datasources_test.go b/internal/resources/fabric/stream/datasources_test.go new file mode 100644 index 000000000..c9d3666ef --- /dev/null +++ b/internal/resources/fabric/stream/datasources_test.go @@ -0,0 +1,96 @@ +package stream_test + +import ( + "fmt" + "testing" + + "github.com/equinix/terraform-provider-equinix/internal/acceptance" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func testAccFabricStreamDataSourcesConfig(name, description string) string { + return fmt.Sprintf(` + + resource "equinix_fabric_stream" "new_stream_1" { + type = "TELEMETRY_STREAM" + name = "%[1]s" + description = "%[2]s" + } + + resource "equinix_fabric_stream" "new_stream_2" { + type = "TELEMETRY_STREAM" + name = "%[1]s" + description = "%[2]s" + } + + resource "equinix_fabric_stream" "new_stream_3" { + type = "TELEMETRY_STREAM" + name = "%[1]s" + description = "%[2]s" + project = { + project_id = "291639000636552" + } + } + + data "equinix_fabric_stream" "data_stream" { + stream_id = equinix_fabric_stream.new_stream_3.id + } + + data "equinix_fabric_streams" "data_streams" { + depends_on = [equinix_fabric_stream.new_stream_1, equinix_fabric_stream.new_stream_2, equinix_fabric_stream.new_stream_3] + pagination = { + limit = 2 + offset = 1 + } + } + `, name, description) +} + +func TestAccFabricStreamDataSources_PFCR(t *testing.T) { + streamName := "stream_PFCR" + streamDescription := "stream acceptance test PFCR" + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.TestAccPreCheck(t); acceptance.TestAccPreCheckProviderConfigured(t) }, + ExternalProviders: acceptance.TestExternalProviders, + ProtoV6ProviderFactories: acceptance.ProtoV6ProviderFactories, + CheckDestroy: CheckStreamDelete, + Steps: []resource.TestStep{ + { + Config: testAccFabricStreamDataSourcesConfig(streamName, streamDescription), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "data.equinix_fabric_stream.data_stream", "name", streamName), + resource.TestCheckResourceAttr( + "data.equinix_fabric_stream.data_stream", "type", "TELEMETRY_STREAM"), + resource.TestCheckResourceAttr( + "data.equinix_fabric_stream.data_stream", "project.project_id", "291639000636552"), + resource.TestCheckResourceAttr( + "data.equinix_fabric_stream.data_stream", "description", streamDescription), + resource.TestCheckResourceAttrSet("data.equinix_fabric_stream.data_stream", "id"), + resource.TestCheckResourceAttrSet("data.equinix_fabric_stream.data_stream", "href"), + resource.TestCheckResourceAttrSet("data.equinix_fabric_stream.data_stream", "assets_count"), + resource.TestCheckResourceAttrSet("data.equinix_fabric_stream.data_stream", "stream_subscriptions_count"), + resource.TestCheckResourceAttrSet("data.equinix_fabric_stream.data_stream", "uuid"), + resource.TestCheckResourceAttrSet("data.equinix_fabric_stream.data_stream", "change_log.created_by"), + resource.TestCheckResourceAttrSet("data.equinix_fabric_streams.data_streams", "id"), + resource.TestCheckResourceAttrSet("data.equinix_fabric_streams.data_streams", "data.0.name"), + resource.TestCheckResourceAttrSet("data.equinix_fabric_streams.data_streams", "data.0.type"), + resource.TestCheckResourceAttrSet("data.equinix_fabric_streams.data_streams", "data.0.project.project_id"), + resource.TestCheckResourceAttrSet("data.equinix_fabric_streams.data_streams", "data.0.description"), + resource.TestCheckResourceAttrSet("data.equinix_fabric_streams.data_streams", "data.0.project.project_id"), + resource.TestCheckResourceAttrSet("data.equinix_fabric_streams.data_streams", "data.0.href"), + resource.TestCheckResourceAttrSet("data.equinix_fabric_streams.data_streams", "data.0.assets_count"), + resource.TestCheckResourceAttrSet("data.equinix_fabric_streams.data_streams", "data.0.stream_subscriptions_count"), + resource.TestCheckResourceAttrSet("data.equinix_fabric_streams.data_streams", "data.0.uuid"), + resource.TestCheckResourceAttrSet("data.equinix_fabric_streams.data_streams", "data.0.change_log.created_by"), + resource.TestCheckResourceAttr("data.equinix_fabric_streams.data_streams", "data.#", "2"), + resource.TestCheckResourceAttr("data.equinix_fabric_streams.data_streams", "pagination.%", "5"), + resource.TestCheckResourceAttr("data.equinix_fabric_streams.data_streams", "pagination.limit", "2"), + resource.TestCheckResourceAttr("data.equinix_fabric_streams.data_streams", "pagination.offset", "1"), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) + +} diff --git a/internal/resources/fabric/stream/models.go b/internal/resources/fabric/stream/models.go new file mode 100644 index 000000000..6c9e2b806 --- /dev/null +++ b/internal/resources/fabric/stream/models.go @@ -0,0 +1,213 @@ +package stream + +import ( + "context" + + "github.com/equinix/terraform-provider-equinix/internal/fabric" + fwtypes "github.com/equinix/terraform-provider-equinix/internal/framework/types" + + "github.com/equinix/equinix-sdk-go/services/fabricv4" + + "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +type DataSourceByIDModel struct { + StreamID types.String `tfsdk:"stream_id"` + ID types.String `tfsdk:"id"` + BaseStreamModel +} + +type DataSourceAllStreamsModel struct { + ID types.String `tfsdk:"id"` + Pagination fwtypes.ObjectValueOf[PaginationModel] `tfsdk:"pagination"` + Data fwtypes.ListNestedObjectValueOf[BaseStreamModel] `tfsdk:"data"` +} + +type PaginationModel struct { + Offset types.Int32 `tfsdk:"offset"` + Limit types.Int32 `tfsdk:"limit"` + Total types.Int32 `tfsdk:"total"` + Next types.String `tfsdk:"next"` + Previous types.String `tfsdk:"previous"` +} + +type ResourceModel struct { + ID types.String `tfsdk:"id"` + Timeouts timeouts.Value `tfsdk:"timeouts"` + BaseStreamModel +} + +type BaseStreamModel struct { + Type types.String `tfsdk:"type"` + Name types.String `tfsdk:"name"` + Description types.String `tfsdk:"description"` + Href types.String `tfsdk:"href"` + UUID types.String `tfsdk:"uuid"` + State types.String `tfsdk:"state"` + AssetsCount types.Int32 `tfsdk:"assets_count"` + StreamSubscriptionsCount types.Int32 `tfsdk:"stream_subscriptions_count"` + Project fwtypes.ObjectValueOf[ProjectModel] `tfsdk:"project"` // Object of ProjectModel + ChangeLog fwtypes.ObjectValueOf[ChangeLogModel] `tfsdk:"change_log"` // Object of ChangeLogModel +} + +type ProjectModel struct { + ProjectID types.String `tfsdk:"project_id"` +} + +type ChangeLogModel struct { + CreatedBy types.String `tfsdk:"created_by"` + CreatedByFullName types.String `tfsdk:"created_by_full_name"` + CreatedByEmail types.String `tfsdk:"created_by_email"` + CreatedDateTime types.String `tfsdk:"created_date_time"` + UpdatedBy types.String `tfsdk:"updated_by"` + UpdatedByFullName types.String `tfsdk:"updated_by_full_name"` + UpdatedByEmail types.String `tfsdk:"updated_by_email"` + UpdatedDateTime types.String `tfsdk:"updated_date_time"` + DeletedBy types.String `tfsdk:"deleted_by"` + DeletedByFullName types.String `tfsdk:"deleted_by_full_name"` + DeletedByEmail types.String `tfsdk:"deleted_by_email"` + DeletedDateTime types.String `tfsdk:"deleted_date_time"` +} + +func (m *DataSourceByIDModel) parse(ctx context.Context, stream *fabricv4.Stream) diag.Diagnostics { + + m.StreamID = types.StringValue(stream.GetUuid()) + m.ID = types.StringValue(stream.GetUuid()) + + diags := parseStream(ctx, stream, + &m.Type, + &m.Name, + &m.Description, + &m.Href, + &m.UUID, + &m.State, + &m.AssetsCount, + &m.StreamSubscriptionsCount, + &m.Project, + &m.ChangeLog) + if diags.HasError() { + return diags + } + + return diags +} + +func (m *DataSourceAllStreamsModel) parse(ctx context.Context, streamsResponse *fabricv4.GetAllStreamResponse) diag.Diagnostics { + var diags diag.Diagnostics + + if len(streamsResponse.GetData()) < 1 { + diags.AddError("no data retrieved by streams data source", + "either the account does not have any streams data to pull or the combination of limit and offset needs to be updated") + return diags + } + + data := make([]BaseStreamModel, len(streamsResponse.GetData())) + streams := streamsResponse.GetData() + for index, stream := range streams { + var streamModel BaseStreamModel + diags = streamModel.parse(ctx, &stream) + if diags.HasError() { + return diags + } + data[index] = streamModel + } + responsePagination := streamsResponse.GetPagination() + pagination := PaginationModel{ + Offset: types.Int32Value(responsePagination.GetOffset()), + Limit: types.Int32Value(responsePagination.GetLimit()), + Total: types.Int32Value(responsePagination.GetTotal()), + Next: types.StringValue(responsePagination.GetNext()), + Previous: types.StringValue(responsePagination.GetPrevious()), + } + + m.ID = types.StringValue(data[0].UUID.ValueString()) + m.Pagination = fwtypes.NewObjectValueOf[PaginationModel](ctx, &pagination) + m.Data = fwtypes.NewListNestedObjectValueOfValueSlice[BaseStreamModel](ctx, data) + + return diags +} + +func (m *ResourceModel) parse(ctx context.Context, stream *fabricv4.Stream) diag.Diagnostics { + m.ID = types.StringValue(stream.GetUuid()) + + diags := parseStream(ctx, stream, + &m.Type, + &m.Name, + &m.Description, + &m.Href, + &m.UUID, + &m.State, + &m.AssetsCount, + &m.StreamSubscriptionsCount, + &m.Project, + &m.ChangeLog) + if diags.HasError() { + return diags + } + + return diags +} + +func (m *BaseStreamModel) parse(ctx context.Context, stream *fabricv4.Stream) diag.Diagnostics { + diags := parseStream(ctx, stream, + &m.Type, + &m.Name, + &m.Description, + &m.Href, + &m.UUID, + &m.State, + &m.AssetsCount, + &m.StreamSubscriptionsCount, + &m.Project, + &m.ChangeLog) + if diags.HasError() { + return diags + } + + return diags +} + +func parseStream(ctx context.Context, stream *fabricv4.Stream, + streamType, name, description, href, uuid, state *basetypes.StringValue, + assetsCount, streamSubscriptionCount *basetypes.Int32Value, + project *fwtypes.ObjectValueOf[ProjectModel], + changeLog *fwtypes.ObjectValueOf[ChangeLogModel]) diag.Diagnostics { + + var diag diag.Diagnostics + + *streamType = types.StringValue(string(stream.GetType())) + *name = types.StringValue(stream.GetName()) + *description = types.StringValue(stream.GetDescription()) + *href = types.StringValue(stream.GetHref()) + *uuid = types.StringValue(stream.GetUuid()) + *state = types.StringValue(stream.GetState()) + *assetsCount = types.Int32Value(stream.GetAssetsCount()) + *streamSubscriptionCount = types.Int32Value(stream.GetStreamSubscriptionsCount()) + + streamProject := stream.GetProject() + projectModel := ProjectModel{ + ProjectID: types.StringValue(streamProject.GetProjectId()), + } + *project = fwtypes.NewObjectValueOf[ProjectModel](ctx, &projectModel) + + streamChangeLog := stream.GetChangeLog() + changeLogModel := ChangeLogModel{ + CreatedBy: types.StringValue(streamChangeLog.GetCreatedBy()), + CreatedByFullName: types.StringValue(streamChangeLog.GetCreatedByFullName()), + CreatedByEmail: types.StringValue(streamChangeLog.GetCreatedByEmail()), + CreatedDateTime: types.StringValue(streamChangeLog.GetCreatedDateTime().Format(fabric.TimeFormat)), + UpdatedBy: types.StringValue(streamChangeLog.GetUpdatedBy()), + UpdatedByFullName: types.StringValue(streamChangeLog.GetUpdatedByFullName()), + UpdatedByEmail: types.StringValue(streamChangeLog.GetUpdatedByEmail()), + UpdatedDateTime: types.StringValue(streamChangeLog.GetUpdatedDateTime().Format(fabric.TimeFormat)), + DeletedBy: types.StringValue(streamChangeLog.GetDeletedBy()), + DeletedByFullName: types.StringValue(streamChangeLog.GetDeletedByFullName()), + DeletedByEmail: types.StringValue(streamChangeLog.GetDeletedByEmail()), + DeletedDateTime: types.StringValue(streamChangeLog.GetDeletedDateTime().Format(fabric.TimeFormat)), + } + *changeLog = fwtypes.NewObjectValueOf[ChangeLogModel](ctx, &changeLogModel) + return diag +} diff --git a/internal/resources/fabric/stream/resource.go b/internal/resources/fabric/stream/resource.go new file mode 100644 index 000000000..7f9d9beba --- /dev/null +++ b/internal/resources/fabric/stream/resource.go @@ -0,0 +1,305 @@ +package stream + +import ( + "context" + "fmt" + "net/http" + "slices" + "time" + + equinix_errors "github.com/equinix/terraform-provider-equinix/internal/errors" + "github.com/equinix/terraform-provider-equinix/internal/framework" + + "github.com/equinix/equinix-sdk-go/services/fabricv4" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" +) + +func NewResource() resource.Resource { + return &Resource{ + BaseResource: framework.NewBaseResource( + framework.BaseResourceConfig{ + Name: "equinix_fabric_stream", + }, + ), + } +} + +type Resource struct { + framework.BaseResource +} + +func (r *Resource) Schema( + ctx context.Context, + _ resource.SchemaRequest, + resp *resource.SchemaResponse, +) { + resp.Schema = resourceSchema(ctx) +} + +func (r *Resource) Create( + ctx context.Context, + req resource.CreateRequest, + resp *resource.CreateResponse, +) { + + var plan ResourceModel + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + // Retrieve the API client from the provider metadata + client := r.Meta.NewFabricClientForFramework(ctx, req.ProviderMeta) + + createRequest, diags := buildCreateRequest(ctx, plan) + if diags.HasError() { + return + } + + stream, _, err := client.StreamsApi.CreateStreams(ctx).StreamPostRequest(createRequest).Execute() + if err != nil { + resp.Diagnostics.AddError("Failed creating Stream", equinix_errors.FormatFabricError(err).Error()) + return + } + + createTimeout, diags := plan.Timeouts.Create(ctx, 10*time.Minute) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + createWaiter := getCreateUpdateWaiter(ctx, client, stream.GetUuid(), createTimeout) + streamChecked, err := createWaiter.WaitForStateContext(ctx) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Failed creating Stream %s", stream.GetUuid()), err.Error()) + return + } + + resp.Diagnostics.Append(diags...) + if diags.HasError() { + return + } + + // Parse API response into the Terraform state + resp.Diagnostics.Append(plan.parse(ctx, streamChecked.(*fabricv4.Stream))...) + if resp.Diagnostics.HasError() { + return + } + + // Set state to fully populated data + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *Resource) Read( + ctx context.Context, + req resource.ReadRequest, + resp *resource.ReadResponse, +) { + var state ResourceModel + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + // Retrieve the API client from the provider metadata + client := r.Meta.NewFabricClientForFramework(ctx, req.ProviderMeta) + + // Extract the ID of the resource from the state + id := state.ID.ValueString() + + stream, _, err := client.StreamsApi.GetStreamByUuid(ctx, id).Execute() + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Failed retrieving Stream %s", id), equinix_errors.FormatFabricError(err).Error()) + return + } + + // Set state to fully populated data + resp.Diagnostics.Append(state.parse(ctx, stream)...) + if resp.Diagnostics.HasError() { + return + } + + // Update the Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *Resource) Update( + ctx context.Context, + req resource.UpdateRequest, + resp *resource.UpdateResponse, +) { + client := r.Meta.NewFabricClientForFramework(ctx, req.ProviderMeta) + + // Retrieve values from plan + var state, plan ResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + id := state.ID.ValueString() + + updateRequest := fabricv4.StreamPutRequest{} + + needsUpdate := plan.Name.ValueString() != state.Name.ValueString() || + plan.Description.ValueString() != state.Description.ValueString() + + if !needsUpdate { + resp.Diagnostics.AddWarning("No updatable fields have changed", + "Terraform detected a config change, but it is for a field that isn't updatable for the stream resource. Please revert to prior config") + return + } + + updateRequest.SetName(plan.Name.ValueString()) + updateRequest.SetDescription(plan.Description.ValueString()) + + _, _, err := client.StreamsApi.UpdateStreamByUuid(ctx, id).StreamPutRequest(updateRequest).Execute() + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Failed updating Stream %s", id), equinix_errors.FormatFabricError(err).Error()) + return + } + + updateTimeout, diags := plan.Timeouts.Update(ctx, 10*time.Minute) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + + updateWaiter := getCreateUpdateWaiter(ctx, client, id, updateTimeout) + streamChecked, err := updateWaiter.WaitForStateContext(ctx) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Failed updating Stream %s", id), err.Error()) + return + } + + // Set state to fully populated data + resp.Diagnostics.Append(plan.parse(ctx, streamChecked.(*fabricv4.Stream))...) + if resp.Diagnostics.HasError() { + return + } + + // Set the updated state back into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *Resource) Delete( + ctx context.Context, + req resource.DeleteRequest, + resp *resource.DeleteResponse, +) { + // Retrieve the API client + client := r.Meta.NewFabricClientForFramework(ctx, req.ProviderMeta) + + // Retrieve the current state + var state ResourceModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + id := state.ID.ValueString() + + _, deleteResp, err := client.StreamsApi.DeleteStreamByUuid(ctx, id).Execute() + if err != nil { + if deleteResp == nil || !slices.Contains([]int{http.StatusForbidden, http.StatusNotFound}, deleteResp.StatusCode) { + resp.Diagnostics.AddError( + fmt.Sprintf("Failed deleting Stream %s", id), equinix_errors.FormatFabricError(err).Error()) + return + } + } + + deleteTimeout, diags := state.Timeouts.Delete(ctx, 10*time.Minute) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + deleteWaiter := getDeleteWaiter(ctx, client, id, deleteTimeout) + _, err = deleteWaiter.WaitForStateContext(ctx) + + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Failed deleting Stream %s", id), err.Error()) + return + } + +} + +func buildCreateRequest(ctx context.Context, plan ResourceModel) (fabricv4.StreamPostRequest, diag.Diagnostics) { + var diags diag.Diagnostics + request := fabricv4.StreamPostRequest{} + + request.SetName(plan.Name.ValueString()) + request.SetType(fabricv4.StreamPostRequestType(plan.Type.ValueString())) + request.SetDescription(plan.Description.ValueString()) + + var project ProjectModel + if !plan.Project.IsNull() && !plan.Project.IsUnknown() { + diags = plan.Project.As(ctx, &project, basetypes.ObjectAsOptions{}) + if diags.HasError() { + return fabricv4.StreamPostRequest{}, diags + } + request.SetProject(fabricv4.Project{ProjectId: project.ProjectID.ValueString()}) + } + + return request, diags +} + +func getCreateUpdateWaiter(ctx context.Context, client *fabricv4.APIClient, id string, timeout time.Duration) *retry.StateChangeConf { + return &retry.StateChangeConf{ + Pending: []string{ + string(fabricv4.STREAMSUBSCRIPTIONSTATE_PROVISIONING), + }, + Target: []string{ + string(fabricv4.STREAMSUBSCRIPTIONSTATE_PROVISIONED), + }, + Refresh: func() (interface{}, string, error) { + stream, _, err := client.StreamsApi.GetStreamByUuid(ctx, id).Execute() + if err != nil { + return 0, "", err + } + return stream, stream.GetState(), nil + }, + Timeout: timeout, + Delay: 10 * time.Second, + MinTimeout: 5 * time.Second, + } +} + +func getDeleteWaiter(ctx context.Context, client *fabricv4.APIClient, id string, timeout time.Duration) *retry.StateChangeConf { + // deletedMarker is a terraform-provider-only value that is used by the waiter + // to indicate that the connection appears to be deleted successfully based on + // status code + deletedMarker := "tf-marker-for-deleted-connection" + return &retry.StateChangeConf{ + Pending: []string{ + string(fabricv4.STREAMSUBSCRIPTIONSTATE_DEPROVISIONING), + }, + Target: []string{ + deletedMarker, + string(fabricv4.STREAMSUBSCRIPTIONSTATE_DEPROVISIONED), + }, + Refresh: func() (interface{}, string, error) { + stream, resp, err := client.StreamsApi.GetStreamByUuid(ctx, id).Execute() + if err != nil { + if resp != nil && slices.Contains([]int{http.StatusForbidden, http.StatusNotFound}, resp.StatusCode) { + return stream, deletedMarker, nil + } + return 0, "", err + } + return stream, stream.GetState(), nil + }, + Timeout: timeout, + Delay: 10 * time.Second, + MinTimeout: 5 * time.Second, + } +} diff --git a/internal/resources/fabric/stream/resource_schema.go b/internal/resources/fabric/stream/resource_schema.go new file mode 100644 index 000000000..6cc6a0a9d --- /dev/null +++ b/internal/resources/fabric/stream/resource_schema.go @@ -0,0 +1,141 @@ +package stream + +import ( + "context" + + "github.com/equinix/terraform-provider-equinix/internal/framework" + fwtypes "github.com/equinix/terraform-provider-equinix/internal/framework/types" + + "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" +) + +func resourceSchema(ctx context.Context) schema.Schema { + return schema.Schema{ + Description: `Fabric V4 API compatible resource allows creation and management of Equinix Fabric Stream + +Additional Documentation: +* Getting Started: https://docs.equinix.com/en-us/Content/KnowledgeCenter/Fabric/GettingStarted/Integrating-with-Fabric-V4-APIs/IntegrateWithSink.htm +* API: https://developer.equinix.com/catalog/fabricv4#tag/Streams`, + Attributes: map[string]schema.Attribute{ + "id": framework.IDAttributeDefaultDescription(), + "timeouts": timeouts.Attributes(ctx, timeouts.Opts{ + Create: true, + Read: true, + Update: true, + Delete: true, + }), + "type": schema.StringAttribute{ + Description: "Equinix defined Streaming Type", + Required: true, + }, + "name": schema.StringAttribute{ + Description: "Customer-provided name of the stream resource", + Required: true, + }, + "description": schema.StringAttribute{ + Description: "Customer-provided description of the stream resource", + Required: true, + }, + "project": schema.SingleNestedAttribute{ + Description: "Equinix Project attribute object", + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Object{ + objectplanmodifier.UseStateForUnknown(), + }, + CustomType: fwtypes.NewObjectTypeOf[ProjectModel](ctx), + Attributes: map[string]schema.Attribute{ + "project_id": schema.StringAttribute{ + Description: "Equinix Subscriber-assigned project ID", + Required: true, + }, + }, + }, + "href": schema.StringAttribute{ + Description: "Equinix auto generated URI to the stream resource in Equinix Portal", + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "uuid": schema.StringAttribute{ + Description: "Equinix-assigned unique id for the stream resource", + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "state": schema.StringAttribute{ + Description: "Value representing provisioning status for the stream resource", + Computed: true, + }, + "assets_count": schema.Int32Attribute{ + Description: "Count of the streaming assets attached to the stream resource", + Computed: true, + }, + "stream_subscriptions_count": schema.Int32Attribute{ + Description: "Count of the client subscriptions on the stream resource", + Computed: true, + }, + "change_log": schema.SingleNestedAttribute{ + Description: "Details of the last change on the stream resource", + Computed: true, + CustomType: fwtypes.NewObjectTypeOf[ChangeLogModel](ctx), + Attributes: map[string]schema.Attribute{ + "created_by": schema.StringAttribute{ + Description: "User name of creator of the stream resource", + Computed: true, + }, + "created_by_full_name": schema.StringAttribute{ + Description: "Legal name of creator of the stream resource", + Computed: true, + }, + "created_by_email": schema.StringAttribute{ + Description: "Email of creator of the stream resource", + Computed: true, + }, + "created_date_time": schema.StringAttribute{ + Description: "Creation time of the stream resource", + Computed: true, + }, + "updated_by": schema.StringAttribute{ + Description: "User name of last updater of the stream resource", + Computed: true, + }, + "updated_by_full_name": schema.StringAttribute{ + Description: "Legal name of last updater of the stream resource", + Computed: true, + }, + "updated_by_email": schema.StringAttribute{ + Description: "Email of last updater of the stream resource", + Computed: true, + }, + "updated_date_time": schema.StringAttribute{ + Description: "Last update time of the stream resource", + Computed: true, + }, + "deleted_by": schema.StringAttribute{ + Description: "User name of deleter of the stream resource", + Computed: true, + }, + "deleted_by_full_name": schema.StringAttribute{ + Description: "Legal name of deleter of the stream resource", + Computed: true, + }, + "deleted_by_email": schema.StringAttribute{ + Description: "Email of deleter of the stream resource", + Computed: true, + }, + "deleted_date_time": schema.StringAttribute{ + Description: "Deletion time of the stream resource", + Computed: true, + }, + }, + }, + }, + } +} diff --git a/internal/resources/fabric/stream/resource_test.go b/internal/resources/fabric/stream/resource_test.go new file mode 100644 index 000000000..c3c817b53 --- /dev/null +++ b/internal/resources/fabric/stream/resource_test.go @@ -0,0 +1,96 @@ +package stream_test + +import ( + "context" + "fmt" + "testing" + + "github.com/equinix/terraform-provider-equinix/internal/acceptance" + "github.com/equinix/terraform-provider-equinix/internal/config" + + "github.com/equinix/equinix-sdk-go/services/fabricv4" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" +) + +func CheckStreamDelete(s *terraform.State) error { + ctx := context.Background() + client := acceptance.TestAccProvider.Meta().(*config.Config).NewFabricClientForTesting(ctx) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "equinix_fabric_stream" { + continue + } + + if stream, _, err := client.StreamsApi.GetStreamByUuid(ctx, rs.Primary.ID).Execute(); err == nil && + stream.GetState() == string(fabricv4.STREAMSUBSCRIPTIONSTATE_PROVISIONED) { + return fmt.Errorf("fabric stream %s still exists and is %s", + rs.Primary.ID, string(fabricv4.STREAMSUBSCRIPTIONSTATE_PROVISIONED)) + } + } + return nil +} + +func testAccFabricStreamConfig(name, description string) string { + return fmt.Sprintf(` + resource "equinix_fabric_stream" "new_stream" { + type = "TELEMETRY_STREAM" + name = "%s" + description = "%s" + project = { + project_id = "291639000636552" + } + } + `, name, description) +} + +func TestAccFabricStream_PFCR(t *testing.T) { + streamName, updatedStreamName := "stream_PFCR", "stream_up_PFCR" + streamDescription, updatedStreamDescription := "stream acceptance test PFCR", "updated stream acceptance test PFCR" + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.TestAccPreCheck(t); acceptance.TestAccPreCheckProviderConfigured(t) }, + ExternalProviders: acceptance.TestExternalProviders, + ProtoV6ProviderFactories: acceptance.ProtoV6ProviderFactories, + CheckDestroy: CheckStreamDelete, + Steps: []resource.TestStep{ + { + Config: testAccFabricStreamConfig(streamName, streamDescription), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "equinix_fabric_stream.new_stream", "name", streamName), + resource.TestCheckResourceAttr( + "equinix_fabric_stream.new_stream", "type", "TELEMETRY_STREAM"), + resource.TestCheckResourceAttr( + "equinix_fabric_stream.new_stream", "project.project_id", "291639000636552"), + resource.TestCheckResourceAttr( + "equinix_fabric_stream.new_stream", "description", streamDescription), + resource.TestCheckResourceAttrSet("equinix_fabric_stream.new_stream", "id"), + resource.TestCheckResourceAttrSet("equinix_fabric_stream.new_stream", "href"), + resource.TestCheckResourceAttrSet("equinix_fabric_stream.new_stream", "assets_count"), + resource.TestCheckResourceAttrSet("equinix_fabric_stream.new_stream", "stream_subscriptions_count"), + resource.TestCheckResourceAttrSet("equinix_fabric_stream.new_stream", "uuid"), + resource.TestCheckResourceAttrSet("equinix_fabric_stream.new_stream", "change_log.created_by"), + ), + }, + { + Config: testAccFabricStreamConfig(updatedStreamName, updatedStreamDescription), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "equinix_fabric_stream.new_stream", "name", updatedStreamName), + resource.TestCheckResourceAttr( + "equinix_fabric_stream.new_stream", "project.project_id", "291639000636552"), + resource.TestCheckResourceAttr( + "equinix_fabric_stream.new_stream", "description", updatedStreamDescription), + resource.TestCheckResourceAttrSet("equinix_fabric_stream.new_stream", "id"), + resource.TestCheckResourceAttrSet("equinix_fabric_stream.new_stream", "href"), + resource.TestCheckResourceAttrSet("equinix_fabric_stream.new_stream", "assets_count"), + resource.TestCheckResourceAttrSet("equinix_fabric_stream.new_stream", "stream_subscriptions_count"), + resource.TestCheckResourceAttrSet("equinix_fabric_stream.new_stream", "uuid"), + resource.TestCheckResourceAttrSet("equinix_fabric_stream.new_stream", "change_log.created_by"), + ), + }, + }, + }) + +} diff --git a/internal/resources/fabric/stream/sweeper.go b/internal/resources/fabric/stream/sweeper.go new file mode 100644 index 000000000..de2e40de6 --- /dev/null +++ b/internal/resources/fabric/stream/sweeper.go @@ -0,0 +1,55 @@ +package stream + +import ( + "context" + "errors" + "fmt" + "log" + "net/http" + + equinix_errors "github.com/equinix/terraform-provider-equinix/internal/errors" + "github.com/equinix/terraform-provider-equinix/internal/sweep" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func AddTestSweeper() { + resource.AddTestSweepers("equinix_fabric_stream", &resource.Sweeper{ + Name: "equinix_fabric_stream", + Dependencies: []string{}, + F: testSweepStreams, + }) +} + +func testSweepStreams(_ string) error { + var errs []error + log.Printf("[DEBUG] Sweeping Fabric Streams") + ctx := context.Background() + meta, err := sweep.GetConfigForFabric() + if err != nil { + return fmt.Errorf("error getting configuration for sweeping Streams: %s", err) + } + configLoadErr := meta.Load(ctx) + if configLoadErr != nil { + return fmt.Errorf("error loading configuration for sweeping Streams: %s", err) + } + fabric := meta.NewFabricClientForTesting(ctx) + limit := int32(100) + + streams, _, err := fabric.StreamsApi.GetStreams(ctx).Limit(limit).Execute() + if err != nil { + return fmt.Errorf("error getting streams list for sweeping fabric streams: %s", err) + } + + for _, stream := range streams.GetData() { + if sweep.IsSweepableFabricTestResource(stream.GetName()) { + log.Printf("[DEBUG] Deleting stream: %s", stream.GetName()) + _, resp, err := fabric.StreamsApi.DeleteStreamByUuid(ctx, stream.GetUuid()).Execute() + if equinix_errors.IgnoreHttpResponseErrors(http.StatusForbidden, http.StatusNotFound)(resp, err) != nil { + errs = append(errs, fmt.Errorf("error deleting fabric stream: %s", err)) + } + } + } + + return errors.Join(errs...) +} diff --git a/internal/sweep/sweep_test.go b/internal/sweep/sweep_test.go index b71b5cc8e..411ff764c 100644 --- a/internal/sweep/sweep_test.go +++ b/internal/sweep/sweep_test.go @@ -6,6 +6,7 @@ import ( fabric_cloud_router "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/cloud_router" fabric_connection "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/connection" fabric_route_filter "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/route_filter" + fabric_stream "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/stream" "github.com/equinix/terraform-provider-equinix/internal/resources/metal/connection" "github.com/equinix/terraform-provider-equinix/internal/resources/metal/device" @@ -33,6 +34,7 @@ func addTestSweepers() { fabric_cloud_router.AddTestSweeper() fabric_connection.AddTestSweeper() fabric_route_filter.AddTestSweeper() + fabric_stream.AddTestSweeper() organization.AddTestSweeper() project.AddTestSweeper() ssh_key.AddTestSweeper()