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

chore: Creates TF models & interfaces for new mongodbatlas_encryption_at_rest_private_endpoint resource #2493

Merged
Merged
Show file tree
Hide file tree
Changes from 8 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
5 changes: 4 additions & 1 deletion internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/mongodb/terraform-provider-mongodbatlas/internal/service/controlplaneipaddresses"
"github.com/mongodb/terraform-provider-mongodbatlas/internal/service/databaseuser"
"github.com/mongodb/terraform-provider-mongodbatlas/internal/service/encryptionatrest"
"github.com/mongodb/terraform-provider-mongodbatlas/internal/service/encryptionatrestprivateendpoint"
"github.com/mongodb/terraform-provider-mongodbatlas/internal/service/project"
"github.com/mongodb/terraform-provider-mongodbatlas/internal/service/projectipaccesslist"
"github.com/mongodb/terraform-provider-mongodbatlas/internal/service/pushbasedlogexport"
Expand Down Expand Up @@ -453,7 +454,9 @@ func (p *MongodbtlasProvider) Resources(context.Context) []func() resource.Resou
streaminstance.Resource,
streamconnection.Resource,
}
previewResources := []func() resource.Resource{} // Resources not yet in GA
previewResources := []func() resource.Resource{ // Resources not yet in GA
EspenAlbert marked this conversation as resolved.
Show resolved Hide resolved
encryptionatrestprivateendpoint.Resource,
}
if providerEnablePreview {
resources = append(resources, previewResources...)
}
Expand Down
34 changes: 34 additions & 0 deletions internal/service/encryptionatrestprivateendpoint/model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package encryptionatrestprivateendpoint

import (
"github.com/mongodb/terraform-provider-mongodbatlas/internal/common/conversion"
"go.mongodb.org/atlas-sdk/v20240805001/admin"
)

func NewTFEarPrivateEndpoint(apiResp *admin.EARPrivateEndpoint) *TFEarPrivateEndpointModel {
if apiResp == nil {
return nil
}
return &TFEarPrivateEndpointModel{
CloudProvider: conversion.StringNullIfEmpty(apiResp.GetCloudProvider()),
ErrorMessage: conversion.StringNullIfEmpty(apiResp.GetErrorMessage()),
ID: conversion.StringNullIfEmpty(apiResp.GetId()),
RegionName: conversion.StringNullIfEmpty(apiResp.GetRegionName()),
Status: conversion.StringNullIfEmpty(apiResp.GetStatus()),
PrivateEndpointConnectionName: conversion.StringNullIfEmpty(apiResp.GetPrivateEndpointConnectionName()),
}
}

func NewEarPrivateEndpointReq(tfPlan *TFEarPrivateEndpointModel) *admin.EARPrivateEndpoint {
if tfPlan == nil {
return nil
}
return &admin.EARPrivateEndpoint{
CloudProvider: tfPlan.CloudProvider.ValueStringPointer(),
ErrorMessage: tfPlan.ErrorMessage.ValueStringPointer(),
Id: tfPlan.ID.ValueStringPointer(),
RegionName: tfPlan.RegionName.ValueStringPointer(),
Status: tfPlan.Status.ValueStringPointer(),
PrivateEndpointConnectionName: tfPlan.PrivateEndpointConnectionName.ValueStringPointer(),
}
}
121 changes: 121 additions & 0 deletions internal/service/encryptionatrestprivateendpoint/model_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package encryptionatrestprivateendpoint_test

import (
"testing"

"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/mongodb/terraform-provider-mongodbatlas/internal/service/encryptionatrestprivateendpoint"
"github.com/stretchr/testify/assert"
"go.mongodb.org/atlas-sdk/v20240805001/admin"
)

const (
testCloudProvider = "AZURE"
testErrMsg = "error occurred"
testID = "6666666999999adsfsgdg"
testRegionName = "US_EAST"
testStatus = "PENDING_ACCEPTANCE"
testPEConnectionName = "mongodb-atlas-US_EAST-666666666067bd1e20a8bf14"
)

type sdkToTFModelTestCase struct {
SDKResp *admin.EARPrivateEndpoint
expectedTFModel *encryptionatrestprivateendpoint.TFEarPrivateEndpointModel
}

