Skip to content

Commit

Permalink
feat(LH-72231): SEC Onboarding Resource (#103)
Browse files Browse the repository at this point in the history
  • Loading branch information
weilueluo authored Nov 15, 2023
1 parent ec4f49c commit 9fd393f
Showing 32 changed files with 768 additions and 13 deletions.
5 changes: 5 additions & 0 deletions client/client.go
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ import (
"context"
"github.com/CiscoDevnet/terraform-provider-cdo/go-client/connector/connectoronboarding"
"github.com/CiscoDevnet/terraform-provider-cdo/go-client/connector/sec"
"github.com/CiscoDevnet/terraform-provider-cdo/go-client/connector/sec/seconboarding"
"net/http"

"github.com/CiscoDevnet/terraform-provider-cdo/go-client/connector"
@@ -240,3 +241,7 @@ func (c *Client) DeleteSec(ctx context.Context, inp sec.DeleteInput) (*sec.Delet
func (c *Client) ReadSec(ctx context.Context, inp sec.ReadInput) (*sec.ReadOutput, error) {
return sec.Read(ctx, c.client, inp)
}

func (c *Client) CreateSecOnboarding(ctx context.Context, inp seconboarding.CreateInput) (*seconboarding.CreateOutput, error) {
return seconboarding.Create(ctx, c.client, inp)
}
8 changes: 4 additions & 4 deletions client/connector/sec/bootstrapdata.go
Original file line number Diff line number Diff line change
@@ -8,8 +8,8 @@ import (
"github.com/CiscoDevnet/terraform-provider-cdo/go-client/user"
)

func ComputeEventOnlyBootstrapData(secName, accessToken, tenantName, baseUrl, host string) string {
bootstrapUrl := fmt.Sprintf("%s/connector/bootstrap/%s/%s", baseUrl, tenantName, secName)
func ComputeEventOnlyBootstrapData(accessToken, tenantName, baseUrl, host string) string {
bootstrapUrl := fmt.Sprintf("%s/sdc/bootstrap/%s", baseUrl, tenantName)

rawBootstrapData := fmt.Sprintf("CDO_TOKEN=%q\nCDO_DOMAIN=%q\nCDO_TENANT=%q\nCDO_BOOTSTRAP_URL=%q\nONLY_EVENTING=\"true\"\n", accessToken, host, tenantName, bootstrapUrl)

@@ -18,11 +18,11 @@ func ComputeEventOnlyBootstrapData(secName, accessToken, tenantName, baseUrl, ho
return bootstrapData
}

func generateBootstrapData(ctx context.Context, client http.Client, secName string) (string, error) {
func generateBootstrapData(ctx context.Context, client http.Client) (string, error) {
userToken, err := user.GetToken(ctx, client, user.NewGetTokenInput())
if err != nil {
return "", err
}

return ComputeEventOnlyBootstrapData(secName, userToken.AccessToken, userToken.TenantName, client.BaseUrl(), client.Host()), nil
return ComputeEventOnlyBootstrapData(userToken.AccessToken, userToken.TenantName, client.BaseUrl(), client.Host()), nil
}
2 changes: 1 addition & 1 deletion client/connector/sec/create.go
Original file line number Diff line number Diff line change
@@ -61,7 +61,7 @@ func Create(ctx context.Context, client http.Client, createInp CreateInput) (*Cr
secBootstrapData := readOutput.BootStrapData

// 4. generate cdo bootstrap data
cdoBootstrapData, err := generateBootstrapData(ctx, client, readOutput.Name)
cdoBootstrapData, err := generateBootstrapData(ctx, client)
if err != nil {
return nil, err
}
2 changes: 1 addition & 1 deletion client/connector/sec/create_test.go
Original file line number Diff line number Diff line change
@@ -29,7 +29,7 @@ func TestSecCreate(t *testing.T) {
Uid("test-sec-uid").
SecBootstrapData("sec-test-bootstrap-data").
Name("test-sec-name").
CdoBoostrapData(sec.ComputeEventOnlyBootstrapData("test-sec-name", successTokenResponse.AccessToken, successTokenResponse.TenantName, baseUrl, domain)).
CdoBoostrapData(sec.ComputeEventOnlyBootstrapData(successTokenResponse.AccessToken, successTokenResponse.TenantName, baseUrl, domain)).
Build()
successReadOutput := sec.NewReadOutputBuilder().
Uid(successCreateOutput.Uid).
1 change: 1 addition & 0 deletions client/connector/sec/read.go
Original file line number Diff line number Diff line change
@@ -15,6 +15,7 @@ type ReadOutput struct {
Uid string `json:"uid"`
BootStrapData string `json:"bootstrapData"`
TokenExpiryTime int64 `json:"tokenExpiryTime"`
EsStatus string `json:"esStatus"`
}

func Read(ctx context.Context, client http.Client, readInp ReadInput) (*ReadOutput, error) {
32 changes: 32 additions & 0 deletions client/connector/sec/read_by_name.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package sec

import (
"context"
"fmt"
"github.com/CiscoDevnet/terraform-provider-cdo/go-client/internal/http"
)

type ReadByNameInput struct {
Name string
}

type ReadByNameOutput = ReadOutput

func ReadByName(ctx context.Context, client http.Client, inp ReadByNameInput) (*ReadByNameOutput, error) {

client.Logger.Println("reading SEC by name")

outp, err := ReadAll(ctx, client, ReadAllInput{})
if err != nil {
return nil, err
}

for _, sec := range *outp {
client.Logger.Printf("checking if sec.Name %q == inp.Name %q\n", sec.Name, inp.Name)
if sec.Name == inp.Name {
return &sec, nil
}
}

return nil, fmt.Errorf("SEC with name: %q not found", inp.Name)
}
20 changes: 20 additions & 0 deletions client/connector/sec/read_by_name_inputbuilder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package sec

type ReadByNameInputBuilder struct {
readByNameInput *ReadByNameInput
}

func NewReadByNameInputBuilder() *ReadByNameInputBuilder {
readByNameInput := &ReadByNameInput{}
b := &ReadByNameInputBuilder{readByNameInput: readByNameInput}
return b
}

func (b *ReadByNameInputBuilder) Name(name string) *ReadByNameInputBuilder {
b.readByNameInput.Name = name
return b
}

func (b *ReadByNameInputBuilder) Build() ReadByNameInput {
return *b.readByNameInput
}
25 changes: 25 additions & 0 deletions client/connector/sec/readall.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package sec

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

type ReadAllInput struct{}

type ReadAllOutput = []ReadOutput

func ReadAll(ctx context.Context, client http.Client, readInp ReadAllInput) (*ReadAllOutput, error) {

client.Logger.Println("reading all SECs")

readAllUrl := url.ReadAllSecs(client.BaseUrl())
readReq := client.NewGet(ctx, readAllUrl)
var readAllOutput []ReadOutput
if err := readReq.Send(&readAllOutput); err != nil {
return nil, err
}

return &readAllOutput, nil
}
50 changes: 50 additions & 0 deletions client/connector/sec/seconboarding/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package seconboarding

import (
"context"
"github.com/CiscoDevnet/terraform-provider-cdo/go-client/connector/sec"
"github.com/CiscoDevnet/terraform-provider-cdo/go-client/internal/http"
"github.com/CiscoDevnet/terraform-provider-cdo/go-client/internal/retry"
"time"
)

type CreateInput struct {
Name string
}

type CreateOutput = sec.ReadOutput

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

client.Logger.Printf("creating SEC Onboarding")

var createOutp CreateOutput
err := retry.Do(
ctx,
func() (bool, error) {
readOutp, err := sec.ReadByName(ctx, client, sec.NewReadByNameInputBuilder().Name(createInput.Name).Build())
if err != nil {
return false, err
}
createOutp = *readOutp
client.Logger.Printf("SEC status=%s\n", readOutp.EsStatus)
if readOutp.EsStatus == "ACTIVE" {
return true, nil
}
return false, nil
},
retry.NewOptionsBuilder().
Message("Waiting for SEC to be ACTIVE...").
Retries(-1).
Delay(3*time.Second).
Timeout(15*time.Minute). // usually takes 5-15 minutes
Logger(client.Logger).
EarlyExitOnError(true).
Build(),
)
if err != nil {
return nil, err
}

return &createOutp, nil
}
20 changes: 20 additions & 0 deletions client/connector/sec/seconboarding/create_inputbuilder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package seconboarding

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) Build() CreateInput {
return *b.createInput
}
4 changes: 4 additions & 0 deletions client/internal/url/url.go
Original file line number Diff line number Diff line change
@@ -169,3 +169,7 @@ func ReadSec(baseUrl string, secUid string) string {
func DeleteSec(baseUrl string, secUid string) string {
return fmt.Sprintf("%s/aegis/rest/v1/services/targets/estreamers/%s", baseUrl, secUid)
}

func ReadAllSecs(baseUrl string) string {
return fmt.Sprintf("%s/aegis/rest/v1/services/targets/estreamers", baseUrl)
}
24 changes: 24 additions & 0 deletions docs/resources/sec_onboarding.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "cdo_sec_onboarding Resource - cdo"
subcategory: ""
description: |-
Use this resource to wait for an SEC to finish onboarding. When an SEC is onboarded, either manually or using the CDO Terraform Modules for AWS https://github.com/CiscoDevNet/terraform-aws-cdo-sec, it can take a few minutes before the SEC is active and capable of proxying communications between CDO and the device. This resource allows you to wait until this is done.
---

# cdo_sec_onboarding (Resource)

Use this resource to wait for an SEC to finish onboarding. When an SEC is onboarded, either manually or using the CDO Terraform Modules for [AWS](https://github.com/CiscoDevNet/terraform-aws-cdo-sec), it can take a few minutes before the SEC is active and capable of proxying communications between CDO and the device. This resource allows you to wait until this is done.



<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `name` (String) Specify the name of the SEC.

### Read-Only

- `id` (String) The unique identifier of this SEC onboarding resource.
Empty file.
33 changes: 33 additions & 0 deletions provider/examples/resources/sec/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
module "example_vpc" {
source = "./vpc"
resource_prefix = "terraform-provider-example"
}

module "example_sec" {
source = "./sec"
vpc_id = module.example_vpc.vpc_id
subnet_id = module.example_vpc.private_subnet_id
resource_prefix = "tf-cdo"
lb_public_subnet_id = module.example_vpc.public_subnet_1_id
lb_secondary_public_subnet_id = module.example_vpc.public_subnet_2_id
hosted_zone_name = "<your aws hosted zone>" # e.g. labs.cdo.cisco.com
dns_prefix = "<your choice of sub domain>" # e.g. test => test.labs.cdo.cisco.com
}

output "sec_bootstrap_data" {
value = module.example_sec.sec_bootstrap_data
sensitive = true
}

output "cdo_bootstrap_data" {
value = module.example_sec.cdo_bootstrap_data
sensitive = true
}

output "sec_fqdn" {
value = module.example_sec.sec_fqdn
}

output "sec_instance_id" {
value = module.example_sec.sec_instance_id
}
12 changes: 12 additions & 0 deletions provider/examples/resources/sec/providers.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
terraform {
required_providers {
cdo = {
source = "CiscoDevnet/cdo"
}
}
}

provider "cdo" {
base_url = "https://ci.dev.lockhart.io"
api_token = file("${path.module}/api_token.txt")
}
29 changes: 29 additions & 0 deletions provider/examples/resources/sec/sec/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
data "aws_route53_zone" "selected" {
name = var.hosted_zone_name
}

# Create SEC. This creates an SEC entry in CDO, and does not bootstrap the SEC. This SEC is configured to be created in AWS; disable this by setting `var.create_resources_in_aws` to false.
resource "cdo_sec" "example" {
}

# Create SDC instance in the private subnet of the AWS VPC. Disable this by setting `var.create_resources_in_aws` to false.
module "sec-instance-in-aws-example" {
source = "CiscoDevNet/cdo-sec/aws"
version = "0.1.0"
cdo_bootstrap_data = cdo_sec.example.cdo_bootstrap_data
# Get the bootstrap data from CDO and pass it to the AWS instance.
sec_bootstrap_data = cdo_sec.example.sec_bootstrap_data
instance_name = "${var.resource_prefix}-sec"
# Deploy the instance in the private subnet of the VPC you created.
vpc_id = var.vpc_id
subnet_id = var.subnet_id
public_subnet_id = var.lb_public_subnet_id
secondary_public_subnet_id = var.lb_secondary_public_subnet_id
hosted_zone_id = data.aws_route53_zone.selected.id
dns_name = "${var.dns_prefix}.${data.aws_route53_zone.selected.name}"
env = "example"
}

resource "cdo_sec_onboarding" "example" {
name = cdo_sec.example.name
}
24 changes: 24 additions & 0 deletions provider/examples/resources/sec/sec/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
output "sec_name" {
value = cdo_sec.example.name
description = "The name of the SDC spun up in AWS."
}

output "sec_bootstrap_data" {
value = cdo_sec.example.sec_bootstrap_data
description = "The bootstrap data of the SEC spun up in AWS."
sensitive = true
}

output "cdo_bootstrap_data" {
value = cdo_sec.example.cdo_bootstrap_data
description = "The bootstrap data of the SEC in CDO."
sensitive = true
}

output "sec_fqdn" {
value = module.sec-instance-in-aws-example.sec_fqdn
}

output "sec_instance_id" {
value = module.sec-instance-in-aws-example.instance_id
}
7 changes: 7 additions & 0 deletions provider/examples/resources/sec/sec/provider.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
terraform {
required_providers {
cdo = {
source = "CiscoDevnet/cdo"
}
}
}
Loading

0 comments on commit 9fd393f

Please sign in to comment.