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
2 changes: 2 additions & 0 deletions mongodbatlas/mongodbatlas.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ type Client struct {
Auditing AuditingsService
AlertConfigurations AlertConfigurationsService
PrivateEndpoints PrivateEndpointsService
ServerlessPrivateEndpoints ServerlessPrivateEndpointsService
PrivateEndpointsDeprecated PrivateEndpointsServiceDeprecated
X509AuthDBUsers X509AuthDBUsersService
ContinuousSnapshots ContinuousSnapshotsService
Expand Down Expand Up @@ -263,6 +264,7 @@ func NewClient(httpClient *http.Client) *Client {
c.Auditing = &AuditingsServiceOp{Client: c}
c.AlertConfigurations = &AlertConfigurationsServiceOp{Client: c}
c.PrivateEndpoints = &PrivateEndpointsServiceOp{Client: c}
c.ServerlessPrivateEndpoints = &ServerlessPrivateEndpointsServiceOp{Client: c}
c.PrivateEndpointsDeprecated = &PrivateEndpointsServiceOpDeprecated{Client: c}
c.X509AuthDBUsers = &X509AuthDBUsersServiceOp{Client: c}
c.ContinuousRestoreJobs = &ContinuousRestoreJobsServiceOp{Client: c}
Expand Down
206 changes: 206 additions & 0 deletions mongodbatlas/serverless_private_endpoints.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
// Copyright 2021 MongoDB Inc
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package mongodbatlas

import (
"context"
"fmt"
"net/http"
"net/url"
)

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

// ServerlessPrivateEndpointsService is an interface for interfacing with the Private Endpoints
// of the MongoDB Atlas API.
//
// See more: See more: https://www.mongodb.com/docs/atlas/reference/api-resources-spec/#tag/Serverless-Private-Endpoints
type ServerlessPrivateEndpointsService interface {
List(context.Context, string, string, *ListOptions) ([]ServerlessPrivateEndpointConnection, *Response, error)
Create(context.Context, string, string, *ServerlessPrivateEndpointConnection) (*ServerlessPrivateEndpointConnection, *Response, error)
Get(context.Context, string, string, string) (*ServerlessPrivateEndpointConnection, *Response, error)
Delete(context.Context, string, string, string) (*Response, error)
Update(context.Context, string, string, string, *ServerlessPrivateEndpointConnection) (*ServerlessPrivateEndpointConnection, *Response, error)
}

// PrivateServerlessEndpointsServiceOp handles communication with the PrivateServerlessEndpoints related methods
// of the MongoDB Atlas API.
type ServerlessPrivateEndpointsServiceOp service

var _ ServerlessPrivateEndpointsService = &ServerlessPrivateEndpointsServiceOp{}

// PrivateEndpointServerlessConnection represents MongoDB Private Endpoint Connection.
type ServerlessPrivateEndpointConnection 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 string `json:"errorMessage,omitempty"` // Error message pertaining to the AWS Service Connect. Returns null if there are no errors.
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.
PrivateEndpointIPAddress string `json:"privateEndpointIpAddress,omitempty"` // IPv4 address of the private endpoint in your Azure VNet that someone added to this private endpoint service.
PrivateLinkServiceResourceID string `json:"privateLinkServiceResourceId,omitempty"` // Root-relative path that identifies the Azure Private Link Service that MongoDB Cloud manages. MongoDB Cloud returns null while it creates the endpoint service.
}

// List retrieve details for all private Serverless endpoint services in one Atlas project.
//
// See more: https://www.mongodb.com/docs/atlas/reference/api-resources-spec/#operation/returnAllPrivateEndpointsForOneServerlessInstance
func (s *ServerlessPrivateEndpointsServiceOp) List(ctx context.Context, groupID, instanceName string, listOptions *ListOptions) ([]ServerlessPrivateEndpointConnection, *Response, error) {
if groupID == "" {
return nil, nil, NewArgError("groupID", "must be set")
}
if instanceName == "" {
return nil, nil, NewArgError("instanceID", "must be set")
}

path := fmt.Sprintf(serverlessPrivateEndpointsPath, groupID, instanceName) // 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([]ServerlessPrivateEndpointConnection)
resp, err := s.Client.Do(ctx, req, root)
if err != nil {
return nil, resp, err
}

return *root, resp, nil
}

// Delete one private serverless endpoint service in an Atlas project.
//
// See more https://www.mongodb.com/docs/atlas/reference/api-resources-spec/#operation/removeOnePrivateEndpointFromOneServerlessInstance
func (s *ServerlessPrivateEndpointsServiceOp) Delete(ctx context.Context, groupID, instanceName, privateEndpointID string) (*Response, error) {
if groupID == "" {
return nil, NewArgError("groupID", "must be set")
}
if privateEndpointID == "" {
return nil, NewArgError("PrivateEndpointID", "must be set")
}
if instanceName == "" {
return nil, NewArgError("instanceName", "must be set")
}

basePath := fmt.Sprintf(serverlessPrivateEndpointsPath, groupID, instanceName)
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)
}

