Skip to content

Commit

Permalink
feat(LH-69453): CdFmc Resource (#84)
Browse files Browse the repository at this point in the history
  • Loading branch information
weilueluo authored Oct 24, 2023
1 parent 737ff56 commit 33a73e2
Show file tree
Hide file tree
Showing 25 changed files with 652 additions and 69 deletions.
4 changes: 4 additions & 0 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,10 @@ func (c *Client) ReadTenantDetails(ctx context.Context) (*tenant.ReadTenantDetai
return tenant.ReadTenantDetails(ctx, c.client)
}

func (c *Client) CreateCloudFmcDevice(ctx context.Context, inp cloudfmc.CreateInput) (*cloudfmc.CreateOutput, error) {
return cloudfmc.Create(ctx, c.client, inp)
}

func (c *Client) ReadCloudFmcDevice(ctx context.Context) (*device.ReadOutput, error) {
return cloudfmc.Read(ctx, c.client, cloudfmc.NewReadInput())
}
Expand Down
7 changes: 7 additions & 0 deletions client/device/application/error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package application

import "fmt"

var (
NotFoundError = fmt.Errorf("unable to find application")
)
49 changes: 49 additions & 0 deletions client/device/application/read.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package application

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/application/applicationstatus"
)

type ReadInput struct {
}

type ReadOutput struct {
Uid string `json:"uid"`
Name string `json:"name"`
Version int `json:"version"`
ApplicationType string `json:"applicationType"`
ApplicationStatus applicationstatus.Type `json:"applicationStatus"`
ApplicationContent ApplicationContent `json:"applicationContent"`
}

type ApplicationContent struct {
Type string `json:"@type"`
FmceDeviceUid interface{} `json:"fmceDeviceUid"`
DevicesCount int `json:"devicesCount"`
SfcnDevicesCount int `json:"sfcnDevicesCount"`
FmcApplianceUid interface{} `json:"fmcApplianceUid"`
RequestedDevicesCount int `json:"requestedDevicesCount"`
EstimatedDevicesCountRange string `json:"estimatedDevicesCountRange"`
}

func Read(ctx context.Context, client http.Client, readInp ReadInput) (*ReadOutput, error) {
// create request
readUrl := url.ReadApplication(client.BaseUrl())
readReq := client.NewGet(ctx, readUrl)

// send request & map response
var readApplicationOutput []ReadOutput
err := readReq.Send(&readApplicationOutput)
if err != nil {
return nil, err
}

// check and return
if len(readApplicationOutput) < 1 {
return nil, NotFoundError
}
return &readApplicationOutput[0], nil
}
27 changes: 16 additions & 11 deletions client/device/asa/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,17 +86,22 @@ func Create(ctx context.Context, client http.Client, createInp CreateInput) (*Cr
metadata = &Metadata{IsNewPolicyObjectModel: "true"}
}
// 1.3 create the device
deviceCreateOutp, err := device.Create(ctx, client, *device.NewCreateRequestInput(
createInp.Name,
"ASA",
createInp.ConnectorUid,
createInp.ConnectorType,
createInp.SocketAddress,
false,
createInp.IgnoreCertificate,
metadata,
createInp.Tags,
))
deviceCreateOutp, err := device.Create(
ctx,
client,
device.NewCreateInputBuilder().
Name(createInp.Name).
DeviceType(devicetype.Asa).
ConnectorUid(createInp.ConnectorUid).
ConnectorType(createInp.ConnectorType).
SocketAddress(createInp.SocketAddress).
Model(false).
IgnoreCertificate(&createInp.IgnoreCertificate).
Metadata(metadata).
Tags(createInp.Tags).
Build(),
)

