Skip to content

Commit

Permalink
fix: create edge_gateway_resource + tests
Browse files Browse the repository at this point in the history
  • Loading branch information
eremid committed Feb 10, 2023
1 parent 125f238 commit 4c0c63f
Show file tree
Hide file tree
Showing 5 changed files with 225 additions and 70 deletions.
140 changes: 105 additions & 35 deletions internal/provider/edge_gateway_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,39 @@ import (
apiclient "github.com/orange-cloudavenue/cloudavenue-sdk-go"
)

const (
defaultCheckJobDelayEdgeGateway = 10 * time.Second
)

// Ensure the implementation satisfies the expected interfaces.
var (
_ resource.Resource = &edgeGatewaysResource{}
_ resource.ResourceWithConfigure = &edgeGatewaysResource{}
_ resource.ResourceWithImportState = &edgeGatewaysResource{}

configEdgeGateway setDefaultEdgeGateway = func() edgeGatewayConfig {
return edgeGatewayConfig{
checkJobDelay: defaultCheckJobDelayEdgeGateway,
}
}
)

// NewEdgeGatewayResource is a helper function to simplify the provider implementation.
func NewEdgeGatewayResource() resource.Resource {
return &edgeGatewaysResource{}
}

type setDefaultEdgeGateway func() edgeGatewayConfig

type edgeGatewayConfig struct {
jobDoneMsg jobStatusMessage
checkJobDelay time.Duration
}

// edgeGatewaysResource is the resource implementation.
type edgeGatewaysResource struct {
client *CloudAvenueClient
edgeGatewayConfig
}