// Create Adds one serverless private endpoint in an Atlas project.
//
// See more: https://www.mongodb.com/docs/atlas/reference/api-resources-spec/#operation/createOnePrivateEndpointForOneServerlessInstance
func (s *ServerlessPrivateEndpointsServiceOp) Create(ctx context.Context, groupID, instanceName string, createRequest *ServerlessPrivateEndpointConnection) (*ServerlessPrivateEndpointConnection, *Response, error) {
if groupID == "" {
return nil, nil, NewArgError("groupID", "must be set")
}

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

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

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

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

return root, resp, err
}

// Get retrieve details for one private serverless endpoint in an Atlas project.
//
// See more: https://www.mongodb.com/docs/atlas/reference/api-resources-spec/#operation/returnOnePrivateEndpointForOneServerlessInstance
func (s *ServerlessPrivateEndpointsServiceOp) Get(ctx context.Context, groupID, instanceName, privateEndpointID string) (*ServerlessPrivateEndpointConnection, *Response, error) {
if groupID == "" {
return nil, nil, NewArgError("groupID", "must be set")
}

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

basePath := fmt.Sprintf(serverlessPrivateEndpointsPath, groupID, instanceName)
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(ServerlessPrivateEndpointConnection)
resp, err := s.Client.Do(ctx, req, root)
if err != nil {
return nil, resp, err
}

return root, resp, err
}

// Update updates the private serverless endpoint setting for one Atlas project.
//
// See more: https://www.mongodb.com/docs/atlas/reference/api-resources-spec/#operation/updateOnePrivateEndpointForOneServerlessInstance
func (s *ServerlessPrivateEndpointsServiceOp) Update(ctx context.Context, groupID, instanceName, privateEndpointID string, updateRequest *ServerlessPrivateEndpointConnection) (*ServerlessPrivateEndpointConnection, *Response, error) {
if groupID == "" {
return nil, nil, NewArgError("groupID", "must be set")
}
if instanceName == "" {
return nil, nil, NewArgError("instanceName", "must be set")
}
if privateEndpointID == "" {
return nil, nil, NewArgError("privateEndpointID", "must be set")
}

basePath := fmt.Sprintf(serverlessPrivateEndpointsPath, groupID, instanceName)
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(ServerlessPrivateEndpointConnection)
resp, err := s.Client.Do(ctx, req, root)
if err != nil {
return nil, resp, err
}

return root, resp, err
}
91 changes: 91 additions & 0 deletions mongodbatlas/serverless_private_endpoints_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright 2021 MongoDB Inc
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package mongodbatlas

import (
"fmt"
"net/http"
"reflect"
"testing"
)

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.ServerlessPrivateEndpoints.Get(ctx, groupID, instanceName, endpointID)
if err != nil {
t.Errorf("PrivateEndpoints.Get returned error: %v", err)
}

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

if !reflect.DeepEqual(interfaceEndpoint, expected) {
t.Errorf("PrivateEndpoints.Get \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 := &ServerlessPrivateEndpointConnection{
CloudProviderEndpointID: "vpce1234567",
ProviderName: "AWS",
}

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

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

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