func TestEncryptionAtRestPrivateEndpointSDKToTFModel(t *testing.T) {
testCases := map[string]sdkToTFModelTestCase{
"Complete SDK response": {
SDKResp: &admin.EARPrivateEndpoint{
CloudProvider: admin.PtrString(testCloudProvider),
ErrorMessage: admin.PtrString(""),
Id: admin.PtrString(testID),
RegionName: admin.PtrString(testRegionName),
Status: admin.PtrString(testStatus),
PrivateEndpointConnectionName: admin.PtrString(testPEConnectionName),
},
expectedTFModel: &encryptionatrestprivateendpoint.TFEarPrivateEndpointModel{
CloudProvider: types.StringValue(testCloudProvider),
ErrorMessage: types.StringNull(),
ID: types.StringValue(testID),
RegionName: types.StringValue(testRegionName),
Status: types.StringValue(testStatus),
PrivateEndpointConnectionName: types.StringValue(testPEConnectionName),
},
},
"Complete SDK response with error message": {
SDKResp: &admin.EARPrivateEndpoint{
CloudProvider: admin.PtrString(testCloudProvider),
ErrorMessage: admin.PtrString(testErrMsg),
Id: admin.PtrString(testID),
RegionName: admin.PtrString(testRegionName),
Status: admin.PtrString(testStatus),
PrivateEndpointConnectionName: admin.PtrString(testPEConnectionName),
},
expectedTFModel: &encryptionatrestprivateendpoint.TFEarPrivateEndpointModel{
CloudProvider: types.StringValue(testCloudProvider),
ErrorMessage: types.StringValue(testErrMsg),
ID: types.StringValue(testID),
RegionName: types.StringValue(testRegionName),
Status: types.StringValue(testStatus),
PrivateEndpointConnectionName: types.StringValue(testPEConnectionName),
},
},
}

for testName, tc := range testCases {
t.Run(testName, func(t *testing.T) {
resultModel := encryptionatrestprivateendpoint.NewTFEarPrivateEndpoint(tc.SDKResp)
assert.Equal(t, tc.expectedTFModel, resultModel, "created terraform model did not match expected output")
})
}
}

type tfToSDKModelTestCase struct {
tfModel *encryptionatrestprivateendpoint.TFEarPrivateEndpointModel
expectedSDKReq *admin.EARPrivateEndpoint
}

func TestEncryptionAtRestPrivateEndpointTFModelToSDK(t *testing.T) {
testCases := map[string]tfToSDKModelTestCase{
"Complete TF state": {
tfModel: &encryptionatrestprivateendpoint.TFEarPrivateEndpointModel{
CloudProvider: types.StringValue(testCloudProvider),
ErrorMessage: types.StringNull(),
ID: types.StringValue(testID),
RegionName: types.StringValue(testRegionName),
Status: types.StringValue(testStatus),
PrivateEndpointConnectionName: types.StringValue(testPEConnectionName)},
expectedSDKReq: &admin.EARPrivateEndpoint{
CloudProvider: admin.PtrString(testCloudProvider),
ErrorMessage: nil,
Id: admin.PtrString(testID),
RegionName: admin.PtrString(testRegionName),
Status: admin.PtrString(testStatus),
PrivateEndpointConnectionName: admin.PtrString(testPEConnectionName)},
},
"Complete TF state with error message": {
tfModel: &encryptionatrestprivateendpoint.TFEarPrivateEndpointModel{
CloudProvider: types.StringValue(testCloudProvider),
ErrorMessage: types.StringValue(testErrMsg),
ID: types.StringValue(testID),
RegionName: types.StringValue(testRegionName),
Status: types.StringValue(testStatus),
PrivateEndpointConnectionName: types.StringValue(testPEConnectionName)},
expectedSDKReq: &admin.EARPrivateEndpoint{
CloudProvider: admin.PtrString(testCloudProvider),
ErrorMessage: admin.PtrString(testErrMsg),
Id: admin.PtrString(testID),
RegionName: admin.PtrString(testRegionName),
Status: admin.PtrString(testStatus),
PrivateEndpointConnectionName: admin.PtrString(testPEConnectionName)},
},
}

for testName, tc := range testCases {
t.Run(testName, func(t *testing.T) {
apiReqResult := encryptionatrestprivateendpoint.NewEarPrivateEndpointReq(tc.tfModel)
assert.Equal(t, tc.expectedSDKReq, apiReqResult, "created sdk model did not match expected output")
})
}
}
110 changes: 110 additions & 0 deletions internal/service/encryptionatrestprivateendpoint/resource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
//nolint:gocritic
Copy link
Collaborator Author

