diff --git a/CHANGELOG.md b/CHANGELOG.md index 43b8ba41..80a63836 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Notes + +- The `private_endpoint_connection` resource can now be used to create private + endpoint connections on every supported cloud-provider and cluster type, + except Serverless clusters on Azure as that configuration is not yet + available. + ### Fixed - Renamed example files to the correct name so they are automatically included diff --git a/docs/resources/private_endpoint_connection.md b/docs/resources/private_endpoint_connection.md index 5527757f..7238d571 100644 --- a/docs/resources/private_endpoint_connection.md +++ b/docs/resources/private_endpoint_connection.md @@ -3,12 +3,12 @@ page_title: "cockroach_private_endpoint_connection Resource - terraform-provider-cockroach" subcategory: "" description: |- - AWS PrivateLink Endpoint Connection. + Private Endpoint Connection. --- # cockroach_private_endpoint_connection (Resource) -AWS PrivateLink Endpoint Connection. +Private Endpoint Connection. @@ -18,13 +18,13 @@ AWS PrivateLink Endpoint Connection. ### Required - `cluster_id` (String) -- `endpoint_id` (String) Client side ID of the PrivateLink connection. +- `endpoint_id` (String) Client side ID of the Private Endpoint Connection. ### Read-Only - `cloud_provider` (String) Cloud provider associated with this connection. - `id` (String) Used with `terraform import`. Format is ":". - `region_name` (String) Cloud provider region code associated with this connection. -- `service_id` (String) Server side ID of the PrivateLink connection. +- `service_id` (String) Server side ID of the Private Endpoint Connection. diff --git a/examples/resources/cockroach_aws_private_endpoint_connection/resource.tf b/examples/resources/cockroach_aws_private_endpoint_connection/resource.tf index 04b182da..7d177b2e 100644 --- a/examples/resources/cockroach_aws_private_endpoint_connection/resource.tf +++ b/examples/resources/cockroach_aws_private_endpoint_connection/resource.tf @@ -1,10 +1,9 @@ variable "cluster_id" { - type = string + type = string + description = "the id for the CockroachDB Cloud cluster" } resource "cockroach_private_endpoint_connection" "cockroach" { cluster_id = var.cluster_id - endpoint_id = "endpoint id assigned by consumer AWS" - cloud_provider = "AWS" - region_name = "AWS region in which the endpoint was created" + endpoint_id = "the endpoint id assigned by cloud provider to the client-side of the connection" } diff --git a/internal/provider/private_endpoint_connection_resource.go b/internal/provider/private_endpoint_connection_resource.go index 630bcc7f..f134bfd1 100644 --- a/internal/provider/private_endpoint_connection_resource.go +++ b/internal/provider/private_endpoint_connection_resource.go @@ -48,7 +48,7 @@ func (r *privateEndpointConnectionResource) Schema( _ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse, ) { resp.Schema = schema.Schema{ - MarkdownDescription: "AWS PrivateLink Endpoint Connection.", + MarkdownDescription: "Private Endpoint Connection.", Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ Computed: true, @@ -76,14 +76,14 @@ func (r *privateEndpointConnectionResource) Schema( PlanModifiers: []planmodifier.String{ stringplanmodifier.RequiresReplace(), }, - Description: "Client side ID of the PrivateLink connection.", + Description: "Client side ID of the Private Endpoint Connection.", }, "service_id": schema.StringAttribute{ Computed: true, PlanModifiers: []planmodifier.String{ stringplanmodifier.UseStateForUnknown(), }, - Description: "Server side ID of the PrivateLink connection.", + Description: "Server side ID of the Private Endpoint Connection.", }, "cluster_id": schema.StringAttribute{ Required: true, @@ -129,7 +129,8 @@ func (r *privateEndpointConnectionResource) Create( return } - cluster, _, err := r.provider.service.GetCluster(ctx, plan.ClusterID.ValueString()) + svc := r.provider.service + cluster, _, err := svc.GetCluster(ctx, plan.ClusterID.ValueString()) if err != nil { resp.Diagnostics.AddError( "Error getting cluster", @@ -138,30 +139,22 @@ func (r *privateEndpointConnectionResource) Create( return } - if cluster.CloudProvider != client.CLOUDPROVIDERTYPE_AWS { - resp.Diagnostics.AddError( - "Incompatible cluster cloud provider", - "Private endpoint services are only available for AWS clusters", - ) - return - } - - connectionStateRequest := client.SetAwsEndpointConnectionStateRequest{ - Status: client.SETAWSENDPOINTCONNECTIONSTATUSTYPE_AVAILABLE, + addRequest := client.AddPrivateEndpointConnectionRequest{ + EndpointId: plan.EndpointID.ValueString(), } - _, _, err = r.provider.service.SetAwsEndpointConnectionState(ctx, plan.ClusterID.ValueString(), plan.EndpointID.ValueString(), &connectionStateRequest) + _, _, err = svc.AddPrivateEndpointConnection(ctx, cluster.Id, &addRequest) if err != nil { resp.Diagnostics.AddError( - "Error establishing AWS Endpoint Connection", - fmt.Sprintf("Could not establish AWS Endpoint Connection: %s", formatAPIErrorMessage(err)), + "Error establishing Private Endpoint Connection", + fmt.Sprintf("Could not establish Private Endpoint Connection: %s", formatAPIErrorMessage(err)), ) return } - var connection client.AwsEndpointConnection + var connection client.PrivateEndpointConnection err = sdk_resource.RetryContext(ctx, endpointConnectionCreateTimeout, - waitForEndpointConnectionCreatedFunc(ctx, cluster.Id, plan.EndpointID.ValueString(), r.provider.service, &connection)) + waitForEndpointConnectionCreatedFunc(ctx, cluster.Id, plan.EndpointID.ValueString(), svc, &connection)) if err != nil { resp.Diagnostics.AddError( "Error accepting private endpoint connection", @@ -192,7 +185,7 @@ func (r *privateEndpointConnectionResource) Read( return } - connections, _, err := r.provider.service.ListAwsEndpointConnections(ctx, state.ClusterID.ValueString()) + connections, _, err := r.provider.service.ListPrivateEndpointConnections(ctx, state.ClusterID.ValueString()) if err != nil { diags.AddError("Unable to get endpoint connection status", fmt.Sprintf("Unexpected error retrieving endpoint status: %s", formatAPIErrorMessage(err))) @@ -212,14 +205,14 @@ func (r *privateEndpointConnectionResource) Read( } func loadEndpointConnectionIntoTerraformState( - apiConnection *client.AwsEndpointConnection, state *PrivateEndpointConnection, + apiConnection *client.PrivateEndpointConnection, state *PrivateEndpointConnection, ) { state.EndpointID = types.StringValue(apiConnection.GetEndpointId()) state.ID = types.StringValue(fmt.Sprintf( privateEndpointConnectionIDFmt, state.ClusterID.ValueString(), apiConnection.GetEndpointId())) - state.ServiceID = types.StringValue(apiConnection.GetServiceId()) + state.ServiceID = types.StringValue(apiConnection.GetEndpointServiceId()) state.CloudProvider = types.StringValue(string(apiConnection.GetCloudProvider())) state.RegionName = types.StringValue(apiConnection.GetRegionName()) } @@ -240,13 +233,11 @@ func (r *privateEndpointConnectionResource) Delete( return } - _, httpResp, err := r.provider.service.SetAwsEndpointConnectionState( + httpResp, err := r.provider.service.DeletePrivateEndpointConnection( ctx, state.ClusterID.ValueString(), state.EndpointID.ValueString(), - &client.SetAwsEndpointConnectionStateRequest{ - Status: client.SETAWSENDPOINTCONNECTIONSTATUSTYPE_REJECTED, - }) + ) if err != nil && httpResp != nil && httpResp.StatusCode != http.StatusNotFound { diags.AddError("Couldn't delete connection", fmt.Sprintf("Unexpected error occurred while setting connection status: %s", formatAPIErrorMessage(err))) @@ -284,10 +275,10 @@ func waitForEndpointConnectionCreatedFunc( ctx context.Context, clusterID, endpointID string, cl client.Service, - connection *client.AwsEndpointConnection, + connection *client.PrivateEndpointConnection, ) sdk_resource.RetryFunc { return func() *sdk_resource.RetryError { - connections, httpResp, err := cl.ListAwsEndpointConnections(ctx, clusterID) + connections, httpResp, err := cl.ListPrivateEndpointConnections(ctx, clusterID) if err != nil { if httpResp != nil && httpResp.StatusCode < http.StatusInternalServerError { return sdk_resource.NonRetryableError(fmt.Errorf("error getting endpoint connections: %s", formatAPIErrorMessage(err))) @@ -299,10 +290,16 @@ func waitForEndpointConnectionCreatedFunc( for _, *connection = range connections.GetConnections() { if connection.GetEndpointId() == endpointID { switch status := connection.GetStatus(); status { - case client.AWSENDPOINTCONNECTIONSTATUSTYPE_AVAILABLE: + case client.PRIVATEENDPOINTCONNECTIONSTATUS_AVAILABLE: return nil - case client.AWSENDPOINTCONNECTIONSTATUSTYPE_PENDING, - client.AWSENDPOINTCONNECTIONSTATUSTYPE_PENDING_ACCEPTANCE: + case client.PRIVATEENDPOINTCONNECTIONSTATUS_PENDING, + client.PRIVATEENDPOINTCONNECTIONSTATUS_PENDING_ACCEPTANCE, + client.PRIVATEENDPOINTCONNECTIONSTATUS_REJECTED: + // Note: A REJECTED state means the user previously called + // DeletePrivateEndpointConnection() on an existing + // connection. A user can re-attach a rejected connection + // by calling AddPrivateEndpointConnection() with the same + // endpointId. return sdk_resource.RetryableError(fmt.Errorf("endpoint connection is not ready yet")) default: return sdk_resource.NonRetryableError(fmt.Errorf("endpoint connection failed with state: %s", status)) diff --git a/internal/provider/private_endpoint_connection_resource_test.go b/internal/provider/private_endpoint_connection_resource_test.go index 2e836688..02c904ab 100644 --- a/internal/provider/private_endpoint_connection_resource_test.go +++ b/internal/provider/private_endpoint_connection_resource_test.go @@ -46,7 +46,7 @@ func TestAccServerlessPrivateEndpointConnectionResource(t *testing.T) { } func TestIntegrationPrivateEndpointConnectionResource(t *testing.T) { - clusterName := fmt.Sprintf("aws-connection-%s", GenerateRandomString(5)) + clusterName := fmt.Sprintf("private-connection-%s", GenerateRandomString(5)) clusterID := uuid.Nil.String() endpointID := "endpoint-id" if os.Getenv(CockroachAPIKey) == "" { @@ -70,15 +70,15 @@ func TestIntegrationPrivateEndpointConnectionResource(t *testing.T) { }, }, } - connection := client.AwsEndpointConnection{ - RegionName: "us-east-1", - CloudProvider: "AWS", - Status: client.AWSENDPOINTCONNECTIONSTATUSTYPE_AVAILABLE, - EndpointId: endpointID, - ServiceId: "service-id", + connection := client.PrivateEndpointConnection{ + RegionName: &services.Services[0].RegionName, + CloudProvider: "AWS", + Status: client.PRIVATEENDPOINTCONNECTIONSTATUS_AVAILABLE, + EndpointId: endpointID, + EndpointServiceId: "service-id", } - connections := &client.AwsEndpointConnections{ - Connections: []client.AwsEndpointConnection{connection}, + connections := &client.PrivateEndpointConnections{ + Connections: []client.PrivateEndpointConnection{connection}, } zeroSpendLimit := int32(0) @@ -151,27 +151,19 @@ func TestIntegrationPrivateEndpointConnectionResource(t *testing.T) { s.EXPECT().ListPrivateEndpointServices(gomock.Any(), clusterID). Return(services, nil, nil). Times(2) - available := client.SETAWSENDPOINTCONNECTIONSTATUSTYPE_AVAILABLE - s.EXPECT().SetAwsEndpointConnectionState( + s.EXPECT().AddPrivateEndpointConnection( gomock.Any(), clusterID, - endpointID, - &client.SetAwsEndpointConnectionStateRequest{ - Status: available, - }). + &client.AddPrivateEndpointConnectionRequest{EndpointId: endpointID}). Return(&connection, nil, nil) - s.EXPECT().ListAwsEndpointConnections(gomock.Any(), clusterID). + s.EXPECT().ListPrivateEndpointConnections(gomock.Any(), clusterID). Return(connections, nil, nil). Times(3) - rejected := client.SETAWSENDPOINTCONNECTIONSTATUSTYPE_REJECTED - s.EXPECT().SetAwsEndpointConnectionState( + s.EXPECT().DeletePrivateEndpointConnection( gomock.Any(), clusterID, - endpointID, - &client.SetAwsEndpointConnectionStateRequest{ - Status: rejected, - }). - Return(&connection, nil, nil) + endpointID). + Return(nil, nil) s.EXPECT().DeleteCluster(gomock.Any(), clusterID) testPrivateEndpointConnectionResource(