Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use new private endpoint connections API #183

Merged
merged 1 commit into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
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.


Original file line number Diff line number Diff line change
@@ -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"
}
59 changes: 28 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,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:
fantapop marked this conversation as resolved.
Show resolved Hide resolved
// 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))
Expand Down
38 changes: 15 additions & 23 deletions internal/provider/private_endpoint_connection_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) == "" {
Expand All @@ -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)
Expand Down Expand Up @@ -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(
Expand Down
Loading