@maastha maastha Aug 13, 2024

Choose a reason for hiding this comment

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

@AgustinBettati lmk if you have any thoughts on this approach, was thinking if it may be possible to ensure this is removed before merge to master, I have added that as an item for tech discussion meeting next week.
For now since this is a dev branch it should be okay to commit this

package encryptionatrestprivateendpoint

import (
"context"

"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/mongodb/terraform-provider-mongodbatlas/internal/config"
)

const (
encryptionAtRestPrivateEndpointName = "encryption_at_rest_private_endpoint"
warnUnsupportedOperation = "Operation not supported"
)

var _ resource.ResourceWithConfigure = &encryptionAtRestPrivateEndpointRS{}
var _ resource.ResourceWithImportState = &encryptionAtRestPrivateEndpointRS{}

func Resource() resource.Resource {
return &encryptionAtRestPrivateEndpointRS{
RSCommon: config.RSCommon{
ResourceName: encryptionAtRestPrivateEndpointName,
},
}
}

type encryptionAtRestPrivateEndpointRS struct {
config.RSCommon
}

func (r *encryptionAtRestPrivateEndpointRS) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = ResourceSchema(ctx)
}

func (r *encryptionAtRestPrivateEndpointRS) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var earPrivateEndpointPlan TFEarPrivateEndpointModel
resp.Diagnostics.Append(req.Plan.Get(ctx, &earPrivateEndpointPlan)...)
if resp.Diagnostics.HasError() {
return
}

// encryptionAtRestPrivateEndpointReq := NewEarPrivateEndpointReq(&earPrivateEndpointPlan)

// TODO: make POST request to Atlas API and handle error in response
// connV2 := r.Client.AtlasV2
// if err != nil {
// resp.Diagnostics.AddError("error creating resource", err.Error())
// return
//}

// TODO: process response into new terraform state
// newencryptionAtRestPrivateEndpointModel := NewTFencryptionAtRestPrivateEndpoint(apiResp)
// resp.Diagnostics.Append(resp.State.Set(ctx, newencryptionAtRestPrivateEndpointModel)...)
}

func (r *encryptionAtRestPrivateEndpointRS) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var earPrivateEndpointState TFEarPrivateEndpointModel
resp.Diagnostics.Append(req.State.Get(ctx, &earPrivateEndpointState)...)
if resp.Diagnostics.HasError() {
return
}

// TODO: make get request to resource
// connV2 := r.Client.AtlasV2
// if err != nil {
// if apiResp != nil && apiResp.StatusCode == http.StatusNotFound {
// resp.State.RemoveResource(ctx)
// return
// }
// resp.Diagnostics.AddError("error fetching resource", err.Error())
// return
//}

// TODO: process response into new terraform state
// resp.Diagnostics.Append(resp.State.Set(ctx, NewTFEarPrivateEndpoint(apiResp))...)
}

func (r *encryptionAtRestPrivateEndpointRS) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
resp.Diagnostics.AddWarning(warnUnsupportedOperation, "Updating the private endpoint for encryption at rest is not supported. To modify your infrastructure, please delete the existing mongodbatlas_encryption_at_rest_private_endpoint resource and create a new one with the necessary updates")
}

func (r *encryptionAtRestPrivateEndpointRS) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var earPrivateEndpointState *TFEarPrivateEndpointModel
resp.Diagnostics.Append(req.State.Get(ctx, &earPrivateEndpointState)...)
if resp.Diagnostics.HasError() {
return
}

// TODO: make Delete request to Atlas API

// connV2 := r.Client.AtlasV2
// if _, _, err := connV2.Api.Delete().Execute(); err != nil {
// resp.Diagnostics.AddError("error deleting resource", err.Error())
// return
// }
}

func (r *encryptionAtRestPrivateEndpointRS) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
// TODO: parse req.ID string taking into account documented format. Example:

// projectID, other, err := splitencryptionAtRestPrivateEndpointImportID(req.ID)
// if err != nil {
// resp.Diagnostics.AddError("error splitting import ID", err.Error())
// return
//}

// TODO: define attributes that are required for read operation to work correctly. Example:

// resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), projectID)...)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package encryptionatrestprivateendpoint

import (
"context"

"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
)

