Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ resources from Terraform provider plugin.
Library gives possibility to manage virtual network devices and associated network
services.

**NOTE**: scope of this library is limited to needs of Terraform provider plugin
**NOTE**: Scope of this library is limited to needs of Terraform provider plugin
and it is not providing full capabilities of Equinix Network Edge API

## Usage
Expand Down
32 changes: 32 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ const (
DeviceStateWaitingPrimary = "WAITING_FOR_PRIMARY"
//DeviceStateWaitingSecondary primary Network Edge device is waiting for provisioning of its redundant (secondary) device
DeviceStateWaitingSecondary = "WAITING_FOR_SECONDARY"
//DeviceStateWaitingClusterNodes primary Network Edge device is waiting for provisioning of its cluster devices
DeviceStateWaitingClusterNodes = "WAITING_FOR_REPLICA_CLUSTER_NODES"
//DeviceStateClusterSetUpInProgress The cluster setup is in progress
DeviceStateClusterSetUpInProgress = "CLUSTER_SETUP_IN_PROGRESS"
//DeviceStateFailed Network Edge device creation and provisioning have failed
DeviceStateFailed = "FAILED"
//DeviceStateProvisioned Network Edge device was successfully provisioned and is fully operational
Expand All @@ -32,6 +36,8 @@ const (
DeviceLicenseStateApplied = "APPLIED"
//DeviceLicenseStateFailed license registration has failed
DeviceLicenseStateFailed = "REGISTRATION_FAILED"
//DeviceLicenseStateWaitingClusterSetUp license is waiting for cluster setup
DeviceLicenseStateWaitingClusterSetUp = "WAITING_FOR_CLUSTER_SETUP"

//BGPStateIdle BGP peer state is idle
BGPStateIdle = "Idle"
Expand Down Expand Up @@ -239,6 +245,7 @@ type Device struct {
LicenseFile *string
LicenseFileID *string
ACLTemplateUUID *string
MgmtAclTemplateUuid *string
SSHIPAddress *string
SSHIPFqdn *string
AccountNumber *string
Expand All @@ -258,6 +265,7 @@ type Device struct {
UserPublicKey *DeviceUserPublicKey
ASN *int
ZoneCode *string
ClusterDetails *ClusterDetails
}

//DeviceInterface describes Network Edge device interface
Expand Down Expand Up @@ -417,3 +425,27 @@ type DeviceLinkGroupLink struct {
SourceZoneCode *string
DestinationZoneCode *string
}

//ClusterDetails describes Network Edge cluster device details
type ClusterDetails struct {
ClusterName *string
NumOfNodes *int
Comment thread
displague marked this conversation as resolved.
ClusterNodeDetails map[string]*ClusterNodeDetail
ClusterId *string
Nodes []ClusterNode
}

//ClusterNodeDetail describes Network Edge cluster node configuration
type ClusterNodeDetail struct {
VendorConfiguration map[string]string
LicenseFileId *string
LicenseToken *string
}

type ClusterNode struct {
UUID *string
Name *string
Node *int
AdminPassword *string
VendorConfiguration map[string]string
}
3 changes: 0 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,12 @@ github.com/equinix/rest-go v1.3.0 h1:m38scYTOfV6N+gcrwchgVDutDffYd+QoYCMm9Jn6jyk
github.com/equinix/rest-go v1.3.0/go.mod h1:7pjEgOdG2MZO9BGkQzSurSgVQxRfzc1enceXJS6hYDw=
github.com/go-resty/resty/v2 v2.3.0 h1:JOOeAvjSlapTT92p8xiS19Zxev1neGikoHsXJeOq8So=
github.com/go-resty/resty/v2 v2.3.0/go.mod h1:UpN9CgLZNsv4e9XG50UU8xdI0F43UQ4HmxLBDwaroHU=
github.com/jarcoal/httpmock v1.0.6 h1:e81vOSexXU3mJuJ4l//geOmKIt+Vkxerk1feQBC8D0g=
github.com/jarcoal/httpmock v1.0.6/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
github.com/jarcoal/httpmock v1.0.8 h1:8kI16SoO6LQKgPE7PvQuV+YuD/inwHd7fOOe2zMbo4k=
github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
Expand Down
41 changes: 39 additions & 2 deletions internal/api/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type Device struct {
LicenseType *string `json:"licenseType,omitempty"`
LicenseFileID *string `json:"licenseFileId,omitempty"`
ACLTemplateUUID *string `json:"aclTemplateUuid,omitempty"`
MgmtAclTemplateUuid *string `json:"mgmtAclTemplateUuid,omitempty"`
SSHIPAddress *string `json:"sshIpAddress,omitempty"`
SSHIPFqdn *string `json:"sshIpFqdn,omitempty"`
AccountNumber *string `json:"accountNumber,omitempty"`
Expand All @@ -38,6 +39,7 @@ type Device struct {
UserPublicKey *DeviceUserPublicKey `json:"userPublicKey,omitempty"`
ASN *int `json:"asn,omitempty"`
ZoneCode *string `json:"zoneCode,omitempty"`
ClusterDetails *ClusterDetails `json:"clusterDetails,omitempty"`
}

//DeviceRequest describes network edge device creation request
Expand All @@ -64,9 +66,11 @@ type DeviceRequest struct {
Core *int `json:"core,omitempty"`
AdditionalBandwidth *int `json:"additionalBandwidth,omitempty,string"`
ACLTemplateUUID *string `json:"aclTemplateUuid,omitempty"`
MgmtAclTemplateUuid *string `json:"mgmtAclTemplateUuid,omitempty"`
VendorConfig map[string]string `json:"vendorConfig,omitempty"`
UserPublicKey *DeviceUserPublicKeyRequest `json:"userPublicKey,omitempty"`
Secondary *SecondaryDeviceRequest `json:"secondary,omitempty"`
ClusterDetails *ClusterDetailsRequest `json:"clusterDetails,omitempty"`
}

//SecondaryDeviceRequest describes secondary device part of device creation request
Expand Down Expand Up @@ -122,8 +126,10 @@ type DeviceCoreInformation struct {

//DeviceRequestResponse describes response for device creation request
type DeviceRequestResponse struct {
UUID *string `json:"uuid,omitempty"`
SecondaryUUID *string `json:"secondaryUUID,omitempty"`
UUID *string `json:"uuid,omitempty"`
SecondaryUUID *string `json:"secondaryUuid,omitempty"`
DeviceIDs map[string]string `json:"deviceIds,omitempty"`
ClusterID *string `json:"clusterId,omitempty"`
}

//DeviceUpdateRequest describes network device update request
Expand Down Expand Up @@ -159,3 +165,34 @@ type DeviceAdditionalBandwidthResponse struct {
type DeviceACLResponse struct {
Status *string `json:"status,omitempty"`
}

//ClusterDetailsRequest describes cluster details of device creation request
type ClusterDetailsRequest struct {
ClusterName *string `json:"clusterName,omitempty"`
NumOfNodes *int `json:"numOfNodes,omitempty"`
ClusterNodeDetails map[string]ClusterNodeDetailRequest `json:"clusterNodeDetails,omitempty"`
}

//ClusterNodeDetailRequest describes cluster node configuration of device creation request
type ClusterNodeDetailRequest struct {
VendorConfiguration map[string]string `json:"vendorConfig,omitempty"`
LicenseFileId *string `json:"licenseFileId,omitempty"`
LicenseToken *string `json:"licenseToken,omitempty"`
}

//ClusterDetails describes cluster details for device response
type ClusterDetails struct {
ClusterId *string `json:"clusterId,omitempty"`
ClusterName *string `json:"clusterName,omitempty"`
NumOfNodes *int `json:"numOfNodes,omitempty"`
Nodes []ClusterNode `json:"nodes,omitempty"`
}

//ClusterNode describes cluster node details for device response
type ClusterNode struct {
UUID *string `json:"uuid,omitempty"`
Name *string `json:"name,omitempty"`
Node *int `json:"node,omitempty"`
AdminPassword *string `json:"adminPwd,omitempty"`
VendorConfiguration map[string]string `json:"vendorConfig,omitempty"`
}
53 changes: 53 additions & 0 deletions rest_device.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ func mapDeviceAPIToDomain(apiDevice api.Device) *Device {
device.LicenseToken = apiDevice.LicenseToken
device.LicenseFileID = apiDevice.LicenseFileID
device.ACLTemplateUUID = apiDevice.ACLTemplateUUID
device.MgmtAclTemplateUuid = apiDevice.MgmtAclTemplateUuid
device.SSHIPAddress = apiDevice.SSHIPAddress
device.SSHIPFqdn = apiDevice.SSHIPFqdn
device.AccountNumber = apiDevice.AccountNumber
Expand Down Expand Up @@ -236,6 +237,7 @@ func mapDeviceAPIToDomain(apiDevice api.Device) *Device {
device.UserPublicKey = mapDeviceUserPublicKeyAPIToDomain(apiDevice.UserPublicKey)
device.ASN = apiDevice.ASN
device.ZoneCode = apiDevice.ZoneCode
device.ClusterDetails = mapDeviceClusterDetailsAPIToDomain(apiDevice.ClusterDetails)
return &device
}

Expand Down Expand Up @@ -276,6 +278,55 @@ func mapDeviceUserPublicKeyDomainToAPI(userKey *DeviceUserPublicKey) *api.Device
}
}

func mapDeviceClusterDetailsAPIToDomain(apiClusterDetails *api.ClusterDetails) *ClusterDetails {
if apiClusterDetails == nil {
return nil
}
clusterDetails := ClusterDetails{}
clusterDetails.ClusterId = apiClusterDetails.ClusterId
clusterDetails.ClusterName = apiClusterDetails.ClusterName
clusterDetails.NumOfNodes = apiClusterDetails.NumOfNodes
apiNodes := apiClusterDetails.Nodes
transformed := make([]ClusterNode, len(apiNodes))
for i := range apiNodes {
transformed[i] = ClusterNode{
UUID: apiNodes[i].UUID,
Name: apiNodes[i].Name,
Node: apiNodes[i].Node,
AdminPassword: apiNodes[i].AdminPassword,
VendorConfiguration: apiNodes[i].VendorConfiguration,
}
}
clusterDetails.Nodes = transformed
return &clusterDetails
}

func mapDeviceClusterDetailsDomainToAPI(clusterDetails *ClusterDetails) *api.ClusterDetailsRequest {
if clusterDetails == nil {
return nil
}
req := api.ClusterDetailsRequest{}
req.ClusterName = clusterDetails.ClusterName
req.NumOfNodes = clusterDetails.NumOfNodes
clusterNodeDetailsRequest := make(map[string]api.ClusterNodeDetailRequest)
for k, v := range clusterDetails.ClusterNodeDetails {
clusterNodeDetailsRequest[k] = *mapDeviceClusterNodeDetailDomainToAPI(v)
}
req.ClusterNodeDetails = clusterNodeDetailsRequest
return &req
}

func mapDeviceClusterNodeDetailDomainToAPI(clusterNodeDetail *ClusterNodeDetail) *api.ClusterNodeDetailRequest {
if clusterNodeDetail == nil {
return nil
}
return &api.ClusterNodeDetailRequest{
VendorConfiguration: clusterNodeDetail.VendorConfiguration,
LicenseFileId: clusterNodeDetail.LicenseFileId,
LicenseToken: clusterNodeDetail.LicenseToken,
}
}

func createDeviceRequest(device Device) api.DeviceRequest {
req := api.DeviceRequest{}
req.Throughput = device.Throughput
Expand Down Expand Up @@ -315,8 +366,10 @@ func createDeviceRequest(device Device) api.DeviceRequest {
req.AdditionalBandwidth = device.AdditionalBandwidth
req.SshInterfaceId = device.WanInterfaceId
req.ACLTemplateUUID = device.ACLTemplateUUID
req.MgmtAclTemplateUuid = device.MgmtAclTemplateUuid
req.VendorConfig = device.VendorConfiguration
req.UserPublicKey = mapDeviceUserPublicKeyDomainToAPI(device.UserPublicKey)
req.ClusterDetails = mapDeviceClusterDetailsDomainToAPI(device.ClusterDetails)
return req
}

Expand Down
117 changes: 117 additions & 0 deletions rest_device_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,76 @@ func TestCreateRedundantDevice(t *testing.T) {
verifyRedundantDeviceRequest(t, primary, secondary, req)
}

func TestCreateClusterDevice(t *testing.T) {
//given
resp := api.DeviceRequestResponse{}
if err := readJSONData("./test-fixtures/ne_device_create_resp.json", &resp); err != nil {
assert.Fail(t, "Cannot read test response")
}
device := Device{
Name: String("PANW-cluster"),
MetroCode: String("SV"),
TypeCode: String("PA-VM"),
IsSelfManaged: Bool(true),
IsBYOL: Bool(true),
PackageCode: String("VM100"),
Notifications: []string{"[email protected]", "[email protected]"},
HostName: String("panwHostName"),
TermLength: Int(24),
AccountNumber: String("177643"),
Version: String("10.1.3"),
InterfaceCount: Int(10),
CoreCount: Int(2),
ACLTemplateUUID: String("4972e8d2-417f-4821-91a8-f4a61a6dcdc3"),
MgmtAclTemplateUuid: String("4972e8d2-417f-4821-91a8-f4a61a6dcdc3"),
UserPublicKey: &DeviceUserPublicKey{
Username: String("testUserName"),
KeyName: String("testKey"),
},
ClusterDetails: &ClusterDetails{
ClusterName: String("clusterName"),
ClusterNodeDetails: map[string]*ClusterNodeDetail{
"node0": {
VendorConfiguration: map[string]string{
"hostName": "panw-host0",
},
LicenseFileId: String("8d180057-8309-4c59-b645-f630f010ad43"),
LicenseToken: String("licenseToken"),
},
"node1": {
VendorConfiguration: map[string]string{
"hostName": "panw-host1",
},
LicenseFileId: String("8d180057-8309-4c59-b645-f630f010ad43"),
LicenseToken: String("licenseToken"),
},
},
},
}
req := api.DeviceRequest{}
testHc := &http.Client{}
httpmock.ActivateNonDefault(testHc)
httpmock.RegisterResponder("POST", fmt.Sprintf("%s/ne/v1/devices", baseURL),
func(r *http.Request) (*http.Response, error) {
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return httpmock.NewStringResponse(400, ""), nil
}
resp, _ := httpmock.NewJsonResponse(202, resp)
return resp, nil
},
)
defer httpmock.DeactivateAndReset()

//when
c := NewClient(context.Background(), baseURL, testHc)
uuid, err := c.CreateDevice(device)

//then
assert.Nil(t, err, "Error is not returned")
assert.Equal(t, uuid, resp.UUID, "UUID matches")
verifyClusterDeviceRequest(t, device, req)
}

func TestGetDevice(t *testing.T) {
//given
resp := api.Device{}
Expand Down Expand Up @@ -413,6 +483,37 @@ func verifyRedundantDeviceRequest(t *testing.T, primary, secondary Device, req a
verifyDeviceUserPublicKeyRequest(t, *secondary.UserPublicKey, *req.Secondary.UserPublicKey)
}

func verifyClusterDeviceRequest(t *testing.T, device Device, req api.DeviceRequest) {
assert.Equal(t, device.Name, req.VirtualDeviceName, "Name matches")
assert.Equal(t, device.MetroCode, req.MetroCode, "MetroCode matches")
assert.Equal(t, device.TypeCode, req.DeviceTypeCode, "TypeCode matches")
if *device.IsSelfManaged {
assert.Equal(t, DeviceManagementTypeSelf, StringValue(req.DeviceManagementType), "DeviceManagementType matches")
} else {
assert.Equal(t, DeviceManagementTypeEquinix, StringValue(req.DeviceManagementType), "DeviceManagementType matches")
}
if *device.IsBYOL {
assert.Equal(t, DeviceLicenseModeBYOL, StringValue(req.LicenseMode), "LicenseMode matches")
} else {
assert.Equal(t, DeviceLicenseModeSubscription, StringValue(req.LicenseMode), "LicenseMode matches")
}
assert.Equal(t, device.PackageCode, req.PackageCode, "PackageCode matches")
assert.ElementsMatch(t, device.Notifications, req.Notifications, "Notifications matches")
assert.Equal(t, device.HostName, req.HostNamePrefix, "HostName matches")
termLengthStr := strconv.Itoa(*device.TermLength)
assert.Equal(t, &termLengthStr, req.TermLength, "TermLength matches")
assert.Equal(t, device.AccountNumber, req.AccountNumber, "AccountNumber matches")
assert.Equal(t, device.Version, req.Version, "Version matches")
assert.Equal(t, device.InterfaceCount, req.InterfaceCount, "InterfaceCount matches")
assert.Equal(t, device.CoreCount, req.Core, "Core matches")
assert.Equal(t, device.ACLTemplateUUID, req.ACLTemplateUUID, "ACLTemplateUUID matches")
assert.Equal(t, device.MgmtAclTemplateUuid, req.MgmtAclTemplateUuid, "MgmtAclTemplateUuid matches")
assert.NotNil(t, req.UserPublicKey, "UserPublicKey is not nil")
verifyDeviceUserPublicKeyRequest(t, *device.UserPublicKey, *req.UserPublicKey)
assert.NotNil(t, req.ClusterDetails, "ClusterDetails are not nil")
verifyClusterDetailsRequest(t, *device.ClusterDetails, *req.ClusterDetails)
}

func verifyDeviceUserPublicKey(t *testing.T, userKey DeviceUserPublicKey, apiUserKey api.DeviceUserPublicKey) {
assert.Equal(t, apiUserKey.Username, userKey.Username, "Username matches")
assert.Equal(t, apiUserKey.KeyName, userKey.KeyName, "KeyName matches")
Expand All @@ -422,3 +523,19 @@ func verifyDeviceUserPublicKeyRequest(t *testing.T, userKey DeviceUserPublicKey,
assert.Equal(t, apiUserKeyReq.Username, userKey.Username, "Username matches")
assert.Equal(t, apiUserKeyReq.KeyName, userKey.KeyName, "KeyName matches")
}

func verifyClusterDetailsRequest(t *testing.T, clusterDetails ClusterDetails, apiClusterDetailsReq api.ClusterDetailsRequest) {
assert.Equal(t, clusterDetails.ClusterName, apiClusterDetailsReq.ClusterName, "ClusterName matches")
apiClusterNodeDetailReqMap := apiClusterDetailsReq.ClusterNodeDetails
assert.NotNil(t, apiClusterNodeDetailReqMap, "ClusterNodeDetails are not nil")
for k, v := range clusterDetails.ClusterNodeDetails {
verifyClusterNodeDetailRequest(t, v, apiClusterNodeDetailReqMap[k])
}
}

func verifyClusterNodeDetailRequest(t *testing.T, clusterNodeDetail *ClusterNodeDetail, apiClusterNodeDetailReq api.ClusterNodeDetailRequest) {
assert.NotNil(t, apiClusterNodeDetailReq, "ClusterNodeDetailRequest is not nil")
assert.Equal(t, clusterNodeDetail.VendorConfiguration, apiClusterNodeDetailReq.VendorConfiguration, "VendorConfigurations match")
assert.Equal(t, clusterNodeDetail.LicenseFileId, apiClusterNodeDetailReq.LicenseFileId, "LicenseFileId matches")
assert.Equal(t, clusterNodeDetail.LicenseToken, apiClusterNodeDetailReq.LicenseToken, "LicenseToken matches")
}
2 changes: 1 addition & 1 deletion test-fixtures/ne_device_create_resp.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"uuid": "877a3aa2-c49a-4af1-98a6-007424e737ae",
"secondaryUUID": "b22c289a-162a-4e51-879d-b3e779805fb7"
"secondaryUuid": "b22c289a-162a-4e51-879d-b3e779805fb7"
}