type edgeGatewaysResourceModel struct {
Expand All @@ -50,12 +68,20 @@ type edgeGatewaysResourceModel struct {
}

// Metadata returns the resource type name.
func (r *edgeGatewaysResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
func (r *edgeGatewaysResource) Metadata(
_ context.Context,
req resource.MetadataRequest,
resp *resource.MetadataResponse,
) {
resp.TypeName = req.ProviderTypeName + "_edge_gateway"
}

// Schema defines the schema for the resource.
func (r *edgeGatewaysResource) Schema(ctx context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
func (r *edgeGatewaysResource) Schema(
ctx context.Context,
_ resource.SchemaRequest,
resp *resource.SchemaResponse,
) {
resp.Schema = schema.Schema{
MarkdownDescription: "The Edge Gateway resource allows you to create and delete Edge Gateways in CloudAvenue.",
Attributes: map[string]schema.Attribute{
Expand Down Expand Up @@ -118,7 +144,11 @@ func (r *edgeGatewaysResource) Schema(ctx context.Context, _ resource.SchemaRequ
}
}

func (r *edgeGatewaysResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
func (r *edgeGatewaysResource) Configure(
ctx context.Context,
req resource.ConfigureRequest,
resp *resource.ConfigureResponse,
) {
// Prevent panic if the provider has not been configured.
if req.ProviderData == nil {
return
Expand All @@ -129,17 +159,25 @@ func (r *edgeGatewaysResource) Configure(ctx context.Context, req resource.Confi
if !ok {
resp.Diagnostics.AddError(
"Unexpected Data Source Configure Type",
fmt.Sprintf("Expected *CloudAvenueClient, got: %T. Please report this issue to the provider developers.", req.ProviderData),
fmt.Sprintf(
"Expected *CloudAvenueClient, got: %T. Please report this issue to the provider developers.",
req.ProviderData,
),
)

return
}

r.client = client
r.edgeGatewayConfig = configEdgeGateway()
}

// Create creates the resource and sets the initial Terraform state.
func (r *edgeGatewaysResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
func (r *edgeGatewaysResource) Create(
ctx context.Context,
req resource.CreateRequest,
resp *resource.CreateResponse,
) {
// Retrieve values from plan
var plan *edgeGatewaysResourceModel

Expand Down Expand Up @@ -184,15 +222,23 @@ func (r *edgeGatewaysResource) Create(ctx context.Context, req resource.CreateRe

switch plan.OwnerType.ValueString() {
case "vdc":
job, httpR, err = r.client.EdgeGatewaysApi.ApiCustomersV20VdcsVdcNameEdgesPost(auth, body, plan.OwnerName.ValueString())
job, httpR, err = r.client.EdgeGatewaysApi.ApiCustomersV20VdcsVdcNameEdgesPost(
auth,
body,
plan.OwnerName.ValueString(),
)
if apiErr := CheckAPIError(err, httpR); apiErr != nil {
resp.Diagnostics.Append(apiErr.GetTerraformDiagnostic())
if resp.Diagnostics.HasError() {
return
}
}
case "vdc-group":
job, httpR, err = r.client.EdgeGatewaysApi.ApiCustomersV20VdcGroupsVdcGroupNameEdgesPost(auth, body, plan.OwnerName.ValueString())
job, httpR, err = r.client.EdgeGatewaysApi.ApiCustomersV20VdcGroupsVdcGroupNameEdgesPost(
auth,
body,
plan.OwnerName.ValueString(),
)
if apiErr := CheckAPIError(err, httpR); apiErr != nil {
resp.Diagnostics.Append(apiErr.GetTerraformDiagnostic())
if resp.Diagnostics.HasError() {
Expand All @@ -208,37 +254,37 @@ func (r *edgeGatewaysResource) Create(ctx context.Context, req resource.CreateRe
return nil, "", err
}

edgeID := ""
edgeGW := apiclient.EdgeGateway{}

if jobStatus.IsDone() {
if jobStatus.isDone() {
// get all edge gateways and find the one that matches the tier0_vrf_id and owner_name
gateways, _, errEdgesGet := r.client.EdgeGatewaysApi.ApiCustomersV20EdgesGet(auth)
if errEdgesGet != nil {
return nil, "err", err
return nil, "err", errEdgesGet
}

for _, gw := range gateways {
if gw.Tier0VrfId == plan.Tier0VrfID.ValueString() && gw.OwnerName == plan.OwnerName.ValueString() {
edgeID = gw.EdgeId
if gw.Tier0VrfId == plan.Tier0VrfID.ValueString() &&
gw.OwnerName == plan.OwnerName.ValueString() {
edgeGW = gw
break
}
}
} else {
return nil, jobStatus.String(), nil
}
return edgeID, jobStatus.String(), nil

return edgeGW, jobStatus.string(), nil
}

createStateConf := &sdkResource.StateChangeConf{
Delay: 10 * time.Second,
Delay: r.checkJobDelay,
Refresh: refreshF,
MinTimeout: 5 * time.Second,
Timeout: 5 * time.Minute,
Pending: JobStatePending(),
Target: JobStateDone(),
Pending: jobStatePending(),
Target: jobStateDone(),
}

edgeID, err := createStateConf.WaitForStateContext(ctxTO)
edgeGW, err := createStateConf.WaitForStateContext(ctxTO)
if err != nil {
resp.Diagnostics.AddError(
"Error creating edge gateway",
Expand All @@ -249,18 +295,20 @@ func (r *edgeGatewaysResource) Create(ctx context.Context, req resource.CreateRe

// Set the ID

id, idIsAString := edgeID.(string)
if !idIsAString {
newEdgeGW, isEdgeGW := edgeGW.(apiclient.EdgeGateway)
if !isEdgeGW {
resp.Diagnostics.AddError(
"Error creating edge gateway",
"Could not create edge gateway, unexpected error: edgeID is not a string",
"Could not create edge gateway, unexpected error: edge gateway is not of type apiclient.EdgeGateway",
)
return
}

plan = &edgeGatewaysResourceModel{
ID: types.StringValue(id),
EdgeID: types.StringValue(id),
ID: types.StringValue(newEdgeGW.EdgeId),
EdgeID: types.StringValue(newEdgeGW.EdgeId),
EdgeName: types.StringValue(newEdgeGW.EdgeName),
Description: types.StringValue(newEdgeGW.Description),
Tier0VrfID: plan.Tier0VrfID,
OwnerName: plan.OwnerName,
OwnerType: plan.OwnerType,
Expand All @@ -277,7 +325,11 @@ func (r *edgeGatewaysResource) Create(ctx context.Context, req resource.CreateRe
}

// Read refreshes the Terraform state with the latest data.
func (r *edgeGatewaysResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
func (r *edgeGatewaysResource) Read(
ctx context.Context,
req resource.ReadRequest,
resp *resource.ReadResponse,
) {
// Get current state
var state *edgeGatewaysResourceModel
diags := req.State.Get(ctx, &state)
Expand Down Expand Up @@ -309,7 +361,10 @@ func (r *edgeGatewaysResource) Read(ctx context.Context, req resource.ReadReques
}

// Get edge gateway
gateway, httpR, err := r.client.EdgeGatewaysApi.ApiCustomersV20EdgesEdgeIdGet(auth, state.EdgeID.ValueString())
gateway, httpR, err := r.client.EdgeGatewaysApi.ApiCustomersV20EdgesEdgeIdGet(
auth,
state.EdgeID.ValueString(),
)
if apiErr := CheckAPIError(err, httpR); apiErr != nil {
resp.Diagnostics.Append(apiErr.GetTerraformDiagnostic())
if resp.Diagnostics.HasError() {
Expand Down Expand Up @@ -341,11 +396,19 @@ func (r *edgeGatewaysResource) Read(ctx context.Context, req resource.ReadReques
}

// Update updates the resource and sets the updated Terraform state on success.
func (r *edgeGatewaysResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
func (r *edgeGatewaysResource) Update(
ctx context.Context,
req resource.UpdateRequest,
resp *resource.UpdateResponse,
) {
}

// Delete deletes the resource and removes the Terraform state on success.
func (r *edgeGatewaysResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
func (r *edgeGatewaysResource) Delete(
ctx context.Context,
req resource.DeleteRequest,
resp *resource.DeleteResponse,
) {
// Get current state
var state edgeGatewaysResourceModel
diags := req.State.Get(ctx, &state)
Expand Down Expand Up @@ -377,7 +440,10 @@ func (r *edgeGatewaysResource) Delete(ctx context.Context, req resource.DeleteRe
}

// Delete the edge gateway
job, httpR, err := r.client.EdgeGatewaysApi.ApiCustomersV20EdgesEdgeIdDelete(auth, state.EdgeID.ValueString())
job, httpR, err := r.client.EdgeGatewaysApi.ApiCustomersV20EdgesEdgeIdDelete(
auth,
state.EdgeID.ValueString(),
)
if apiErr := CheckAPIError(err, httpR); apiErr != nil {
resp.Diagnostics.Append(apiErr.GetTerraformDiagnostic())
if resp.Diagnostics.HasError() {
Expand All @@ -387,19 +453,19 @@ func (r *edgeGatewaysResource) Delete(ctx context.Context, req resource.DeleteRe

// Wait for job to complete
deleteStateConf := &sdkResource.StateChangeConf{
Delay: 10 * time.Second,
Delay: r.checkJobDelay,
Refresh: func() (interface{}, string, error) {
jobStatus, errGetJob := getJobStatus(auth, r.client, job.JobId)
if errGetJob != nil {
return nil, "", err
return nil, "", errGetJob
}

return jobStatus, jobStatus.String(), nil
return jobStatus, jobStatus.string(), nil
},
MinTimeout: 5 * time.Second,
Timeout: 5 * time.Minute,
Pending: JobStatePending(),
Target: JobStateDone(),
Pending: jobStatePending(),
Target: jobStateDone(),
}

_, err = deleteStateConf.WaitForStateContext(ctxTO)
Expand All @@ -412,7 +478,11 @@ func (r *edgeGatewaysResource) Delete(ctx context.Context, req resource.DeleteRe
}
}

func (r *edgeGatewaysResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
func (r *edgeGatewaysResource) ImportState(
ctx context.Context,
req resource.ImportStateRequest,
resp *resource.ImportStateResponse,
) {
// Retrieve import ID and save to edge_id attribute
resource.ImportStatePassthroughID(ctx, path.Root("edge_id"), req, resp)
}
81 changes: 81 additions & 0 deletions internal/provider/edge_gateway_resource_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package provider

import (
"testing"
"time"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

const testAccEdgeGatewayResourceConfig = `
resource "cloudavenue_edge_gateway" "test" {
owner_type = "vdc"
owner_name = "myVDC01"
tier0_vrf_name = "prvrf01iocb0000001allsp01"
}
`

// const testAccEdgeGatewayResourceWithBadOwnerTypeConfig = `
// resource "cloudavenue_edge_gateway" "test" {
// owner_type = "vdc-bad"
// owner_name = "myVDC01"
// tier0_vrf_name = "prvrf01iocb0000001allsp01"
// }
// `

func TestAccEdgeGatewayResource(t *testing.T) {
resourceName := "cloudavenue_edge_gateway.test"

// resourceNameVDCGroup := "cloudavenue_edge_gateway.test-group"
configEdgeGateway = func() edgeGatewayConfig {
return edgeGatewayConfig{
checkJobDelay: 10 * time.Millisecond,
}
}

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// Read testing
{
Config: testAccEdgeGatewayResourceConfig,
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "id", "cc1f35c2-90a2-48d1-9359-62794faf44ad"),
resource.TestCheckResourceAttr(resourceName, "edge_id", "cc1f35c2-90a2-48d1-9359-62794faf44ad"),
resource.TestCheckResourceAttr(resourceName, "owner_type", "vdc"),
resource.TestCheckResourceAttr(resourceName, "owner_name", "myVDC01"),
resource.TestCheckResourceAttr(resourceName, "tier0_vrf_name", "prvrf01iocb0000001allsp01"),
resource.TestCheckResourceAttr(resourceName, "edge_name", "edgeName"),
resource.TestCheckResourceAttr(resourceName, "description", "description"),
),
},
// ImportState testing
// {
// ResourceName: resourceName,
// ImportState: true,
// ImportStateId: "cc1f35c2-90a2-48d1-9359-62794faf44ad",
// ImportStateVerify: true,
// },
// check bad owner_type
// https://github.com/hashicorp/terraform-plugin-sdk/issues/609
// {
// Config: testAccEdgeGatewayResourceWithBadOwnerTypeConfig,
// ExpectError: regexp.MustCompile(`.*`),
// Destroy: true,
// },
// {
// Config: testAccEdgeGatewayGroupResourceConfig,
// Check: resource.ComposeAggregateTestCheckFunc(
// resource.TestCheckResourceAttr(resourceNameVDCGroup, "id", "cc1f35c2-90a2-48d1-9359-62794faf44ae"),
// resource.TestCheckResourceAttr(resourceNameVDCGroup, "edge_id", "cc1f35c2-90a2-48d1-9359-62794faf44ae"),
// resource.TestCheckResourceAttr(resourceNameVDCGroup, "owner_type", "vdc-group"),
// resource.TestCheckResourceAttr(resourceNameVDCGroup, "owner_name", "myVDC02"),
// resource.TestCheckResourceAttr(resourceNameVDCGroup, "tier0_vrf_id", "prvrf01iocb0000001allsp01"),
// resource.TestCheckResourceAttr(resourceNameVDCGroup, "edge_name", "edgeName2"),
// resource.TestCheckResourceAttr(resourceNameVDCGroup, "description", "description"),
// ),
// },
},
})
}
Loading

0 comments on commit 4c0c63f

Please sign in to comment.