func ResourceSchema(ctx context.Context) schema.Schema {
return schema.Schema{
Attributes: map[string]schema.Attribute{
"cloud_provider": schema.StringAttribute{
Required: true,
Description: "Human-readable label that identifies the cloud provider for the Encryption At Rest private endpoint.",
MarkdownDescription: "Human-readable label that identifies the cloud provider for the Encryption At Rest private endpoint.",
},
"endpoint_id": schema.StringAttribute{
Optional: true,
Computed: true,
Description: "Unique 24-hexadecimal digit string that identifies the private endpoint.",
MarkdownDescription: "Unique 24-hexadecimal digit string that identifies the private endpoint.",
},
"error_message": schema.StringAttribute{
Computed: true,
Description: "Error message for failures associated with the Encryption At Rest private endpoint.",
MarkdownDescription: "Error message for failures associated with the Encryption At Rest private endpoint.",
},
"project_id": schema.StringAttribute{
Required: true,
Description: "Unique 24-hexadecimal digit string that identifies your project. Use the [/groups](#tag/Projects/operation/listProjects) endpoint to retrieve all projects to which the authenticated user has access.\n\n**NOTE**: Groups and projects are synonymous terms. Your group id is the same as your project id. For existing groups, your group/project id remains the same. The resource and corresponding endpoints use the term groups.",
MarkdownDescription: "Unique 24-hexadecimal digit string that identifies your project. Use the [/groups](#tag/Projects/operation/listProjects) endpoint to retrieve all projects to which the authenticated user has access.\n\n**NOTE**: Groups and projects are synonymous terms. Your group id is the same as your project id. For existing groups, your group/project id remains the same. The resource and corresponding endpoints use the term groups.",
},
"id": schema.StringAttribute{
Computed: true,
Description: "Unique 24-hexadecimal digit string that identifies the Private Endpoint Service.",
MarkdownDescription: "Unique 24-hexadecimal digit string that identifies the Private Endpoint Service.",
},
"private_endpoint_connection_name": schema.StringAttribute{
Computed: true,
Description: "Connection name of the Azure Private Endpoint.",
MarkdownDescription: "Connection name of the Azure Private Endpoint.",
},
"region_name": schema.StringAttribute{
Required: true,
Description: "Cloud provider region in which the Encryption At Rest private endpoint is located.",
MarkdownDescription: "Cloud provider region in which the Encryption At Rest private endpoint is located.",
},
"status": schema.StringAttribute{
Computed: true,
Description: "State of the Encryption At Rest private endpoint.",
MarkdownDescription: "State of the Encryption At Rest private endpoint.",
},
},
}
}

type TFEarPrivateEndpointModel struct {
CloudProvider types.String `tfsdk:"cloud_provider"`
EndpointID types.String `tfsdk:"endpoint_id"`
ErrorMessage types.String `tfsdk:"error_message"`
ProjectID types.String `tfsdk:"project_id"`
ID types.String `tfsdk:"id"`
PrivateEndpointConnectionName types.String `tfsdk:"private_endpoint_connection_name"`
RegionName types.String `tfsdk:"region_name"`
Status types.String `tfsdk:"status"`
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//nolint:gocritic
package encryptionatrestprivateendpoint_test

// TODO: if acceptance test will be run in an existing CI group of resources, the name should include the group in the prefix followed by the name of the resource e.i. TestAccStreamRSStreamInstance_basic
// In addition, if acceptance test contains testing of both resource and data sources, the RS/DS can be omitted.
// func TestAccEncryptionatrestprivateendpointRS_basic(t *testing.T) {
// resource.ParallelTest(t, resource.TestCase{
// PreCheck: func() { acc.PreCheckBasic(t) },
// ProtoV6ProviderFactories: acc.TestAccProviderV6Factories,
// // CheckDestroy: checkDestroyEncryptionatrestprivateendpoint,
// Steps: []resource.TestStep{ // TODO: verify updates and import in case of resources
// // {
// // Config: encryptionatrestprivateendpointConfig(),
// // Check: encryptionatrestprivateendpointAttributeChecks(),
// // },
// // {
// // Config: encryptionatrestprivateendpointConfig(),
// // Check: encryptionatrestprivateendpointAttributeChecks(),
// // },
// // {
// // Config: encryptionatrestprivateendpointConfig(),
// // ResourceName: resourceName,
// // ImportStateIdFunc: checkEncryptionatrestprivateendpointImportStateIDFunc,
// // ImportState: true,
// // ImportStateVerify: true,
// },
// },
// )
// }
Loading