var createdResourceId *string = nil
if deviceCreateOutp != nil {
createdResourceId = &deviceCreateOutp.Uid
Expand Down
123 changes: 123 additions & 0 deletions client/device/cloudfmc/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package cloudfmc

import (
"context"
"errors"
"github.com/CiscoDevnet/terraform-provider-cdo/go-client/device"
"github.com/CiscoDevnet/terraform-provider-cdo/go-client/device/application"
"github.com/CiscoDevnet/terraform-provider-cdo/go-client/internal/goutil"
"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/application/applicationstatus"
"github.com/CiscoDevnet/terraform-provider-cdo/go-client/model/devicetype"
"time"
)

type CreateInput struct {
}

func NewCreateInput() CreateInput {
return CreateInput{}
}

type createApplicationBody struct {
ApplicationType string `json:"applicationType"`
ApplicationStatus string `json:"applicationStatus"`
ApplicationContent applicationContent `json:"applicationContent"`
}

type applicationContent struct {
Type string `json:"@type"`
}

type CreateOutput = device.CreateOutput

func Create(ctx context.Context, client http.Client, createInp CreateInput) (*CreateOutput, error) {

client.Logger.Println("Creating application object for cdFMC")

// 1. POST /aegis/rest/v1/services/targets/applications
createApplicationUrl := url.CreateApplication(client.BaseUrl())
createApplicationReq := client.NewPost(
ctx,
createApplicationUrl,
createApplicationBody{
ApplicationType: "FMCE",
ApplicationStatus: "REQUESTED",
ApplicationContent: applicationContent{
Type: "FmceApplicationContent",
},
},
)
var createApplicationOutp device.CreateOutput
err := createApplicationReq.Send(&createApplicationOutp)
if err != nil {
return nil, err
}

client.Logger.Println("creating cloud FMC device")

// 2. POST /aegis/rest/v1/services/targets/devices
createOutp, err := device.Create(ctx, client, device.NewCreateInputBuilder().
Name("FMC").
DeviceType(devicetype.CloudFmc).
Model(false).
ConnectorType("CDG").
IgnoreCertificate(goutil.NewBoolPointer(true)).
EnableOobDetection(goutil.NewBoolPointer(false)).
Build(),
)
if err != nil {
return nil, err
}

client.Logger.Println("waiting for fmce state machine to be done")

err = retry.Do(untilApplicationActive(ctx, client),
retry.NewOptionsBuilder().
Retries(-1).
Timeout(30*time.Minute). // usually takes about 15-20 minutes
Delay(3*time.Second).
EarlyExitOnError(true).
Logger(client.Logger).
Build(),
)
if err != nil {
return nil, err
}

client.Logger.Println("cloud FMC application successfully created")

return createOutp, nil
}

func untilApplicationActive(ctx context.Context, client http.Client) retry.Func {
var unreachable bool
var initialUnreachableTime time.Time
return func() (bool, error) {
fmc, err := application.Read(ctx, client, application.ReadInput{})
if err != nil {
if !errors.Is(err, application.NotFoundError) {
// maybe the application is not created yet, and hopefully this is temporarily, ignoring
return false, nil
}
return false, err
}
if fmc.ApplicationStatus == applicationstatus.Unreachable {
// initial unreachable is possibly caused by https://jira-eng-rtp3.cisco.com/jira/browse/LH-71821
// wait for some time to confirm it is actually unreachable
if unreachable {
if initialUnreachableTime.Add(time.Minute * 5).After(time.Now()) {
// if long enough time has passed, and we are still unreachable, treat it as actual error
return false, err
}
} else {
unreachable = true
initialUnreachableTime = time.Now()
return false, nil
}
}
return fmc.ApplicationStatus == applicationstatus.Active, nil
}
}
43 changes: 14 additions & 29 deletions client/device/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,45 +3,30 @@ package device
import (
"context"
"github.com/CiscoDevnet/terraform-provider-cdo/go-client/model/device/tags"
"github.com/CiscoDevnet/terraform-provider-cdo/go-client/model/devicetype"

"github.com/CiscoDevnet/terraform-provider-cdo/go-client/internal/http"
"github.com/CiscoDevnet/terraform-provider-cdo/go-client/internal/url"
)

type CreateInput struct {
Name string `json:"name"`
DeviceType string `json:"deviceType"`
ConnectorUid string `json:"larUid,omitempty"`
ConnectorType string `json:"larType"`
SocketAddress string `json:"ipv4"`
Model bool `json:"model"`
IgnoreCertificate bool `json:"ignoreCertificate"`
Metadata *interface{} `json:"metadata,omitempty"`
Tags tags.Type `json:"tags,omitempty"`
// required
Name string `json:"name"`
DeviceType devicetype.Type `json:"deviceType"`
Model bool `json:"model"`

// optional
ConnectorUid string `json:"larUid,omitempty"`
ConnectorType string `json:"larType,omitempty"`
SocketAddress string `json:"ipv4,omitempty"`
IgnoreCertificate *bool `json:"ignoreCertificate,omitempty"`
Metadata *interface{} `json:"metadata,omitempty"`
Tags tags.Type `json:"tags,omitempty"`
EnableOobDetection *bool `json:"enableOobDetection,omitempty"`
}

type CreateOutput = ReadOutput

func NewCreateRequestInput(name, deviceType, connectorUid, connectorType, socketAddress string, model bool, ignoreCertificate bool, metadata interface{}, tags tags.Type) *CreateInput {
// convert interface{} to a pointer
var metadataPtr *interface{} = nil
if metadata != nil {
metadataPtr = &metadata
}

return &CreateInput{
Name: name,
DeviceType: deviceType,
ConnectorUid: connectorUid,
ConnectorType: connectorType,
SocketAddress: socketAddress,
Model: model,
IgnoreCertificate: ignoreCertificate,
Metadata: metadataPtr,
Tags: tags,
}
}

func NewCreateRequest(ctx context.Context, client http.Client, createIn CreateInput) *http.Request {

url := url.CreateDevice(client.BaseUrl())
Expand Down
72 changes: 72 additions & 0 deletions client/device/create_inputbuilder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package device

import (
"github.com/CiscoDevnet/terraform-provider-cdo/go-client/internal/goutil"
"github.com/CiscoDevnet/terraform-provider-cdo/go-client/model/device/tags"
"github.com/CiscoDevnet/terraform-provider-cdo/go-client/model/devicetype"
)

// CreateInput builder pattern code
type CreateInputBuilder struct {
createInput *CreateInput
}

func NewCreateInputBuilder() *CreateInputBuilder {
createInput := &CreateInput{}
b := &CreateInputBuilder{createInput: createInput}
return b
}

func (b *CreateInputBuilder) Name(name string) *CreateInputBuilder {
b.createInput.Name = name
return b
}

func (b *CreateInputBuilder) DeviceType(deviceType devicetype.Type) *CreateInputBuilder {
b.createInput.DeviceType = deviceType
return b
}

func (b *CreateInputBuilder) Model(model bool) *CreateInputBuilder {
b.createInput.Model = model
return b
}

func (b *CreateInputBuilder) ConnectorUid(connectorUid string) *CreateInputBuilder {
b.createInput.ConnectorUid = connectorUid
return b
}

func (b *CreateInputBuilder) ConnectorType(connectorType string) *CreateInputBuilder {
b.createInput.ConnectorType = connectorType
return b
}

func (b *CreateInputBuilder) SocketAddress(socketAddress string) *CreateInputBuilder {
b.createInput.SocketAddress = socketAddress
return b
}

func (b *CreateInputBuilder) IgnoreCertificate(ignoreCertificate *bool) *CreateInputBuilder {
b.createInput.IgnoreCertificate = ignoreCertificate
return b
}

func (b *CreateInputBuilder) Metadata(metadata interface{}) *CreateInputBuilder {
b.createInput.Metadata = goutil.AsPointer(metadata)
return b
}

func (b *CreateInputBuilder) Tags(tags tags.Type) *CreateInputBuilder {
b.createInput.Tags = tags
return b
}

func (b *CreateInputBuilder) EnableOobDetection(enableOobDetection *bool) *CreateInputBuilder {
b.createInput.EnableOobDetection = enableOobDetection
return b
}

func (b *CreateInputBuilder) Build() CreateInput {
return *b.createInput
}
Loading

0 comments on commit 33a73e2

Please sign in to comment.