From 21e4b70e27d88287aa56dd8409eb6536300bb2af Mon Sep 17 00:00:00 2001 From: Michael Li Date: Wed, 31 Aug 2022 12:08:46 -0400 Subject: [PATCH] test(e2e): Add end-to-end tests (connect to first target) This adds the initial end-to-end tests. These tests validate the admin workflow of setting up their first target --- enos/README.md | 5 + enos/enos-modules.hcl | 3 + enos/enos-scenario-e2e-target.hcl | 110 +++++++ enos/modules/test_e2e/main.tf | 62 ++++ go.mod | 1 + go.sum | 2 + testing/internal/e2e/README.md | 42 +++ testing/internal/e2e/boundary/boundary.go | 101 +++++++ testing/internal/e2e/helpers.go | 55 ++++ testing/internal/e2e/target/target_test.go | 316 +++++++++++++++++++++ 10 files changed, 697 insertions(+) create mode 100644 enos/enos-scenario-e2e-target.hcl create mode 100644 enos/modules/test_e2e/main.tf create mode 100644 testing/internal/e2e/README.md create mode 100644 testing/internal/e2e/boundary/boundary.go create mode 100644 testing/internal/e2e/helpers.go create mode 100644 testing/internal/e2e/target/target_test.go diff --git a/enos/README.md b/enos/README.md index 5724f5889f..6a16ef7f79 100644 --- a/enos/README.md +++ b/enos/README.md @@ -111,3 +111,8 @@ you could test that version against the cluster by setting `local_boundary_dir` in the Boundary cluster and then executes the Bats CLI UI tests against it. This scenario requires the machine executing `enos` to be configured for the Bats tests as described in the Requirements section. + +## End-to-end tests + +Scenarios with `e2e_` invoke an end-to-end test suite written in Go. Different tests +are invoked depending on the scenario. diff --git a/enos/enos-modules.hcl b/enos/enos-modules.hcl index 6abcbe2298..2fc4c0daca 100644 --- a/enos/enos-modules.hcl +++ b/enos/enos-modules.hcl @@ -66,3 +66,6 @@ module "test_cli_ui" { source = "./modules/test_cli_ui" } +module "test_e2e" { + source = "./modules/test_e2e" +} diff --git a/enos/enos-scenario-e2e-target.hcl b/enos/enos-scenario-e2e-target.hcl new file mode 100644 index 0000000000..08bab9ed29 --- /dev/null +++ b/enos/enos-scenario-e2e-target.hcl @@ -0,0 +1,110 @@ +scenario "e2e_target" { + terraform_cli = terraform_cli.default + terraform = terraform.default + providers = [ + provider.aws.default, + provider.enos.default + ] + + matrix { + builder = ["local", "crt"] + } + + locals { + aws_ssh_private_key_path = abspath(var.aws_ssh_private_key_path) + boundary_install_dir = abspath(var.boundary_install_dir) + local_boundary_dir = abspath(var.local_boundary_dir) + build_path = { + "local" = "/tmp", + "crt" = var.crt_bundle_path == null ? null : abspath(var.crt_bundle_path) + } + } + + step "find_azs" { + module = module.az_finder + + variables { + instance_type = [ + var.worker_instance_type, + var.controller_instance_type + ] + } + } + + step "create_db_password" { + module = module.random_stringifier + } + + step "build_boundary" { + module = matrix.builder == "crt" ? module.build_crt : module.build_local + + variables { + path = local.build_path[matrix.builder] + } + } + + step "create_base_infra" { + module = module.infra + + variables { + availability_zones = step.find_azs.availability_zones + } + } + + step "create_boundary_cluster" { + module = module.boundary + depends_on = [ + step.create_base_infra, + step.build_boundary + ] + + variables { + boundary_install_dir = local.boundary_install_dir + controller_instance_type = var.controller_instance_type + controller_count = var.controller_count + db_pass = step.create_db_password.string + kms_key_arn = step.create_base_infra.kms_key_arn + local_artifact_path = step.build_boundary.artifact_path + ubuntu_ami_id = step.create_base_infra.ami_ids["ubuntu"]["amd64"] + vpc_id = step.create_base_infra.vpc_id + worker_count = var.worker_count + worker_instance_type = var.worker_instance_type + } + } + + step "launch_targets" { + module = module.target + depends_on = [step.create_base_infra] + + variables { + ami_id = step.create_base_infra.ami_ids["ubuntu"]["amd64"] + aws_ssh_keypair_name = var.aws_ssh_keypair_name + enos_user = var.enos_user + instance_type = var.target_instance_type + vpc_id = step.create_base_infra.vpc_id + } + } + + step "run_e2e_targets_test" { + module = module.test_e2e + depends_on = [ + step.create_boundary_cluster, + step.launch_targets + ] + + variables { + test_package = "github.com/hashicorp/boundary/testing/internal/e2e/target" + alb_boundary_api_addr = step.create_boundary_cluster.alb_boundary_api_addr + auth_method_id = step.create_boundary_cluster.auth_method_id + auth_login_name = step.create_boundary_cluster.auth_login_name + auth_password = step.create_boundary_cluster.auth_password + local_boundary_dir = local.local_boundary_dir + aws_ssh_private_key_path = local.aws_ssh_private_key_path + target_ips = step.launch_targets.target_ips + } + } + + output "test_results" { + value = step.run_e2e_targets_test.test_results + } +} diff --git a/enos/modules/test_e2e/main.tf b/enos/modules/test_e2e/main.tf new file mode 100644 index 0000000000..00cbf418d7 --- /dev/null +++ b/enos/modules/test_e2e/main.tf @@ -0,0 +1,62 @@ +terraform { + required_providers { + enos = { + source = "app.terraform.io/hashicorp-qti/enos" + } + } +} + +variable "test_package" { + description = "Name of Go test package to run" + type = string +} +variable "alb_boundary_api_addr" { + description = "URL of the Boundary instance" + type = string +} +variable "auth_method_id" { + description = "Id of Auth Method used to login to Boundary instance" + type = string +} +variable "auth_login_name" { + description = "Name of admin user" + type = string +} +variable "auth_password" { + description = "Password of admin user" + type = string +} +variable "local_boundary_dir" { + description = "Local Path to boundary executable" + type = string +} +variable "aws_ssh_private_key_path" { + description = "Local Path to key used to SSH onto created hosts" + type = string +} +variable "target_ips" { + description = "List of IP Addresses of created hosts" + type = list(string) +} + +locals { + aws_ssh_private_key_path = abspath(var.aws_ssh_private_key_path) +} + +resource "enos_local_exec" "run_e2e_test" { + environment = { + BOUNDARY_ADDR = var.alb_boundary_api_addr, + E2E_PASSWORD_AUTH_METHOD_ID = var.auth_method_id, + E2E_PASSWORD_ADMIN_LOGIN_NAME = var.auth_login_name, + E2E_PASSWORD_ADMIN_PASSWORD = var.auth_password, + E2E_TARGET_IP = var.target_ips[0], + E2E_SSH_USER = "ubuntu" + E2E_SSH_KEY_PATH = local.aws_ssh_private_key_path, + } + + inline = ["PATH=\"${var.local_boundary_dir}:$PATH\" go test -v ${var.test_package}"] +} + +output "test_results" { + value = enos_local_exec.run_e2e_test.stdout +} diff --git a/go.mod b/go.mod index 7f0b8615f6..d993b9240c 100644 --- a/go.mod +++ b/go.mod @@ -93,6 +93,7 @@ require ( github.com/DATA-DOG/go-sqlmock v1.5.0 github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20220711120347-32232bae6803 github.com/hashicorp/nodeenrollment v0.1.16 + github.com/kelseyhightower/envconfig v1.4.0 golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e ) diff --git a/go.sum b/go.sum index 0c296c0929..8f7969b4fd 100644 --- a/go.sum +++ b/go.sum @@ -890,6 +890,8 @@ github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaR github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= +github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= diff --git a/testing/internal/e2e/README.md b/testing/internal/e2e/README.md new file mode 100644 index 0000000000..e4332de2d2 --- /dev/null +++ b/testing/internal/e2e/README.md @@ -0,0 +1,42 @@ +# boundary-e2e-tests + +This test suite tests Boundary in an end-to-end setting using [Enos](https://github.com/hashicorp/Enos-Docs) to spin up the desired infrastructure and [go test](https://pkg.go.dev/testing) to perform user workflows. + +## Getting Started +### Usage +#### Enos +Setup Enos as described [here](../../enos/README.md) + +```shell +enos scenario run e2e_{scenario} builder:local // runs and destroys infra + +enos scenario launch e2e_{scenario} builder:local // runs and keeps infra online +enos scenario output // displays any defined enos output + +enos scenario destroy // destroys infra +``` + +Enos scenarios set up the infrastructure, set the appropriate environment variables, and run the selected tests. Folders in this directory correspond to an enos scenario (e.g. `enos/enos-scenario-e2e-target.hcl` runs tests in `testing/e2e/target`) + +#### Local +Set the appropriate environment variables... +```shell +export BOUNDARY_ADDR= # e.g. http://127.0.0.1:9200 +export E2E_PASSWORD_AUTH_METHOD_ID= # e.g. ampw_1234567890 +export E2E_PASSWORD_ADMIN_LOGIN_NAME= # e.g. "admin" +export E2E_PASSWORD_ADMIN_PASSWORD= # e.g. "password" + +# For e2e/target +export E2E_TARGET_IP= # e.g. 192.168.0.1 +export E2E_SSH_KEY_PATH= # e.g. /Users/username/key.pem +export E2E_SSH_USER= # e.g. ubuntu +export E2E_SSH_PORT= # e.g. 22 +``` + +Then, run... +```shell +go test github.com/hashicorp/boundary/testing/e2e/target // run target tests +go test ./target/ // run target tests if running from this directory +go test github.com/hashicorp/boundary/testing/e2e/target -v // verbose +go test github.com/hashicorp/boundary/testing/e2e/target -v -run '^TestCreateTargetApi$' // run a specific test +``` diff --git a/testing/internal/e2e/boundary/boundary.go b/testing/internal/e2e/boundary/boundary.go new file mode 100644 index 0000000000..57022e9e7e --- /dev/null +++ b/testing/internal/e2e/boundary/boundary.go @@ -0,0 +1,101 @@ +// Package boundary provides methods for commonly used boundary actions that are used in end-to-end tests. +package boundary + +import ( + "context" + "errors" + "fmt" + + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/authmethods" + "github.com/hashicorp/boundary/testing/internal/e2e" + "github.com/kelseyhightower/envconfig" +) + +type config struct { + Address string `envconfig:"BOUNDARY_ADDR"` // e.g. http://127.0.0.1:9200 + AuthMethodId string `envconfig:"E2E_PASSWORD_AUTH_METHOD_ID"` // e.g. ampw_1234567890 + AdminLoginName string `envconfig:"E2E_PASSWORD_ADMIN_LOGIN_NAME" default:"admin"` + AdminLoginPassword string `envconfig:"E2E_PASSWORD_ADMIN_PASSWORD"` +} + +func (c *config) validate() error { + if c.Address == "" { + return errors.New("Address is empty. Set environment variable: BOUNDARY_ADDR") + } + if c.AuthMethodId == "" { + return errors.New("AuthMethodId is empty. Set environment variable: E2E_PASSWORD_AUTH_METHOD_ID") + } + if c.AdminLoginName == "" { + return errors.New("AdminLoginName is empty. Set environment variable: E2E_PASSWORD_ADMIN_LOGIN_NAME") + } + if c.AdminLoginPassword == "" { + return errors.New("AdminLoginPassword is empty. Set environment variable: E2E_PASSWORD_ADMIN_PASSWORD") + } + + return nil +} + +func loadConfig() (*config, error) { + var c config + err := envconfig.Process("", &c) + if err != nil { + return nil, err + } + + return &c, err +} + +// NewApiClient creates a new Api client for the specified Boundary instance and +// attempts to authenticate it. Returns the client. +func NewApiClient() (*api.Client, error) { + c, err := loadConfig() + if err != nil { + return nil, err + } + err = c.validate() + if err != nil { + return nil, err + } + + client, err := api.NewClient(&api.Config{Addr: c.Address}) + if err != nil { + return nil, err + } + + ctx := context.Background() + authmethodsClient := authmethods.NewClient(client) + authenticationResult, err := authmethodsClient.Authenticate(ctx, c.AuthMethodId, "login", + map[string]interface{}{ + "login_name": c.AdminLoginName, + "password": c.AdminLoginPassword, + }, + ) + if err != nil { + return nil, err + } + + client.SetToken(fmt.Sprint(authenticationResult.Attributes["token"])) + return client, err +} + +// AuthenticateCli uses the cli to authenticate the specified Boundary instance. +// Returns the result of the command. +func AuthenticateCli() *e2e.CommandResult { + c, err := loadConfig() + if err != nil { + return &e2e.CommandResult{Err: err} + } + err = c.validate() + if err != nil { + return &e2e.CommandResult{Err: err} + } + + return e2e.RunCommand( + "boundary", "authenticate", "password", + "-addr", c.Address, + "-auth-method-id", c.AuthMethodId, + "-login-name", c.AdminLoginName, + "-password", "env://E2E_PASSWORD_ADMIN_PASSWORD", + ) +} diff --git a/testing/internal/e2e/helpers.go b/testing/internal/e2e/helpers.go new file mode 100644 index 0000000000..4d38f35e35 --- /dev/null +++ b/testing/internal/e2e/helpers.go @@ -0,0 +1,55 @@ +package e2e + +import ( + "bytes" + "errors" + "fmt" + "os" + "os/exec" + "testing" +) + +// CommandResult encapsulates the output from running an external command +type CommandResult struct { + Stdout []byte + Stderr []byte + ExitCode int + Err error +} + +const EnvToCheckSkip = "E2E_PASSWORD_AUTH_METHOD_ID" + +// RunCommand executes external commands on the system. Returns the results +// of running the provided command. CommandResult is always valid even if there is +// an error. +func RunCommand(name string, args ...string) *CommandResult { + var outbuf, errbuf bytes.Buffer + + cmd := exec.Command(name, args...) + cmd.Stdout = &outbuf + cmd.Stderr = &errbuf + + err := cmd.Run() + + var ee *exec.ExitError + var exitCode int + if errors.As(err, &ee) { + exitCode = ee.ExitCode() + } + + return &CommandResult{ + Stdout: outbuf.Bytes(), + Stderr: errbuf.Bytes(), + ExitCode: exitCode, + Err: err, + } +} + +func MaybeSkipTest(t *testing.T) { + if _, ok := os.LookupEnv(EnvToCheckSkip); !ok { + t.Skip(fmt.Sprintf( + "Skipping test because environment variable '%s' is not set. This is needed for e2e tests.", + EnvToCheckSkip, + )) + } +} diff --git a/testing/internal/e2e/target/target_test.go b/testing/internal/e2e/target/target_test.go new file mode 100644 index 0000000000..194097d80b --- /dev/null +++ b/testing/internal/e2e/target/target_test.go @@ -0,0 +1,316 @@ +package target_test + +import ( + "context" + "encoding/json" + "errors" + "strings" + "testing" + + "github.com/hashicorp/boundary/api/hostcatalogs" + "github.com/hashicorp/boundary/api/hosts" + "github.com/hashicorp/boundary/api/hostsets" + "github.com/hashicorp/boundary/api/scopes" + "github.com/hashicorp/boundary/api/targets" + "github.com/hashicorp/boundary/testing/internal/e2e" + "github.com/hashicorp/boundary/testing/internal/e2e/boundary" + "github.com/kelseyhightower/envconfig" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type config struct { + TargetIp string `envconfig:"E2E_TARGET_IP"` // e.g. 192.168.0.1 + TargetSshKeyPath string `envconfig:"E2E_SSH_KEY_PATH"` // e.g. /Users/username/key.pem + TargetSshUser string `envconfig:"E2E_SSH_USER"` // e.g. ubuntu + TargetPort string `envconfig:"E2E_SSH_PORT" default:"22"` +} + +func (c *config) validate() error { + if c.TargetIp == "" { + return errors.New("TargetIp is empty. Set environment variable: E2E_TARGET_IP") + } + if c.TargetSshKeyPath == "" { + return errors.New("TargetSshKeyPath is empty. Set environment variable: E2E_SSH_KEY_PATH") + } + if c.TargetSshUser == "" { + return errors.New("TargetSshUser is empty. Set environment variable: E2E_SSH_USER") + } + if c.TargetPort == "" { + return errors.New("TargetPort is empty. Set environment variable: E2E_SSH_PORT") + } + + return nil +} + +func loadConfig() (*config, error) { + var c config + err := envconfig.Process("", &c) + if err != nil { + return nil, err + } + + return &c, err +} + +// TestConnectTargetCli uses the boundary cli to create a number of supporting objects +// to connect to a target. It then attempts to connect to that target and verifies that +// the connection was successful. +func TestConnectTargetCli(t *testing.T) { + e2e.MaybeSkipTest(t) + + c, err := loadConfig() + require.NoError(t, err) + err = c.validate() + require.NoError(t, err) + + output := boundary.AuthenticateCli() + require.NoError(t, output.Err, string(output.Stderr)) + + // Create an org + output = e2e.RunCommand( + "boundary", "scopes", "create", + "-name", "e2e Automated Test Org", + "-format", "json", + ) + require.NoError(t, output.Err, string(output.Stderr)) + var newOrgResult scopes.ScopeCreateResult + err = json.Unmarshal(output.Stdout, &newOrgResult) + require.NoError(t, err) + newOrg := newOrgResult.Item + t.Cleanup(func() { + output := e2e.RunCommand("boundary", "scopes", "delete", "-id", newOrg.Id) + require.NoError(t, output.Err, string(output.Stderr)) + }) + t.Logf("Created Org Id: %s", newOrg.Id) + + // Create a project + output = e2e.RunCommand( + "boundary", "scopes", "create", + "-scope-id", newOrg.Id, + "-name", "e2e Automated Test Project", + "-format", "json", + ) + require.NoError(t, output.Err, string(output.Stderr)) + var newProjectResult scopes.ScopeCreateResult + err = json.Unmarshal(output.Stdout, &newProjectResult) + require.NoError(t, err) + newProject := newProjectResult.Item + t.Cleanup(func() { + output := e2e.RunCommand("boundary", "scopes", "delete", "-id", newProject.Id) + require.NoError(t, output.Err, string(output.Stderr)) + }) + t.Logf("Created Project Id: %s", newProject.Id) + + // Create a host catalog + output = e2e.RunCommand( + "boundary", "host-catalogs", "create", "static", + "-scope-id", newProject.Id, + "-name", "e2e Automated Test Host Catalog", + "-format", "json", + ) + require.NoError(t, output.Err, string(output.Stderr)) + var newHostCatalogResult hostcatalogs.HostCatalogCreateResult + err = json.Unmarshal(output.Stdout, &newHostCatalogResult) + require.NoError(t, err) + newHostCatalog := newHostCatalogResult.Item + t.Cleanup(func() { + output := e2e.RunCommand("boundary", "host-catalogs", "delete", "-id", newHostCatalog.Id) + require.NoError(t, output.Err, string(output.Stderr)) + }) + t.Logf("Created Host Catalog: %s", newHostCatalog.Id) + + // Create a host set and add to catalog + output = e2e.RunCommand( + "boundary", "host-sets", "create", "static", + "-host-catalog-id", newHostCatalog.Id, + "-name", "e2e Automated Test Host Set", + "-format", "json", + ) + require.NoError(t, output.Err, string(output.Stderr)) + var newHostSetResult hostsets.HostSetCreateResult + err = json.Unmarshal(output.Stdout, &newHostSetResult) + require.NoError(t, err) + newHostSet := newHostSetResult.Item + t.Cleanup(func() { + output := e2e.RunCommand("boundary", "host-sets", "delete", "-id", newHostSet.Id) + require.NoError(t, output.Err, string(output.Stderr)) + }) + t.Logf("Created Host Set: %s", newHostSet.Id) + + // Create a host + output = e2e.RunCommand( + "boundary", "hosts", "create", "static", + "-host-catalog-id", newHostCatalog.Id, + "-name", c.TargetIp, + "-address", c.TargetIp, + "-format", "json", + ) + require.NoError(t, output.Err, string(output.Stderr)) + var newHostResult hosts.HostCreateResult + err = json.Unmarshal(output.Stdout, &newHostResult) + require.NoError(t, err) + newHost := newHostResult.Item + t.Cleanup(func() { + output := e2e.RunCommand("boundary", "hosts", "delete", "-id", newHost.Id) + require.NoError(t, output.Err, string(output.Stderr)) + }) + t.Logf("Created Host: %s", newHost.Id) + + // Add host to host set + output = e2e.RunCommand( + "boundary", "host-sets", "add-hosts", + "-id", newHostSet.Id, + "-host", newHost.Id, + ) + require.NoError(t, output.Err, string(output.Stderr)) + + // Create a target + output = e2e.RunCommand( + "boundary", "targets", "create", "tcp", + "-scope-id", newProject.Id, + "-default-port", c.TargetPort, + "-name", "e2e Automated Test Target", + "-format", "json", + ) + require.NoError(t, output.Err, string(output.Stderr)) + var newTargetResult targets.TargetCreateResult + err = json.Unmarshal(output.Stdout, &newTargetResult) + require.NoError(t, err) + newTarget := newTargetResult.Item + t.Cleanup(func() { + output := e2e.RunCommand("boundary", "targets", "delete", "-id", newTarget.Id) + require.NoError(t, output.Err, string(output.Stderr)) + }) + t.Logf("Created Target: %s", newTarget.Id) + + // Add host set to target + output = e2e.RunCommand( + "boundary", "targets", "add-host-sources", + "-id", newTarget.Id, + "-host-source", newHostSet.Id, + ) + require.NoError(t, output.Err, string(output.Stderr)) + + // Connect to target and print host's IP address + output = e2e.RunCommand( + "boundary", "connect", + "-target-id", newTarget.Id, + "-exec", "/usr/bin/ssh", "--", + "-l", c.TargetSshUser, + "-i", c.TargetSshKeyPath, + "-o", "UserKnownHostsFile=/dev/null", + "-o", "StrictHostKeyChecking=no", + "-o", "IdentitiesOnly=yes", // forces the use of the provided key + "-p", "{{boundary.port}}", // this is provided by boundary + "{{boundary.ip}}", + "hostname", "-i", + ) + require.NoError(t, output.Err, string(output.Stderr)) + + parts := strings.Fields(string(output.Stdout)) + hostIp := parts[len(parts)-1] + assert.Equal(t, c.TargetIp, hostIp, "SSH session did not return expected output") + t.Log("Successfully connected to target") +} + +// TestCreateTargetApi uses the boundary go api to create a number of supporting objects +// to connect to a target. This test does not connect to the target due to the complexity +// when not using the cli. +func TestCreateTargetApi(t *testing.T) { + e2e.MaybeSkipTest(t) + + c, err := loadConfig() + require.NoError(t, err) + err = c.validate() + require.NoError(t, err) + + client, err := boundary.NewApiClient() + require.NoError(t, err) + + ctx := context.Background() + + // Create an org + scopeClient := scopes.NewClient(client) + newOrgResult, err := scopeClient.Create(ctx, "global", scopes.WithName("e2e Automated Test Org")) + require.NoError(t, err) + newOrg := newOrgResult.Item + t.Cleanup(func() { + _, err := scopeClient.Delete(ctx, newOrg.Id) + require.NoError(t, err) + }) + t.Logf("Created Org Id: %s", newOrg.Id) + + // Create a project + newProjectResult, err := scopeClient.Create(ctx, newOrg.Id, scopes.WithName("e2e Automated Test Project")) + require.NoError(t, err) + newProject := newProjectResult.Item + t.Cleanup(func() { + _, err := scopeClient.Delete(ctx, newProject.Id) + require.NoError(t, err) + }) + t.Logf("Created Project Id: %s", newProject.Id) + + // Create a host catalog + hcClient := hostcatalogs.NewClient(client) + newHostCatalogResult, err := hcClient.Create(ctx, "static", newProject.Id, + hostcatalogs.WithName("e2e Automated Test Host Catalog"), + ) + require.NoError(t, err) + newHostCatalog := newHostCatalogResult.Item + t.Cleanup(func() { + _, err := hcClient.Delete(ctx, newHostCatalog.Id) + require.NoError(t, err) + }) + t.Logf("Created Host Catalog: %s", newHostCatalog.Id) + + // Create a host set and add to catalog + hsClient := hostsets.NewClient(client) + newHostSetResult, err := hsClient.Create(ctx, newHostCatalog.Id) + require.NoError(t, err) + newHostSet := newHostSetResult.Item + t.Cleanup(func() { + _, err := hsClient.Delete(ctx, newHostSet.Id) + require.NoError(t, err) + }) + t.Logf("Created Host Set: %s", newHostSet.Id) + + // Create a host + hClient := hosts.NewClient(client) + newHostResult, err := hClient.Create(ctx, newHostCatalog.Id, + hosts.WithName(c.TargetIp), + hosts.WithStaticHostAddress(c.TargetIp), + ) + require.NoError(t, err) + newHost := newHostResult.Item + t.Cleanup(func() { + _, err := hClient.Delete(ctx, newHost.Id) + require.NoError(t, err) + }) + t.Logf("Created Host: %s", newHost.Id) + + // Add host to host set + _, err = hsClient.AddHosts(ctx, newHostSet.Id, 0, []string{newHost.Id}, hostsets.WithAutomaticVersioning(true)) + require.NoError(t, err) + + // Create a target + tClient := targets.NewClient(client) + newTargetResult, err := tClient.Create(ctx, "tcp", newProject.Id, + targets.WithName("e2e Automated Test Target"), + targets.WithTcpTargetDefaultPort(22), + ) + require.NoError(t, err) + newTarget := newTargetResult.Item + t.Cleanup(func() { + _, err := tClient.Delete(ctx, newTarget.Id) + require.NoError(t, err) + }) + t.Logf("Created Target: %s", newTarget.Id) + + // Add host set to target + _, err = tClient.AddHostSources(ctx, newTarget.Id, 0, + []string{newHostSet.Id}, + targets.WithAutomaticVersioning(true), + ) + require.NoError(t, err) +}