diff --git a/.github/workflows/nightly-ecs-examples-validator.yml b/.github/workflows/nightly-ecs-examples-validator.yml index f3120119..3832fcfa 100644 --- a/.github/workflows/nightly-ecs-examples-validator.yml +++ b/.github/workflows/nightly-ecs-examples-validator.yml @@ -83,10 +83,32 @@ jobs: scenario: ${{ matrix.scenario }} go-version: ${{ needs.get-go-version.outputs.go-version }} secrets: inherit - multi-cluster: + gateways: needs: - single-cluster - get-go-version + strategy: + matrix: + name: + - API Gateway + - Terminating Gateway + include: + - name: API Gateway + scenario: API_GATEWAY + + - name: Terminating Gateway + scenario: TERMINATING_GATEWAY + fail-fast: false + uses: ./.github/workflows/reusable-ecs-example-validator.yml + with: + name: ${{ matrix.name }} + scenario: ${{ matrix.scenario }} + go-version: ${{ needs.get-go-version.outputs.go-version }} + secrets: inherit + multi-cluster: + needs: + - gateways + - get-go-version strategy: matrix: name: diff --git a/examples/terminating-gateway/outputs.tf b/examples/terminating-gateway/outputs.tf index 88d0fa0f..0e0d69ca 100644 --- a/examples/terminating-gateway/outputs.tf +++ b/examples/terminating-gateway/outputs.tf @@ -13,6 +13,6 @@ output "mesh_client_lb_address" { value = "http://${aws_lb.example_client_app.dns_name}:9090/ui" } -# output "non_mesh_server_lb_address" { -# value = "http://${aws_lb.example_server_app.dns_name}:9090" -# } \ No newline at end of file +output "non_mesh_server_lb_address" { + value = "http://${aws_lb.example_server_app.dns_name}:9090" +} \ No newline at end of file diff --git a/test/acceptance/examples/main_test.go b/test/acceptance/examples/main_test.go index c789ec8a..9232a34c 100644 --- a/test/acceptance/examples/main_test.go +++ b/test/acceptance/examples/main_test.go @@ -14,12 +14,14 @@ import ( "github.com/gruntwork-io/terratest/modules/terraform" "github.com/hashicorp/consul/sdk/testutil/retry" "github.com/hashicorp/terraform-aws-consul-ecs/test/acceptance/examples/scenarios" + apigateway "github.com/hashicorp/terraform-aws-consul-ecs/test/acceptance/examples/scenarios/api-gateway" clusterpeering "github.com/hashicorp/terraform-aws-consul-ecs/test/acceptance/examples/scenarios/cluster-peering" "github.com/hashicorp/terraform-aws-consul-ecs/test/acceptance/examples/scenarios/ec2" "github.com/hashicorp/terraform-aws-consul-ecs/test/acceptance/examples/scenarios/fargate" "github.com/hashicorp/terraform-aws-consul-ecs/test/acceptance/examples/scenarios/hcp" localityawarerouting "github.com/hashicorp/terraform-aws-consul-ecs/test/acceptance/examples/scenarios/locality-aware-routing" sameness "github.com/hashicorp/terraform-aws-consul-ecs/test/acceptance/examples/scenarios/service-sameness" + terminatinggateway "github.com/hashicorp/terraform-aws-consul-ecs/test/acceptance/examples/scenarios/terminating-gateway" "github.com/hashicorp/terraform-aws-consul-ecs/test/acceptance/examples/scenarios/wan-federation" "github.com/hashicorp/terraform-aws-consul-ecs/test/acceptance/framework/logger" "github.com/stretchr/testify/require" @@ -90,6 +92,8 @@ func setupScenarios() scenarios.ScenarioRegistry { sameness.RegisterScenario(reg) wan.RegisterScenario(reg) localityawarerouting.RegisterScenario(reg) + apigateway.RegisterScenario(reg) + terminatinggateway.RegisterScenario(reg) return reg } diff --git a/test/acceptance/examples/scenarios/api-gateway/main.go b/test/acceptance/examples/scenarios/api-gateway/main.go new file mode 100644 index 00000000..c8834855 --- /dev/null +++ b/test/acceptance/examples/scenarios/api-gateway/main.go @@ -0,0 +1,103 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package apigateway + +import ( + "encoding/json" + "fmt" + "testing" + + "github.com/hashicorp/terraform-aws-consul-ecs/test/acceptance/examples/scenarios" + "github.com/hashicorp/terraform-aws-consul-ecs/test/acceptance/examples/scenarios/common" + "github.com/hashicorp/terraform-aws-consul-ecs/test/acceptance/framework/logger" + "github.com/stretchr/testify/require" +) + +type TFOutputs struct { + ConsulServerLBAddr string `json:"consul_server_lb_address"` + ConsulServerToken string `json:"consul_server_bootstrap_token"` + APIGatewayLBURL string `json:"api_gateway_lb_url"` +} + +func RegisterScenario(r scenarios.ScenarioRegistry) { + tfResourcesName := fmt.Sprintf("ecs-%s", common.GenerateRandomStr(6)) + + r.Register(scenarios.ScenarioRegistration{ + Name: "API_GATEWAY", + FolderName: "api-gateway", + TerraformInputVars: getTerraformVars(tfResourcesName), + Validate: validate(tfResourcesName), + }) +} + +func getTerraformVars(tfResName string) scenarios.TerraformInputVarsHook { + return func() (map[string]interface{}, error) { + vars := map[string]interface{}{ + "region": "us-east-1", + "name": tfResName, + } + + publicIP, err := common.GetPublicIP() + if err != nil { + return nil, err + } + vars["lb_ingress_ip"] = publicIP + + return vars, nil + } +} + +func validate(tfResName string) scenarios.ValidateHook { + return func(t *testing.T, data []byte) { + logger.Log(t, "Fetching required output terraform variables") + + var tfOutputs *TFOutputs + require.NoError(t, json.Unmarshal(data, &tfOutputs)) + + consulServerLBAddr := tfOutputs.ConsulServerLBAddr + apiGatewayLBURL := tfOutputs.APIGatewayLBURL + + logger.Log(t, "Setting up the Consul client") + consulClient, err := common.SetupConsulClient(t, consulServerLBAddr) + require.NoError(t, err) + + clientAppName := fmt.Sprintf("%s-example-client-app", tfResName) + serverAppName := fmt.Sprintf("%s-example-server-app", tfResName) + + consulClient.EnsureServiceReadiness(clientAppName, nil) + consulClient.EnsureServiceReadiness(serverAppName, nil) + consulClient.EnsureServiceReadiness(fmt.Sprintf("%s-api-gateway", tfResName), nil) + + // Perform assertions by hitting the gateway's LB + logger.Log(t, "calling API gateway's load balancer to see if the server app is reachable") + common.ValidateFakeServiceResponse(t, apiGatewayLBURL, serverAppName) + + // Test if the API gateway load balances requests + + // Append the path `/echo` to the LB's URL. + apiGatewayLBURL = fmt.Sprintf("%s/echo", apiGatewayLBURL) + + type echoServiceResp struct { + Service string `json:"service"` + } + + // We hit the API gateway's LB URL along with the `/echo` path for a finite number + // of times to check if the gateway performs weighted load balancing between + // the two replicas of the echo service. + svcMap := make(map[string]struct{}) + for i := 0; i <= 20; i++ { + resp, err := common.HTTPGet(apiGatewayLBURL) + require.NoError(t, err) + + var echoSvcResp *echoServiceResp + require.NoError(t, json.Unmarshal(resp, &echoSvcResp)) + + fmt.Println(echoSvcResp.Service) + svcMap[echoSvcResp.Service] = struct{}{} + } + + // Ensure that both the echo service's were hit + require.Len(t, svcMap, 2) + } +} diff --git a/test/acceptance/examples/scenarios/common/fake_service.go b/test/acceptance/examples/scenarios/common/fake_service.go index 168f5b61..c5f6740b 100644 --- a/test/acceptance/examples/scenarios/common/fake_service.go +++ b/test/acceptance/examples/scenarios/common/fake_service.go @@ -58,7 +58,7 @@ func ValidateFakeServiceResponse(t *testing.T, lbURL, expectedUpstream string) * // json which can be used by the caller to validate if the request went // through as expected. func GetFakeServiceResponse(addr string) (*FakeServiceResponse, error) { - resp, err := httpGet(addr) + resp, err := HTTPGet(addr) if err != nil { return nil, err } @@ -72,7 +72,7 @@ func GetFakeServiceResponse(addr string) (*FakeServiceResponse, error) { return fakeSvcResp, nil } -func httpGet(addr string) ([]byte, error) { +func HTTPGet(addr string) ([]byte, error) { resp, err := http.Get(addr) if err != nil { return nil, err diff --git a/test/acceptance/examples/scenarios/terminating-gateway/main.go b/test/acceptance/examples/scenarios/terminating-gateway/main.go new file mode 100644 index 00000000..63f79ee9 --- /dev/null +++ b/test/acceptance/examples/scenarios/terminating-gateway/main.go @@ -0,0 +1,77 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package terminatinggateway + +import ( + "encoding/json" + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform-aws-consul-ecs/test/acceptance/examples/scenarios" + "github.com/hashicorp/terraform-aws-consul-ecs/test/acceptance/examples/scenarios/common" + "github.com/hashicorp/terraform-aws-consul-ecs/test/acceptance/framework/logger" + "github.com/stretchr/testify/require" +) + +type TFOutputs struct { + ConsulServerLBAddr string `json:"consul_server_lb_address"` + ConsulServerToken string `json:"consul_server_bootstrap_token"` + MeshClientLBAddr string `json:"mesh_client_lb_address"` +} + +func RegisterScenario(r scenarios.ScenarioRegistry) { + tfResourcesName := fmt.Sprintf("ecs-%s", common.GenerateRandomStr(6)) + + r.Register(scenarios.ScenarioRegistration{ + Name: "TERMINATING_GATEWAY", + FolderName: "terminating-gateway", + TerraformInputVars: getTerraformVars(tfResourcesName), + Validate: validate(tfResourcesName), + }) +} + +func getTerraformVars(tfResName string) scenarios.TerraformInputVarsHook { + return func() (map[string]interface{}, error) { + vars := map[string]interface{}{ + "region": "us-east-2", + "name": tfResName, + } + + publicIP, err := common.GetPublicIP() + if err != nil { + return nil, err + } + vars["lb_ingress_ip"] = publicIP + + return vars, nil + } +} + +func validate(tfResName string) scenarios.ValidateHook { + return func(t *testing.T, data []byte) { + logger.Log(t, "Fetching required output terraform variables") + + var tfOutputs *TFOutputs + require.NoError(t, json.Unmarshal(data, &tfOutputs)) + + consulServerLBAddr := tfOutputs.ConsulServerLBAddr + meshClientLBAddr := tfOutputs.MeshClientLBAddr + meshClientLBAddr = strings.TrimSuffix(meshClientLBAddr, "/ui") + + logger.Log(t, "Setting up the Consul client") + consulClient, err := common.SetupConsulClient(t, consulServerLBAddr) + require.NoError(t, err) + + clientAppName := fmt.Sprintf("%s-example-client-app", tfResName) + serverAppName := fmt.Sprintf("%s-external-server-app", tfResName) + + consulClient.EnsureServiceReadiness(clientAppName, nil) + consulClient.EnsureServiceReadiness(serverAppName, nil) + + // Perform assertions by hitting the client app's LB + logger.Log(t, "calling client app's load balancer to see if the server app is reachable") + common.ValidateFakeServiceResponse(t, meshClientLBAddr, serverAppName) + } +}