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

INTMDB-364: [Terraform] Add support for serverless private endpoints #314

Merged
merged 12 commits into from
Oct 14, 2022
170 changes: 168 additions & 2 deletions mongodbatlas/private_endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ import (
)

const (
privateEndpointsPath = "api/atlas/v1.0/groups/%s/privateEndpoint"
regionalModePath = privateEndpointsPath + "/regionalMode"
privateEndpointsPath = "api/atlas/v1.0/groups/%s/privateEndpoint"
serverlessPrivateEndpointsPath = "api/atlas/v1.0/groups/%s/privateEndpoint/serverless/instance/%s/endpoint"
regionalModePath = privateEndpointsPath + "/regionalMode"
)

// PrivateEndpointsService is an interface for interfacing with the Private Endpoints
Expand All @@ -40,6 +41,11 @@ type PrivateEndpointsService interface {
DeleteOnePrivateEndpoint(context.Context, string, string, string, string) (*Response, error)
UpdateRegionalizedPrivateEndpointSetting(context.Context, string, bool) (*RegionalizedPrivateEndpointSetting, *Response, error)
GetRegionalizedPrivateEndpointSetting(context.Context, string) (*RegionalizedPrivateEndpointSetting, *Response, error)
ListPrivateServerlessEndpoint(context.Context, string, string, *ListOptions) ([]PrivateServerlessEndpointConnection, *Response, error)
AddOnePrivateServerlessEndpoint(context.Context, string, string, *PrivateServerlessEndpointConnection) (*PrivateServerlessEndpointConnection, *Response, error)
GetOnePrivateServerlessEndpoint(context.Context, string, string, string) (*PrivateServerlessEndpointConnection, *Response, error)
DeleteOnePrivateServerlessEndpoint(context.Context, string, string, string) (*Response, error)
UpdateOnePrivateServerlessEndpoint(context.Context, string, string, string, *PrivateServerlessEndpointConnection) (*PrivateServerlessEndpointConnection, *Response, error)
}

// PrivateEndpointsServiceOp handles communication with the PrivateEndpoints related methods
Expand Down Expand Up @@ -94,6 +100,17 @@ type GCPEndpoint struct {
ServiceAttachmentName string `json:"serviceAttachmentName,omitempty"` // Unique alphanumeric and special character strings that identify the service attachment associated with the endpoint.
}

// PrivateEndpointServerlessConnection represents MongoDB Private Endpoint Connection.
type PrivateServerlessEndpointConnection struct {
ID string `json:"_id,omitempty"` // Unique identifier of the Serverless PrivateLink Service.
CloudProviderEndpointID string `json:"cloudProviderEndpointId,omitempty"`
Comment string `json:"comment,omitempty"`
EndpointServiceName string `json:"endpointServiceName,omitempty"` // Name of the PrivateLink endpoint service in AWS. Returns null while the endpoint service is being created.
ErrorMessage interface{} `json:"errorMessage,omitempty"` // Error message pertaining to the AWS Service Connect. Returns null if there are no errors.
gssbzn marked this conversation as resolved.
Show resolved Hide resolved
Status string `json:"status,omitempty"` // Status of the AWS Serverless PrivateLink connection: INITIATING, WAITING_FOR_USER, FAILED, DELETING, AVAILABLE.
ProviderName string `json:"providerName,omitempty"` // Human-readable label that identifies the cloud provider. Values include AWS or AZURE. Atlas currently supports only AWS.
}

// Create one private endpoint service for AWS or Azure in an Atlas project.
//
// See more: https://docs.atlas.mongodb.com/reference/api/private-endpoints-service-create-one/
Expand Down Expand Up @@ -353,3 +370,152 @@ func (s *PrivateEndpointsServiceOp) GetRegionalizedPrivateEndpointSetting(ctx co

return root, resp, err
}

// List retrieve details for all private Serverless endpoint services in one Atlas project.
//
// See more: https://www.mongodb.com/docs/atlas/reference/api/serverless-private-endpoints-get-all/
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[q] Could we stop using this doc page that will be deprecated soon and start using https://www.mongodb.com/docs/atlas/reference/api-resources-spec/#tag/Serverless-Private-Endpoints (autogenerated with open API annotations)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

