diff --git a/client/device/cloudfmc/fmcappliance/update.go b/client/device/cloudfmc/fmcappliance/update.go index 297d8702..4117f448 100644 --- a/client/device/cloudfmc/fmcappliance/update.go +++ b/client/device/cloudfmc/fmcappliance/update.go @@ -9,10 +9,10 @@ import ( type UpdateInput struct { FmcApplianceUid string QueueTriggerState string - StateMachineContext *map[string]string + StateMachineContext map[string]string } -func NewUpdateInput(FmcApplianceUid, queueTriggerState string, stateMachineContext *map[string]string) UpdateInput { +func NewUpdateInput(FmcApplianceUid, queueTriggerState string, stateMachineContext map[string]string) UpdateInput { return UpdateInput{ FmcApplianceUid: FmcApplianceUid, QueueTriggerState: queueTriggerState, @@ -27,9 +27,8 @@ type UpdateOutput struct { } type updateRequestBody struct { - QueueTriggerState string `json:"queueTriggerState"` - StateMachineContext *map[string]string `json:"stateMachineContext,omitempty"` - Uid string `json:"uid,omitempty"` + QueueTriggerState string `json:"queueTriggerState"` + StateMachineContext map[string]string `json:"stateMachineContext"` } func Update(ctx context.Context, client http.Client, updateInp UpdateInput) (*UpdateOutput, error) { @@ -37,7 +36,6 @@ func Update(ctx context.Context, client http.Client, updateInp UpdateInput) (*Up updateBody := newUpdateRequestBodyBuilder(). QueueTriggerState(updateInp.QueueTriggerState). StateMachineContext(updateInp.StateMachineContext). - Uid(updateInp.FmcApplianceUid). Build() req := client.NewPut(ctx, updateUrl, updateBody) var updateOup UpdateOutput diff --git a/client/device/cloudfmc/fmcappliance/update_inputbuilder.go b/client/device/cloudfmc/fmcappliance/update_inputbuilder.go index eab12f77..648753bb 100644 --- a/client/device/cloudfmc/fmcappliance/update_inputbuilder.go +++ b/client/device/cloudfmc/fmcappliance/update_inputbuilder.go @@ -20,7 +20,7 @@ func (b *UpdateInputBuilder) QueueTriggerState(queueTriggerState string) *Update return b } -func (b *UpdateInputBuilder) StateMachineContext(stateMachineContext *map[string]string) *UpdateInputBuilder { +func (b *UpdateInputBuilder) StateMachineContext(stateMachineContext map[string]string) *UpdateInputBuilder { b.updateInput.StateMachineContext = stateMachineContext return b } diff --git a/client/device/cloudfmc/fmcappliance/update_outputbuilder.go b/client/device/cloudfmc/fmcappliance/update_outputbuilder.go index e9562559..455a473e 100644 --- a/client/device/cloudfmc/fmcappliance/update_outputbuilder.go +++ b/client/device/cloudfmc/fmcappliance/update_outputbuilder.go @@ -44,16 +44,11 @@ func (b *updateRequestBodyBuilder) QueueTriggerState(queueTriggerState string) * return b } -func (b *updateRequestBodyBuilder) StateMachineContext(stateMachineContext *map[string]string) *updateRequestBodyBuilder { +func (b *updateRequestBodyBuilder) StateMachineContext(stateMachineContext map[string]string) *updateRequestBodyBuilder { b.updateRequestBody.StateMachineContext = stateMachineContext return b } -func (b *updateRequestBodyBuilder) Uid(uid string) *updateRequestBodyBuilder { - b.updateRequestBody.Uid = uid - return b -} - func (b *updateRequestBodyBuilder) Build() updateRequestBody { return *b.updateRequestBody } diff --git a/client/device/cloudfmc/fmcappliance/update_test.go b/client/device/cloudfmc/fmcappliance/update_test.go index 852e6123..cb7273e0 100644 --- a/client/device/cloudfmc/fmcappliance/update_test.go +++ b/client/device/cloudfmc/fmcappliance/update_test.go @@ -31,7 +31,7 @@ func TestUpdate(t *testing.T) { }{ { testName: "successfully updates FMC Appliance name", - input: fmcappliance.NewUpdateInput(fmcApplianceUid, queueTriggerState, &stateMachineContext), + input: fmcappliance.NewUpdateInput(fmcApplianceUid, queueTriggerState, stateMachineContext), setupFunc: func() { httpmock.RegisterResponder( @@ -50,7 +50,7 @@ func TestUpdate(t *testing.T) { { testName: "error when update FMC Appliance name error", - input: fmcappliance.NewUpdateInput(fmcApplianceUid, queueTriggerState, &stateMachineContext), + input: fmcappliance.NewUpdateInput(fmcApplianceUid, queueTriggerState, stateMachineContext), setupFunc: func() { httpmock.RegisterResponder( diff --git a/client/device/cloudfmc/fmcplatform/read_devicelicenses.go b/client/device/cloudfmc/fmcplatform/read_devicelicenses.go deleted file mode 100644 index 3a3d13ee..00000000 --- a/client/device/cloudfmc/fmcplatform/read_devicelicenses.go +++ /dev/null @@ -1,43 +0,0 @@ -package fmcplatform - -import ( - "context" - "fmt" - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/internal/http" - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/internal/url" - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/model/cloudfmc/devicelicense" -) - -type ReadDeviceLicensesInput struct { - FmcHost string -} - -func NewReadDeviceLicensesInput(fmcHost string) ReadDeviceLicensesInput { - return ReadDeviceLicensesInput{ - FmcHost: fmcHost, - } -} - -type ReadDeviceLicensesOutput = devicelicense.Item - -var NewReadDeviceLicensesOutputBuilder = devicelicense.NewItemBuilder - -func ReadDeviceLicenses(ctx context.Context, client http.Client, readInp ReadDeviceLicensesInput) (*ReadDeviceLicensesOutput, error) { - - client.Logger.Println("reading FMC device licenses") - - readUrl := url.ReadFmcDeviceLicenses(client.BaseUrl()) - req := client.NewGet(ctx, readUrl) - req.Header.Set("Fmc-Hostname", readInp.FmcHost) - - var outp devicelicense.DeviceLicense - if err := req.Send(&outp); err != nil { - return nil, err - } - - if len(outp.Items) != 1 { - return nil, fmt.Errorf("failed to get device licenses") - } - - return &outp.Items[0], nil -} diff --git a/client/device/cloudfmc/fmcplatform/read_devicelicenses_inputbuilder.go b/client/device/cloudfmc/fmcplatform/read_devicelicenses_inputbuilder.go deleted file mode 100644 index 955fa24d..00000000 --- a/client/device/cloudfmc/fmcplatform/read_devicelicenses_inputbuilder.go +++ /dev/null @@ -1,20 +0,0 @@ -package fmcplatform - -type ReadDeviceLicensesInputBuilder struct { - readDeviceLicensesInput *ReadDeviceLicensesInput -} - -func NewReadDeviceLicensesInputBuilder() *ReadDeviceLicensesInputBuilder { - readDeviceLicensesInput := &ReadDeviceLicensesInput{} - b := &ReadDeviceLicensesInputBuilder{readDeviceLicensesInput: readDeviceLicensesInput} - return b -} - -func (b *ReadDeviceLicensesInputBuilder) FmcHost(fmcHost string) *ReadDeviceLicensesInputBuilder { - b.readDeviceLicensesInput.FmcHost = fmcHost - return b -} - -func (b *ReadDeviceLicensesInputBuilder) Build() ReadDeviceLicensesInput { - return *b.readDeviceLicensesInput -} diff --git a/client/device/cloudfmc/fmcplatform/read_domaininfo.go b/client/device/cloudfmc/fmcplatform/readdomaininfo.go similarity index 100% rename from client/device/cloudfmc/fmcplatform/read_domaininfo.go rename to client/device/cloudfmc/fmcplatform/readdomaininfo.go diff --git a/client/device/cloudfmc/fmcplatform/read_domaininfo_test.go b/client/device/cloudfmc/fmcplatform/readdomaininfo_test.go similarity index 100% rename from client/device/cloudfmc/fmcplatform/read_domaininfo_test.go rename to client/device/cloudfmc/fmcplatform/readdomaininfo_test.go diff --git a/client/device/cloudfmc/fmcplatform/update_devicelicenses.go b/client/device/cloudfmc/fmcplatform/update_devicelicenses.go deleted file mode 100644 index f6e75cad..00000000 --- a/client/device/cloudfmc/fmcplatform/update_devicelicenses.go +++ /dev/null @@ -1,50 +0,0 @@ -package fmcplatform - -import ( - "context" - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/internal/http" - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/internal/url" - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/model/cloudfmc/devicelicense" - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/model/ftd/license" -) - -type UpdateDeviceLicensesInput struct { - FmcHost string - LicenseTypes []license.Type -} - -type UpdateDeviceLicensesOutput = devicelicense.Item - -var NewUpdateDeviceLicensesOutputBuilder = devicelicense.NewItemBuilder - -type updateRequestBody struct { - Type string `json:"type"` - Id string `json:"id"` - LicenseTypes []license.Type `json:"licenseTypes"` // must be FMC license types -} - -func UpdateDeviceLicenses(ctx context.Context, client http.Client, updateInp UpdateDeviceLicensesInput) (*UpdateDeviceLicensesOutput, error) { - - client.Logger.Println("updating FMC device licenses") - - readOutput, err := ReadDeviceLicenses(ctx, client, NewReadDeviceLicensesInputBuilder().FmcHost(updateInp.FmcHost).Build()) - if err != nil { - return nil, err - } - - updateUrl := url.UpdateFmcDeviceLicenses(client.BaseUrl(), readOutput.Id) - updateBody := updateRequestBody{ - Type: "DeviceLicense", - Id: readOutput.Id, - LicenseTypes: license.LicensesToFmcLicenses(updateInp.LicenseTypes), - } - req := client.NewPut(ctx, updateUrl, updateBody) - req.Header.Set("Fmc-Hostname", updateInp.FmcHost) - - var outp UpdateDeviceLicensesOutput - if err := req.Send(&outp); err != nil { - return nil, err - } - - return &outp, nil -} diff --git a/client/device/cloudfmc/fmcplatform/update_devicelicenses_inputbuilder.go b/client/device/cloudfmc/fmcplatform/update_devicelicenses_inputbuilder.go deleted file mode 100644 index a818c496..00000000 --- a/client/device/cloudfmc/fmcplatform/update_devicelicenses_inputbuilder.go +++ /dev/null @@ -1,27 +0,0 @@ -package fmcplatform - -import "github.com/CiscoDevnet/terraform-provider-cdo/go-client/model/ftd/license" - -type UpdateDeviceLicensesInputBuilder struct { - updateDeviceLicensesInput *UpdateDeviceLicensesInput -} - -func NewUpdateDeviceLicensesInputBuilder() *UpdateDeviceLicensesInputBuilder { - updateDeviceLicensesInput := &UpdateDeviceLicensesInput{} - b := &UpdateDeviceLicensesInputBuilder{updateDeviceLicensesInput: updateDeviceLicensesInput} - return b -} - -func (b *UpdateDeviceLicensesInputBuilder) FmcHost(fmcHost string) *UpdateDeviceLicensesInputBuilder { - b.updateDeviceLicensesInput.FmcHost = fmcHost - return b -} - -func (b *UpdateDeviceLicensesInputBuilder) LicenseTypes(licenseTypes []license.Type) *UpdateDeviceLicensesInputBuilder { - b.updateDeviceLicensesInput.LicenseTypes = licenseTypes - return b -} - -func (b *UpdateDeviceLicensesInputBuilder) Build() UpdateDeviceLicensesInput { - return *b.updateDeviceLicensesInput -} diff --git a/client/device/cloudfmc/read.go b/client/device/cloudfmc/read.go index b00bdfb6..97cf7bc8 100644 --- a/client/device/cloudfmc/read.go +++ b/client/device/cloudfmc/read.go @@ -19,8 +19,6 @@ func NewReadInput() ReadInput { type ReadOutput = device.ReadOutput -var NewReadOutputBuilder = device.NewReadOutputBuilder - func Read(ctx context.Context, client http.Client, readInp ReadInput) (*ReadOutput, error) { client.Logger.Println("reading cloud FMC") diff --git a/client/device/cloudfmc/readspecific.go b/client/device/cloudfmc/readspecific.go index 55f379e1..1f6a26dc 100644 --- a/client/device/cloudfmc/readspecific.go +++ b/client/device/cloudfmc/readspecific.go @@ -4,7 +4,6 @@ import ( "context" "github.com/CiscoDevnet/terraform-provider-cdo/go-client/device" "github.com/CiscoDevnet/terraform-provider-cdo/go-client/internal/http" - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/model/statemachine" "github.com/CiscoDevnet/terraform-provider-cdo/go-client/model/statemachine/state" ) @@ -13,11 +12,10 @@ type ReadSpecificInput struct { } type ReadSpecificOutput struct { - SpecificUid string `json:"uid"` - DomainUid string `json:"domainUid"` - State state.Type `json:"state"` - Status string `json:"status"` - StateMachineDetails statemachine.Details `json:"stateMachineDetails"` + SpecificUid string `json:"uid"` + DomainUid string `json:"domainUid"` + State state.Type `json:"state"` + Status string `json:"status"` } func NewReadSpecificInput(fmcId string) ReadSpecificInput { @@ -28,8 +26,6 @@ func NewReadSpecificInput(fmcId string) ReadSpecificInput { func ReadSpecific(ctx context.Context, client http.Client, inp ReadSpecificInput) (*ReadSpecificOutput, error) { - client.Logger.Println("reading Cloud FMC specific") - req := device.NewReadSpecificRequest(ctx, client, *device.NewReadSpecificInput(inp.FmcId)) var readSpecificOutp ReadSpecificOutput diff --git a/client/device/cloudfmc/retry.go b/client/device/cloudfmc/retry.go deleted file mode 100644 index 644e281f..00000000 --- a/client/device/cloudfmc/retry.go +++ /dev/null @@ -1,25 +0,0 @@ -package cloudfmc - -import ( - "context" - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/internal/http" - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/internal/retry" - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/internal/statemachine" - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/model/statemachine/state" -) - -func UntilStateDone(ctx context.Context, client http.Client, uid string) retry.Func { - return func() (bool, error) { - fmcReadSpecificRes, err := ReadSpecific(ctx, client, NewReadSpecificInput(uid)) - if err != nil { - return false, err - } - client.Logger.Printf("fmcReadSpecificRes.State=%s\n", fmcReadSpecificRes.State) - if fmcReadSpecificRes.State == state.DONE { - return true, nil - } else if fmcReadSpecificRes.State == state.ERROR { - return false, statemachine.NewWorkflowErrorFromDetails(fmcReadSpecificRes.StateMachineDetails) - } - return false, nil - } -} diff --git a/client/device/cloudftd/cloudftdonboarding/create.go b/client/device/cloudftd/cloudftdonboarding/create.go index 84fd4625..c2979c3d 100644 --- a/client/device/cloudftd/cloudftdonboarding/create.go +++ b/client/device/cloudftd/cloudftdonboarding/create.go @@ -104,7 +104,7 @@ func Create(ctx context.Context, client http.Client, createInp CreateInput) (*Cr // 3.1 read ftd metadata // 3.1.5 handle license - licenseCaps, err := license.StringToCdoLicenses(readFtdOutp.Metadata.LicenseCaps) + licenseCaps, err := license.DeserializeAllFromCdo(readFtdOutp.Metadata.LicenseCaps) if err != nil { return nil, err } diff --git a/client/device/cloudftd/create.go b/client/device/cloudftd/create.go index da744220..ed6d11e4 100644 --- a/client/device/cloudftd/create.go +++ b/client/device/cloudftd/create.go @@ -119,7 +119,7 @@ func Create(ctx context.Context, client http.Client, createInp CreateInput) (*Cr Metadata: &Metadata{ AccessPolicyName: selectedPolicy.Name, AccessPolicyUid: selectedPolicy.Id, - LicenseCaps: license.LicensesToString(*createInp.Licenses), + LicenseCaps: license.SerializeAllAsCdo(*createInp.Licenses), PerformanceTier: performanceTier, }, State: "NEW", diff --git a/client/device/cloudftd/create_metadatabuilder.go b/client/device/cloudftd/create_metadatabuilder.go index 4e03be0a..945baefa 100644 --- a/client/device/cloudftd/create_metadatabuilder.go +++ b/client/device/cloudftd/create_metadatabuilder.go @@ -36,7 +36,7 @@ func (b *MetadataBuilder) GeneratedCommand(generatedCommand string) *MetadataBui } func (b *MetadataBuilder) LicenseCaps(licenseCaps *[]license.Type) *MetadataBuilder { - b.metadata.LicenseCaps = license.LicensesToString(*licenseCaps) + b.metadata.LicenseCaps = license.SerializeAllAsCdo(*licenseCaps) return b } diff --git a/client/device/cloudftd/delete.go b/client/device/cloudftd/delete.go index 9ea5e0b5..5176cf71 100644 --- a/client/device/cloudftd/delete.go +++ b/client/device/cloudftd/delete.go @@ -43,7 +43,7 @@ func Delete(ctx context.Context, client http.Client, deleteInp DeleteInput) (*De fmcappliance.NewUpdateInputBuilder(). FmcApplianceUid(fmcReadSpecificRes.SpecificUid). QueueTriggerState("PENDING_DELETE_FTDC"). - StateMachineContext(&map[string]string{"ftdCDeviceIDs": deleteInp.Uid}). + StateMachineContext(map[string]string{"ftdCDeviceIDs": deleteInp.Uid}). Build(), ) if err != nil { diff --git a/client/device/cloudftd/update.go b/client/device/cloudftd/update.go index ca0364c9..51d3f93f 100644 --- a/client/device/cloudftd/update.go +++ b/client/device/cloudftd/update.go @@ -2,30 +2,22 @@ package cloudftd import ( "context" - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/device/cloudfmc" - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/device/cloudfmc/fmcappliance" - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/device/cloudfmc/fmcplatform" "github.com/CiscoDevnet/terraform-provider-cdo/go-client/internal/http" - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/internal/retry" "github.com/CiscoDevnet/terraform-provider-cdo/go-client/internal/url" "github.com/CiscoDevnet/terraform-provider-cdo/go-client/model/device/tags" - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/model/ftd/license" - "time" ) type UpdateInput struct { - Uid string - Name string - Tags tags.Type - Licenses []license.Type + Uid string + Name string + Tags tags.Type } -func NewUpdateInput(uid, name string, tags tags.Type, licenses []license.Type) UpdateInput { +func NewUpdateInput(uid, name string, tags tags.Type) UpdateInput { return UpdateInput{ - Uid: uid, - Name: name, - Tags: tags, - Licenses: licenses, + Uid: uid, + Name: name, + Tags: tags, } } @@ -36,15 +28,8 @@ type updateRequestBody struct { type UpdateOutput = ReadOutput -var NewUpdateOutputBuilder = NewReadOutputBuilder - func Update(ctx context.Context, client http.Client, updateInp UpdateInput) (*UpdateOutput, error) { - client.Logger.Println("updating FTD") - - client.Logger.Println("updating CDO settings") - - // update CDO settings updateUrl := url.UpdateDevice(client.BaseUrl(), updateInp.Uid) updateBody := updateRequestBody{ Name: updateInp.Name, @@ -56,78 +41,6 @@ func Update(ctx context.Context, client http.Client, updateInp UpdateInput) (*Up return nil, err } - // read FMC host - client.Logger.Println("reading FMC host") - fmcRes, err := cloudfmc.Read(ctx, client, cloudfmc.NewReadInput()) - if err != nil { - return nil, err - } - - // update FTD license through FMC api - client.Logger.Println("updating FTD licenses") - _, err = fmcplatform.UpdateDeviceLicenses( - ctx, - client, - fmcplatform.NewUpdateDeviceLicensesInputBuilder(). - FmcHost(fmcRes.Host). - LicenseTypes(updateInp.Licenses). - Build(), - ) - if err != nil { - return nil, err - } - - // trigger oob detection in cdo to sync license changes - - // read FMC that manages this cloud FTD for its uid, so that we can get its specific uid later - fmcReadRes, err := cloudfmc.Read(ctx, client, cloudfmc.NewReadInput()) - if err != nil { - return nil, err - } - - // read FMC specific device, i.e. the actual FMC for its device uid - fmcReadSpecificRes, err := cloudfmc.ReadSpecific(ctx, client, cloudfmc.NewReadSpecificInput(fmcReadRes.Uid)) - if err != nil { - return nil, err - } - - // trigger oob detection - _, err = fmcappliance.Update( - ctx, - client, - fmcappliance.NewUpdateInputBuilder(). - FmcApplianceUid(fmcReadSpecificRes.SpecificUid). - QueueTriggerState("PENDING_OOB_DETECTION"). - Build(), - ) - if err != nil { - return nil, err - } - - // waiting for oob to be done - err = retry.Do( - ctx, - cloudfmc.UntilStateDone(ctx, client, fmcReadRes.Uid), - retry.NewOptionsBuilder(). - Message("Waiting for FTD to be updated..."). - Retries(-1). - Timeout(5*time.Minute). - Logger(client.Logger). - EarlyExitOnError(true). - Delay(3*time.Second). - Build(), - ) - if err != nil { - return nil, err - } - - client.Logger.Println("re-reading FTD for latest info") - - // re-read the FTD for new license change - ftdReadRes, err := ReadByUid(ctx, client, NewReadByUidInput(updateInp.Uid)) - if err != nil { - return nil, err - } + return &updateOutp, nil - return ftdReadRes, nil } diff --git a/client/device/cloudftd/update_inputbuilder.go b/client/device/cloudftd/update_inputbuilder.go deleted file mode 100644 index dd8e057b..00000000 --- a/client/device/cloudftd/update_inputbuilder.go +++ /dev/null @@ -1,40 +0,0 @@ -package cloudftd - -import ( - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/model/device/tags" - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/model/ftd/license" -) - -type UpdateInputBuilder struct { - updateInput *UpdateInput -} - -func NewUpdateInputBuilder() *UpdateInputBuilder { - updateInput := &UpdateInput{} - b := &UpdateInputBuilder{updateInput: updateInput} - return b -} - -func (b *UpdateInputBuilder) Uid(uid string) *UpdateInputBuilder { - b.updateInput.Uid = uid - return b -} - -func (b *UpdateInputBuilder) Name(name string) *UpdateInputBuilder { - b.updateInput.Name = name - return b -} - -func (b *UpdateInputBuilder) Tags(tags tags.Type) *UpdateInputBuilder { - b.updateInput.Tags = tags - return b -} - -func (b *UpdateInputBuilder) Licenses(licenses []license.Type) *UpdateInputBuilder { - b.updateInput.Licenses = licenses - return b -} - -func (b *UpdateInputBuilder) Build() UpdateInput { - return *b.updateInput -} diff --git a/client/device/cloudftd/update_test.go b/client/device/cloudftd/update_test.go deleted file mode 100644 index 6b9f4d10..00000000 --- a/client/device/cloudftd/update_test.go +++ /dev/null @@ -1,164 +0,0 @@ -package cloudftd_test - -import ( - "context" - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/device/cloudfmc" - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/device/cloudfmc/fmcplatform" - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/device/cloudftd" - internalHttp "github.com/CiscoDevnet/terraform-provider-cdo/go-client/internal/http" - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/internal/url" - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/model/cloudfmc/devicelicense" - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/model/device/tags" - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/model/ftd/license" - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/model/statemachine/state" - "github.com/jarcoal/httpmock" - "github.com/stretchr/testify/assert" - "net/http" - "testing" - "time" -) - -func TestUpdateCloudFtd(t *testing.T) { - httpmock.Activate() - defer httpmock.DeactivateAndReset() - - testHost := "test-host" - var testPort uint = 443 - - testCloudFtdInput := cloudftd.NewUpdateInputBuilder(). - Uid("test-uid"). - Licenses([]license.Type{license.Essentials}). - Name("test-name"). - Tags(tags.Type{Labels: []string{"test-tag"}}). - Build() - - testMetadata := cloudftd.NewMetadataBuilder(). - LicenseCaps(&[]license.Type{}). - Build() - - successFmcSpecificOutput := cloudfmc.NewReadSpecificOutputBuilder(). - SpecificUid("test-specific-uid"). - State(state.DONE). - Build() - - successCloudFtdOutput := cloudftd.NewUpdateOutputBuilder(). - Uid(testCloudFtdInput.Uid). - Metadata(testMetadata). - Name(testCloudFtdInput.Name). - Build() - - successCloudFmcOutput := cloudfmc.NewReadOutputBuilder(). - WithUid(successFmcSpecificOutput.SpecificUid). - WithLocation(testHost, testPort). - Build() - - successReadDeviceLicenseOutput := fmcplatform.NewReadDeviceLicensesOutputBuilder(). - Id("test-license-id"). - Build() - - successUpdateDeviceLicenseOutput := fmcplatform.NewUpdateDeviceLicensesOutputBuilder(). - Id("test-license-id"). - Build() - - successFmcApplianceOutput := cloudftd.NewUpdateOutputBuilder(). - Uid(successFmcSpecificOutput.SpecificUid). - Build() - - testCases := []struct { - testName string - input cloudftd.UpdateInput - setupFunc func() - assertFunc func(output *cloudftd.UpdateOutput, err error, t *testing.T) - }{ - { - testName: "successfully update Cloud FTD", - input: testCloudFtdInput, - setupFunc: func() { - updateCdoFtdSettings(baseUrl, successCloudFtdOutput) - readCloudFmc(baseUrl, successCloudFmcOutput) - readFtdDeviceLicense(baseUrl, successReadDeviceLicenseOutput) - updateFtdDeviceLicense(baseUrl, successUpdateDeviceLicenseOutput) - readCloudFmcSpecific(baseUrl, successFmcSpecificOutput) - updateCloudFmcAppliance(baseUrl, successFmcApplianceOutput) - readFtd(baseUrl, successCloudFtdOutput) - }, - assertFunc: func(output *cloudftd.UpdateOutput, err error, t *testing.T) { - assert.Nil(t, err) - assert.NotNil(t, output) - assert.Equal(t, successCloudFtdOutput, *output) - }, - }, - } - - for _, testCase := range testCases { - t.Run(testCase.testName, func(t *testing.T) { - httpmock.Reset() - - testCase.setupFunc() - - output, err := cloudftd.Update( - context.Background(), - *internalHttp.MustNewWithConfig(baseUrl, "a_valid_token", 0, 0, time.Minute), - testCase.input, - ) - - testCase.assertFunc(output, err, t) - }) - } -} - -func updateCdoFtdSettings(baseUrl string, output cloudftd.UpdateOutput) { - httpmock.RegisterResponder( - http.MethodPut, - url.UpdateDevice(baseUrl, output.Uid), - httpmock.NewJsonResponderOrPanic(http.StatusOK, output), - ) -} - -func readCloudFmc(baseUrl string, output cloudfmc.ReadOutput) { - httpmock.RegisterResponder( - http.MethodGet, - url.ReadAllDevicesByType(baseUrl), - httpmock.NewJsonResponderOrPanic(http.StatusOK, []cloudfmc.ReadOutput{output}), - ) -} - -func readCloudFmcSpecific(baseUrl string, output cloudfmc.ReadSpecificOutput) { - httpmock.RegisterResponder( - http.MethodGet, - url.ReadSpecificDevice(baseUrl, output.SpecificUid), - httpmock.NewJsonResponderOrPanic(http.StatusOK, output), - ) -} - -func updateCloudFmcAppliance(baseUrl string, output cloudftd.UpdateOutput) { - httpmock.RegisterResponder( - http.MethodPut, - url.UpdateFmcAppliance(baseUrl, output.Uid), - httpmock.NewJsonResponderOrPanic(http.StatusOK, output), - ) -} - -func readFtdDeviceLicense(baseUrl string, readOutput fmcplatform.ReadDeviceLicensesOutput) { - httpmock.RegisterResponder( - http.MethodGet, - url.ReadFmcDeviceLicenses(baseUrl), - httpmock.NewJsonResponderOrPanic(http.StatusOK, devicelicense.DeviceLicense{Items: []fmcplatform.ReadDeviceLicensesOutput{readOutput}}), - ) -} - -func updateFtdDeviceLicense(baseUrl string, updateOutput fmcplatform.UpdateDeviceLicensesOutput) { - httpmock.RegisterResponder( - http.MethodPut, - url.UpdateFmcDeviceLicenses(baseUrl, updateOutput.Id), - httpmock.NewJsonResponderOrPanic(http.StatusOK, updateOutput), - ) -} - -func readFtd(baseUrl string, output cloudftd.ReadOutput) { - httpmock.RegisterResponder( - http.MethodGet, - url.ReadDevice(baseUrl, output.Uid), - httpmock.NewJsonResponderOrPanic(http.StatusOK, output), - ) -} diff --git a/client/internal/goutil/goutil.go b/client/internal/goutil/goutil.go index ec9f7697..845f19e6 100644 --- a/client/internal/goutil/goutil.go +++ b/client/internal/goutil/goutil.go @@ -1,9 +1,6 @@ package goutil -import ( - "golang.org/x/exp/constraints" - "sort" -) +import "golang.org/x/exp/constraints" // AsPointer convert interface{} to *interface{}, if input is not nil func AsPointer(obj interface{}) *interface{} { @@ -36,11 +33,3 @@ func Max[T constraints.Ordered](a, b T) T { } return b } - -// SortStrings return a sorted copy of the input string array. -func SortStrings(inp []string) []string { - temp := make([]string, len(inp)) - copy(temp, inp) - sort.Strings(temp) - return temp -} diff --git a/client/internal/url/url.go b/client/internal/url/url.go index 235815de..df39d298 100644 --- a/client/internal/url/url.go +++ b/client/internal/url/url.go @@ -98,14 +98,6 @@ func ReadFmcDomainInfo(fmcHost string) string { return fmt.Sprintf("https://%s/api/fmc_platform/v1/info/domain", fmcHost) } -func ReadFmcDeviceLicenses(baseUrl string) string { - return fmt.Sprintf("%s/fmc/api/fmc_platform/v1/license/devicelicenses", baseUrl) -} - -func UpdateFmcDeviceLicenses(baseUrl string, objectId string) string { - return fmt.Sprintf("%s/fmc/api/fmc_platform/v1/license/devicelicenses/%s", baseUrl, objectId) -} - func CreateUser(baseUrl string, username string) string { return fmt.Sprintf("%s/anubis/rest/v1/users/%s", baseUrl, username) } diff --git a/client/model/cloudfmc/devicelicense/devicelicense.go b/client/model/cloudfmc/devicelicense/devicelicense.go deleted file mode 100644 index 5e99e6d0..00000000 --- a/client/model/cloudfmc/devicelicense/devicelicense.go +++ /dev/null @@ -1,45 +0,0 @@ -package devicelicense - -import ( - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/model/cloudfmc/internal" - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/model/ftd/license" - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/model/ftd/tier" -) - -type DeviceLicense struct { - Links Links `json:"links"` - Paging Paging `json:"paging"` - Items []Item `json:"items"` -} - -func NewDeviceLicense(links Links, paging Paging, items []Item) DeviceLicense { - return DeviceLicense{ - Links: links, - Paging: paging, - Items: items, - } -} - -type Item struct { - Id string `json:"id"` - Type string `json:"type"` - LicenseTypes []license.Type `json:"licenseTypes"` - PerformanceTier tier.Type `json:"performanceTier"` - Links Links `json:"links"` -} - -func NewItem(id, type_ string, licenseTypes []license.Type, performanceTier tier.Type, links Links) Item { - return Item{ - Type: type_, - Id: id, - LicenseTypes: licenseTypes, - PerformanceTier: performanceTier, - Links: links, - } -} - -type Links = internal.Links -type Paging = internal.Paging - -var NewLinks = internal.NewLinks -var NewPaging = internal.NewPaging diff --git a/client/model/cloudfmc/devicelicense/item_builder.go b/client/model/cloudfmc/devicelicense/item_builder.go deleted file mode 100644 index d07de7b6..00000000 --- a/client/model/cloudfmc/devicelicense/item_builder.go +++ /dev/null @@ -1,45 +0,0 @@ -package devicelicense - -import ( - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/model/ftd/license" - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/model/ftd/tier" -) - -type ItemBuilder struct { - item *Item -} - -func NewItemBuilder() *ItemBuilder { - item := &Item{} - b := &ItemBuilder{item: item} - return b -} - -func (b *ItemBuilder) Id(id string) *ItemBuilder { - b.item.Id = id - return b -} - -func (b *ItemBuilder) Type(type_ string) *ItemBuilder { - b.item.Type = type_ - return b -} - -func (b *ItemBuilder) LicenseTypes(licenseTypes []license.Type) *ItemBuilder { - b.item.LicenseTypes = licenseTypes - return b -} - -func (b *ItemBuilder) PerformanceTier(performanceTier tier.Type) *ItemBuilder { - b.item.PerformanceTier = performanceTier - return b -} - -func (b *ItemBuilder) Links(links Links) *ItemBuilder { - b.item.Links = links - return b -} - -func (b *ItemBuilder) Build() Item { - return *b.item -} diff --git a/client/model/ftd/license/license.go b/client/model/ftd/license/license.go index faacd71c..da0b2ce2 100644 --- a/client/model/ftd/license/license.go +++ b/client/model/ftd/license/license.go @@ -12,18 +12,15 @@ type Type string // https://www.cisco.com/c/en/us/td/docs/security/firepower/70/fdm/fptd-fdm-config-guide-700/fptd-fdm-license.html const ( - // CDO terms - Base Type = "BASE" - Carrier Type = "CARRIER" - Threat Type = "THREAT" - Malware Type = "MALWARE" - URLFilter Type = "URLFilter" - - // FMC terms + Base Type = "BASE" Essentials Type = "ESSENTIALS" - URL Type = "URL" + Carrier Type = "CARRIER" + Threat Type = "THREAT" IPS Type = "IPS" + Malware Type = "MALWARE" MalwareDefense Type = "MALWARE_DEFENSE" + URLFilter Type = "URLFilter" + URL Type = "URL" ) var All = []Type{ @@ -38,20 +35,6 @@ var All = []Type{ URL, } -var fmcToCdoLicenseNameMap = map[Type]Type{ - Essentials: Base, - IPS: Threat, - URL: URLFilter, - MalwareDefense: Malware, -} - -var cdoToFmcLicenseNameMap = map[Type]Type{ - Base: Essentials, - Threat: IPS, - URLFilter: URL, - Malware: MalwareDefense, -} - var AllAsString = make([]string, len(All)) var nameToTypeMap = make(map[string]Type, len(All)) @@ -71,7 +54,7 @@ func (t *Type) UnmarshalJSON(b []byte) error { if len(b) <= 2 || b == nil { return fmt.Errorf("cannot unmarshal empty tring as a license type, it should be one of valid roles: %+v", AllAsString) } - deserialized, err := stringToLicense(string(b[1 : len(b)-1])) // strip off quote + deserialized, err := Deserialize(string(b[1 : len(b)-1])) // strip off quote if err != nil { return err } @@ -79,27 +62,35 @@ func (t *Type) UnmarshalJSON(b []byte) error { return nil } -// replaceFmcLicenseTermsWithCdoTerms is used to tell terraform how the licenses returned by FMC map to licenses expected by CDO +// ReplaceFmcLicenseTermsWithCdoTerms is used to tell terraform how the licenses returned by FMC map to licenses expected by CDO // We need to tell Terraform during read that they are the same thing, so when reading it back, we need the conversion -func replaceFmcTermWithCdoTerm(fmcLicense Type) Type { - cdoLicense, ok := fmcToCdoLicenseNameMap[fmcLicense] - if ok { - return cdoLicense - } else { - return fmcLicense +func ReplaceFmcLicenseTermsWithCdoTerms(licenses []string) []string { + for i, l := range licenses { + if l == string(Essentials) { + licenses[i] = string(Base) + } + if l == string(IPS) { + licenses[i] = string(Threat) + } + if l == string(URL) { + licenses[i] = string(URLFilter) + } + if l == string(MalwareDefense) { + licenses[i] = string(Malware) + } } + return licenses } -func replaceCdoTermWithFmcTerm(cdoLicense Type) Type { - fmcLicense, ok := cdoToFmcLicenseNameMap[cdoLicense] - if ok { - return fmcLicense - } else { - return cdoLicense +func MustParse(name string) Type { + l, ok := nameToTypeMap[name] + if !ok { + panic(fmt.Errorf("FTD License of name: \"%s\" not found, should be one of %+v", name, AllAsString)) } + return l } -func stringToLicense(name string) (Type, error) { +func Deserialize(name string) (Type, error) { l, ok := nameToTypeMap[name] if !ok { return "", fmt.Errorf("FTD License of name: \"%s\" not found, should be one of: %+v", name, AllAsString) @@ -107,39 +98,18 @@ func stringToLicense(name string) (Type, error) { return l, nil } -func LicensesToString(licenses []Type) string { - return strings.Join(LicensesToStrings(licenses), ",") -} - -func LicensesToStrings(licenses []Type) []string { - return sliceutil.Map(licenses, func(l Type) string { return string(l) }) +func SerializeAllAsCdo(licenses []Type) string { + return strings.Join(sliceutil.Map(licenses, func(l Type) string { return string(l) }), ",") } -// StringToCdoLicenses exists because CDO store license caps as one comma-sep string -// but fmc store it as list of string, and they have different name for some licenses, -// use this method to handle this special case by converting FMC representation to -// CDO representation -func StringToCdoLicenses(licenses string) ([]Type, error) { +// DeserializeAllFromCdo exists because CDO store license caps as one comma-sep string +// but fmc store it as list of string, use this method to handle CDO's special case +func DeserializeAllFromCdo(licenses string) ([]Type, error) { return sliceutil.MapWithError(strings.Split(licenses, ","), func(l string) (Type, error) { t, ok := nameToTypeMap[l] if !ok { return "", fmt.Errorf("cannot deserialize %s as license, should be one of %+v", l, All) } - t = replaceFmcTermWithCdoTerm(t) return t, nil }) } - -func LicensesToFmcLicenses(licenses []Type) []Type { - return sliceutil.Map(licenses, func(l Type) Type { - return replaceCdoTermWithFmcTerm(l) - }) -} - -func StringToCdoStrings(licenses string) ([]string, error) { - licenseTypes, err := StringToCdoLicenses(licenses) - if err != nil { - return nil, err - } - return LicensesToStrings(licenseTypes), nil -} diff --git a/provider/examples/resources/ftd/cdo_ftd.tf b/provider/examples/resources/ftd/cdo_ftd.tf index 70e9edba..192f2658 100644 --- a/provider/examples/resources/ftd/cdo_ftd.tf +++ b/provider/examples/resources/ftd/cdo_ftd.tf @@ -1,7 +1,7 @@ terraform { required_providers { cdo = { - source = "CiscoDevnet/cdo" + source = "hashicorp.com/CiscoDevnet/cdo" } aws = { source = "hashicorp/aws" diff --git a/provider/internal/device/ftd/operation.go b/provider/internal/device/ftd/operation.go index 7abdd6c7..455a2803 100644 --- a/provider/internal/device/ftd/operation.go +++ b/provider/internal/device/ftd/operation.go @@ -2,10 +2,10 @@ package ftd import ( "context" - "github.com/CiscoDevnet/terraform-provider-cdo/go-client/model/ftd/license" "strings" "github.com/CiscoDevnet/terraform-provider-cdo/go-client/device/cloudftd" + "github.com/CiscoDevnet/terraform-provider-cdo/go-client/model/ftd/license" "github.com/CiscoDevnet/terraform-provider-cdo/go-client/model/ftd/tier" "github.com/CiscoDevnet/terraform-provider-cdo/internal/util" "github.com/hashicorp/terraform-plugin-framework/types" @@ -45,19 +45,13 @@ func Read(ctx context.Context, resource *Resource, stateData *ResourceModel) err return err } - // handle licenses - licenseStrings, err := license.StringToCdoStrings(res.Metadata.LicenseCaps) - if err != nil { - return err - } - // map return struct to model stateData.ID = types.StringValue(res.Uid) stateData.Name = types.StringValue(res.Name) stateData.AccessPolicyName = types.StringValue(res.Metadata.AccessPolicyName) stateData.AccessPolicyUid = types.StringValue(res.Metadata.AccessPolicyUid) stateData.Virtual = types.BoolValue(res.Metadata.PerformanceTier != nil) - stateData.Licenses = util.GoStringSliceToTFStringSet(licenseStrings) + stateData.Licenses = util.GoStringSliceToTFStringSet(license.ReplaceFmcLicenseTermsWithCdoTerms(strings.Split(res.Metadata.LicenseCaps, ","))) if res.Metadata.PerformanceTier != nil { // nil means physical cloudftd stateData.PerformanceTier = types.StringValue(string(*res.Metadata.PerformanceTier)) } @@ -110,18 +104,12 @@ func Create(ctx context.Context, resource *Resource, planData *ResourceModel) er return err } - // convert licenses - licenseStrings, err := license.StringToCdoStrings(res.Metadata.LicenseCaps) - if err != nil { - return err - } - // map return struct to model planData.ID = types.StringValue(res.Uid) planData.Name = types.StringValue(res.Name) planData.AccessPolicyName = types.StringValue(res.Metadata.AccessPolicyName) planData.AccessPolicyUid = types.StringValue(res.Metadata.AccessPolicyUid) - planData.Licenses = util.GoStringSliceToTFStringSet(licenseStrings) + planData.Licenses = util.GoStringSliceToTFStringSet(strings.Split(res.Metadata.LicenseCaps, ",")) planData.Labels = util.GoStringSliceToTFStringSet(res.Tags.Labels) if res.Metadata.PerformanceTier != nil { // nil means physical cloud ftd planData.PerformanceTier = types.StringValue(string(*res.Metadata.PerformanceTier)) @@ -144,17 +132,10 @@ func Update(ctx context.Context, resource *Resource, planData *ResourceModel, st return err } - // convert tf license to go license - licenses, err := util.TFStringSetToLicenses(ctx, planData.Licenses) - if err != nil { - return err - } - inp := cloudftd.NewUpdateInput( planData.ID.ValueString(), planData.Name.ValueString(), planTags, - licenses, ) res, err := resource.client.UpdateCloudFtd(ctx, inp) if err != nil { @@ -164,7 +145,6 @@ func Update(ctx context.Context, resource *Resource, planData *ResourceModel, st // map return struct to model stateData.Name = types.StringValue(res.Name) stateData.Labels = planData.Labels - stateData.Licenses = planData.Licenses return nil } diff --git a/provider/internal/device/ftd/resource.go b/provider/internal/device/ftd/resource.go index b745d20b..fa2edf84 100644 --- a/provider/internal/device/ftd/resource.go +++ b/provider/internal/device/ftd/resource.go @@ -17,6 +17,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/setdefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/setplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" @@ -101,6 +102,9 @@ func (r *Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp ElementType: types.StringType, MarkdownDescription: "Comma-separated list of licenses to apply to this FTD. You must enable at least the \"BASE\" license. Allowed values are: [\"BASE\", \"CARRIER\", \"THREAT\", \"MALWARE\", \"URLFilter\",].", Required: true, + PlanModifiers: []planmodifier.Set{ + setplanmodifier.RequiresReplace(), + }, Validators: []validator.Set{ setvalidator.SizeAtLeast(1), setvalidator.ValueStringsAre(stringvalidator.OneOf(license.AllAsString...)), diff --git a/provider/internal/util/togo.go b/provider/internal/util/togo.go index 75833ba7..38e2be29 100644 --- a/provider/internal/util/togo.go +++ b/provider/internal/util/togo.go @@ -39,7 +39,7 @@ func TFStringSetToLicenses(ctx context.Context, s types.Set) ([]license.Type, er if err != nil { return nil, err } - licenses, err := license.StringToCdoLicenses(strings.Join(licensesGoList, ",")) + licenses, err := license.DeserializeAllFromCdo(strings.Join(licensesGoList, ",")) if err != nil { return nil, err }