From 568a2264f94a0b28b7d5d7fe007faf8f51f04b41 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 1 Mar 2023 15:11:55 +0100 Subject: [PATCH] Cloud deployment with local agent (#2143) (#2336) Cloud deployment with local agent (#2143) (cherry picked from commit 06a8da1527bf46c966a5f892eb810e860d0a4ade) Co-authored-by: Michal Pristas --- README.md | 28 +++ magefile.go | 120 ++++++++- testing/environments/cloud/.gitignore | 1 + .../environments/cloud/.terraform.lock.hcl | 81 ++++++ testing/environments/cloud/Makefile | 93 +++++++ testing/environments/cloud/README.md | 27 ++ .../cloud/docker_image.auto.tfvars.sample | 2 + testing/environments/cloud/main.tf | 39 +++ testing/environments/cloud/outputs.tf | 37 +++ testing/environments/cloud/terraform.tfvars | 2 + testing/environments/cloud/variables.tf | 62 +++++ .../modules/ec_deployment/.gitignore | 5 + .../terraform/modules/ec_deployment/README.md | 79 ++++++ .../modules/ec_deployment/deployment.tf | 233 ++++++++++++++++++ .../terraform/modules/ec_deployment/header.md | 5 + .../modules/ec_deployment/outputs.tf | 45 ++++ .../scripts/custom-apm-integration-pkg.tftpl | 12 + .../ec_deployment/scripts/drop_pipeline.tftpl | 15 ++ .../ec_deployment/scripts/enable_expvar.tftpl | 34 +++ .../ec_deployment/scripts/index_shards.tftpl | 24 ++ .../ec_deployment/scripts/secret_token.tftpl | 9 + .../modules/ec_deployment/terraform.tf | 8 + .../modules/ec_deployment/variables.tf | 141 +++++++++++ 23 files changed, 1098 insertions(+), 4 deletions(-) create mode 100644 testing/environments/cloud/.gitignore create mode 100644 testing/environments/cloud/.terraform.lock.hcl create mode 100644 testing/environments/cloud/Makefile create mode 100644 testing/environments/cloud/README.md create mode 100644 testing/environments/cloud/docker_image.auto.tfvars.sample create mode 100644 testing/environments/cloud/main.tf create mode 100644 testing/environments/cloud/outputs.tf create mode 100644 testing/environments/cloud/terraform.tfvars create mode 100644 testing/environments/cloud/variables.tf create mode 100644 testing/environments/infra/terraform/modules/ec_deployment/.gitignore create mode 100644 testing/environments/infra/terraform/modules/ec_deployment/README.md create mode 100644 testing/environments/infra/terraform/modules/ec_deployment/deployment.tf create mode 100644 testing/environments/infra/terraform/modules/ec_deployment/header.md create mode 100644 testing/environments/infra/terraform/modules/ec_deployment/outputs.tf create mode 100644 testing/environments/infra/terraform/modules/ec_deployment/scripts/custom-apm-integration-pkg.tftpl create mode 100644 testing/environments/infra/terraform/modules/ec_deployment/scripts/drop_pipeline.tftpl create mode 100644 testing/environments/infra/terraform/modules/ec_deployment/scripts/enable_expvar.tftpl create mode 100644 testing/environments/infra/terraform/modules/ec_deployment/scripts/index_shards.tftpl create mode 100644 testing/environments/infra/terraform/modules/ec_deployment/scripts/secret_token.tftpl create mode 100644 testing/environments/infra/terraform/modules/ec_deployment/terraform.tf create mode 100644 testing/environments/infra/terraform/modules/ec_deployment/variables.tf diff --git a/README.md b/README.md index 6c8939c65b1..345117413a5 100644 --- a/README.md +++ b/README.md @@ -144,6 +144,34 @@ kubectl apply -f elastic-agent-${ELASTIC_AGENT_MODE}-kubernetes.yaml kubectl -n kube-system get pods -l app=elastic-agent ``` +## Testing on Elastic Cloud + +Elastic employees can create an Elastic Cloud deployment with a locally +built Elastic Agent, by pushing images to an internal Docker repository. The images will be +based on the SNAPSHOT images with the version defined in `version/version.go`. + +Prerequisite to running following commands is having `terraform` installed and running `terraform init` from within `testing/environments/cloud`. + +Running a shorthand `make deploy_local` in `testing/environments/cloud` will build Agent, tag the docker image correctly, push it to the repository and deploy to Elastic Cloud. + +For more advanced scenarios: +Running `make build_elastic_agent_docker_image` in `testing/environments/cloud` will build and push the images. +Running `make push_elastic_agent_docker_image` in `testing/environments/cloud` will publish built docker image to CI docker repository. + +Once docker images are published you can run `EC_API_KEY=your_api_key make apply` from `testing/environments/cloud` directory to deploy them to Elastic Cloud. +To get `EC_API_KEY` follow [this guide](https://www.elastic.co/guide/en/cloud/current/ec-api-authentication.html) + +The custom images are tagged with the current version, commit and timestamp. The +timestamp is included to force a new Docker image to be used, which enables pushing new +binaries without recreating the deployment. + +To specify custom images create your `docker_image.auto.tfvars` file similar to `docker_image.auto.tfvars.sample`. + +You can also use `mage cloud:image` and `mage cloud:push` respectively from repo root directory. +To deploy your changes use `make apply` (from `testing/environments/cloud`) with `EC_API_KEY` instead of `make deploy_local` described above. + +SNAPSHOT images are used by default. To use non-snapshot image specify `SNAPSHOT=false` explicitly. + ## Updating dependencies/PRs Even though we prefer `mage` to our automation, we still have some rules implemented on our `Makefile` as well as CI will use the diff --git a/magefile.go b/magefile.go index 0b28182918d..1a071d5e4cf 100644 --- a/magefile.go +++ b/magefile.go @@ -50,10 +50,15 @@ const ( snapshotEnv = "SNAPSHOT" devEnv = "DEV" externalArtifacts = "EXTERNAL" + platformsEnv = "PLATFORMS" + packagesEnv = "PACKAGES" configFile = "elastic-agent.yml" agentDropPath = "AGENT_DROP_PATH" specSuffix = ".spec.yml" checksumFilename = "checksum.yml" + commitLen = 7 + + cloudImageTmpl = "docker.elastic.co/observability-ci/elastic-agent:%s" ) // Aliases for commands required by master makefile @@ -96,6 +101,9 @@ type Demo mg.Namespace // Dev runs package and build for dev purposes. type Dev mg.Namespace +// Cloud produces or pushes cloud image for cloud testing. +type Cloud mg.Namespace + func CheckNoChanges() error { fmt.Println(">> fmt - go run") err := sh.RunV("go", "mod", "tidy", "-v") @@ -547,6 +555,111 @@ func (Demo) NoEnroll() error { return runAgent(env) } +// Image builds a cloud image +func (Cloud) Image() { + platforms := os.Getenv(platformsEnv) + defer os.Setenv(platformsEnv, platforms) + + packages := os.Getenv(packagesEnv) + defer os.Setenv(packagesEnv, packages) + + snapshot := os.Getenv(snapshotEnv) + defer os.Setenv(snapshotEnv, snapshot) + + dev := os.Getenv(devEnv) + defer os.Setenv(devEnv, dev) + + os.Setenv(platformsEnv, "linux/amd64") + os.Setenv(packagesEnv, "docker") + os.Setenv(devEnv, "true") + + if s, err := strconv.ParseBool(snapshot); err == nil && !s { + // only disable SNAPSHOT build when explicitely defined + os.Setenv(snapshotEnv, "false") + devtools.Snapshot = false + } else { + os.Setenv(snapshotEnv, "true") + devtools.Snapshot = true + } + + devtools.DevBuild = true + devtools.Platforms = devtools.Platforms.Filter("linux/amd64") + devtools.SelectedPackageTypes = []devtools.PackageType{devtools.Docker} + + if _, hasExternal := os.LookupEnv(externalArtifacts); !hasExternal { + devtools.ExternalBuild = true + } + + Package() +} + +// Push builds a cloud image tags it correctly and pushes to remote image repo. +// Previous login to elastic registry is required! +func (Cloud) Push() error { + snapshot := os.Getenv(snapshotEnv) + defer os.Setenv(snapshotEnv, snapshot) + + os.Setenv(snapshotEnv, "true") + + version := getVersion() + var tag string + if envTag, isPresent := os.LookupEnv("CUSTOM_IMAGE_TAG"); isPresent && len(envTag) > 0 { + tag = envTag + } else { + commit := dockerCommitHash() + time := time.Now().Unix() + + tag = fmt.Sprintf("%s-%s-%d", version, commit, time) + } + + sourceCloudImageName := fmt.Sprintf("docker.elastic.co/beats-ci/elastic-agent-cloud:%s", version) + var targetCloudImageName string + if customImage, isPresent := os.LookupEnv("CI_ELASTIC_AGENT_DOCKER_IMAGE"); isPresent && len(customImage) > 0 { + targetCloudImageName = fmt.Sprintf("%s:%s", customImage, tag) + } else { + targetCloudImageName = fmt.Sprintf(cloudImageTmpl, tag) + } + + fmt.Printf(">> Setting a docker image tag to %s\n", targetCloudImageName) + err := sh.RunV("docker", "tag", sourceCloudImageName, targetCloudImageName) + if err != nil { + return fmt.Errorf("Failed setting a docker image tag: %w", err) + } + fmt.Println(">> Docker image tag updated successfully") + + fmt.Println(">> Pushing a docker image to remote registry") + err = sh.RunV("docker", "image", "push", targetCloudImageName) + if err != nil { + return fmt.Errorf("Failed pushing docker image: %w", err) + } + fmt.Printf(">> Docker image pushed to remote registry successfully: %s\n", targetCloudImageName) + + return nil +} + +func dockerCommitHash() string { + commit, err := devtools.CommitHash() + if err == nil && len(commit) > commitLen { + return commit[:commitLen] + } + + return "" +} + +func getVersion() string { + version, found := os.LookupEnv("BEAT_VERSION") + if !found { + version = release.Version() + } + if !strings.Contains(version, "SNAPSHOT") { + if _, ok := os.LookupEnv(snapshotEnv); ok { + version += "-SNAPSHOT" + } + } + + return version +} + func runAgent(env map[string]string) error { prevPlatforms := os.Getenv("PLATFORMS") defer os.Setenv("PLATFORMS", prevPlatforms) @@ -964,12 +1077,11 @@ func dockerBuild(tag string) error { } func dockerTag() string { - const commitLen = 7 tagBase := "elastic-agent" - commit, err := devtools.CommitHash() - if err == nil && len(commit) > commitLen { - return fmt.Sprintf("%s-%s", tagBase, commit[:commitLen]) + commit := dockerCommitHash() + if len(commit) > 0 { + return fmt.Sprintf("%s-%s", tagBase, commit) } return tagBase diff --git a/testing/environments/cloud/.gitignore b/testing/environments/cloud/.gitignore new file mode 100644 index 00000000000..ed29e6ccd5f --- /dev/null +++ b/testing/environments/cloud/.gitignore @@ -0,0 +1 @@ +*.auto.tfvars diff --git a/testing/environments/cloud/.terraform.lock.hcl b/testing/environments/cloud/.terraform.lock.hcl new file mode 100644 index 00000000000..e6e1e1eec01 --- /dev/null +++ b/testing/environments/cloud/.terraform.lock.hcl @@ -0,0 +1,81 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/elastic/ec" { + version = "0.5.0" + constraints = ">= 0.4.0, >= 0.4.1" + hashes = [ + "h1:4BiRazUuyKHramn/5a7QwhXK5lbVxEQ1FzC2fZN8FYA=", + "zh:1b0bf155960e1aaccf59359080ca30bf5ae44d5d46c47d9834fc343971edd798", + "zh:24ae7fc6becf3a6f5dedf0871223a4dd3c46ab4166dc0c2148cb05cff2157786", + "zh:4091ad8ed4c6dfaa28cc0f5c1187ab17320f1b55966e2aa25cb832fe09b52eec", + "zh:4a38f5e35bef1b0f406534fb2dfd06fe7e38caa1320fcda0377ce26735a9f652", + "zh:4e830e9ce3c2e2137363227bd4c969c10a7a971fdccfae82fbaf1de6d7237960", + "zh:5111eaa5402bbd4d48eff01a9137dae533c0951be8deb9939445fef0e8561a8f", + "zh:59e6401dfaf8c55043bdcfbc495af37dc4ebbf31e2f4de56c628da9ec71005a7", + "zh:8abf6310913b1085f75e3f241ba544ee9f1fcdd58460570aad9bde4303b47303", + "zh:9338b828fdeee291b81f1816083dcb315b8757364a17d52e215b719b6b0c8e6a", + "zh:935ac2d3732afe93009c1216a5622ef8a5ea9469bafda4a5dee6961c315f0096", + "zh:acffff390c5b056a3c3a59e1dd1e0cf47b6b2cf3d8b00b3cbadd5d2c6c1fa25b", + "zh:b09ab3ebb8a7a436e7fd68c8cc99973c7b44017c9fb7604df38fd0c83e1b8ddc", + "zh:bc104d683ca679c99f85819ee6b98d229084f378f435c0f217225d42a467b0c4", + "zh:cc9777310d7f511648f9f412aea207ff60f4cd3d5e8489b38859e10bd212b92e", + ] +} + +provider "registry.terraform.io/hashicorp/external" { + version = "2.2.3" + hashes = [ + "h1:648ZjJR81c2W1OLtYmUQa9/1rGr3vvZSuX9dR1ucGWY=", + "zh:184ecd339d764de845db0e5b8a9c87893dcd0c9d822167f73658f89d80ec31c9", + "zh:2661eaca31d17d6bbb18a8f673bbfe3fe1b9b7326e60d0ceb302017003274e3c", + "zh:2c0a180f6d1fc2ba6e03f7dfc5f73b617e45408681f75bca75aa82f3796df0e4", + "zh:4b92ae44c6baef4c4952c47be00541055cb5280dd3bc8031dba5a1b2ee982387", + "zh:5641694d5daf3893d7ea90be03b6fa575211a08814ffe70998d5adb8b59cdc0a", + "zh:5bd55a2be8a1c20d732ac9c604b839e1cadc8c49006315dffa4d709b6874df32", + "zh:6e0ef5d11e1597202424b7d69b9da7b881494c9b13a3d4026fc47012dc651c79", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:9e19f89fa25004d3b926a8d15ea630b4bde62f1fa4ed5e11a3d27aabddb77353", + "zh:b763efdd69fd097616b4a4c89cf333b4cee9699ac6432d73d2756f8335d1213f", + "zh:e3b561efdee510b2b445f76a52a902c52bee8e13095e7f4bed7c80f10f8d294a", + "zh:fe660bb8781ee043a093b9a20e53069974475dcaa5791a1f45fd03c61a26478a", + ] +} + +provider "registry.terraform.io/hashicorp/local" { + version = "2.2.3" + hashes = [ + "h1:FvRIEgCmAezgZUqb2F+PZ9WnSSnR5zbEM2ZI+GLmbMk=", + "zh:04f0978bb3e052707b8e82e46780c371ac1c66b689b4a23bbc2f58865ab7d5c0", + "zh:6484f1b3e9e3771eb7cc8e8bab8b35f939a55d550b3f4fb2ab141a24269ee6aa", + "zh:78a56d59a013cb0f7eb1c92815d6eb5cf07f8b5f0ae20b96d049e73db915b238", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:8aa9950f4c4db37239bcb62e19910c49e47043f6c8587e5b0396619923657797", + "zh:996beea85f9084a725ff0e6473a4594deb5266727c5f56e9c1c7c62ded6addbb", + "zh:9a7ef7a21f48fabfd145b2e2a4240ca57517ad155017e86a30860d7c0c109de3", + "zh:a63e70ac052aa25120113bcddd50c1f3cfe61f681a93a50cea5595a4b2cc3e1c", + "zh:a6e8d46f94108e049ad85dbed60354236dc0b9b5ec8eabe01c4580280a43d3b8", + "zh:bb112ce7efbfcfa0e65ed97fa245ef348e0fd5bfa5a7e4ab2091a9bd469f0a9e", + "zh:d7bec0da5c094c6955efed100f3fe22fca8866859f87c025be1760feb174d6d9", + "zh:fb9f271b72094d07cef8154cd3d50e9aa818a0ea39130bc193132ad7b23076fd", + ] +} + +provider "registry.terraform.io/hashicorp/null" { + version = "3.2.1" + hashes = [ + "h1:ydA0/SNRVB1o95btfshvYsmxA+jZFRZcvKzZSB+4S1M=", + "zh:58ed64389620cc7b82f01332e27723856422820cfd302e304b5f6c3436fb9840", + "zh:62a5cc82c3b2ddef7ef3a6f2fedb7b9b3deff4ab7b414938b08e51d6e8be87cb", + "zh:63cff4de03af983175a7e37e52d4bd89d990be256b16b5c7f919aff5ad485aa5", + "zh:74cb22c6700e48486b7cabefa10b33b801dfcab56f1a6ac9b6624531f3d36ea3", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:79e553aff77f1cfa9012a2218b8238dd672ea5e1b2924775ac9ac24d2a75c238", + "zh:a1e06ddda0b5ac48f7e7c7d59e1ab5a4073bbcf876c73c0299e4610ed53859dc", + "zh:c37a97090f1a82222925d45d84483b2aa702ef7ab66532af6cbcfb567818b970", + "zh:e4453fbebf90c53ca3323a92e7ca0f9961427d2f0ce0d2b65523cc04d5d999c2", + "zh:e80a746921946d8b6761e77305b752ad188da60688cfd2059322875d363be5f5", + "zh:fbdb892d9822ed0e4cb60f2fedbdbb556e4da0d88d3b942ae963ed6ff091e48f", + "zh:fca01a623d90d0cad0843102f9b8b9fe0d3ff8244593bd817f126582b52dd694", + ] +} diff --git a/testing/environments/cloud/Makefile b/testing/environments/cloud/Makefile new file mode 100644 index 00000000000..5908f984dfc --- /dev/null +++ b/testing/environments/cloud/Makefile @@ -0,0 +1,93 @@ +MAKEFILE_PATH:=$(abspath $(lastword ${MAKEFILE_LIST})) +MAKEFILE_DIR:=$(dir ${MAKEFILE_PATH}) +REPO_ROOT:=$(abspath ${MAKEFILE_DIR}/../../../) +SNAPSHOT_SUFFIX:=-SNAPSHOT +ifeq (${SNAPSHOT}, false) +SNAPSHOT_SUFFIX:="" +endif +ifeq (${SNAPSHOT}, 0) +SNAPSHOT_SUFFIX:="" +endif +VERSION:=$(shell grep defaultBeatVersion ${REPO_ROOT}/version/version.go | cut -d\" -f2)${SNAPSHOT_SUFFIX} + +export DOCKER_BUILDKIT=1 + +ELASTIC_AGENT_DOCKER_IMAGE=docker.elastic.co/cloud-release/elastic-agent-cloud +ELASTICSEARCH_DOCKER_IMAGE=docker.elastic.co/cloud-release/elasticsearch-cloud-ess +KIBANA_DOCKER_IMAGE=docker.elastic.co/cloud-release/kibana-cloud + +CI_ELASTIC_AGENT_DOCKER_IMAGE=docker.elastic.co/observability-ci/elastic-agent +CI_KIBANA_DOCKER_IMAGE=docker.elastic.co/observability-ci/kibana + +# Tag custom images with the username and current timestamp. +# The timestamp must be included to force images to be pulled. +USER_NAME?=${USER} +CUSTOM_IMAGE_TAG:=${VERSION}-${USER_NAME}-$(shell date +%s) + +USER_ELASTIC_AGENT_DOCKER_IMAGE=docker.elastic.co/observability-ci/${USER_NAME}-elastic-agent +USER_KIBANA_DOCKER_IMAGE=docker.elastic.co/observability-ci/${USER_NAME}-kibana + +.DEFAULT_GOAL := docker_image.auto.tfvars + +############################################################################## +# Target for creating a .tfvars file, defining the custom Docker images to +# use in the deployment. +############################################################################## + +docker_image.auto.tfvars: kibana_docker_image elastic_agent_docker_image + @echo 'docker_image_override={"elasticsearch":"${ELASTICSEARCH_DOCKER_IMAGE}","kibana":"${CI_KIBANA_DOCKER_IMAGE}","agent":"${CI_ELASTIC_AGENT_DOCKER_IMAGE}"}' > $@ + @echo 'docker_image_tag_override={"elasticsearch":"${VERSION}","kibana":"${VERSION}","agent":"${CUSTOM_IMAGE_TAG}"}' >> $@ + +############################################################################## +# Terraform shortcut rules. +############################################################################## + +.PHONY: apply +apply: + @terraform apply -auto-approve + + +.PHONY: snap +snap: + @echo ${VERSION} + +.PHONY: destroy +destroy: + @terraform destroy -auto-approve + + +.PHONY: deploy_local +deploy_local: build_elastic_agent_docker_image push_elastic_agent_docker_image + @echo 'docker_image_override={"elasticsearch":"${ELASTICSEARCH_DOCKER_IMAGE}","kibana":"${KIBANA_DOCKER_IMAGE}","agent":"${CI_ELASTIC_AGENT_DOCKER_IMAGE}"}' > docker_image.auto.tfvars + @echo 'docker_image_tag_override={"elasticsearch":"${VERSION}","kibana":"${VERSION}","agent":"${CUSTOM_IMAGE_TAG}"}' >> docker_image.auto.tfvars + @terraform apply -auto-approve + +############################################################################## +# Targets for building and pushing custom Kibana and Elastic Agent images. +############################################################################## + +# kibana_docker_image builds the Cloud Kibana image. +# The image will be based off the +# stack version defined in ${REPO_ROOT}/.env, unless overridden. +.PHONY: build_kibana_docker_image +kibana_docker_image: build_kibana_docker_image + docker push ${CI_KIBANA_DOCKER_IMAGE}:${CUSTOM_IMAGE_TAG} +build_kibana_docker_image: + $(MAKE) -C ${REPO_ROOT} build-package + docker build -t ${CI_KIBANA_DOCKER_IMAGE}:${CUSTOM_IMAGE_TAG} \ + -f "${REPO_ROOT}/testing/docker/kibana/Dockerfile-apmpackage" \ + --build-arg KIBANA_IMAGE=${KIBANA_DOCKER_IMAGE}:${IMAGE_TAG} \ + --platform linux/amd64 \ + "${REPO_ROOT}/build/packages" + +# build_elastic_agent_docker_image builds the Cloud Elastic Agent image. +# The image will be based +# off the version defined in version.go, +# CUSTOM_IMAGE_TAG is appended to the version resulting in e.g: +# 8.7.0-SNAPSHOT-doe-1673961915 +# unless overridden. +.PHONY: build_elastic_agent_docker_image +push_elastic_agent_docker_image: + cd ${REPO_ROOT}; CI_ELASTIC_AGENT_DOCKER_IMAGE=${CI_ELASTIC_AGENT_DOCKER_IMAGE} CUSTOM_IMAGE_TAG=${CUSTOM_IMAGE_TAG} mage cloud:push +build_elastic_agent_docker_image: + cd ${REPO_ROOT}; DOCKER_BUILDKIT=0 COMPOSE_DOCKER_CLI_BUILD=0 DOCKER_DEFAULT_PLATFORM=linux/amd64 mage cloud:image diff --git a/testing/environments/cloud/README.md b/testing/environments/cloud/README.md new file mode 100644 index 00000000000..90f3fdac1ff --- /dev/null +++ b/testing/environments/cloud/README.md @@ -0,0 +1,27 @@ +# Cloud-first testing + +Elastic employees can create an Elastic Cloud deployment with a locally +built Elastic Agent, by pushing images to an internal Docker repository. The images will be +based on the SNAPSHOT images with the version defined in `version/version.go`. + +Prerequisite to running following commands is having `terraform` installed and running `terraform init` from within `testing/environments/cloud`. + +Running a shorthand `make deploy_local` in `testing/environments/cloud` will build Agent, tag the docker image correctly, push it to the repository and deploy to Elastic Cloud. + +For more advanced scenarios: +Running `make build_elastic_agent_docker_image` in `testing/environments/cloud` will build and push the images. +Running `make push_elastic_agent_docker_image` in `testing/environments/cloud` will publish built docker image to CI docker repository. + +Once docker images are published you can run `EC_API_KEY=your_api_key make apply` from `testing/environments/cloud` directory to deploy them to Elastic Cloud. +To get `EC_API_KEY` follow [this guide](https://www.elastic.co/guide/en/cloud/current/ec-api-authentication.html) + +The custom images are tagged with the current version, commit and timestamp. The +timestamp is included to force a new Docker image to be used, which enables pushing new +binaries without recreating the deployment. + +To specify custom images create your `docker_image.auto.tfvars` file similar to `docker_image.auto.tfvars.sample`. + +You can also use `mage cloud:image` and `mage cloud:push` respectively from repo root directory. +To deploy your changes use `make apply` (from `testing/environments/cloud`) with `EC_API_KEY` instead of `make deploy_local` described above. + +SNAPSHOT images are used by default. To use non-snapshot image specify `SNAPSHOT=false` explicitly. \ No newline at end of file diff --git a/testing/environments/cloud/docker_image.auto.tfvars.sample b/testing/environments/cloud/docker_image.auto.tfvars.sample new file mode 100644 index 00000000000..182551100a8 --- /dev/null +++ b/testing/environments/cloud/docker_image.auto.tfvars.sample @@ -0,0 +1,2 @@ +docker_image_override={"elasticsearch":"docker.elastic.co/cloud-release/elasticsearch-cloud-ess","kibana":"docker.elastic.co/cloud-release/kibana-cloud","agent":"docker.elastic.co/observability-ci/elastic-agent"} +docker_image_tag_override={"elasticsearch":"8.6.0-f96862e0-SNAPSHOT","kibana":"8.6.0-f96862e0-SNAPSHOT","agent":"mysampletag"} \ No newline at end of file diff --git a/testing/environments/cloud/main.tf b/testing/environments/cloud/main.tf new file mode 100644 index 00000000000..360a30ad299 --- /dev/null +++ b/testing/environments/cloud/main.tf @@ -0,0 +1,39 @@ +terraform { + required_version = ">= 1.1.8, < 2.0.0" + required_providers { + ec = { + source = "elastic/ec" + version = ">=0.4.0" + } + } +} + +provider "ec" {} + +locals { + match = regex("const defaultBeatVersion = \"(.*)\"", file("${path.module}/../../../version/version.go"))[0] + stack_version = format("%s-SNAPSHOT", local.match) +} + +module "ec_deployment" { + source = "../infra/terraform/modules/ec_deployment" + + region = var.ess_region + stack_version = local.stack_version + + deployment_template = var.deployment_template + deployment_name_prefix = "elastic-agent-server-testing" + + apm_server_size = var.apm_server_size + apm_server_zone_count = var.apm_server_zone_count + + elasticsearch_size = var.elasticsearch_size + elasticsearch_zone_count = var.elasticsearch_zone_count + + docker_image = var.docker_image_override + docker_image_tag_override = { + "elasticsearch" : coalesce(var.docker_image_tag_override["elasticsearch"], local.stack_version), + "kibana" : coalesce(var.docker_image_tag_override["kibana"], local.stack_version), + "agent" : coalesce(var.docker_image_tag_override["agent"], local.stack_version) + } +} diff --git a/testing/environments/cloud/outputs.tf b/testing/environments/cloud/outputs.tf new file mode 100644 index 00000000000..eff0b9deee6 --- /dev/null +++ b/testing/environments/cloud/outputs.tf @@ -0,0 +1,37 @@ +output "elasticsearch_url" { + value = module.ec_deployment.elasticsearch_url + description = "The secure Elasticsearch URL" +} + +output "elasticsearch_username" { + value = module.ec_deployment.elasticsearch_username + sensitive = true + description = "The Elasticsearch username" +} + +output "elasticsearch_password" { + value = module.ec_deployment.elasticsearch_password + sensitive = true + description = "The Elasticsearch password" +} + +output "kibana_url" { + value = module.ec_deployment.kibana_url + description = "The secure Kibana URL" +} + +output "apm_secret_token" { + value = module.ec_deployment.apm_secret_token + description = "The APM Server secret token" + sensitive = true +} + +output "apm_server_url" { + value = module.ec_deployment.apm_url + description = "The APM Server URL" +} + +output "admin_console_url" { + value = module.ec_deployment.admin_console_url + description = "The admin console URL" +} diff --git a/testing/environments/cloud/terraform.tfvars b/testing/environments/cloud/terraform.tfvars new file mode 100644 index 00000000000..9a02c8be609 --- /dev/null +++ b/testing/environments/cloud/terraform.tfvars @@ -0,0 +1,2 @@ +integrations_server=false +stack_version="7.17.8-c416a4cb-SNAPSHOT" \ No newline at end of file diff --git a/testing/environments/cloud/variables.tf b/testing/environments/cloud/variables.tf new file mode 100644 index 00000000000..0250fb91904 --- /dev/null +++ b/testing/environments/cloud/variables.tf @@ -0,0 +1,62 @@ +## Deployment configuration + +variable "ess_region" { + default = "gcp-us-west2" + description = "Optional ESS region where the deployment will be created. Defaults to gcp-us-west2" + type = string +} + +variable "deployment_template" { + default = "gcp-compute-optimized-v2" + description = "Optional deployment template. Defaults to the CPU optimized template for GCP" + type = string +} + +variable "stack_version" { + default = "latest" + description = "Optional stack version" + type = string +} + +variable "apm_server_size" { + default = "1g" + type = string + description = "Optional apm server instance size" +} + +variable "apm_server_zone_count" { + default = 1 + type = number + description = "Optional apm server zone count" +} + +variable "elasticsearch_size" { + default = "8g" + type = string + description = "Optional Elasticsearch instance size" +} + +variable "elasticsearch_zone_count" { + default = 1 + type = number + description = "Optional Elasticsearch zone count" +} + +variable "docker_image_tag_override" { + default = { + "elasticsearch" : "", + "kibana" : "", + "agent" : "", + } + description = "Optional docker image tag override" + type = map(string) +} + +variable "docker_image_override" { + default = { + "elasticsearch" : "docker.elastic.co/cloud-release/elasticsearch-cloud-ess", + "kibana" : "docker.elastic.co/cloud-release/kibana-cloud", + "agent" : "docker.elastic.co/cloud-release/elastic-agent-cloud", + } + type = map(string) +} diff --git a/testing/environments/infra/terraform/modules/ec_deployment/.gitignore b/testing/environments/infra/terraform/modules/ec_deployment/.gitignore new file mode 100644 index 00000000000..66f537b860e --- /dev/null +++ b/testing/environments/infra/terraform/modules/ec_deployment/.gitignore @@ -0,0 +1,5 @@ +scripts/enable_expvar.sh +scripts/secret_token.sh +scripts/index_shards.sh +scripts/custom-apm-integration-pkg.sh +scripts/drop_pipeline.sh diff --git a/testing/environments/infra/terraform/modules/ec_deployment/README.md b/testing/environments/infra/terraform/modules/ec_deployment/README.md new file mode 100644 index 00000000000..fcf1a115dbe --- /dev/null +++ b/testing/environments/infra/terraform/modules/ec_deployment/README.md @@ -0,0 +1,79 @@ + +# Elastic Cloud Deployment module + +This module can be used to create an opinionated Elastic Cloud deployment with the configured inputs. +It requires access to either ESS or an ECE installation. To see which environment variables can be +used to configure the module, please refer to the [EC Provider docs](https://registry.terraform.io/providers/elastic/ec/latest/docs#authentication). + +## Requirements + +| Name | Version | +|------|---------| +| [ec](#requirement\_ec) | >=0.4.1 | + +## Providers + +| Name | Version | +|------|---------| +| [ec](#provider\_ec) | 0.4.0 | +| [external](#provider\_external) | n/a | +| [local](#provider\_local) | n/a | +| [null](#provider\_null) | n/a | + +## Resources + +| Name | Type | +|------|------| +| [ec_deployment.deployment](https://registry.terraform.io/providers/elastic/ec/latest/docs/resources/deployment) | resource | +| [ec_deployment.deployment_monitor](https://registry.terraform.io/providers/elastic/ec/latest/docs/resources/deployment) | resource | +| [local_file.custom_apm_integration_pkg](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource | +| [local_file.drop_pipeline](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource | +| [local_file.enable_expvar](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource | +| [local_file.secret_token](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource | +| [local_file.shard_settings](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource | +| [null_resource.custom_apm_integration_pkg](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | +| [null_resource.drop_pipeline](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | +| [null_resource.enable_expvar](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | +| [null_resource.secret_token](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | +| [null_resource.shard_settings](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | +| [ec_stack.deployment_version](https://registry.terraform.io/providers/elastic/ec/latest/docs/data-sources/stack) | data source | +| [external_external.secret_token](https://registry.terraform.io/providers/hashicorp/external/latest/docs/data-sources/external) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [apm\_index\_shards](#input\_apm\_index\_shards) | The number of shards to set for APM Indices | `number` | `0` | no | +| [apm\_server\_expvar](#input\_apm\_server\_expvar) | Whether or not to enable APM Server's expvar endpoint. Defaults to false | `bool` | `false` | no | +| [apm\_server\_pprof](#input\_apm\_server\_pprof) | Whether or not to enable APM Server's pprof endpoint. Defaults to false | `bool` | `false` | no | +| [apm\_server\_size](#input\_apm\_server\_size) | Optional apm server instance size | `string` | `"1g"` | no | +| [apm\_server\_zone\_count](#input\_apm\_server\_zone\_count) | Optional apm server zone count | `number` | `1` | no | +| [custom\_apm\_integration\_pkg\_path](#input\_custom\_apm\_integration\_pkg\_path) | Path to the zipped custom APM integration package, if empty custom apm integration pkg is not installed | `string` | `""` | no | +| [deployment\_name\_prefix](#input\_deployment\_name\_prefix) | Optional ESS or ECE region. Defaults to GCP US West 2 (Los Angeles) | `string` | `"apmserver"` | no | +| [deployment\_template](#input\_deployment\_template) | Optional deployment template. Defaults to the CPU optimized template for GCP | `string` | `"gcp-compute-optimized-v2"` | no | +| [docker\_image](#input\_docker\_image) | Optional docker image overrides. The full map needs to be specified | `map(string)` |
{
"apm": "docker.elastic.co/cloud-release/elastic-agent-cloud",
"elasticsearch": "docker.elastic.co/cloud-release/elasticsearch-cloud-ess",
"kibana": "docker.elastic.co/cloud-release/kibana-cloud"
}
| no | +| [docker\_image\_tag\_override](#input\_docker\_image\_tag\_override) | Optional docker image tag overrides, The full map needs to be specified | `map(string)` |
{
"apm": "",
"elasticsearch": "",
"kibana": ""
}
| no | +| [drop\_pipeline](#input\_drop\_pipeline) | Whether or not to install an Elasticsearch ingest pipeline to drop all incoming APM documents. Defaults to false | `bool` | `false` | no | +| [elasticsearch\_autoscale](#input\_elasticsearch\_autoscale) | Optional autoscale the Elasticsearch cluster | `bool` | `false` | no | +| [elasticsearch\_dedicated\_masters](#input\_elasticsearch\_dedicated\_masters) | Optionally use dedicated masters for the Elasticsearch cluster | `bool` | `false` | no | +| [elasticsearch\_size](#input\_elasticsearch\_size) | Optional Elasticsearch instance size | `string` | `"8g"` | no | +| [elasticsearch\_zone\_count](#input\_elasticsearch\_zone\_count) | Optional Elasticsearch zone count | `number` | `2` | no | +| [integrations\_server](#input\_integrations\_server) | Optionally disable the integrations server block and use the apm block (7.x only) | `bool` | `true` | no | +| [monitor\_deployment](#input\_monitor\_deployment) | Optionally monitor the deployment in a separate deployment | `bool` | `false` | no | +| [region](#input\_region) | Optional ESS or ECE region. Defaults to GCP US West 2 (Los Angeles) | `string` | `"gcp-us-west2"` | no | +| [stack\_version](#input\_stack\_version) | Optional stack version | `string` | `"latest"` | no | +| [tags](#input\_tags) | Optional set of tags to use for all deployments | `map(string)` | `{}` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [admin\_console\_url](#output\_admin\_console\_url) | n/a | +| [apm\_secret\_token](#output\_apm\_secret\_token) | The APM Secret token | +| [apm\_url](#output\_apm\_url) | The secure APM URL | +| [elasticsearch\_password](#output\_elasticsearch\_password) | The Elasticsearch password | +| [elasticsearch\_url](#output\_elasticsearch\_url) | The secure Elasticsearch URL | +| [elasticsearch\_username](#output\_elasticsearch\_username) | The Elasticsearch username | +| [kibana\_url](#output\_kibana\_url) | The secure Kibana URL | +| [stack\_version](#output\_stack\_version) | The matching stack pack version from the provided stack\_version | + \ No newline at end of file diff --git a/testing/environments/infra/terraform/modules/ec_deployment/deployment.tf b/testing/environments/infra/terraform/modules/ec_deployment/deployment.tf new file mode 100644 index 00000000000..203500e7f86 --- /dev/null +++ b/testing/environments/infra/terraform/modules/ec_deployment/deployment.tf @@ -0,0 +1,233 @@ +locals { + region = var.region + version = var.stack_version + deployment_template = var.deployment_template + name_prefix = var.deployment_name_prefix +} + +data "ec_stack" "deployment_version" { + version_regex = local.version + region = local.region +} + +resource "ec_deployment" "deployment" { + name = "${local.name_prefix}-${data.ec_stack.deployment_version.version}" + version = data.ec_stack.deployment_version.version + region = local.region + deployment_template_id = local.deployment_template + tags = var.tags + + elasticsearch { + autoscale = var.elasticsearch_autoscale + topology { + id = "hot_content" + size = var.elasticsearch_size + zone_count = var.elasticsearch_zone_count + } + dynamic "topology" { + for_each = var.elasticsearch_dedicated_masters ? [3] : [] + content { + id = "master" + size = "1g" + zone_count = topology.value # Dedicated masters always need to be set in all zones + } + } + dynamic "config" { + for_each = var.docker_image_tag_override["elasticsearch"] != "" ? [var.docker_image["elasticsearch"]] : [] + content { + docker_image = "${config.value}:${var.docker_image_tag_override["elasticsearch"]}" + } + } + } + kibana { + dynamic "config" { + for_each = var.docker_image_tag_override["kibana"] != "" ? [var.docker_image["kibana"]] : [] + content { + docker_image = "${config.value}:${var.docker_image_tag_override["kibana"]}" + } + } + } + dynamic "apm" { + for_each = var.integrations_server ? [] : [1] + content { + dynamic "config" { + for_each = var.docker_image_tag_override["agent"] != "" ? [var.docker_image["agent"]] : [] + content { + docker_image = "${config.value}:${var.docker_image_tag_override["agent"]}" + } + } + topology { + size = var.apm_server_size + zone_count = var.apm_server_zone_count + } + } + } + + dynamic "integrations_server" { + for_each = var.integrations_server ? [1] : [] + content { + dynamic "config" { + for_each = var.docker_image_tag_override["agent"] != "" ? [var.docker_image["agent"]] : [] + content { + docker_image = "${config.value}:${var.docker_image_tag_override["agent"]}" + } + } + topology { + size = var.apm_server_size + zone_count = var.apm_server_zone_count + } + } + } + + dynamic "observability" { + for_each = ec_deployment.deployment_monitor.* + content { + deployment_id = observability.value.id + logs = true + metrics = true + ref_id = "main-elasticsearch" + } + } +} + +resource "ec_deployment" "deployment_monitor" { + count = var.monitor_deployment ? 1 : 0 + + name = "monitor-${local.name_prefix}-${local.version}" + version = data.ec_stack.deployment_version.version + region = local.region + deployment_template_id = local.deployment_template + tags = var.tags + + elasticsearch { + topology { + id = "hot_content" + size = "2g" + } + } + kibana {} +} + +resource "local_file" "enable_expvar" { + content = templatefile("${path.module}/scripts/enable_expvar.tftpl", { + kibana_url = ec_deployment.deployment.kibana.0.https_endpoint, + elastic_password = ec_deployment.deployment.elasticsearch_password, + enable_expvar = var.apm_server_expvar + enable_pprof = var.apm_server_pprof + }) + filename = "${path.module}/scripts/enable_expvar.sh" +} + +resource "local_file" "secret_token" { + count = var.integrations_server ? 1 : 0 + content = templatefile("${path.module}/scripts/secret_token.tftpl", { + kibana_url = ec_deployment.deployment.kibana.0.https_endpoint, + elastic_password = ec_deployment.deployment.elasticsearch_password, + }) + filename = "${path.module}/scripts/secret_token.sh" +} + +resource "local_file" "shard_settings" { + count = var.apm_index_shards > 0 ? 1 : 0 + content = templatefile("${path.module}/scripts/index_shards.tftpl", { + elasticsearch_url = ec_deployment.deployment.elasticsearch.0.https_endpoint, + elasticsearch_password = ec_deployment.deployment.elasticsearch_password, + elasticsearch_username = ec_deployment.deployment.elasticsearch_username, + shards = var.apm_index_shards, + }) + filename = "${path.module}/scripts/index_shards.sh" +} + +resource "local_file" "custom_apm_integration_pkg" { + count = var.custom_apm_integration_pkg_path != "" ? 1 : 0 + content = templatefile("${path.module}/scripts/custom-apm-integration-pkg.tftpl", { + kibana_url = ec_deployment.deployment.kibana.0.https_endpoint, + elastic_password = ec_deployment.deployment.elasticsearch_password, + custom_apm_integration_pkg_path = var.custom_apm_integration_pkg_path, + }) + filename = "${path.module}/scripts/custom-apm-integration-pkg.sh" +} + +resource "null_resource" "enable_expvar" { + triggers = { + shell_hash = local_file.enable_expvar.id + integrations_server = var.integrations_server + } + provisioner "local-exec" { + command = "scripts/enable_expvar.sh" + interpreter = ["/bin/bash", "-c"] + working_dir = path.module + } +} + +resource "null_resource" "secret_token" { + count = var.integrations_server ? 1 : 0 + triggers = { + deployment_id = ec_deployment.deployment.id + shell_hash = local_file.secret_token.0.id + } + provisioner "local-exec" { + command = "scripts/secret_token.sh" + interpreter = ["/bin/bash", "-c"] + working_dir = path.module + } +} + +resource "null_resource" "shard_settings" { + count = var.apm_index_shards > 0 ? 1 : 0 + triggers = { + deployment_id = ec_deployment.deployment.id + shell_hash = local_file.shard_settings.0.id + } + provisioner "local-exec" { + command = "scripts/index_shards.sh" + interpreter = ["/bin/bash", "-c"] + working_dir = path.module + } +} + +resource "null_resource" "custom_apm_integration_pkg" { + count = var.custom_apm_integration_pkg_path != "" ? 1 : 0 + triggers = { + deployment_id = ec_deployment.deployment.id + pkg_update = filesha256(var.custom_apm_integration_pkg_path) + } + provisioner "local-exec" { + command = "scripts/custom-apm-integration-pkg.sh" + interpreter = ["/bin/bash", "-c"] + working_dir = path.module + } +} + +# Since the secret token value is set in the APM Integration policy, we need +# an "external" resource to run a shell script that returns the secret token +# as {"value":"SECRET_TOKEN"}. +data "external" "secret_token" { + count = var.integrations_server ? 1 : 0 + depends_on = [local_file.secret_token] + program = ["/bin/bash", "-c", "scripts/secret_token.sh"] + working_dir = path.module +} + +resource "null_resource" "drop_pipeline" { + count = var.drop_pipeline ? 1 : 0 + triggers = { + deployment_id = ec_deployment.deployment.id + shell_hash = local_file.drop_pipeline.0.id + } + provisioner "local-exec" { + command = "scripts/drop_pipeline.sh" + interpreter = ["/bin/bash", "-c"] + working_dir = path.module + } +} + +resource "local_file" "drop_pipeline" { + count = var.drop_pipeline ? 1 : 0 + content = templatefile("${path.module}/scripts/drop_pipeline.tftpl", { + elasticsearch_url = ec_deployment.deployment.elasticsearch.0.https_endpoint, + elasticsearch_password = ec_deployment.deployment.elasticsearch_password, + elasticsearch_username = ec_deployment.deployment.elasticsearch_username, + }) + filename = "${path.module}/scripts/drop_pipeline.sh" +} diff --git a/testing/environments/infra/terraform/modules/ec_deployment/header.md b/testing/environments/infra/terraform/modules/ec_deployment/header.md new file mode 100644 index 00000000000..cc714a7ff07 --- /dev/null +++ b/testing/environments/infra/terraform/modules/ec_deployment/header.md @@ -0,0 +1,5 @@ +# Elastic Cloud Deployment module + +This module can be used to create an opinionated Elastic Cloud deployment with the configured inputs. +It requires access to either ESS or an ECE installation. To see which environment variables can be +used to configure the module, please refer to the [EC Provider docs](https://registry.terraform.io/providers/elastic/ec/latest/docs#authentication). diff --git a/testing/environments/infra/terraform/modules/ec_deployment/outputs.tf b/testing/environments/infra/terraform/modules/ec_deployment/outputs.tf new file mode 100644 index 00000000000..40ed6f409fa --- /dev/null +++ b/testing/environments/infra/terraform/modules/ec_deployment/outputs.tf @@ -0,0 +1,45 @@ +output "kibana_url" { + value = ec_deployment.deployment.kibana.0.https_endpoint + description = "The secure Kibana URL" +} + +output "apm_url" { + value = var.integrations_server ? ec_deployment.deployment.integrations_server.0.https_endpoint : ec_deployment.deployment.apm.0.https_endpoint + description = "The secure APM URL" +} + +output "apm_secret_token" { + value = var.integrations_server ? data.external.secret_token.0.result.value : ec_deployment.deployment.apm_secret_token + sensitive = true + description = "The APM Secret token" +} + +output "elasticsearch_url" { + value = ec_deployment.deployment.elasticsearch.0.https_endpoint + description = "The secure Elasticsearch URL" + depends_on = [ + null_resource.shard_settings, + null_resource.custom_apm_integration_pkg, + ] +} + +output "elasticsearch_username" { + value = ec_deployment.deployment.elasticsearch_username + sensitive = true + description = "The Elasticsearch username" +} + +output "elasticsearch_password" { + value = ec_deployment.deployment.elasticsearch_password + sensitive = true + description = "The Elasticsearch password" +} + +output "stack_version" { + value = data.ec_stack.deployment_version.version + description = "The matching stack pack version from the provided stack_version" +} + +output "admin_console_url" { + value = "https://admin.found.no/deployments/${ec_deployment.deployment.id}/integrations_server" +} diff --git a/testing/environments/infra/terraform/modules/ec_deployment/scripts/custom-apm-integration-pkg.tftpl b/testing/environments/infra/terraform/modules/ec_deployment/scripts/custom-apm-integration-pkg.tftpl new file mode 100644 index 00000000000..bfe877906eb --- /dev/null +++ b/testing/environments/infra/terraform/modules/ec_deployment/scripts/custom-apm-integration-pkg.tftpl @@ -0,0 +1,12 @@ +#!/bin/bash + +KIBANA_ENDPOINT=${kibana_url}/api/fleet/epm/packages +KIBANA_AUTH=elastic:${elastic_password} + +curl -s \ + -X POST \ + -u $${KIBANA_AUTH} \ + -H "Content-Type: application/zip" \ + -H "kbn-xsrf:1" \ + --data-binary @${custom_apm_integration_pkg_path} \ + $${KIBANA_ENDPOINT} diff --git a/testing/environments/infra/terraform/modules/ec_deployment/scripts/drop_pipeline.tftpl b/testing/environments/infra/terraform/modules/ec_deployment/scripts/drop_pipeline.tftpl new file mode 100644 index 00000000000..cdb1b82e1d3 --- /dev/null +++ b/testing/environments/infra/terraform/modules/ec_deployment/scripts/drop_pipeline.tftpl @@ -0,0 +1,15 @@ +#!/bin/bash + +set -eo + +ingest_pipelines=("logs-apm.app@custom" "logs-apm.error@custom" "metrics-apm.app@custom" "metrics-apm.internal@custom" "traces-apm.rum@custom" "traces-apm@custom") + +for tpl in $${ingest_pipelines[@]}; do + curl -s --fail-with-body -H 'Content-Type: application/json' -XPUT -u ${elasticsearch_username}:${elasticsearch_password} "${elasticsearch_url}/_ingest/pipeline/$${tpl}" -d '{ + "processors": [ + { + "drop": {} + } + ] +}' +done diff --git a/testing/environments/infra/terraform/modules/ec_deployment/scripts/enable_expvar.tftpl b/testing/environments/infra/terraform/modules/ec_deployment/scripts/enable_expvar.tftpl new file mode 100644 index 00000000000..0737afe7906 --- /dev/null +++ b/testing/environments/infra/terraform/modules/ec_deployment/scripts/enable_expvar.tftpl @@ -0,0 +1,34 @@ +#!/bin/bash + +set -e + +KIBANA_ENDPOINT=${kibana_url}/api/fleet/package_policies/elastic-cloud-apm +KIBANA_AUTH=elastic:${elastic_password} + +# Check if the policy has expvar disabled: +POLICY=$(curl -sk -u $${KIBANA_AUTH} $${KIBANA_ENDPOINT}) +NOT_FOUND_MSG='statusCode":404' + +if [[ "$${POLICY}" == *"$${NOT_FOUND_MSG}"* ]]; then + echo "APM policy not found, expvar and pprof won't be enabled." + exit 0 +fi + +echo $${POLICY} | grep '"expvar_enabled":{"type":"bool","value":true}' > /dev/null 2>&1 && EXPVAR_ENABLED=true +echo $${POLICY} | grep '"pprof_enabled":{"type":"bool","value":true}' > /dev/null 2>&1 && PPROF_ENABLED=true +if [[ $${EXPVAR_ENABLED} || $${PPROF_ENABLED} ]] ; then + echo "expvar or pprof already enabled" + exit 0 +fi + +# Download and modify the APM policy +echo $${POLICY} | jq '.item' | \ + jq 'del(.id)' | jq 'del(.elasticsearch)'| jq 'del(.inputs[].compiled_input)' | jq 'del(.revision)' |\ + jq 'del(.created_at)' | jq 'del(.created_by)' | jq 'del(.updated_at)' | jq 'del(.updated_by)' |\ + jq 'select(.inputs[].policy_template == "apmserver").inputs[].vars.expvar_enabled = {type: "bool", value: ${enable_expvar}}' |\ + jq 'select(.inputs[].policy_template == "apmserver").inputs[].vars.pprof_enabled = {type: "bool", value: ${enable_pprof}}' > policy.json + +# Update the policy +curl -s -H 'content-type: application/json' -H 'kbn-xsrf: true' -X PUT -k -d@policy.json -u $${KIBANA_AUTH} $${KIBANA_ENDPOINT} + +rm -f policy.json diff --git a/testing/environments/infra/terraform/modules/ec_deployment/scripts/index_shards.tftpl b/testing/environments/infra/terraform/modules/ec_deployment/scripts/index_shards.tftpl new file mode 100644 index 00000000000..9e15ec2c2e8 --- /dev/null +++ b/testing/environments/infra/terraform/modules/ec_deployment/scripts/index_shards.tftpl @@ -0,0 +1,24 @@ +#!/bin/bash + +set -eo + +component_templates=("logs-apm.app@custom" "logs-apm.error@custom" "metrics-apm.app@custom" "metrics-apm.internal@custom" "traces-apm.rum@custom" "traces-apm@custom") + +for tpl in $${component_templates[@]}; do + curl -s -H 'Content-Type: application/json' --fail -XPUT -u ${elasticsearch_username}:${elasticsearch_password} "${elasticsearch_url}/_component_template/$${tpl}" -d '{ + "template": { + "settings": { + "index": { + "number_of_shards": "${shards}" + } + } + }, + "_meta": { + "package": { + "name": "apm" + }, + "managed_by": "fleet", + "managed": true + } +}' +done diff --git a/testing/environments/infra/terraform/modules/ec_deployment/scripts/secret_token.tftpl b/testing/environments/infra/terraform/modules/ec_deployment/scripts/secret_token.tftpl new file mode 100644 index 00000000000..84555d11b33 --- /dev/null +++ b/testing/environments/infra/terraform/modules/ec_deployment/scripts/secret_token.tftpl @@ -0,0 +1,9 @@ +#!/bin/bash + +KIBANA_ENDPOINT=${kibana_url}/api/fleet/package_policies/elastic-cloud-apm +KIBANA_AUTH=elastic:${elastic_password} + +SECRET_TOKEN=$(curl -s -u $${KIBANA_AUTH} $${KIBANA_ENDPOINT} $${KIBANA_ENDPOINT} |\ + jq -r '.item | select(.inputs[].policy_template == "apmserver") .inputs[].vars.secret_token.value' | uniq) + +echo "{\"value\":\"$${SECRET_TOKEN}\"}" diff --git a/testing/environments/infra/terraform/modules/ec_deployment/terraform.tf b/testing/environments/infra/terraform/modules/ec_deployment/terraform.tf new file mode 100644 index 00000000000..a11a0be35b8 --- /dev/null +++ b/testing/environments/infra/terraform/modules/ec_deployment/terraform.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + ec = { + source = "elastic/ec" + version = ">=0.4.1" + } + } +} diff --git a/testing/environments/infra/terraform/modules/ec_deployment/variables.tf b/testing/environments/infra/terraform/modules/ec_deployment/variables.tf new file mode 100644 index 00000000000..63226c26ba9 --- /dev/null +++ b/testing/environments/infra/terraform/modules/ec_deployment/variables.tf @@ -0,0 +1,141 @@ +# Deployment settings + +variable "deployment_name_prefix" { + default = "apmserver" + description = "Optional ESS or ECE region. Defaults to GCP US West 2 (Los Angeles)" + type = string +} + +variable "region" { + default = "gcp-us-west2" + description = "Optional ESS or ECE region. Defaults to GCP US West 2 (Los Angeles)" + type = string +} + +variable "deployment_template" { + default = "gcp-compute-optimized-v2" + description = "Optional deployment template. Defaults to the CPU optimized template for GCP" + type = string +} + +variable "stack_version" { + default = "latest" + description = "Optional stack version" + type = string +} + +variable "monitor_deployment" { + default = false + type = bool + description = "Optionally monitor the deployment in a separate deployment" +} + +variable "tags" { + type = map(string) + default = {} + description = "Optional set of tags to use for all deployments" +} + +# APM Server topology + +variable "apm_server_size" { + default = "1g" + type = string + description = "Optional apm server instance size" +} + +variable "apm_server_zone_count" { + default = 1 + type = number + description = "Optional apm server zone count" +} + +variable "integrations_server" { + description = "Optionally disable the integrations server block and use the apm block (7.x only)" + type = bool + default = true +} + +# Elasticsearch topology + +variable "elasticsearch_size" { + default = "8g" + type = string + description = "Optional Elasticsearch instance size" +} + +variable "elasticsearch_zone_count" { + default = 2 + type = number + description = "Optional Elasticsearch zone count" +} + +variable "elasticsearch_autoscale" { + default = false + type = bool + description = "Optional autoscale the Elasticsearch cluster" +} + +variable "elasticsearch_dedicated_masters" { + default = false + type = bool + description = "Optionally use dedicated masters for the Elasticsearch cluster" +} + +# Docker image overrides + +variable "docker_image_tag_override" { + default = { + "elasticsearch" : "", + "kibana" : "", + "agent" : "", + } + description = "Optional docker image tag overrides, The full map needs to be specified" + type = map(string) +} + +variable "docker_image" { + default = { + "elasticsearch" : "docker.elastic.co/cloud-release/elasticsearch-cloud-ess", + "kibana" : "docker.elastic.co/cloud-release/kibana-cloud", + "agent" : "docker.elastic.co/cloud-release/elastic-agent-cloud", + } + type = map(string) + description = "Optional docker image overrides. The full map needs to be specified" +} + +# Enable APM Server's expvar + +variable "apm_server_expvar" { + default = false + description = "Whether or not to enable APM Server's expvar endpoint. Defaults to false" + type = bool +} + +variable "apm_server_pprof" { + default = false + description = "Whether or not to enable APM Server's pprof endpoint. Defaults to false" + type = bool +} + +variable "apm_index_shards" { + default = 0 + description = "The number of shards to set for APM Indices" + type = number +} + +# Install custom APM integration package + +variable "custom_apm_integration_pkg_path" { + type = string + description = "Path to the zipped custom APM integration package, if empty custom apm integration pkg is not installed" + default = "" +} + +# Install a custom pipeline to drop all the incoming APM documents. It can be +# useful to benchmark the theoretical APM Server output. +variable "drop_pipeline" { + default = false + description = "Whether or not to install an Elasticsearch ingest pipeline to drop all incoming APM documents. Defaults to false" + type = bool +}