From 1022180c65e599261d388c112fcbbb74f0f7796d Mon Sep 17 00:00:00 2001 From: Siddhu Warrier Date: Wed, 30 Oct 2024 23:59:15 +0000 Subject: [PATCH] fix(lh-87215): allow deletion of MSP-managed tenants (#147) This is a faux deletion, as described in the documentation. It removes the tenant from the MSP portal, but doesn't actually delete it from the database. --- client/client.go | 4 ++ client/internal/url/url.go | 2 +- client/msp/tenants/delete.go | 17 +++++++ client/msp/tenants/delete_test.go | 50 ++++++++++++++++++++ client/msp/tenants/models.go | 7 +++ client/msp/tenants/read.go | 2 +- client/msp/users/delete.go | 2 +- docs/resources/msp_managed_tenant.md | 4 +- provider/internal/msp/msp_tenant/resource.go | 21 ++++++-- 9 files changed, 100 insertions(+), 9 deletions(-) create mode 100644 client/msp/tenants/delete.go create mode 100644 client/msp/tenants/delete_test.go diff --git a/client/client.go b/client/client.go index 76d74ad..2e5c64f 100644 --- a/client/client.go +++ b/client/client.go @@ -284,6 +284,10 @@ func (c *Client) ReadMspManagedTenantByUid(ctx context.Context, readByUidInput t return tenants.ReadByUid(ctx, c.client, readByUidInput) } +func (c *Client) DeleteMspManagedTenantByUid(ctx context.Context, deleteByUidInput tenants.DeleteByUidInput) (interface{}, error) { + return tenants.DeleteByUid(ctx, c.client, deleteByUidInput) +} + func (c *Client) FindMspManagedTenantByName(ctx context.Context, readByNameInput tenants.ReadByNameInput) (*tenants.MspTenantsOutput, error) { return tenants.ReadByName(ctx, c.client, readByNameInput) } diff --git a/client/internal/url/url.go b/client/internal/url/url.go index 9bafcab..41f9e81 100644 --- a/client/internal/url/url.go +++ b/client/internal/url/url.go @@ -210,7 +210,7 @@ func CreateMspManagedTenant(baseUrl string) string { return fmt.Sprintf("%s/api/rest/v1/msp/tenants/create", baseUrl) } -func ReadMspManagedTenantByUid(baseUrl string, tenantUid string) string { +func MspManagedTenantByUid(baseUrl string, tenantUid string) string { return fmt.Sprintf("%s/api/rest/v1/msp/tenants/%s", baseUrl, tenantUid) } diff --git a/client/msp/tenants/delete.go b/client/msp/tenants/delete.go new file mode 100644 index 0000000..9f1b21e --- /dev/null +++ b/client/msp/tenants/delete.go @@ -0,0 +1,17 @@ +package tenants + +import ( + "context" + "github.com/CiscoDevnet/terraform-provider-cdo/go-client/internal/http" + "github.com/CiscoDevnet/terraform-provider-cdo/go-client/internal/url" +) + +func DeleteByUid(ctx context.Context, client http.Client, deleteInp DeleteByUidInput) (interface{}, error) { + client.Logger.Println("Removing tenant by UID from the MSP portal " + deleteInp.Uid) + deleteUrl := url.MspManagedTenantByUid(client.BaseUrl(), deleteInp.Uid) + if err := client.NewDelete(ctx, deleteUrl).Send(&DeleteOutput{}); err != nil { + return nil, err + } + + return nil, nil +} diff --git a/client/msp/tenants/delete_test.go b/client/msp/tenants/delete_test.go new file mode 100644 index 0000000..2923bf3 --- /dev/null +++ b/client/msp/tenants/delete_test.go @@ -0,0 +1,50 @@ +package tenants_test + +import ( + "context" + "github.com/CiscoDevnet/terraform-provider-cdo/go-client/internal/http" + "github.com/CiscoDevnet/terraform-provider-cdo/go-client/msp/tenants" + "github.com/google/uuid" + "github.com/jarcoal/httpmock" + "github.com/stretchr/testify/assert" + netHttp "net/http" + "testing" + "time" +) + +func TestDelete(t *testing.T) { + httpmock.Activate() + defer httpmock.DeactivateAndReset() + + t.Run("successfully delete tenant", func(t *testing.T) { + httpmock.Reset() + var tenantUid = uuid.New().String() + var deleteInput = tenants.DeleteByUidInput{ + Uid: tenantUid, + } + httpmock.RegisterResponder( + netHttp.MethodDelete, + "/api/rest/v1/msp/tenants/"+tenantUid, + httpmock.NewJsonResponderOrPanic(204, nil), + ) + response, err := tenants.DeleteByUid(context.Background(), *http.MustNewWithConfig(baseUrl, "valid_token", 0, 0, time.Minute), deleteInput) + assert.Nil(t, response) + assert.Nil(t, err) + }) + + t.Run("send through error if deletion failed", func(t *testing.T) { + httpmock.Reset() + var tenantUid = uuid.New().String() + var deleteInput = tenants.DeleteByUidInput{ + Uid: tenantUid, + } + httpmock.RegisterResponder( + netHttp.MethodDelete, + "/api/rest/v1/msp/tenants/"+tenantUid, + httpmock.NewJsonResponderOrPanic(500, "Not found"), + ) + response, err := tenants.DeleteByUid(context.Background(), *http.MustNewWithConfig(baseUrl, "valid_token", 0, 0, time.Minute), deleteInput) + assert.Nil(t, response) + assert.NotNil(t, err) + }) +} diff --git a/client/msp/tenants/models.go b/client/msp/tenants/models.go index cebb1df..b3a1810 100644 --- a/client/msp/tenants/models.go +++ b/client/msp/tenants/models.go @@ -26,3 +26,10 @@ type ReadByUidInput struct { type ReadByNameInput struct { Name string `json:"name"` } + +type DeleteByUidInput struct { + Uid string `json:"uid"` +} + +type DeleteOutput struct { +} diff --git a/client/msp/tenants/read.go b/client/msp/tenants/read.go index d24ff41..74bf07c 100644 --- a/client/msp/tenants/read.go +++ b/client/msp/tenants/read.go @@ -9,7 +9,7 @@ import ( func ReadByUid(ctx context.Context, client http.Client, readInp ReadByUidInput) (*MspTenantOutput, error) { client.Logger.Println("reading tenant by UID " + readInp.Uid) - readUrl := url.ReadMspManagedTenantByUid(client.BaseUrl(), readInp.Uid) + readUrl := url.MspManagedTenantByUid(client.BaseUrl(), readInp.Uid) req := client.NewGet(ctx, readUrl) var outp MspTenantOutput diff --git a/client/msp/users/delete.go b/client/msp/users/delete.go index 1620d55..a42402d 100644 --- a/client/msp/users/delete.go +++ b/client/msp/users/delete.go @@ -21,7 +21,7 @@ func Delete(ctx context.Context, client http.Client, deleteInp MspDeleteUsersInp return nil, err } - transaction, err = publicapi.WaitForTransactionToFinishWithDefaults( + _, err = publicapi.WaitForTransactionToFinishWithDefaults( ctx, client, transaction, diff --git a/docs/resources/msp_managed_tenant.md b/docs/resources/msp_managed_tenant.md index cdbf3c5..8723bbb 100644 --- a/docs/resources/msp_managed_tenant.md +++ b/docs/resources/msp_managed_tenant.md @@ -3,12 +3,12 @@ page_title: "cdo_msp_managed_tenant Resource - cdo" subcategory: "" description: |- - Provides an MSP managed tenant resource. This allows MSP managed tenants to be created. + Provides an MSP managed tenant resource. This allows MSP managed tenants to be created. Note: deleting this resource removes the created tenant from the MSP portal by disassociating the tenant from the MSP portal, but the tenant will continue to exist. To completely delete a tenant, please contact Cisco TAC. --- # cdo_msp_managed_tenant (Resource) -Provides an MSP managed tenant resource. This allows MSP managed tenants to be created. +Provides an MSP managed tenant resource. This allows MSP managed tenants to be created. Note: deleting this resource removes the created tenant from the MSP portal by disassociating the tenant from the MSP portal, but the tenant will continue to exist. To completely delete a tenant, please contact Cisco TAC. diff --git a/provider/internal/msp/msp_tenant/resource.go b/provider/internal/msp/msp_tenant/resource.go index 69e3bbf..6ce903a 100644 --- a/provider/internal/msp/msp_tenant/resource.go +++ b/provider/internal/msp/msp_tenant/resource.go @@ -27,7 +27,7 @@ func (*TenantResource) Metadata(ctx context.Context, request resource.MetadataRe func (*TenantResource) Schema(ctx context.Context, request resource.SchemaRequest, response *resource.SchemaResponse) { response.Schema = schema.Schema{ - MarkdownDescription: "Provides an MSP managed tenant resource. This allows MSP managed tenants to be created.", + MarkdownDescription: "Provides an MSP managed tenant resource. This allows MSP managed tenants to be created. Note: deleting this resource removes the created tenant from the MSP portal by disassociating the tenant from the MSP portal, but the tenant will continue to exist. To completely delete a tenant, please contact Cisco TAC.", Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ MarkdownDescription: "Universally unique identifier of the tenant", @@ -62,7 +62,7 @@ func (*TenantResource) Schema(ctx context.Context, request resource.SchemaReques } } -func (resource *TenantResource) Configure(ctx context.Context, req resource.ConfigureRequest, res *resource.ConfigureResponse) { +func (t *TenantResource) Configure(ctx context.Context, req resource.ConfigureRequest, res *resource.ConfigureResponse) { if req.ProviderData == nil { return } @@ -78,7 +78,7 @@ func (resource *TenantResource) Configure(ctx context.Context, req resource.Conf return } - resource.client = client + t.client = client } func (t *TenantResource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) { @@ -150,7 +150,20 @@ func (t *TenantResource) Update(ctx context.Context, request resource.UpdateRequ } func (t *TenantResource) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) { - response.Diagnostics.AddError("Cannot delete a created tenant", "Please reach out to CDO TAC if you really want to delete a CDO tenant. You can choose to manually remove the tenant from the Terraform state if you want to remove the tenant from your Terraform configuration.") + tflog.Debug(ctx, "'Deleting' a CDO tenant by removing it from the MSP portal...") + var stateData *TenantResourceModel + response.Diagnostics.Append(request.State.Get(ctx, &stateData)...) + if response.Diagnostics.HasError() { + return + } + + deleteInp := tenants.DeleteByUidInput{ + Uid: stateData.Id.ValueString(), + } + _, err := t.client.DeleteMspManagedTenantByUid(ctx, deleteInp) + if err != nil { + response.Diagnostics.AddError("failed to delete tenant from MSP portal", err.Error()) + } } type PreventUpdatePlanModifier struct{}