func (s *PrivateEndpointsServiceOp) ListPrivateServerlessEndpoint(ctx context.Context, groupID, instanceID string, listOptions *ListOptions) ([]PrivateServerlessEndpointConnection, *Response, error) {
if groupID == "" {
return nil, nil, NewArgError("groupID", "must be set")
}
if instanceID == "" {
return nil, nil, NewArgError("instanceID", "must be set")
}

path := fmt.Sprintf(serverlessPrivateEndpointsPath, groupID, instanceID) // Add query params from listOptions
path, err := setListOptions(path, listOptions)
if err != nil {
return nil, nil, err
}

req, err := s.Client.NewRequest(ctx, http.MethodGet, path, nil)
if err != nil {
return nil, nil, err
}

root := new([]PrivateServerlessEndpointConnection)
resp, err := s.Client.Do(ctx, req, root)
if err != nil {
return nil, resp, err
}

return *root, resp, nil
}

// DeleteOnePrivateServerlessEndpoint one private serverless endpoint service in an Atlas project.
//
// See more https://docs.atlas.mongodb.com/reference/api/private-endpoints-service-delete-one/
func (s *PrivateEndpointsServiceOp) DeleteOnePrivateServerlessEndpoint(ctx context.Context, groupID, instanceID, privateEndpointID string) (*Response, error) {
if groupID == "" {
return nil, NewArgError("groupID", "must be set")
}
if privateEndpointID == "" {
return nil, NewArgError("PrivateEndpointID", "must be set")
}
if instanceID == "" {
return nil, NewArgError("instanceID", "must be set")
}

basePath := fmt.Sprintf(serverlessPrivateEndpointsPath, groupID, instanceID)
path := fmt.Sprintf("%s/%s", basePath, url.PathEscape(privateEndpointID))

req, err := s.Client.NewRequest(ctx, http.MethodDelete, path, nil)
if err != nil {
return nil, err
}

return s.Client.Do(ctx, req, nil)
}

// AddOnePrivateServerlessEndpoint Adds one serverless private endpoint in an Atlas project.
//
// See more: https://www.mongodb.com/docs/atlas/reference/api/serverless-private-endpoints-get-one/
func (s *PrivateEndpointsServiceOp) AddOnePrivateServerlessEndpoint(ctx context.Context, groupID, instanceID string, createRequest *PrivateServerlessEndpointConnection) (*PrivateServerlessEndpointConnection, *Response, error) {
if groupID == "" {
return nil, nil, NewArgError("groupID", "must be set")
}

if instanceID == "" {
return nil, nil, NewArgError("instanceID", "must be set")
}
if createRequest == nil {
return nil, nil, NewArgError("createRequest", "cannot be nil")
}

path := fmt.Sprintf(serverlessPrivateEndpointsPath, groupID, instanceID)

req, err := s.Client.NewRequest(ctx, http.MethodPost, path, createRequest)
if err != nil {
return nil, nil, err
}

root := new(PrivateServerlessEndpointConnection)
resp, err := s.Client.Do(ctx, req, root)
if err != nil {
return nil, resp, err
}

return root, resp, err
}

// GetOnePrivateServerlessEndpoint retrieve details for one private serverless endpoint in an Atlas project.
//
// See more: https://www.mongodb.com/docs/atlas/reference/api/serverless-private-endpoints-get-one/
func (s *PrivateEndpointsServiceOp) GetOnePrivateServerlessEndpoint(ctx context.Context, groupID, instanceID, privateEndpointID string) (*PrivateServerlessEndpointConnection, *Response, error) {
if groupID == "" {
return nil, nil, NewArgError("groupID", "must be set")
}

if instanceID == "" {
return nil, nil, NewArgError("instanceID", "must be set")
}
if privateEndpointID == "" {
return nil, nil, NewArgError("privateEndpointID", "must be set")
}

basePath := fmt.Sprintf(serverlessPrivateEndpointsPath, groupID, instanceID)
path := fmt.Sprintf("%s/%s", basePath, url.PathEscape(privateEndpointID))

req, err := s.Client.NewRequest(ctx, http.MethodGet, path, nil)
if err != nil {
return nil, nil, err
}

root := new(PrivateServerlessEndpointConnection)
resp, err := s.Client.Do(ctx, req, root)
if err != nil {
return nil, resp, err
}

return root, resp, err
}

