Skip to content

Commit

Permalink
provider: private connection support on all clouds
Browse files Browse the repository at this point in the history
Previously, the private_endpoint_connection resource only supported AWS
private link connections. This commit updates the provider code to use
the new CC API Private Endpoint Connections methods. These cloud-neutral
methods will work for all cloud providers and cluster types, except
Serverless clusters on Azure as that configuration is not yet
supported.
  • Loading branch information
carloruiz committed Mar 10, 2024
1 parent e328294 commit b75ef36
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 35 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

- The `private_endpoint_connection` resource can now be created on every
supported cloud-provider and cluster type, expect Serverless clusters on
Azure as that configuration is not yet available.

## [1.3.1] - 2023-12-01

### Fixed
Expand Down
8 changes: 4 additions & 4 deletions docs/resources/private_endpoint_connection.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.



Expand All @@ -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 "<cluster ID>:<endpoint ID>".
- `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.


53 changes: 22 additions & 31 deletions internal/provider/private_endpoint_connection_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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",
Expand All @@ -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",
Expand Down Expand Up @@ -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)))
Expand All @@ -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())
}
Expand All @@ -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)))
Expand Down Expand Up @@ -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)))
Expand All @@ -299,10 +290,10 @@ 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:
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))
Expand Down

0 comments on commit b75ef36

Please sign in to comment.