diff --git a/CHANGELOG.md b/CHANGELOG.md index d6f70331..82fec0fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,27 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased](https://github.com/fivetran/terraform-provider-fivetran/compare/v1.3.2...HEAD) +## [Unreleased](https://github.com/fivetran/terraform-provider-fivetran/compare/v1.4.1...HEAD) + +## [1.4.1](https://github.com/fivetran/terraform-provider-fivetran/compare/v1.4.0...v1.4.1) + +## Added +- New resource `fivetran_private_link` that allows to manage Private Links. +- New data source `fivetran_private_link` that allows to retrieve details of the existing Private Link for a given identifier. +- New data source `fivetran_private_links` that allows to retrieve the list of existing Private Links available for the current account. + +Updates to support management of private links: +- Resource `fivetran_connector` updates: + - Added field `fivetran_connector.private_link_id`. + +- Resource `fivetran_destination` updates: + - Added field `fivetran_destination.private_link_id`. + +- Datasource `fivetran_connector` updates: + - Added field `fivetran_connector.private_link_id`. + +- Datasource `fivetran_destination` updates: + - Added field `fivetran_destination.private_link_id`. ## [1.3.2](https://github.com/fivetran/terraform-provider-fivetran/compare/v1.3.1...v1.3.2) diff --git a/docs/data-sources/connector.md b/docs/data-sources/connector.md index b0bc4027..92a4e313 100644 --- a/docs/data-sources/connector.md +++ b/docs/data-sources/connector.md @@ -35,6 +35,7 @@ data "fivetran_connector" "connector" { - `networking_method` (String) Possible values: Directly, SshTunnel, ProxyAgent. - `pause_after_trial` (Boolean) Specifies whether the connector should be paused after the free trial period has ended. - `paused` (Boolean) Specifies whether the connector is paused. +- `private_link_id` (String) The private link ID. - `proxy_agent_id` (String) The proxy agent ID. - `schedule_type` (String) The connector schedule configuration type. Supported values: auto, manual. - `service` (String) The connector type id within the Fivetran system. diff --git a/docs/data-sources/destination.md b/docs/data-sources/destination.md index 9446c137..d715f20b 100644 --- a/docs/data-sources/destination.md +++ b/docs/data-sources/destination.md @@ -28,6 +28,7 @@ data "fivetran_destination" "dest" { - `group_id` (String) The unique identifier for the Group within the Fivetran system. - `local_processing_agent_id` (String) The local processing agent ID that refers to the controller created for the group the connection belongs to. If the value is specified, the system will try to associate the connection with an existing agent. - `networking_method` (String) Possible values: Directly, SshTunnel, ProxyAgent. +- `private_link_id` (String) The private link ID. - `region` (String) Data processing location. This is where Fivetran will operate and run computation on data. - `service` (String) The destination type id within the Fivetran system. - `setup_status` (String) Destination setup status. diff --git a/docs/data-sources/private_link.md b/docs/data-sources/private_link.md new file mode 100644 index 00000000..6b5edf9a --- /dev/null +++ b/docs/data-sources/private_link.md @@ -0,0 +1,34 @@ +--- +page_title: "Data Source: fivetran_private_link" +--- + +# Data Source: fivetran_private_link + +This data source returns a private link object. + +## Example Usage + +```hcl +data "fivetran_private_link" "private_link" { + id = "private_link_id" +} +``` + + +## Schema + +### Required + +- `id` (String) The unique identifier for the private link within the Fivetran system. + +### Read-Only + +- `cloud_provider` (String) The cloud provider name. +- `config_map` (Map of String) Configuration. +- `created_at` (String) The date and time the membership was created. +- `created_by` (String) The unique identifier for the User within the Fivetran system. +- `name` (String) The private link name within the account. The name must start with a letter or underscore and can only contain letters, numbers, or underscores. Maximum size of name is 23 characters. +- `region` (String) Data processing location. This is where Fivetran will operate and run computation on data. +- `service` (String) Service type. +- `state` (String) The state of the private link. +- `state_summary` (String) The state of the private link. \ No newline at end of file diff --git a/docs/data-sources/private_links.md b/docs/data-sources/private_links.md new file mode 100644 index 00000000..e0ad87fa --- /dev/null +++ b/docs/data-sources/private_links.md @@ -0,0 +1,35 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "fivetran_private_links Data Source - terraform-provider-fivetran" +subcategory: "" +description: |- + +--- + +# fivetran_private_links (Data Source) + + + + + + +## Schema + +### Read-Only + +- `items` (Block Set) (see [below for nested schema](#nestedblock--items)) + + +### Nested Schema for `items` + +Read-Only: + +- `cloud_provider` (String) The cloud provider name. +- `created_at` (String) The date and time the membership was created. +- `created_by` (String) The unique identifier for the User within the Fivetran system. +- `id` (String) The unique identifier for the private link within the Fivetran system. +- `name` (String) The private link name within the account. The name must start with a letter or underscore and can only contain letters, numbers, or underscores. Maximum size of name is 23 characters. +- `region` (String) Data processing location. This is where Fivetran will operate and run computation on data. +- `service` (String) Service type. +- `state` (String) The state of the private link. +- `state_summary` (String) The state of the private link. diff --git a/docs/data-sources/private_links.md.tmpl b/docs/data-sources/private_links.md.tmpl new file mode 100644 index 00000000..7b7387c7 --- /dev/null +++ b/docs/data-sources/private_links.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "Data Source: fivetran_private_links" +--- + +# Data Source: fivetran_private_links + +This data source returns a list of all private links within your Fivetran account. + +## Example Usage + +```hcl +data "fivetran_private_links" "private_links" { +} +``` + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/docs/resources/connector.md b/docs/resources/connector.md index f1066d60..9d5f968e 100644 --- a/docs/resources/connector.md +++ b/docs/resources/connector.md @@ -72,6 +72,7 @@ resource "fivetran_connector" "amplitude" { - `destination_schema` (Block, Optional) (see [below for nested schema](#nestedblock--destination_schema)) - `local_processing_agent_id` (String) The local processing agent ID that refers to the controller created for the group the connection belongs to. If the value is specified, the system will try to associate the connection with an existing agent. - `networking_method` (String) Possible values: Directly, SshTunnel, ProxyAgent. +- `private_link_id` (String) The private link ID. - `proxy_agent_id` (String) The proxy agent ID. - `run_setup_tests` (Boolean) Specifies whether the setup tests should be run automatically. The default value is FALSE. - `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts)) diff --git a/docs/resources/connector_schema_config.md b/docs/resources/connector_schema_config.md index ed971658..ddcfa775 100644 --- a/docs/resources/connector_schema_config.md +++ b/docs/resources/connector_schema_config.md @@ -182,8 +182,8 @@ resource "fivetran_connector_schema_config" "schema" { ### Optional -- `schema_change_handling` (String) The value specifying how new source data is handled. - `schema` (Block Set, Deprecated) (see [below for nested schema](#nestedblock--schema)) +- `schema_change_handling` (String) The value specifying how new source data is handled. - `schemas` (Attributes Map) Map of schema configurations. (see [below for nested schema](#nestedatt--schemas)) - `schemas_json` (String) Schema settings in Json format, following Fivetran API endpoint contract for `schemas` field (a map of schemas). - `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts)) diff --git a/docs/resources/destination.md b/docs/resources/destination.md index 70616211..594e1e1a 100644 --- a/docs/resources/destination.md +++ b/docs/resources/destination.md @@ -50,6 +50,7 @@ resource "fivetran_destination" "dest" { - `daylight_saving_time_enabled` (Boolean) Shift my UTC offset with daylight savings time (US Only) - `local_processing_agent_id` (String) The local processing agent ID that refers to the controller created for the group the connection belongs to. If the value is specified, the system will try to associate the connection with an existing agent. - `networking_method` (String) Possible values: Directly, SshTunnel, ProxyAgent. +- `private_link_id` (String) The private link ID. - `run_setup_tests` (Boolean) Specifies whether the setup tests should be run automatically. The default value is TRUE. - `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts)) - `trust_certificates` (Boolean) Specifies whether we should trust the certificate automatically. The default value is FALSE. If a certificate is not trusted automatically, it has to be approved with [Certificates Management API Approve a destination certificate](https://fivetran.com/docs/rest-api/certificates#approveadestinationcertificate). diff --git a/docs/resources/private_link.md b/docs/resources/private_link.md new file mode 100644 index 00000000..8e11cca7 --- /dev/null +++ b/docs/resources/private_link.md @@ -0,0 +1,55 @@ +--- +page_title: "Resource: fivetran_private_link" +--- + +# Resource: fivetran_private_link + +This resource allows you to create, update, and delete private links. + +## Example Usage + +```hcl +resource "fivetran_private_link" "test_pl" { + provider = fivetran-provider + + name = "name" + region = "region" + service = "service" + + config { + connection_service_name = "connection_service_name" + } +} +``` + + +## Schema + +### Required + +- `config_map` (Map of String) Configuration. + +#### Possible values +-- `connection_service_name` (String): The name of your connection service. +-- `account_url` (String): The URL of your account. +-- `vpce_id` (String): The ID of your Virtual Private Cloud Endpoint. +-- `aws_account_id` (String): The ID of your AWS account. +-- `cluster_identifier` (String): The cluster identifier. +-- `connection_service_id` (String): The ID of your connection service. +-- `workspace_url` (String): The URL of your workspace. +-- `pls_id` (String): The ID of your Azure Private Link service. +-- `sub_resource_name` (String): The name of subresource. +-- `private_dns_regions` (String): Private DNS Regions. +-- `private_connection_service_id` (String): The ID of your connection service. +- `name` (String) The private link name within the account. The name must start with a letter or underscore and can only contain letters, numbers, or underscores. Maximum size of name is 23 characters. +- `region` (String) Data processing location. This is where Fivetran will operate and run computation on data. +- `service` (String) Service type. + +### Read-Only + +- `cloud_provider` (String) The cloud provider name. +- `created_at` (String) The date and time the membership was created. +- `created_by` (String) The unique identifier for the User within the Fivetran system. +- `id` (String) The unique identifier for the private link within the Fivetran system. +- `state` (String) The state of the private link. +- `state_summary` (String) The state of the private link. diff --git a/fivetran/framework/core/model/connector.go b/fivetran/framework/core/model/connector.go index 54087bfc..8ca64edf 100644 --- a/fivetran/framework/core/model/connector.go +++ b/fivetran/framework/core/model/connector.go @@ -36,6 +36,7 @@ type ConnectorDatasourceModel struct { ProxyAgentId types.String `tfsdk:"proxy_agent_id"` NetworkingMethod types.String `tfsdk:"networking_method"` LocalProcessingAgentId types.String `tfsdk:"local_processing_agent_id"` + PrivateLinkId types.String `tfsdk:"private_link_id"` Status types.Object `tfsdk:"status"` @@ -61,6 +62,7 @@ func (d *ConnectorDatasourceModel) ReadFromResponse(resp connectors.DetailsWithC d.ScheduleType = types.StringValue(resp.Data.ScheduleType) d.Paused = types.BoolValue(*resp.Data.Paused) d.PauseAfterTrial = types.BoolValue(*resp.Data.PauseAfterTrial) + if resp.Data.DailySyncTime != "" { d.DailySyncTime = types.StringValue(resp.Data.DailySyncTime) } else { @@ -125,6 +127,7 @@ type ConnectorResourceModel struct { ProxyAgentId types.String `tfsdk:"proxy_agent_id"` NetworkingMethod types.String `tfsdk:"networking_method"` LocalProcessingAgentId types.String `tfsdk:"local_processing_agent_id"` + PrivateLinkId types.String `tfsdk:"private_link_id"` Config types.Object `tfsdk:"config"` Auth types.Object `tfsdk:"auth"` @@ -204,6 +207,12 @@ func (d *ConnectorResourceModel) ReadFromContainer(c ConnectorModelContainer, fo d.DestinationSchema = getDestinationSchemaValue(c.Service, c.Schema) + if c.PrivateLinkId != "" { + d.PrivateLinkId = types.StringValue(c.PrivateLinkId) + } else { + d.PrivateLinkId = types.StringNull() + } + if c.ProxyAgentId != "" { d.ProxyAgentId = types.StringValue(c.ProxyAgentId) } else { @@ -237,7 +246,13 @@ func (d *ConnectorDatasourceModel) ReadFromContainer(c ConnectorModelContainer) d.LocalProcessingAgentId = types.StringNull() } - d.DestinationSchema = getDestinationSchemaValue(c.Service, c.Schema) + d.DestinationSchema = getDestinationSchemaValue(c.Service, c.Schema) + + if c.PrivateLinkId != "" { + d.PrivateLinkId = types.StringValue(c.PrivateLinkId) + } else { + d.PrivateLinkId = types.StringNull() + } if c.ProxyAgentId != "" { d.ProxyAgentId = types.StringValue(c.ProxyAgentId) @@ -270,6 +285,7 @@ type ConnectorModelContainer struct { ProxyAgentId string NetworkingMethod string LocalProcessingAgentId string + PrivateLinkId string Config map[string]interface{} @@ -296,8 +312,12 @@ func (c *ConnectorModelContainer) ReadFromResponseData(data connectors.DetailsRe c.NetworkingMethod = data.NetworkingMethod } - if data.LocalProcessingAgentId != "" { - c.LocalProcessingAgentId = data.LocalProcessingAgentId + if data.HybridDeploymentAgentId != "" { + c.LocalProcessingAgentId = data.HybridDeploymentAgentId + } + + if data.PrivateLinkId != "" { + c.PrivateLinkId = data.PrivateLinkId } } diff --git a/fivetran/framework/core/model/destination.go b/fivetran/framework/core/model/destination.go index 1b6e9838..1b36f4e4 100644 --- a/fivetran/framework/core/model/destination.go +++ b/fivetran/framework/core/model/destination.go @@ -14,6 +14,7 @@ type destinationModel interface { SetDaylightSavingTimeEnabled(bool) SetLocalProcessingAgentId(string) SetNetworkingMethod(string) + SetPrivateLinkId(string) SetConfig(map[string]interface{}) } @@ -25,7 +26,8 @@ func readFromResponse(d destinationModel, resp destinations.DestinationDetailsBa d.SetSetupStatus(resp.SetupStatus) d.SetTimeZonOffset(resp.TimeZoneOffset) d.SetDaylightSavingTimeEnabled(resp.DaylightSavingTimeEnabled) - d.SetLocalProcessingAgentId(resp.LocalProcessingAgentId) + d.SetLocalProcessingAgentId(resp.HybridDeploymentAgentId) d.SetNetworkingMethod(resp.NetworkingMethod) + d.SetPrivateLinkId(resp.PrivateLinkId) d.SetConfig(config) } diff --git a/fivetran/framework/core/model/destination_datasource_model.go b/fivetran/framework/core/model/destination_datasource_model.go index 83fc76b5..1beaa1bd 100644 --- a/fivetran/framework/core/model/destination_datasource_model.go +++ b/fivetran/framework/core/model/destination_datasource_model.go @@ -17,6 +17,7 @@ type DestinationDatasourceModel struct { DaylightSavingTimeEnabled types.Bool `tfsdk:"daylight_saving_time_enabled"` LocalProcessingAgentId types.String `tfsdk:"local_processing_agent_id"` NetworkingMethod types.String `tfsdk:"networking_method"` + PrivateLinkId types.String `tfsdk:"private_link_id"` Config types.Object `tfsdk:"config"` } @@ -43,6 +44,13 @@ func (d *DestinationDatasourceModel) SetSetupStatus(value string) { func (d *DestinationDatasourceModel) SetDaylightSavingTimeEnabled(value bool) { d.DaylightSavingTimeEnabled = types.BoolValue(value) } +func (d *DestinationDatasourceModel) SetPrivateLinkId(value string) { + if value != "" { + d.PrivateLinkId = types.StringValue(value) + } else { + d.PrivateLinkId = types.StringNull() + } +} func (d *DestinationDatasourceModel) SetLocalProcessingAgentId(value string) { if value != "" { d.LocalProcessingAgentId = types.StringValue(value) diff --git a/fivetran/framework/core/model/destination_resource_model.go b/fivetran/framework/core/model/destination_resource_model.go index 34136a45..8ed14430 100644 --- a/fivetran/framework/core/model/destination_resource_model.go +++ b/fivetran/framework/core/model/destination_resource_model.go @@ -20,6 +20,7 @@ type DestinationResourceModel struct { Timeouts timeouts.Value `tfsdk:"timeouts"` LocalProcessingAgentId types.String `tfsdk:"local_processing_agent_id"` NetworkingMethod types.String `tfsdk:"networking_method"` + PrivateLinkId types.String `tfsdk:"private_link_id"` RunSetupTests types.Bool `tfsdk:"run_setup_tests"` TrustCertificates types.Bool `tfsdk:"trust_certificates"` @@ -61,6 +62,13 @@ func (d *DestinationResourceModel) SetNetworkingMethod(value string) { d.NetworkingMethod = types.StringValue(value) } } +func (d *DestinationResourceModel) SetPrivateLinkId(value string) { + if value != "" { + d.PrivateLinkId = types.StringValue(value) + } else { + d.PrivateLinkId = types.StringNull() + } +} func (d *DestinationResourceModel) SetConfig(value map[string]interface{}) { if d.Service.IsNull() || d.Service.IsUnknown() { panic("Service type is null. Can't handle config without service type.") diff --git a/fivetran/framework/core/model/local_processing_agent.go b/fivetran/framework/core/model/local_processing_agent.go index 1acbce32..7b520422 100644 --- a/fivetran/framework/core/model/local_processing_agent.go +++ b/fivetran/framework/core/model/local_processing_agent.go @@ -1,7 +1,7 @@ package model import ( - "github.com/fivetran/go-fivetran/local_processing_agent" + localprocessingagent "github.com/fivetran/go-fivetran/hybrid_deployment_agent" ) type localProcessingAgentModel interface { @@ -12,10 +12,10 @@ type localProcessingAgentModel interface { SetConfigJson(string) SetAuthJson(string) SetDockerComposeYaml(string) - SetUsage([]localprocessingagent.LocalProcessingAgentUsageDetails) + SetUsage([]localprocessingagent.HybridDeploymentAgentUsageDetails) } -func readLocalProcessingAgentFromResponse(d localProcessingAgentModel, resp localprocessingagent.LocalProcessingAgentDetailsResponse) { +func readLocalProcessingAgentFromResponse(d localProcessingAgentModel, resp localprocessingagent.HybridDeploymentAgentDetailsResponse) { d.SetId(resp.Data.Id) d.SetDisplayName(resp.Data.DisplayName) d.SetGroupId(resp.Data.GroupId) @@ -23,7 +23,7 @@ func readLocalProcessingAgentFromResponse(d localProcessingAgentModel, resp loca d.SetUsage(resp.Data.Usage) } -func readLocalProcessingAgentFromCreateResponse(d localProcessingAgentModel, resp localprocessingagent.LocalProcessingAgentCreateResponse) { +func readLocalProcessingAgentFromCreateResponse(d localProcessingAgentModel, resp localprocessingagent.HybridDeploymentAgentCreateResponse) { d.SetId(resp.Data.Id) d.SetDisplayName(resp.Data.DisplayName) d.SetGroupId(resp.Data.GroupId) diff --git a/fivetran/framework/core/model/local_processing_agent_datasource_model.go b/fivetran/framework/core/model/local_processing_agent_datasource_model.go index 83273d94..1c9ab041 100644 --- a/fivetran/framework/core/model/local_processing_agent_datasource_model.go +++ b/fivetran/framework/core/model/local_processing_agent_datasource_model.go @@ -1,7 +1,7 @@ package model import ( - "github.com/fivetran/go-fivetran/local_processing_agent" + localprocessingagent "github.com/fivetran/go-fivetran/hybrid_deployment_agent" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/attr" ) @@ -36,7 +36,7 @@ func (d *LocalProcessingAgentDatasourceModel) SetDisplayName(value string) { func (d *LocalProcessingAgentDatasourceModel) SetRegisteredAt(value string) { d.RegisteredAt = types.StringValue(value) } -func (d *LocalProcessingAgentDatasourceModel) SetUsage(value []localprocessingagent.LocalProcessingAgentUsageDetails) { +func (d *LocalProcessingAgentDatasourceModel) SetUsage(value []localprocessingagent.HybridDeploymentAgentUsageDetails) { if value == nil { d.Usage = types.SetNull(types.ObjectType{AttrTypes: elementType}) } @@ -59,7 +59,7 @@ func (d *LocalProcessingAgentDatasourceModel) SetConfigJson(value string) {} func (d *LocalProcessingAgentDatasourceModel) SetAuthJson(value string) {} func (d *LocalProcessingAgentDatasourceModel) SetDockerComposeYaml(value string) {} -func (d *LocalProcessingAgentDatasourceModel) ReadFromResponse(resp localprocessingagent.LocalProcessingAgentDetailsResponse) { +func (d *LocalProcessingAgentDatasourceModel) ReadFromResponse(resp localprocessingagent.HybridDeploymentAgentDetailsResponse) { var model localProcessingAgentModel = d readLocalProcessingAgentFromResponse(model, resp) } \ No newline at end of file diff --git a/fivetran/framework/core/model/local_processing_agent_resource_model.go b/fivetran/framework/core/model/local_processing_agent_resource_model.go index 550e0999..4957d05d 100644 --- a/fivetran/framework/core/model/local_processing_agent_resource_model.go +++ b/fivetran/framework/core/model/local_processing_agent_resource_model.go @@ -1,7 +1,7 @@ package model import ( - "github.com/fivetran/go-fivetran/local_processing_agent" + localprocessingagent "github.com/fivetran/go-fivetran/hybrid_deployment_agent" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/attr" ) @@ -41,7 +41,7 @@ func (d *LocalProcessingAgentResourceModel) SetAuthJson(value string) { func (d *LocalProcessingAgentResourceModel) SetDockerComposeYaml(value string) { d.DockerComposeYaml = types.StringValue(value) } -func (d *LocalProcessingAgentResourceModel) SetUsage(value []localprocessingagent.LocalProcessingAgentUsageDetails) { +func (d *LocalProcessingAgentResourceModel) SetUsage(value []localprocessingagent.HybridDeploymentAgentUsageDetails) { if value == nil { d.Usage = types.SetNull(types.ObjectType{AttrTypes: elementType}) } @@ -59,13 +59,13 @@ func (d *LocalProcessingAgentResourceModel) SetUsage(value []localprocessingagen d.Usage, _ = types.SetValue(types.ObjectType{AttrTypes: elementType}, items) } -func (d *LocalProcessingAgentResourceModel) ReadFromCreateResponse(resp localprocessingagent.LocalProcessingAgentCreateResponse) { +func (d *LocalProcessingAgentResourceModel) ReadFromCreateResponse(resp localprocessingagent.HybridDeploymentAgentCreateResponse) { var model localProcessingAgentModel = d readLocalProcessingAgentFromCreateResponse(model, resp) d.AuthenticationCounter = types.Int64Value(d.AuthenticationCounter.ValueInt64() + 1) } -func (d *LocalProcessingAgentResourceModel) ReadFromResponse(resp localprocessingagent.LocalProcessingAgentDetailsResponse) { +func (d *LocalProcessingAgentResourceModel) ReadFromResponse(resp localprocessingagent.HybridDeploymentAgentDetailsResponse) { var model localProcessingAgentModel = d readLocalProcessingAgentFromResponse(model, resp) } \ No newline at end of file diff --git a/fivetran/framework/core/model/local_processing_agents.go b/fivetran/framework/core/model/local_processing_agents.go index 20b7a3d7..a8596ccd 100644 --- a/fivetran/framework/core/model/local_processing_agents.go +++ b/fivetran/framework/core/model/local_processing_agents.go @@ -3,7 +3,7 @@ package model import ( "context" - "github.com/fivetran/go-fivetran/local_processing_agent" + localprocessingagent "github.com/fivetran/go-fivetran/hybrid_deployment_agent" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/attr" ) @@ -12,7 +12,7 @@ type LocalProcessingAgents struct { Items types.Set `tfsdk:"items"` } -func (d *LocalProcessingAgents) ReadFromResponse(ctx context.Context, resp localprocessingagent.LocalProcessingAgentListResponse) { +func (d *LocalProcessingAgents) ReadFromResponse(ctx context.Context, resp localprocessingagent.HybridDeploymentAgentListResponse) { subSetElementType := map[string]attr.Type{ "connection_id": types.StringType, "schema": types.StringType, diff --git a/fivetran/framework/core/model/private_link.go b/fivetran/framework/core/model/private_link.go new file mode 100644 index 00000000..ab8e0141 --- /dev/null +++ b/fivetran/framework/core/model/private_link.go @@ -0,0 +1,158 @@ +package model + +import ( + "context" + + "github.com/fivetran/go-fivetran/private_link" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-framework/attr" +) + +type PrivateLink struct { + Id types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + Region types.String `tfsdk:"region"` + Service types.String `tfsdk:"service"` + CloudProvider types.String `tfsdk:"cloud_provider"` + State types.String `tfsdk:"state"` + StateSummary types.String `tfsdk:"state_summary"` + CreatedAt types.String `tfsdk:"created_at"` + CreatedBy types.String `tfsdk:"created_by"` + ConfigMap types.Map `tfsdk:"config_map"` +} + +var PrivateLinkConfigType = map[string]attr.Type{ + "connection_service_name": types.StringType, + "account_url": types.StringType, + "vpce_id": types.StringType, + "aws_account_id": types.StringType, + "cluster_identifier": types.StringType, + "connection_service_id": types.StringType, + "workspace_url": types.StringType, + "pls_id": types.StringType, + "sub_resource_name": types.StringType, + "private_dns_regions": types.StringType, + "private_connection_service_id": types.StringType, +} + +func (d *PrivateLink) ReadFromResponse(ctx context.Context, resp privatelink.PrivateLinkResponse) { + d.Id = types.StringValue(resp.Data.Id) + d.Name = types.StringValue(resp.Data.Name) + d.Service = types.StringValue(resp.Data.Service) + d.Region = types.StringValue(resp.Data.Region) + d.CloudProvider = types.StringValue(resp.Data.CloudProvider) + d.State = types.StringValue(resp.Data.State) + d.StateSummary = types.StringValue(resp.Data.StateSummary) + d.CreatedAt = types.StringValue(resp.Data.CreatedAt) + d.CreatedBy = types.StringValue(resp.Data.CreatedBy) + + config := map[string]attr.Value{} + + if resp.Data.Config.ConnectionServiceName != "" { + config["connection_service_name"] = types.StringValue(resp.Data.Config.ConnectionServiceName) + } else { + config["connection_service_name"] = types.StringNull() + } + + if resp.Data.Config.AccountUrl != "" { + config["account_url"] = types.StringValue(resp.Data.Config.AccountUrl) + } else { + config["account_url"] = types.StringNull() + } + + if resp.Data.Config.VpceId != "" { + config["vpce_id"] = types.StringValue(resp.Data.Config.VpceId) + } else { + config["vpce_id"] = types.StringNull() + } + + if resp.Data.Config.AwsAccountId != "" { + config["aws_account_id"] = types.StringValue(resp.Data.Config.AwsAccountId) + } else { + config["aws_account_id"] = types.StringNull() + } + + if resp.Data.Config.ClusterIdentifier != "" { + config["cluster_identifier"] = types.StringValue(resp.Data.Config.ClusterIdentifier) + } else { + config["cluster_identifier"] = types.StringNull() + } + + if resp.Data.Config.ConnectionServiceId != "" { + config["connection_service_id"] = types.StringValue(resp.Data.Config.ConnectionServiceId) + } else { + config["connection_service_id"] = types.StringNull() + } + + if resp.Data.Config.WorkspaceUrl != "" { + config["workspace_url"] = types.StringValue(resp.Data.Config.WorkspaceUrl) + } else { + config["workspace_url"] = types.StringNull() + } + + if resp.Data.Config.PlsId != "" { + config["pls_id"] = types.StringValue(resp.Data.Config.PlsId) + } else { + config["pls_id"] = types.StringNull() + } + + if resp.Data.Config.SubResourceName != "" { + config["sub_resource_name"] = types.StringValue(resp.Data.Config.SubResourceName) + } else { + config["sub_resource_name"] = types.StringNull() + } + + if resp.Data.Config.PrivateDnsRegions != "" { + config["private_dns_regions"] = types.StringValue(resp.Data.Config.PrivateDnsRegions) + } else { + config["private_dns_regions"] = types.StringNull() + } + + if resp.Data.Config.PrivateConnectionServiceId != "" { + config["private_connection_service_id"] = types.StringValue(resp.Data.Config.PrivateConnectionServiceId) + } else { + config["private_connection_service_id"] = types.StringNull() + } + + d.ConfigMap, _ = types.MapValue(types.StringType, config) +} + +func (d *PrivateLink) ReadFromCustomResponse(ctx context.Context, resp privatelink.PrivateLinkCustomResponse) { + d.Id = types.StringValue(resp.Data.Id) + d.Name = types.StringValue(resp.Data.Name) + d.Service = types.StringValue(resp.Data.Service) + d.Region = types.StringValue(resp.Data.Region) + d.CloudProvider = types.StringValue(resp.Data.CloudProvider) + d.State = types.StringValue(resp.Data.State) + d.StateSummary = types.StringValue(resp.Data.StateSummary) + d.CreatedAt = types.StringValue(resp.Data.CreatedAt) + d.CreatedBy = types.StringValue(resp.Data.CreatedBy) + + config := map[string]attr.Value{} + for k, v := range resp.Data.Config { + config[k] = types.StringValue(v.(string)) + } + d.ConfigMap, _ = types.MapValue(types.StringType, config) +} + +func (d *PrivateLink) GetConfigMap() map[string]interface{} { + config := make(map[string]interface{}) + for k, v := range d.ConfigMap.Elements(){ + if !v.IsUnknown() && !v.IsNull() { + if t, ok := v.(basetypes.Int64Value); ok { + config[k] = t.ValueInt64() + } + + if t, ok := v.(basetypes.BoolValue); ok { + config[k] = t.ValueBool() + } + + if t, ok := v.(basetypes.StringValue); ok { + config[k] = t.ValueString() + } + } + } + + return config +} \ No newline at end of file diff --git a/fivetran/framework/core/model/private_links.go b/fivetran/framework/core/model/private_links.go new file mode 100644 index 00000000..21ae79dd --- /dev/null +++ b/fivetran/framework/core/model/private_links.go @@ -0,0 +1,51 @@ +package model + +import ( + "context" + + "github.com/fivetran/go-fivetran/private_link" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/attr" +) + +type PrivateLinks struct { + Items types.Set `tfsdk:"items"` +} + +func (d *PrivateLinks) ReadFromResponse(ctx context.Context, resp privatelink.PrivateLinkListResponse) { + elementType := map[string]attr.Type{ + "id": types.StringType, + "region": types.StringType, + "name": types.StringType, + "service": types.StringType, + "cloud_provider": types.StringType, + "state": types.StringType, + "state_summary": types.StringType, + "created_by": types.StringType, + "created_at": types.StringType, + } + + if resp.Data.Items == nil { + d.Items = types.SetNull(types.ObjectType{AttrTypes: elementType}) + } + + items := []attr.Value{} + + for _, v := range resp.Data.Items { + item := map[string]attr.Value{} + item["id"] = types.StringValue(v.Id) + item["region"] = types.StringValue(v.Region) + item["name"] = types.StringValue(v.Name) + item["service"] = types.StringValue(v.Service) + item["cloud_provider"] = types.StringValue(v.CloudProvider) + item["state"] = types.StringValue(v.State) + item["state_summary"] = types.StringValue(v.StateSummary) + item["created_at"] = types.StringValue(v.CreatedAt) + item["created_by"] = types.StringValue(v.CreatedBy) + + objectValue, _ := types.ObjectValue(elementType, item) + items = append(items, objectValue) + } + + d.Items, _ = types.SetValue(types.ObjectType{AttrTypes: elementType}, items) +} \ No newline at end of file diff --git a/fivetran/framework/core/schema/connector.go b/fivetran/framework/core/schema/connector.go index 5cb04896..ad6026f6 100644 --- a/fivetran/framework/core/schema/connector.go +++ b/fivetran/framework/core/schema/connector.go @@ -113,6 +113,10 @@ func ConnectorAttributesSchema() core.Schema { ValueType: core.String, Description: "The local processing agent ID that refers to the controller created for the group the connection belongs to. If the value is specified, the system will try to associate the connection with an existing agent.", }, + "private_link_id": { + ValueType: core.String, + Description: "The private link ID.", + }, }, } } diff --git a/fivetran/framework/core/schema/destination.go b/fivetran/framework/core/schema/destination.go index f6dad707..d5e66982 100644 --- a/fivetran/framework/core/schema/destination.go +++ b/fivetran/framework/core/schema/destination.go @@ -63,6 +63,10 @@ func DestinationAttributesSchema() core.Schema { ValueType: core.Boolean, Description: "Shift my UTC offset with daylight savings time (US Only)", }, + "private_link_id": { + ValueType: core.String, + Description: "The private link ID.", + }, "local_processing_agent_id": { ValueType: core.String, Description: "The local processing agent ID that refers to the controller created for the group the connection belongs to. If the value is specified, the system will try to associate the connection with an existing agent.", diff --git a/fivetran/framework/core/schema/private_link.go b/fivetran/framework/core/schema/private_link.go new file mode 100644 index 00000000..8fe425b7 --- /dev/null +++ b/fivetran/framework/core/schema/private_link.go @@ -0,0 +1,170 @@ +package schema + +import ( + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" + resourceSchema "github.com/hashicorp/terraform-plugin-framework/resource/schema" + datasourceSchema "github.com/hashicorp/terraform-plugin-framework/datasource/schema" +) + +func PrivateLinkResource() resourceSchema.Schema { + return resourceSchema.Schema { + Attributes: map[string]resourceSchema.Attribute{ + "id": resourceSchema.StringAttribute{ + Computed: true, + Description: "The unique identifier for the private link within the Fivetran system.", + }, + "region": resourceSchema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + Description: "Data processing location. This is where Fivetran will operate and run computation on data.", + }, + "name": resourceSchema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + Description: "The private link name within the account. The name must start with a letter or underscore and can only contain letters, numbers, or underscores. Maximum size of name is 23 characters.", + }, + "service": resourceSchema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + Description: "Service type.", + }, + "cloud_provider": resourceSchema.StringAttribute{ + Computed: true, + Description: "The cloud provider name.", + }, + "state": resourceSchema.StringAttribute{ + Computed: true, + Description: "The state of the private link.", + }, + "state_summary": resourceSchema.StringAttribute{ + Computed: true, + Description: "The state of the private link.", + }, + "created_at": resourceSchema.StringAttribute{ + Computed: true, + Description: "The date and time the membership was created.", + }, + "created_by": resourceSchema.StringAttribute{ + Computed: true, + Description: "The unique identifier for the User within the Fivetran system.", + }, + "config_map": resourceSchema.MapAttribute{ + ElementType: types.StringType, + Required: true, + MarkdownDescription: `Configuration. + +#### Possible values +-- ` + "`connection_service_name` (String)" + `: The name of your connection service. +-- ` + "`account_url` (String)" + `: The URL of your account. +-- ` + "`vpce_id` (String)" + `: The ID of your Virtual Private Cloud Endpoint. +-- ` + "`aws_account_id` (String)" + `: The ID of your AWS account. +-- ` + "`cluster_identifier` (String)" + `: The cluster identifier. +-- ` + "`connection_service_id` (String)" + `: The ID of your connection service. +-- ` + "`workspace_url` (String)" + `: The URL of your workspace. +-- ` + "`pls_id` (String)" + `: The ID of your Azure Private Link service. +-- ` + "`sub_resource_name` (String)" + `: The name of subresource. +-- ` + "`private_dns_regions` (String)" + `: Private DNS Regions. +-- ` + "`private_connection_service_id` (String)" + `: The ID of your connection service.`, + }, + }, + } +} + +func PrivateLinkDatasource() datasourceSchema.Schema { + return datasourceSchema.Schema { + Attributes: map[string]datasourceSchema.Attribute{ + "id": datasourceSchema.StringAttribute{ + Required: true, + Description: "The unique identifier for the private link within the Fivetran system.", + }, + "region": datasourceSchema.StringAttribute{ + Computed: true, + Description: "Data processing location. This is where Fivetran will operate and run computation on data.", + }, + "name": datasourceSchema.StringAttribute{ + Computed: true, + Description: "The private link name within the account. The name must start with a letter or underscore and can only contain letters, numbers, or underscores. Maximum size of name is 23 characters.", + }, + "service": datasourceSchema.StringAttribute{ + Computed: true, + Description: "Service type.", + }, + "cloud_provider": datasourceSchema.StringAttribute{ + Computed: true, + Description: "The cloud provider name.", + }, + "state": datasourceSchema.StringAttribute{ + Computed: true, + Description: "The state of the private link.", + }, + "state_summary": datasourceSchema.StringAttribute{ + Computed: true, + Description: "The state of the private link.", + }, + "created_at": datasourceSchema.StringAttribute{ + Computed: true, + Description: "The date and time the membership was created.", + }, + "created_by": datasourceSchema.StringAttribute{ + Computed: true, + Description: "The unique identifier for the User within the Fivetran system.", + }, + "config_map": resourceSchema.MapAttribute{ + ElementType: types.StringType, + Computed: true, + MarkdownDescription: `Configuration.`, + }, + }, + } +} + +func PrivateLinksDatasource() datasourceSchema.Schema { + return datasourceSchema.Schema { + Blocks: map[string]datasourceSchema.Block{ + "items": datasourceSchema.SetNestedBlock{ + NestedObject: datasourceSchema.NestedBlockObject{ + Attributes: map[string]datasourceSchema.Attribute{ + "id": datasourceSchema.StringAttribute{ + Computed: true, + Description: "The unique identifier for the private link within the Fivetran system.", + }, + "region": datasourceSchema.StringAttribute{ + Computed: true, + Description: "Data processing location. This is where Fivetran will operate and run computation on data.", + }, + "name": datasourceSchema.StringAttribute{ + Computed: true, + Description: "The private link name within the account. The name must start with a letter or underscore and can only contain letters, numbers, or underscores. Maximum size of name is 23 characters.", + }, + "service": datasourceSchema.StringAttribute{ + Computed: true, + Description: "Service type.", + }, + "cloud_provider": datasourceSchema.StringAttribute{ + Computed: true, + Description: "The cloud provider name.", + }, + "state": datasourceSchema.StringAttribute{ + Computed: true, + Description: "The state of the private link.", + }, + "state_summary": datasourceSchema.StringAttribute{ + Computed: true, + Description: "The state of the private link.", + }, + "created_at": datasourceSchema.StringAttribute{ + Computed: true, + Description: "The date and time the membership was created.", + }, + "created_by": datasourceSchema.StringAttribute{ + Computed: true, + Description: "The unique identifier for the User within the Fivetran system.", + }, + }, + }, + }, + }, + } +} diff --git a/fivetran/framework/datasources/local_processing_agent.go b/fivetran/framework/datasources/local_processing_agent.go index 28840514..3e588bca 100644 --- a/fivetran/framework/datasources/local_processing_agent.go +++ b/fivetran/framework/datasources/local_processing_agent.go @@ -44,7 +44,7 @@ func (d *localProcessingAgent) Read(ctx context.Context, req datasource.ReadRequ resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) - detailsResponse, err := d.GetClient().NewLocalProcessingAgentDetails().AgentId(data.Id.ValueString()).Do(ctx) + detailsResponse, err := d.GetClient().NewHybridDeploymentAgentDetails().AgentId(data.Id.ValueString()).Do(ctx) if err != nil { resp.Diagnostics.AddError( diff --git a/fivetran/framework/datasources/local_processing_agent_test.go b/fivetran/framework/datasources/local_processing_agent_test.go index 66faa40c..8f57ebf5 100644 --- a/fivetran/framework/datasources/local_processing_agent_test.go +++ b/fivetran/framework/datasources/local_processing_agent_test.go @@ -41,7 +41,7 @@ const ( func setupMockClientLocalProcessingAgentDataSourceConfigMapping(t *testing.T) { tfmock.MockClient().Reset() - lpaDataSourceMockGetHandler = tfmock.MockClient().When(http.MethodGet, "/v1/local-processing-agents/lpa_id").ThenCall( + lpaDataSourceMockGetHandler = tfmock.MockClient().When(http.MethodGet, "/v1/hybrid-deployment-agents/lpa_id").ThenCall( func(req *http.Request) (*http.Response, error) { lpaDataSourceMockData = tfmock.CreateMapFromJsonString(t, lpaMappingResponse) return tfmock.FivetranSuccessResponse(t, req, http.StatusOK, "Success", lpaDataSourceMockData), nil @@ -66,12 +66,6 @@ func TestDataSourceLocalProcessingAgentMappingMock(t *testing.T) { resource.TestCheckResourceAttr("data.fivetran_local_processing_agent.test_lpa", "display_name", "display_name"), resource.TestCheckResourceAttr("data.fivetran_local_processing_agent.test_lpa", "group_id", "group_id"), resource.TestCheckResourceAttr("data.fivetran_local_processing_agent.test_lpa", "registered_at", "registered_at"), - /*resource.TestCheckResourceAttr("data.fivetran_local_processing_agent.test_lpa", "usage.0.connection_id", "connection_id1"), - resource.TestCheckResourceAttr("data.fivetran_local_processing_agent.test_lpa", "usage.0.schema", "schema1"), - resource.TestCheckResourceAttr("data.fivetran_local_processing_agent.test_lpa", "usage.0.service", "service1"), - resource.TestCheckResourceAttr("data.fivetran_local_processing_agent.test_lpa", "usage.1.connection_id", "connection_id2"), - resource.TestCheckResourceAttr("data.fivetran_local_processing_agent.test_lpa", "usage.1.schema", "schema2"), - resource.TestCheckResourceAttr("data.fivetran_local_processing_agent.test_lpa", "usage.1.service", "service2"),*/ ), } diff --git a/fivetran/framework/datasources/local_processing_agents.go b/fivetran/framework/datasources/local_processing_agents.go index 3b71d769..b1034a15 100644 --- a/fivetran/framework/datasources/local_processing_agents.go +++ b/fivetran/framework/datasources/local_processing_agents.go @@ -7,7 +7,7 @@ import ( "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core" "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core/model" "github.com/hashicorp/terraform-plugin-framework/datasource" - sdk "github.com/fivetran/go-fivetran/local_processing_agent" + sdk "github.com/fivetran/go-fivetran/hybrid_deployment_agent" fivetranSchema "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core/schema" ) @@ -45,13 +45,13 @@ func (d *localProcessingAgents) Read(ctx context.Context, req datasource.ReadReq resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) var respNextCursor string - var listResponse sdk.LocalProcessingAgentListResponse + var listResponse sdk.HybridDeploymentAgentListResponse limit := 1000 for { var err error - var tmpResp sdk.LocalProcessingAgentListResponse - svc := d.GetClient().NewLocalProcessingAgentList() + var tmpResp sdk.HybridDeploymentAgentListResponse + svc := d.GetClient().NewHybridDeploymentAgentList() if respNextCursor == "" { tmpResp, err = svc.Limit(limit).Do(ctx) @@ -66,7 +66,7 @@ func (d *localProcessingAgents) Read(ctx context.Context, req datasource.ReadReq "Read error.", fmt.Sprintf("%v; code: %v", err, tmpResp.Code), ) - listResponse = sdk.LocalProcessingAgentListResponse{} + listResponse = sdk.HybridDeploymentAgentListResponse{} } listResponse.Data.Items = append(listResponse.Data.Items, tmpResp.Data.Items...) diff --git a/fivetran/framework/datasources/local_processing_agents_test.go b/fivetran/framework/datasources/local_processing_agents_test.go index 259242d8..c1cb2842 100644 --- a/fivetran/framework/datasources/local_processing_agents_test.go +++ b/fivetran/framework/datasources/local_processing_agents_test.go @@ -63,7 +63,7 @@ const ( func setupMockClientLocalProcessingAgentsDataSourceConfigMapping(t *testing.T) { tfmock.MockClient().Reset() - localProcessingAgentsDataSourceMockGetHandler = tfmock.MockClient().When(http.MethodGet, "/v1/local-processing-agents").ThenCall( + localProcessingAgentsDataSourceMockGetHandler = tfmock.MockClient().When(http.MethodGet, "/v1/hybrid-deployment-agents").ThenCall( func(req *http.Request) (*http.Response, error) { localProcessingAgentsDataSourceMockData = tfmock.CreateMapFromJsonString(t, localProcessingAgentsMappingResponse) return tfmock.FivetranSuccessResponse(t, req, http.StatusOK, "Success", localProcessingAgentsDataSourceMockData), nil @@ -87,23 +87,9 @@ func TestDataSourceLocalProcessingAgentsMappingMock(t *testing.T) { resource.TestCheckResourceAttr("data.fivetran_local_processing_agents.test_lpa", "items.0.display_name", "display_name1"), resource.TestCheckResourceAttr("data.fivetran_local_processing_agents.test_lpa", "items.0.group_id", "group_id1"), resource.TestCheckResourceAttr("data.fivetran_local_processing_agents.test_lpa", "items.0.registered_at", "registered_at1"), - /*resource.TestCheckResourceAttr("data.fivetran_local_processing_agents.test_lpa", "items.0.usage.0.connection_id", "connection_id11"), - resource.TestCheckResourceAttr("data.fivetran_local_processing_agents.test_lpa", "items.0.usage.0.schema", "schema11"), - resource.TestCheckResourceAttr("data.fivetran_local_processing_agents.test_lpa", "items.0.usage.0.service", "service11"), - resource.TestCheckResourceAttr("data.fivetran_local_processing_agents.test_lpa", "items.0.usage.1.connection_id", "connection_id12"), - resource.TestCheckResourceAttr("data.fivetran_local_processing_agents.test_lpa", "items.0.usage.1.schema", "schema12"), - resource.TestCheckResourceAttr("data.fivetran_local_processing_agents.test_lpa", "items.0.usage.1.service", "service12"),*/ - resource.TestCheckResourceAttr("data.fivetran_local_processing_agents.test_lpa", "items.1.display_name", "display_name2"), resource.TestCheckResourceAttr("data.fivetran_local_processing_agents.test_lpa", "items.1.group_id", "group_id2"), - resource.TestCheckResourceAttr("data.fivetran_local_processing_agents.test_lpa", "items.1.registered_at", "registered_at2"), - /*resource.TestCheckResourceAttr("data.fivetran_local_processing_agents.test_lpa", "items.1.usage.0.connection_id", "connection_id21"), - resource.TestCheckResourceAttr("data.fivetran_local_processing_agents.test_lpa", "items.1.usage.0.schema", "schema21"), - resource.TestCheckResourceAttr("data.fivetran_local_processing_agents.test_lpa", "items.1.usage.0.service", "service21"), - resource.TestCheckResourceAttr("data.fivetran_local_processing_agents.test_lpa", "items.1.usage.1.connection_id", "connection_id22"), - resource.TestCheckResourceAttr("data.fivetran_local_processing_agents.test_lpa", "items.1.usage.1.schema", "schema22"), - resource.TestCheckResourceAttr("data.fivetran_local_processing_agents.test_lpa", "items.1.usage.1.service", "service22"),*/ - ), + resource.TestCheckResourceAttr("data.fivetran_local_processing_agents.test_lpa", "items.1.registered_at", "registered_at2"), ), } resource.Test( diff --git a/fivetran/framework/datasources/private_link.go b/fivetran/framework/datasources/private_link.go new file mode 100644 index 00000000..d506fe6d --- /dev/null +++ b/fivetran/framework/datasources/private_link.go @@ -0,0 +1,60 @@ +package datasources + +import ( + "context" + "fmt" + + "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core" + "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core/model" + "github.com/hashicorp/terraform-plugin-framework/datasource" + + fivetranSchema "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core/schema" +) + +func PrivateLink() datasource.DataSource { + return &privateLink{} +} + +// Ensure the implementation satisfies the desired interfaces. +var _ datasource.DataSourceWithConfigure = &privateLink{} + +type privateLink struct { + core.ProviderDatasource +} + +func (d *privateLink) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = "fivetran_private_link" +} + +func (d *privateLink) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = fivetranSchema.PrivateLinkDatasource() +} + +func (d *privateLink) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + if d.GetClient() == nil { + resp.Diagnostics.AddError( + "Unconfigured Fivetran Client", + "Please report this issue to the provider developers.", + ) + + return + } + + var data model.PrivateLink + + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + + detailsResponse, err := d.GetClient().NewPrivateLinkDetails().PrivateLinkId(data.Id.ValueString()).Do(ctx) + + if err != nil { + resp.Diagnostics.AddError( + "Read error.", + fmt.Sprintf("%v; code: %v", err, detailsResponse.Code), + ) + return + } + + data.ReadFromResponse(ctx, detailsResponse) + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} \ No newline at end of file diff --git a/fivetran/framework/datasources/private_link_test.go b/fivetran/framework/datasources/private_link_test.go new file mode 100644 index 00000000..4b842aea --- /dev/null +++ b/fivetran/framework/datasources/private_link_test.go @@ -0,0 +1,92 @@ +package datasources_test + +import ( + "net/http" + "testing" + + "github.com/fivetran/go-fivetran/tests/mock" + tfmock "github.com/fivetran/terraform-provider-fivetran/fivetran/tests/mock" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" +) + +const ( + privateLinkMappingResponse = ` + { + "id": "id", + "name": "name", + "region": "region", + "service": "service", + "account_id": "account_id", + "cloud_provider": "cloud_provider", + "state": "state", + "state_summary": "state_summary", + "created_at": "created_at", + "created_by": "created_by", + "config": { + "connection_service_name": "connection_service_name" + } + } + ` +) + +var ( + privateLinkDataSourceMockGetHandler *mock.Handler + + privateLinkDataSourceMockData map[string]interface{} +) + +func setupMockClientPrivateLinkDataSourceConfigMapping(t *testing.T) { + tfmock.MockClient().Reset() + + privateLinkDataSourceMockGetHandler = tfmock.MockClient().When(http.MethodGet, "/v1/private-links/id").ThenCall( + func(req *http.Request) (*http.Response, error) { + privateLinkDataSourceMockData = tfmock.CreateMapFromJsonString(t, privateLinkMappingResponse) + return tfmock.FivetranSuccessResponse(t, req, http.StatusOK, "Success", privateLinkDataSourceMockData), nil + }, + ) +} + +func TestDataSourcePrivateLinkConfigMappingMock(t *testing.T) { + step1 := resource.TestStep{ + Config: ` + data "fivetran_private_link" "test_pl" { + provider = fivetran-provider + id = "id" + }`, + + Check: resource.ComposeAggregateTestCheckFunc( + func(s *terraform.State) error { + tfmock.AssertEqual(t, privateLinkDataSourceMockGetHandler.Interactions, 1) + tfmock.AssertNotEmpty(t, privateLinkDataSourceMockData) + return nil + }, + resource.TestCheckResourceAttr("data.fivetran_private_link.test_pl", "name", "name"), + resource.TestCheckResourceAttr("data.fivetran_private_link.test_pl", "region", "region"), + resource.TestCheckResourceAttr("data.fivetran_private_link.test_pl", "service", "service"), + resource.TestCheckResourceAttr("data.fivetran_private_link.test_pl", "cloud_provider", "cloud_provider"), + resource.TestCheckResourceAttr("data.fivetran_private_link.test_pl", "state", "state"), + resource.TestCheckResourceAttr("data.fivetran_private_link.test_pl", "state_summary", "state_summary"), + resource.TestCheckResourceAttr("data.fivetran_private_link.test_pl", "created_at", "created_at"), + resource.TestCheckResourceAttr("data.fivetran_private_link.test_pl", "created_by", "created_by"), + + resource.TestCheckResourceAttr("data.fivetran_private_link.test_pl", "config_map.connection_service_name", "connection_service_name"), + ), + } + + resource.Test( + t, + resource.TestCase{ + PreCheck: func() { + setupMockClientPrivateLinkDataSourceConfigMapping(t) + }, + ProtoV6ProviderFactories: tfmock.ProtoV6ProviderFactories, + CheckDestroy: func(s *terraform.State) error { + return nil + }, + Steps: []resource.TestStep{ + step1, + }, + }, + ) +} diff --git a/fivetran/framework/datasources/private_links.go b/fivetran/framework/datasources/private_links.go new file mode 100644 index 00000000..005bc5cf --- /dev/null +++ b/fivetran/framework/datasources/private_links.go @@ -0,0 +1,84 @@ +package datasources + +import ( + "context" + "fmt" + + "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core" + "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core/model" + "github.com/hashicorp/terraform-plugin-framework/datasource" + sdk "github.com/fivetran/go-fivetran/private_link" + + fivetranSchema "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core/schema" +) + +func PrivateLinks() datasource.DataSource { + return &privateLinks{} +} + +// Ensure the implementation satisfies the desired interfaces. +var _ datasource.DataSourceWithConfigure = &privateLinks{} + +type privateLinks struct { + core.ProviderDatasource +} + +func (d *privateLinks) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = "fivetran_private_links" +} + +func (d *privateLinks) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = fivetranSchema.PrivateLinksDatasource() +} + +func (d *privateLinks) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + if d.GetClient() == nil { + resp.Diagnostics.AddError( + "Unconfigured Fivetran Client", + "Please report this issue to the provider developers.", + ) + + return + } + + var data model.PrivateLinks + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + + var respNextCursor string + var listResponse sdk.PrivateLinkListResponse + limit := 1000 + + for { + var err error + var tmpResp sdk.PrivateLinkListResponse + svc := d.GetClient().NewPrivateLinkList() + + if respNextCursor == "" { + tmpResp, err = svc.Limit(limit).Do(ctx) + } + + if respNextCursor != "" { + tmpResp, err = svc.Limit(limit).Cursor(respNextCursor).Do(ctx) + } + + if err != nil { + resp.Diagnostics.AddError( + "Read error.", + fmt.Sprintf("%v; code: %v", err, tmpResp.Code), + ) + listResponse = sdk.PrivateLinkListResponse{} + } + + listResponse.Data.Items = append(listResponse.Data.Items, tmpResp.Data.Items...) + + if tmpResp.Data.NextCursor == "" { + break + } + + respNextCursor = tmpResp.Data.NextCursor + } + + data.ReadFromResponse(ctx, listResponse) + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} \ No newline at end of file diff --git a/fivetran/framework/datasources/private_links_test.go b/fivetran/framework/datasources/private_links_test.go new file mode 100644 index 00000000..cc603639 --- /dev/null +++ b/fivetran/framework/datasources/private_links_test.go @@ -0,0 +1,111 @@ +package datasources_test + +import ( + "net/http" + "testing" + + "github.com/fivetran/go-fivetran/tests/mock" + tfmock "github.com/fivetran/terraform-provider-fivetran/fivetran/tests/mock" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" +) + +const ( + privateLinksMappingResponse = ` + { + "items": [ + { + "id": "id1", + "name": "name1", + "region": "region1", + "service": "service1", + "account_id": "account_id1", + "cloud_provider": "cloud_provider1", + "state": "state1", + "state_summary": "state_summary1", + "created_at": "created_at1", + "created_by": "created_by1" + }, + { + "id": "id2", + "name": "name2", + "region": "region2", + "service": "service2", + "account_id": "account_id2", + "cloud_provider": "cloud_provider2", + "state": "state2", + "state_summary": "state_summary2", + "created_at": "created_at2", + "created_by": "created_by2" + } + ], + "next_cursor": null + }` +) + +var ( + privateLinksDataSourceMockGetHandler *mock.Handler + + privateLinksDataSourceMockData map[string]interface{} +) + +func setupMockClientPrivateLinksDataSourceConfigMapping(t *testing.T) { + tfmock.MockClient().Reset() + + privateLinksDataSourceMockGetHandler = tfmock.MockClient().When(http.MethodGet, "/v1/private-links").ThenCall( + func(req *http.Request) (*http.Response, error) { + privateLinksDataSourceMockData = tfmock.CreateMapFromJsonString(t, privateLinksMappingResponse) + return tfmock.FivetranSuccessResponse(t, req, http.StatusOK, "Success", privateLinksDataSourceMockData), nil + }, + ) +} + +func TestDataSourcePrivateLinksConfigMappingMock(t *testing.T) { + step1 := resource.TestStep{ + Config: ` + data "fivetran_private_links" "test_pl" { + provider = fivetran-provider + }`, + + Check: resource.ComposeAggregateTestCheckFunc( + func(s *terraform.State) error { + tfmock.AssertEqual(t, privateLinksDataSourceMockGetHandler.Interactions, 1) + tfmock.AssertNotEmpty(t, privateLinksDataSourceMockData) + return nil + }, + resource.TestCheckResourceAttr("data.fivetran_private_links.test_pl", "items.0.name", "name1"), + resource.TestCheckResourceAttr("data.fivetran_private_links.test_pl", "items.0.region", "region1"), + resource.TestCheckResourceAttr("data.fivetran_private_links.test_pl", "items.0.service", "service1"), + resource.TestCheckResourceAttr("data.fivetran_private_links.test_pl", "items.0.cloud_provider", "cloud_provider1"), + resource.TestCheckResourceAttr("data.fivetran_private_links.test_pl", "items.0.state", "state1"), + resource.TestCheckResourceAttr("data.fivetran_private_links.test_pl", "items.0.state_summary", "state_summary1"), + resource.TestCheckResourceAttr("data.fivetran_private_links.test_pl", "items.0.created_at", "created_at1"), + resource.TestCheckResourceAttr("data.fivetran_private_links.test_pl", "items.0.created_by", "created_by1"), + + resource.TestCheckResourceAttr("data.fivetran_private_links.test_pl", "items.1.name", "name2"), + resource.TestCheckResourceAttr("data.fivetran_private_links.test_pl", "items.1.region", "region2"), + resource.TestCheckResourceAttr("data.fivetran_private_links.test_pl", "items.1.service", "service2"), + resource.TestCheckResourceAttr("data.fivetran_private_links.test_pl", "items.1.cloud_provider", "cloud_provider2"), + resource.TestCheckResourceAttr("data.fivetran_private_links.test_pl", "items.1.state", "state2"), + resource.TestCheckResourceAttr("data.fivetran_private_links.test_pl", "items.1.state_summary", "state_summary2"), + resource.TestCheckResourceAttr("data.fivetran_private_links.test_pl", "items.1.created_at", "created_at2"), + resource.TestCheckResourceAttr("data.fivetran_private_links.test_pl", "items.1.created_by", "created_by2"), + ), + } + + resource.Test( + t, + resource.TestCase{ + PreCheck: func() { + setupMockClientPrivateLinksDataSourceConfigMapping(t) + }, + ProtoV6ProviderFactories: tfmock.ProtoV6ProviderFactories, + CheckDestroy: func(s *terraform.State) error { + return nil + }, + Steps: []resource.TestStep{ + step1, + }, + }, + ) +} diff --git a/fivetran/framework/provider.go b/fivetran/framework/provider.go index d29c966a..d6d018a8 100644 --- a/fivetran/framework/provider.go +++ b/fivetran/framework/provider.go @@ -17,7 +17,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" ) -const Version = "1.3.2" // Current provider version +const Version = "1.4.0" // Current provider version type fivetranProvider struct { mockClient httputils.HttpClient @@ -121,6 +121,7 @@ func (p *fivetranProvider) Resources(ctx context.Context) []func() resource.Reso resources.ProxyAgent, resources.LocalProcessingAgent, resources.DbtGitProjectConfig, + resources.PrivateLink, } } @@ -160,5 +161,7 @@ func (p *fivetranProvider) DataSources(ctx context.Context) []func() datasource. datasources.ProxyAgents, datasources.LocalProcessingAgent, datasources.LocalProcessingAgents, + datasources.PrivateLink, + datasources.PrivateLinks, } } diff --git a/fivetran/framework/resources/connector.go b/fivetran/framework/resources/connector.go index 825c28a0..a7014f8a 100644 --- a/fivetran/framework/resources/connector.go +++ b/fivetran/framework/resources/connector.go @@ -45,7 +45,6 @@ func (r *connector) ImportState(ctx context.Context, req resource.ImportStateReq } func (r *connector) UpgradeState(ctx context.Context) map[int64]resource.StateUpgrader { - v0ConfigTfTypes := model.GetTfTypes(common.GetConfigFieldsMap(), 1) v0ConfigTfTypes["servers"] = tftypes.String @@ -70,6 +69,12 @@ func (r *connector) UpgradeState(ctx context.Context) map[int64]resource.StateUp upgradeConnectorState(ctx, req, resp, 2) }, }, + // State upgrade implementation from 2 (prior state version) to 4 (Schema.Version) + 3: { + StateUpgrader: func(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) { + upgradeConnectorState(ctx, req, resp, 3) + }, + }, } } @@ -154,7 +159,11 @@ func (r *connector) Create(ctx context.Context, req resource.CreateRequest, resp } if data.LocalProcessingAgentId.ValueString() != "" { - svc.LocalProcessingAgentId(data.LocalProcessingAgentId.ValueString()) + svc.HybridDeploymentAgentId(data.LocalProcessingAgentId.ValueString()) + } + + if data.PrivateLinkId.ValueString() != "" { + svc.PrivateLinkId(data.PrivateLinkId.ValueString()) } if !noAuth { @@ -318,7 +327,11 @@ func (r *connector) Update(ctx context.Context, req resource.UpdateRequest, resp ConnectorID(state.Id.ValueString()) if plan.LocalProcessingAgentId.ValueString() != "" { - svc.LocalProcessingAgentId(plan.LocalProcessingAgentId.ValueString()) + svc.HybridDeploymentAgentId(plan.LocalProcessingAgentId.ValueString()) + } + + if plan.PrivateLinkId.ValueString() != "" { + svc.PrivateLinkId(plan.PrivateLinkId.ValueString()) } if len(patch) > 0 { diff --git a/fivetran/framework/resources/connector_migrations.go b/fivetran/framework/resources/connector_migrations.go index 5369aea8..dde1180a 100644 --- a/fivetran/framework/resources/connector_migrations.go +++ b/fivetran/framework/resources/connector_migrations.go @@ -36,8 +36,8 @@ func upgradeConnectorState(ctx context.Context, req resource.UpgradeStateRequest } dynamicValue, err := tfprotov6.NewDynamicValue( - getConnectorStateModel(3), - tftypes.NewValue(getConnectorStateModel(3), map[string]tftypes.Value{ + getConnectorStateModel(4), + tftypes.NewValue(getConnectorStateModel(4), map[string]tftypes.Value{ "id": rawState["id"], "name": rawState["name"], "connected_by": rawState["connected_by"], @@ -48,6 +48,7 @@ func upgradeConnectorState(ctx context.Context, req resource.UpgradeStateRequest "networking_method": tftypes.NewValue(tftypes.String, nil), "proxy_agent_id": tftypes.NewValue(tftypes.String, nil), "local_processing_agent_id": tftypes.NewValue(tftypes.String, nil), + "private_link_id": tftypes.NewValue(tftypes.String, nil), "run_setup_tests": convertStringStateValueToBool("run_setup_tests", rawState["run_setup_tests"], resp.Diagnostics), "trust_fingerprints": convertStringStateValueToBool("trust_fingerprints", rawState["trust_fingerprints"], resp.Diagnostics), @@ -96,16 +97,15 @@ func getConnectorStateModel(version int) tftypes.Type { }, }, } - if version == 3 { + if version == 3 || version == 4 { base["destination_schema"] = dsObj base["run_setup_tests"] = tftypes.Bool base["trust_certificates"] = tftypes.Bool base["trust_fingerprints"] = tftypes.Bool - base["proxy_agent_id"] = tftypes.String base["networking_method"] = tftypes.String base["local_processing_agent_id"] = tftypes.String - + base["private_link_id"] = tftypes.String base["config"] = tftypes.Object{AttributeTypes: model.GetTfTypes(common.GetConfigFieldsMap(), 3)} base["auth"] = tftypes.Object{AttributeTypes: model.GetTfTypes(common.GetAuthFieldsMap(), 3)} } else { diff --git a/fivetran/framework/resources/destination.go b/fivetran/framework/resources/destination.go index 791f287c..21cc359c 100644 --- a/fivetran/framework/resources/destination.go +++ b/fivetran/framework/resources/destination.go @@ -49,6 +49,11 @@ func (r *destination) UpgradeState(ctx context.Context) map[int64]resource.State upgradeDestinationState(ctx, req, resp, 0) }, }, + 1: { + StateUpgrader: func(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) { + upgradeDestinationState(ctx, req, resp, 1) + }, + }, } } @@ -101,13 +106,17 @@ func (r *destination) Create(ctx context.Context, req resource.CreateRequest, re ConfigCustom(&configMap) if data.LocalProcessingAgentId.ValueString() != "" { - svc.LocalProcessingAgentId(data.LocalProcessingAgentId.ValueString()) + svc.HybridDeploymentAgentId(data.LocalProcessingAgentId.ValueString()) } if data.NetworkingMethod.ValueString() != "" { svc.NetworkingMethod(data.NetworkingMethod.ValueString()) } + if data.PrivateLinkId.ValueString() != "" { + svc.PrivateLinkId(data.PrivateLinkId.ValueString()) + } + response, err := svc. DoCustom(ctx) @@ -283,13 +292,17 @@ func (r *destination) Update(ctx context.Context, req resource.UpdateRequest, re DestinationID(state.Id.ValueString()) if plan.LocalProcessingAgentId.ValueString() != "" { - svc.LocalProcessingAgentId(plan.LocalProcessingAgentId.ValueString()) + svc.HybridDeploymentAgentId(plan.LocalProcessingAgentId.ValueString()) } if plan.NetworkingMethod.ValueString() != "" { svc.NetworkingMethod(plan.NetworkingMethod.ValueString()) } + if plan.PrivateLinkId.ValueString() != "" { + svc.PrivateLinkId(plan.PrivateLinkId.ValueString()) + } + if len(patch) > 0 { svc.ConfigCustom(&patch) } diff --git a/fivetran/framework/resources/destination_migrations.go b/fivetran/framework/resources/destination_migrations.go index c49c0354..f64255c2 100644 --- a/fivetran/framework/resources/destination_migrations.go +++ b/fivetran/framework/resources/destination_migrations.go @@ -32,8 +32,8 @@ func upgradeDestinationState(ctx context.Context, req resource.UpgradeStateReque } dynamicValue, err := tfprotov6.NewDynamicValue( - getDestinationStateModel(1), - tftypes.NewValue(getDestinationStateModel(1), map[string]tftypes.Value{ + getDestinationStateModel(2), + tftypes.NewValue(getDestinationStateModel(2), map[string]tftypes.Value{ "id": rawState["id"], "group_id": rawState["group_id"], "service": rawState["service"], @@ -44,6 +44,7 @@ func upgradeDestinationState(ctx context.Context, req resource.UpgradeStateReque "daylight_saving_time_enabled": tftypes.NewValue(tftypes.Bool, nil), "local_processing_agent_id": tftypes.NewValue(tftypes.String, nil), "networking_method": tftypes.NewValue(tftypes.String, nil), + "private_link_id": tftypes.NewValue(tftypes.String, nil), "run_setup_tests": convertStringStateValueToBool("run_setup_tests", rawState["run_setup_tests"], resp.Diagnostics), "trust_fingerprints": convertStringStateValueToBool("trust_fingerprints", rawState["trust_fingerprints"], resp.Diagnostics), @@ -75,7 +76,7 @@ func getDestinationStateModel(version int) tftypes.Type { }, }, } - if version == 1 { + if version == 1 || version == 2 { base["run_setup_tests"] = tftypes.Bool base["trust_certificates"] = tftypes.Bool base["trust_fingerprints"] = tftypes.Bool diff --git a/fivetran/framework/resources/local_processing_agent.go b/fivetran/framework/resources/local_processing_agent.go index 7f5b09db..6652ff0c 100644 --- a/fivetran/framework/resources/local_processing_agent.go +++ b/fivetran/framework/resources/local_processing_agent.go @@ -54,7 +54,7 @@ func (r *localProcessingAgent) Create(ctx context.Context, req resource.CreateRe return } - svc := r.GetClient().NewLocalProcessingAgentCreate() + svc := r.GetClient().NewHybridDeploymentAgentCreate() svc.GroupId(data.GroupId.ValueString()) svc.DisplayName(data.DisplayName.ValueString()) svc.EnvType("DOCKER") @@ -90,7 +90,7 @@ func (r *localProcessingAgent) Read(ctx context.Context, req resource.ReadReques // Read Terraform prior state data into the model resp.Diagnostics.Append(req.State.Get(ctx, &data)...) - readResponse, err := r.GetClient().NewLocalProcessingAgentDetails().AgentId(data.Id.ValueString()).Do(ctx) + readResponse, err := r.GetClient().NewHybridDeploymentAgentDetails().AgentId(data.Id.ValueString()).Do(ctx) if err != nil { resp.Diagnostics.AddError( @@ -120,7 +120,7 @@ func (r *localProcessingAgent) Update(ctx context.Context, req resource.UpdateRe resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - svc := r.GetClient().NewLocalProcessingAgentReAuth().AgentId(state.Id.ValueString()) + svc := r.GetClient().NewHybridDeploymentAgentReAuth().AgentId(state.Id.ValueString()) updateResponse, err := svc.Do(ctx) if err != nil { @@ -150,7 +150,7 @@ func (r *localProcessingAgent) Delete(ctx context.Context, req resource.DeleteRe resp.Diagnostics.Append(req.State.Get(ctx, &data)...) - deleteResponse, err := r.GetClient().NewLocalProcessingAgentDelete().AgentId(data.Id.ValueString()).Do(ctx) + deleteResponse, err := r.GetClient().NewHybridDeploymentAgentDelete().AgentId(data.Id.ValueString()).Do(ctx) if err != nil { resp.Diagnostics.AddError( "Unable to Delete Local Processing Agent Resource.", diff --git a/fivetran/framework/resources/local_processing_agent_test.go b/fivetran/framework/resources/local_processing_agent_test.go index 97a804bb..c28db858 100644 --- a/fivetran/framework/resources/local_processing_agent_test.go +++ b/fivetran/framework/resources/local_processing_agent_test.go @@ -31,20 +31,20 @@ func setupMockClientLocalProcessingAgentResource(t *testing.T) { } }` - localProcessingAgentPostHandler = tfmock.MockClient().When(http.MethodPost, "/v1/local-processing-agents").ThenCall( + localProcessingAgentPostHandler = tfmock.MockClient().When(http.MethodPost, "/v1/hybrid-deployment-agents").ThenCall( func(req *http.Request) (*http.Response, error) { localProcessingAgentData = tfmock.CreateMapFromJsonString(t, localProcessingAgentResponse) return tfmock.FivetranSuccessResponse(t, req, http.StatusCreated, "Local Processing Agent has been created", localProcessingAgentData), nil }, ) - tfmock.MockClient().When(http.MethodGet, "/v1/local-processing-agents/lpa_id").ThenCall( + tfmock.MockClient().When(http.MethodGet, "/v1/hybrid-deployment-agents/lpa_id").ThenCall( func(req *http.Request) (*http.Response, error) { return tfmock.FivetranSuccessResponse(t, req, http.StatusOK, "", localProcessingAgentData), nil }, ) - localProcessingAgentDeleteHandler = tfmock.MockClient().When(http.MethodDelete, "/v1/local-processing-agents/lpa_id").ThenCall( + localProcessingAgentDeleteHandler = tfmock.MockClient().When(http.MethodDelete, "/v1/hybrid-deployment-agents/lpa_id").ThenCall( func(req *http.Request) (*http.Response, error) { return tfmock.FivetranSuccessResponse(t, req, 200, "Local Processing Agent has been deleted", nil), nil }, diff --git a/fivetran/framework/resources/private_link.go b/fivetran/framework/resources/private_link.go new file mode 100644 index 00000000..47b97fd2 --- /dev/null +++ b/fivetran/framework/resources/private_link.go @@ -0,0 +1,167 @@ +package resources + +import ( + "context" + "fmt" + + "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core" + "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core/model" + fivetranSchema "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core/schema" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +func PrivateLink() resource.Resource { + return &privateLink{} +} + +type privateLink struct { + core.ProviderResource +} + +// Ensure the implementation satisfies the desired interfaces. +var _ resource.ResourceWithConfigure = &privateLink{} +var _ resource.ResourceWithImportState = &privateLink{} + +func (r *privateLink) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_private_link" +} + +func (r *privateLink) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = fivetranSchema.PrivateLinkResource() +} + +func (r *privateLink) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *privateLink) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + if r.GetClient() == nil { + resp.Diagnostics.AddError( + "Unconfigured Fivetran Client", + "Please report this issue to the provider developers.", + ) + + return + } + + var data model.PrivateLink + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + svc := r.GetClient().NewPrivateLinkCreate() + svc.Region(data.Region.ValueString()) + svc.Service(data.Service.ValueString()) + svc.Name(data.Name.ValueString()) + + configMap := data.GetConfigMap() + svc.ConfigCustom(&configMap) + + createResponse, err := svc.DoCustom(ctx) + if err != nil { + resp.Diagnostics.AddError( + "Unable to Create Private Link Resource.", + fmt.Sprintf("%v; code: %v; message: %v", err, createResponse.Code, createResponse.Message), + ) + + return + } + + data.ReadFromCustomResponse(ctx, createResponse) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *privateLink) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + if r.GetClient() == nil { + resp.Diagnostics.AddError( + "Unconfigured Fivetran Client", + "Please report this issue to the provider developers.", + ) + + return + } + + var data model.PrivateLink + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + readResponse, err := r.GetClient().NewPrivateLinkDetails().PrivateLinkId(data.Id.ValueString()).DoCustom(ctx) + + if err != nil { + resp.Diagnostics.AddError( + "Unable to Read Private Link Resource.", + fmt.Sprintf("%v; code: %v", err, readResponse.Code), + ) + return + } + + data.ReadFromCustomResponse(ctx, readResponse) + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *privateLink) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + resp.Diagnostics.AddError( + "Update Private Link does not support", + "Please report this issue to the provider developers.", + ) + return + + var plan, state model.PrivateLink + hasChanges := false + + svc := r.GetClient().NewPrivateLinkModify().PrivateLinkId(state.Id.ValueString()) + + if !plan.ConfigMap.Equal(state.ConfigMap) { + configMap := plan.GetConfigMap() + svc.ConfigCustom(&configMap) + hasChanges = true + } + + if hasChanges { + updateResponse, err := svc.DoCustom(ctx) + + if err != nil { + resp.Diagnostics.AddError( + "Unable to Update Private Link Resource.", + fmt.Sprintf("%v; code: %v; message: %v", err, updateResponse.Code, updateResponse.Message), + ) + return + } + + state.ReadFromCustomResponse(ctx, updateResponse) + } + + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *privateLink) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + if r.GetClient() == nil { + resp.Diagnostics.AddError( + "Unconfigured Fivetran Client", + "Please report this issue to the provider developers.", + ) + + return + } + + var data model.PrivateLink + + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + deleteResponse, err := r.GetClient().NewPrivateLinkDelete().PrivateLinkId(data.Id.ValueString()).Do(ctx) + if err != nil { + resp.Diagnostics.AddError( + "Unable to Delete Private Link Resource.", + fmt.Sprintf("%v; code: %v; message: %v", err, deleteResponse.Code, deleteResponse.Message), + ) + return + } +} diff --git a/fivetran/framework/resources/private_link_test.go b/fivetran/framework/resources/private_link_test.go new file mode 100644 index 00000000..5310a3d5 --- /dev/null +++ b/fivetran/framework/resources/private_link_test.go @@ -0,0 +1,104 @@ +package resources_test + +import ( + "net/http" + "testing" + + tfmock "github.com/fivetran/terraform-provider-fivetran/fivetran/tests/mock" + "github.com/fivetran/go-fivetran/tests/mock" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" +) + +var ( + privateLinkPostHandler *mock.Handler + privateLinkPatchHandler *mock.Handler + privateLinkDeleteHandler *mock.Handler + privateLinkData map[string]interface{} +) + +func setupMockClientPrivateLinkResource(t *testing.T) { + tfmock.MockClient().Reset() + privateLinkResponse := + `{ + "id": "pl_id", + "name": "name", + "region": "region", + "service": "service", + "account_id": "account_id", + "cloud_provider": "cloud_provider", + "state": "state", + "state_summary": "state_summary", + "created_at": "created_at", + "created_by": "created_by", + "config": { + "connection_service_name": "connection_service_name" + } + }` + + + privateLinkPostHandler = tfmock.MockClient().When(http.MethodPost, "/v1/private-links").ThenCall( + func(req *http.Request) (*http.Response, error) { + privateLinkData = tfmock.CreateMapFromJsonString(t, privateLinkResponse) + return tfmock.FivetranSuccessResponse(t, req, http.StatusCreated, "PrivateLink has been created", privateLinkData), nil + }, + ) + + tfmock.MockClient().When(http.MethodGet, "/v1/private-links/pl_id").ThenCall( + func(req *http.Request) (*http.Response, error) { + return tfmock.FivetranSuccessResponse(t, req, http.StatusOK, "", privateLinkData), nil + }, + ) + + privateLinkDeleteHandler = tfmock.MockClient().When(http.MethodDelete, "/v1/private-links/pl_id").ThenCall( + func(req *http.Request) (*http.Response, error) { + return tfmock.FivetranSuccessResponse(t, req, 200, "PrivateLink has been deleted", nil), nil + }, + ) +} + +func TestResourcePrivateLinkMock(t *testing.T) { + step1 := resource.TestStep{ + Config: ` + resource "fivetran_private_link" "test_pl" { + provider = fivetran-provider + + name = "name" + region = "region" + service = "service" + + config_map = { + connection_service_name = "connection_service_name" + } + }`, + + Check: resource.ComposeAggregateTestCheckFunc( + func(s *terraform.State) error { + tfmock.AssertEqual(t, privateLinkPostHandler.Interactions, 1) + return nil + }, + resource.TestCheckResourceAttr("fivetran_private_link.test_pl", "name", "name"), + resource.TestCheckResourceAttr("fivetran_private_link.test_pl", "region", "region"), + resource.TestCheckResourceAttr("fivetran_private_link.test_pl", "service", "service"), + resource.TestCheckResourceAttr("fivetran_private_link.test_pl", "config_map.connection_service_name", "connection_service_name"), + ), + } + + resource.Test( + t, + resource.TestCase{ + PreCheck: func() { + setupMockClientPrivateLinkResource(t) + }, + ProtoV6ProviderFactories: tfmock.ProtoV6ProviderFactories, + CheckDestroy: func(s *terraform.State) error { + tfmock.AssertEqual(t, privateLinkDeleteHandler.Interactions, 1) + return nil + }, + + Steps: []resource.TestStep{ + step1, + }, + }, + ) +} diff --git a/fivetran/tests/e2e/fivetran_test.go b/fivetran/tests/e2e/fivetran_test.go index 9d29bcbc..e06c7500 100644 --- a/fivetran/tests/e2e/fivetran_test.go +++ b/fivetran/tests/e2e/fivetran_test.go @@ -5,6 +5,8 @@ import ( "log" "os" "testing" + "math/rand" + "time" gofivetran "github.com/fivetran/go-fivetran" "github.com/fivetran/terraform-provider-fivetran/fivetran/framework" @@ -26,6 +28,7 @@ const ( var testProvioderFramework provider.Provider var client *gofivetran.Client +var seededRand *rand.Rand = rand.New(rand.NewSource(time.Now().UnixNano())) var ProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){ "fivetran-provider": func() (tfprotov6.ProviderServer, error) { @@ -89,6 +92,7 @@ func cleanupAccount() { cleanupWebhooks() cleanupTeams() cleanupProxyAgents() + cleanupPrivateLinks() cleanupLocalProcessingAgents() } @@ -277,13 +281,30 @@ func cleanupProxyAgents() { } } +func cleanupPrivateLinks() { + plList, err := client.NewPrivateLinkList().Do(context.Background()) + if err != nil { + log.Fatal(err) + } + for _, pl := range plList.Data.Items { + _, err := client.NewPrivateLinkDelete().PrivateLinkId(pl.Id).Do(context.Background()) + if err != nil { + log.Fatal(err) + } + } + + if plList.Data.NextCursor != "" { + cleanupPrivateLinks() + } +} + func cleanupLocalProcessingAgents() { - lpaList, err := client.NewLocalProcessingAgentList().Do(context.Background()) + lpaList, err := client.NewHybridDeploymentAgentList().Do(context.Background()) if err != nil { log.Fatal(err) } for _, lpa := range lpaList.Data.Items { - _, err := client.NewLocalProcessingAgentDelete().AgentId(lpa.Id).Do(context.Background()) + _, err := client.NewHybridDeploymentAgentDelete().AgentId(lpa.Id).Do(context.Background()) if err != nil { log.Fatal(err) } diff --git a/fivetran/tests/e2e/resource_group_e2e_test.go b/fivetran/tests/e2e/resource_group_e2e_test.go index 610f75f2..ad23d020 100644 --- a/fivetran/tests/e2e/resource_group_e2e_test.go +++ b/fivetran/tests/e2e/resource_group_e2e_test.go @@ -12,55 +12,16 @@ import ( "github.com/hashicorp/terraform-plugin-testing/terraform" ) -func TestResourceGroupE2E(t *testing.T) { - resource.Test(t, resource.TestCase{ - PreCheck: func() {}, - ProtoV6ProviderFactories: ProtoV6ProviderFactories, - CheckDestroy: testFivetranGroupResourceDestroy, - Steps: []resource.TestStep{ - { - Config: ` +var groupResourceConfig = ` resource "fivetran_group" "testgroup" { provider = fivetran-provider - name = "TestResourceGroupE2E" - } - `, - Check: resource.ComposeAggregateTestCheckFunc( - testFivetranGroupResourceCreate(t, "fivetran_group.testgroup"), - resource.TestCheckResourceAttr("fivetran_group.testgroup", "name", "TestResourceGroupE2E"), - resource.TestCheckResourceAttrSet("fivetran_group.testgroup", "created_at"), - ), - }, - { - Config: ` - resource "fivetran_group" "testgroup" { - provider = fivetran-provider - name = "TestResourceGroupE2E_updated" - } - `, - Check: resource.ComposeAggregateTestCheckFunc( - testFivetranGroupResourceUpdate(t, "fivetran_group.testgroup"), - resource.TestCheckResourceAttr("fivetran_group.testgroup", "name", "TestResourceGroupE2E_updated"), - resource.TestCheckResourceAttrSet("fivetran_group.testgroup", "created_at"), - resource.TestCheckResourceAttrSet("fivetran_group.testgroup", "last_updated"), - ), - }, - }, - }) -} + name = "%v" + }` -func TestResourceGroupWithUsersE2E(t *testing.T) { - //t.Skip("Endpoint to add user to group doesn't support new RBAC role names. It will be fixed soon") - resource.Test(t, resource.TestCase{ - PreCheck: func() {}, - ProtoV6ProviderFactories: ProtoV6ProviderFactories, - CheckDestroy: testFivetranGroupResourceDestroy, - Steps: []resource.TestStep{ - { - Config: ` +var groupResourceWithUsersConfig = ` resource "fivetran_user" "userjohn" { provider = fivetran-provider - email = "john.black@testmail.com" + email = "%v" family_name = "Black" given_name = "John" phone = "+19876543210" @@ -70,7 +31,7 @@ func TestResourceGroupWithUsersE2E(t *testing.T) { resource "fivetran_group" "testgroup" { provider = fivetran-provider - name = "TestResourceGroupWithUsersE2E" + name = "%v" } resource "fivetran_group_users" "testgroup_users" { @@ -79,21 +40,15 @@ func TestResourceGroupWithUsersE2E(t *testing.T) { user { email = fivetran_user.userjohn.email - role = "Destination Reviewer" + role = "%v" } } - `, - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttrSet("fivetran_group_users.testgroup_users", "user.0.id"), - resource.TestCheckResourceAttr("fivetran_group_users.testgroup_users", "user.0.role", "Destination Reviewer"), - resource.TestCheckResourceAttr("fivetran_group_users.testgroup_users", "user.0.email", "john.black@testmail.com"), - ), - }, - { - Config: ` + ` + +var groupResourceWithEmptyUsersConfig = ` resource "fivetran_user" "userjohn" { provider = fivetran-provider - email = "john.black@testmail.com" + email = "%v" family_name = "Black" given_name = "John" phone = "+19876543210" @@ -103,49 +58,85 @@ func TestResourceGroupWithUsersE2E(t *testing.T) { resource "fivetran_group" "testgroup" { provider = fivetran-provider - name = "TestResourceGroupWithUsersE2E" + name = "%v" } resource "fivetran_group_users" "testgroup_users" { provider = fivetran-provider group_id = fivetran_group.testgroup.id - - user { - email = fivetran_user.userjohn.email - role = "Destination Administrator" - } } - `, + ` + +func TestResourceGroupE2E(t *testing.T) { + suffix := strconv.Itoa(seededRand.Int()) + groupCreateName := "TestResourceGroupE2E" + suffix + "created" + groupUpdateName := "TestResourceGroupE2E" + suffix + "updated" + + resourceCreateConfig := fmt.Sprintf(groupResourceConfig, groupCreateName) + resourceUpdateConfig := fmt.Sprintf(groupResourceConfig, groupUpdateName) + + resource.Test(t, resource.TestCase{ + PreCheck: func() {}, + ProtoV6ProviderFactories: ProtoV6ProviderFactories, + CheckDestroy: testFivetranGroupResourceDestroy, + Steps: []resource.TestStep{ + { + Config: resourceCreateConfig, Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttrSet("fivetran_group_users.testgroup_users", "user.0.id"), - resource.TestCheckResourceAttr("fivetran_group_users.testgroup_users", "user.0.role", "Destination Administrator"), - resource.TestCheckResourceAttr("fivetran_group_users.testgroup_users", "user.0.email", "john.black@testmail.com"), + testFivetranGroupResourceCreate(t, "fivetran_group.testgroup"), + resource.TestCheckResourceAttr("fivetran_group.testgroup", "name", groupCreateName), + resource.TestCheckResourceAttrSet("fivetran_group.testgroup", "created_at"), ), }, { - Config: ` - resource "fivetran_user" "userjohn" { - provider = fivetran-provider - email = "john.black@testmail.com" - family_name = "Black" - given_name = "John" - phone = "+19876543210" - picture = "https://myPicturecom" - role = "Account Reviewer" - } + Config: resourceUpdateConfig, + Check: resource.ComposeAggregateTestCheckFunc( + testFivetranGroupResourceUpdate(t, "fivetran_group.testgroup", groupUpdateName), + resource.TestCheckResourceAttr("fivetran_group.testgroup", "name", groupUpdateName), + resource.TestCheckResourceAttrSet("fivetran_group.testgroup", "created_at"), + resource.TestCheckResourceAttrSet("fivetran_group.testgroup", "last_updated"), + ), + }, + }, + }) +} - resource "fivetran_group" "testgroup" { - provider = fivetran-provider - name = "TestResourceGroupWithUsersE2E" - } +func TestResourceGroupWithUsersE2E(t *testing.T) { + suffix := strconv.Itoa(seededRand.Int()) + groupName := "TestResourceGroupE2E" + suffix + "created" + userName := "john.black" + suffix + "@testmail.com" + roleCreate := "Destination Reviewer" + roleUpdate := "Destination Administrator" - resource "fivetran_group_users" "testgroup_users" { - provider = fivetran-provider - group_id = fivetran_group.testgroup.id - } - `, + resourceWithUsersCreateConfig := fmt.Sprintf(groupResourceWithUsersConfig, userName, groupName, roleCreate) + resourceWithUsersUpdateConfig := fmt.Sprintf(groupResourceWithUsersConfig, userName, groupName, roleUpdate) + resourceWithUsersEmptyConfig := fmt.Sprintf(groupResourceWithEmptyUsersConfig, userName, groupName) + + resource.Test(t, resource.TestCase{ + PreCheck: func() {}, + ProtoV6ProviderFactories: ProtoV6ProviderFactories, + CheckDestroy: testFivetranGroupResourceDestroy, + Steps: []resource.TestStep{ + { + Config: resourceWithUsersCreateConfig, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet("fivetran_group_users.testgroup_users", "user.0.id"), + resource.TestCheckResourceAttr("fivetran_group_users.testgroup_users", "user.0.role", roleCreate), + resource.TestCheckResourceAttr("fivetran_group_users.testgroup_users", "user.0.email", userName), + ), + }, + { + Config: resourceWithUsersUpdateConfig, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet("fivetran_group_users.testgroup_users", "user.0.id"), + resource.TestCheckResourceAttr("fivetran_group_users.testgroup_users", "user.0.role", roleUpdate), + resource.TestCheckResourceAttr("fivetran_group_users.testgroup_users", "user.0.email", userName), + ), + }, + { + Config: resourceWithUsersEmptyConfig, Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr("fivetran_group.testgroup", "name", "TestResourceGroupWithUsersE2E"), + resource.TestCheckResourceAttr("fivetran_group.testgroup", "name", groupName), resource.TestCheckResourceAttr("fivetran_group_users.testgroup_users", "user.#", "0"), ), }, @@ -153,7 +144,7 @@ func TestResourceGroupWithUsersE2E(t *testing.T) { }) } -func testFivetranGroupResourceUpdate(t *testing.T, resourceName string) resource.TestCheckFunc { +func testFivetranGroupResourceUpdate(t *testing.T, resourceName string, groupName string) resource.TestCheckFunc { return func(s *terraform.State) error { rs := GetResource(t, s, resourceName) response, err := client.NewGroupDetails().GroupID(rs.Primary.ID).Do(context.Background()) @@ -162,8 +153,8 @@ func testFivetranGroupResourceUpdate(t *testing.T, resourceName string) resource return err } - if response.Data.Name != "TestResourceGroupE2E_updated" { - return fmt.Errorf("Group has name %v different from expected (%v)", response.Data.Name, "TestResourceGroupE2E_updated") + if response.Data.Name != groupName { + return fmt.Errorf("Group has name %v different from expected (%v)", response.Data.Name, groupName) } return nil diff --git a/fivetran/tests/e2e/resource_local_processing_agent_e2e_test.go b/fivetran/tests/e2e/resource_local_processing_agent_e2e_test.go index 256a75db..a0cea9e6 100644 --- a/fivetran/tests/e2e/resource_local_processing_agent_e2e_test.go +++ b/fivetran/tests/e2e/resource_local_processing_agent_e2e_test.go @@ -43,7 +43,7 @@ func testFivetranLocalProcessingAgentResourceCreate(t *testing.T, resourceName s return func(s *terraform.State) error { rs := GetResource(t, s, resourceName) - _, err := client.NewLocalProcessingAgentDetails().AgentId(rs.Primary.ID).Do(context.Background()) + _, err := client.NewHybridDeploymentAgentDetails().AgentId(rs.Primary.ID).Do(context.Background()) if err != nil { fmt.Println(err) return err @@ -59,7 +59,7 @@ func testFivetranLocalProcessingAgentResourceDestroy(s *terraform.State) error { continue } - response, err := client.NewLocalProcessingAgentDetails().AgentId(rs.Primary.ID).Do(context.Background()) + response, err := client.NewHybridDeploymentAgentDetails().AgentId(rs.Primary.ID).Do(context.Background()) if err.Error() != "status code: 404; expected: 200" { return err } diff --git a/fivetran/tests/e2e/resource_private_link_e2e_test.go b/fivetran/tests/e2e/resource_private_link_e2e_test.go new file mode 100644 index 00000000..4b362e98 --- /dev/null +++ b/fivetran/tests/e2e/resource_private_link_e2e_test.go @@ -0,0 +1,66 @@ +package e2e_test + +import ( + "context" + "fmt" + "strconv" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" +) + +var privateLinkResourceConfig = ` + resource "fivetran_private_link" "test_pl" { + provider = fivetran-provider + + name = "%v" + region = "AWS_US_EAST_1" + service = "REDSHIFT_AWS" + + config_map = { + aws_account_id = "%v" + cluster_identifier = "%v" + } + }` + +func TestResourcePrivateLinkE2E(t *testing.T) { + //t.Skip("Private links have a strict limit on the number of entities created. This test should only be used for intermediate tests when changes are made directly to Private links.") + suffix := strconv.Itoa(seededRand.Int()) + privateLinkName := suffix + privateLinkCfgValue := "privatelink_" + suffix + + resourceConfig := fmt.Sprintf(privateLinkResourceConfig, privateLinkName, privateLinkCfgValue, privateLinkCfgValue) + + resource.Test(t, resource.TestCase{ + PreCheck: func() {}, + ProtoV6ProviderFactories: ProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: resourceConfig, + Check: resource.ComposeAggregateTestCheckFunc( + testFivetranPrivateLinkResourceCreate(t, "fivetran_private_link.test_pl"), + resource.TestCheckResourceAttr("fivetran_private_link.test_pl", "name", privateLinkName), + resource.TestCheckResourceAttr("fivetran_private_link.test_pl", "region", "AWS_US_EAST_1"), + resource.TestCheckResourceAttr("fivetran_private_link.test_pl", "service", "REDSHIFT_AWS"), + resource.TestCheckResourceAttr("fivetran_private_link.test_pl", "config_map.aws_account_id", privateLinkCfgValue), + resource.TestCheckResourceAttr("fivetran_private_link.test_pl", "config_map.cluster_identifier", privateLinkCfgValue), + ), + }, + }, + }) +} + +func testFivetranPrivateLinkResourceCreate(t *testing.T, resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs := GetResource(t, s, resourceName) + + _, err := client.NewPrivateLinkDetails().PrivateLinkId(rs.Primary.ID).Do(context.Background()) + if err != nil { + fmt.Println(err) + return err + } + //todo: check response _ fields if needed + return nil + } +} \ No newline at end of file diff --git a/go.mod b/go.mod index fa4d2459..23f53bac 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,7 @@ module github.com/fivetran/terraform-provider-fivetran require ( - github.com/fivetran/go-fivetran v0.9.4 + github.com/fivetran/go-fivetran v1.0.4 github.com/hashicorp/terraform-plugin-framework v1.8.0 github.com/hashicorp/terraform-plugin-framework-timeouts v0.4.1 github.com/hashicorp/terraform-plugin-framework-validators v0.12.0 diff --git a/go.sum b/go.sum index 56018fee..fd5b7121 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,5 @@ -dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= -dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/Jeffail/gabs/v2 v2.7.0 h1:Y2edYaTcE8ZpRsR2AtmPu5xQdFDIthFG0jYhu5PY8kg= github.com/Jeffail/gabs/v2 v2.7.0/go.mod h1:dp5ocw1FvBBQYssgHsG7I1WYsiLRtkUaB1FEtSwvNUw= -github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= -github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/ProtonMail/go-crypto v1.1.0-alpha.0 h1:nHGfwXmFvJrSR9xu8qL7BkO4DqTHXE9N5vPhgY2I+j0= github.com/ProtonMail/go-crypto v1.1.0-alpha.0/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE= @@ -11,35 +7,18 @@ github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= -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= github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= -github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= -github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= -github.com/fivetran/go-fivetran v0.9.2 h1:H3AY9nB3mJ7Kjd2vzT3bQfko5JZ/vVYu1zuM47bRaG8= -github.com/fivetran/go-fivetran v0.9.2/go.mod h1:EIy5Uwn1zylQCr/7O+8rrwvmjvhW3PPpzHkQj26ON7Y= -github.com/fivetran/go-fivetran v0.9.4 h1:9F7XzoezZxnhCW331NpN49ace8YfsJ8+92oyjxIj88o= -github.com/fivetran/go-fivetran v0.9.4/go.mod h1:EIy5Uwn1zylQCr/7O+8rrwvmjvhW3PPpzHkQj26ON7Y= -github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= -github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= -github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= -github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= -github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4= -github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY= -github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= -github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/fivetran/go-fivetran v1.0.3 h1:1x/lmuvvQj7DPICoMZDi8bQitgtDm767SJ3to5RYwD8= +github.com/fivetran/go-fivetran v1.0.3/go.mod h1:EIy5Uwn1zylQCr/7O+8rrwvmjvhW3PPpzHkQj26ON7Y= +github.com/fivetran/go-fivetran v1.0.4 h1:wYNS72Zw/bEZpuBKFYGm07TRQtqoo1/Y6oWayHAqEHk= +github.com/fivetran/go-fivetran v1.0.4/go.mod h1:EIy5Uwn1zylQCr/7O+8rrwvmjvhW3PPpzHkQj26ON7Y= github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= @@ -100,21 +79,11 @@ github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= -github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= -github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= -github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= -github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= -github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -136,18 +105,9 @@ github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zx github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= -github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= -github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= -github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= @@ -156,8 +116,6 @@ github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IU github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= -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/zclconf/go-cty v1.14.3 h1:1JXy1XroaGrzZuG6X9dt7HL6s9AwbY+l4UNL8o5B6ho= github.com/zclconf/go-cty v1.14.3/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= @@ -177,8 +135,6 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/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.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.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-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -221,10 +177,6 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 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= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= -gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/templates/data-sources/private_link.md.tmpl b/templates/data-sources/private_link.md.tmpl new file mode 100644 index 00000000..6839b3a2 --- /dev/null +++ b/templates/data-sources/private_link.md.tmpl @@ -0,0 +1,17 @@ +--- +page_title: "Data Source: fivetran_private_link" +--- + +# Data Source: fivetran_private_link + +This data source returns a private link object. + +## Example Usage + +```hcl +data "fivetran_private_link" "private_link" { + id = "private_link_id" +} +``` + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/data-sources/private_links.md.tmpl b/templates/data-sources/private_links.md.tmpl new file mode 100644 index 00000000..7b7387c7 --- /dev/null +++ b/templates/data-sources/private_links.md.tmpl @@ -0,0 +1,16 @@ +--- +page_title: "Data Source: fivetran_private_links" +--- + +# Data Source: fivetran_private_links + +This data source returns a list of all private links within your Fivetran account. + +## Example Usage + +```hcl +data "fivetran_private_links" "private_links" { +} +``` + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/resources/private_link.md.tmpl b/templates/resources/private_link.md.tmpl new file mode 100644 index 00000000..2a09150c --- /dev/null +++ b/templates/resources/private_link.md.tmpl @@ -0,0 +1,25 @@ +--- +page_title: "Resource: fivetran_private_link" +--- + +# Resource: fivetran_private_link + +This resource allows you to create, update, and delete private links. + +## Example Usage + +```hcl +resource "fivetran_private_link" "test_pl" { + provider = fivetran-provider + + name = "name" + region = "region" + service = "service" + + config { + connection_service_name = "connection_service_name" + } +} +``` + +{{ .SchemaMarkdown | trimspace }}