// UpdateOnePrivateServerlessEndpoint updates the private serverless endpoint setting for one Atlas project.
//
// See more: https://docs.atlas.mongodb.com/reference/api/private-endpoints-update-regional-mode
func (s *PrivateEndpointsServiceOp) UpdateOnePrivateServerlessEndpoint(ctx context.Context, groupID, instanceID, privateEndpointID string, updateRequest *PrivateServerlessEndpointConnection) (*PrivateServerlessEndpointConnection, *Response, error) {
if groupID == "" {
return nil, nil, NewArgError("groupID", "must be set")
}
if instanceID == "" {
return nil, nil, NewArgError("instanceID", "must be set")
}
if privateEndpointID == "" {
return nil, nil, NewArgError("privateEndpointID", "must be set")
}

basePath := fmt.Sprintf(serverlessPrivateEndpointsPath, groupID, instanceID)
path := fmt.Sprintf("%s/%s", basePath, url.PathEscape(privateEndpointID))
req, err := s.Client.NewRequest(ctx, http.MethodPatch, path, updateRequest)
if err != nil {
return nil, nil, err
}

root := new(PrivateServerlessEndpointConnection)
resp, err := s.Client.Do(ctx, req, root)
if err != nil {
return nil, resp, err
}

return root, resp, err
}
69 changes: 69 additions & 0 deletions mongodbatlas/private_endpoints_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1003,3 +1003,72 @@ func TestPrivateEndpoints_GetRegionalizedPrivateEndpointSetting(t *testing.T) {
t.Errorf("PrivateEndpoints.UpdateRegionalizedPrivateEndpointSetting\n got=%#v\nwant=%#v", interfaceEndpoint, expected)
}
}

func TestPrivateEndpoints_GetOnePrivateServerlessEndpoint(t *testing.T) {
client, mux, teardown := setup()
defer teardown()

groupID := "1"
instanceName := "serverless"
endpointID := "3658569708087079"

mux.HandleFunc(fmt.Sprintf("/api/atlas/v1.0/groups/%s/privateEndpoint/serverless/instance/%s/endpoint/%s", groupID, instanceName, endpointID), func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, http.MethodGet)
fmt.Fprint(w, `{
"ID":"", "CloudProviderEndpointID":"vpce-12356", "Comment":"Test Serverless", "EndpointServiceName":"", "Status":"AVAILABLE", "ProviderName":"AWS"
}`)
})

interfaceEndpoint, _, err := client.PrivateEndpoints.GetOnePrivateServerlessEndpoint(ctx, groupID, instanceName, endpointID)
if err != nil {
t.Errorf("PrivateEndpoints.GetOnePrivateServerlessEndpoint returned error: %v", err)
}

expected := &PrivateServerlessEndpointConnection{
Comment: "Test Serverless",
ProviderName: "AWS",
Status: "AVAILABLE",
CloudProviderEndpointID: "vpce-12356",
}

if !reflect.DeepEqual(interfaceEndpoint, expected) {
t.Errorf("PrivateEndpoints.GetOnePrivateServerlessEndpoint\n got=%#v\nwant=%#v", interfaceEndpoint, expected)
}
}

func TestPrivateEndpoints_UpdateOnePrivateServerlessEndpoint(t *testing.T) {
client, mux, teardown := setup()
defer teardown()

groupID := "1"
instanceName := "serverless"
endpointID := "3658569708087079"

mux.HandleFunc(fmt.Sprintf("/api/atlas/v1.0/groups/%s/privateEndpoint/serverless/instance/%s/endpoint/%s", groupID, instanceName, endpointID), func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, http.MethodPatch)
fmt.Fprint(w, `{
"ID":"", "CloudProviderEndpointID":"vpce1234567", "Comment":"Test Serverless", "EndpointServiceName":"", "Status":"AVAILABLE", "ProviderName":"AWS"
}`)
})

update := &PrivateServerlessEndpointConnection{
CloudProviderEndpointID: "vpce1234567",
ProviderName: "AWS",
}

interfaceEndpoint, _, err := client.PrivateEndpoints.UpdateOnePrivateServerlessEndpoint(ctx, groupID, instanceName, endpointID, update)
if err != nil {
t.Errorf("PrivateEndpoints.UpdateOnePrivateServerlessEndpoint returned error: %v", err)
}

expected := &PrivateServerlessEndpointConnection{
Comment: "Test Serverless",
ProviderName: "AWS",
Status: "AVAILABLE",
CloudProviderEndpointID: "vpce1234567",
}

if !reflect.DeepEqual(interfaceEndpoint, expected) {
t.Errorf("PrivateEndpoints.UpdateOnePrivateServerlessEndpoint\n got=%#v\nwant=%#v", interfaceEndpoint, expected)
}
}