diff --git a/.ci/Jenkinsfile b/.ci/Jenkinsfile deleted file mode 100644 index 0af60ca4..00000000 --- a/.ci/Jenkinsfile +++ /dev/null @@ -1,248 +0,0 @@ -#!/usr/bin/env groovy - -@Library('apm@current') _ - -pipeline { - agent { label 'ubuntu-18 && immutable' } - environment { - REPO = 'apm-aws-lambda' - GO_VERSION = '1.19' - BASE_DIR = "src/github.com/elastic/${env.REPO}" - PIPELINE_LOG_LEVEL = 'INFO' - SUFFIX_ARN_FILE = 'arn-file.md' - RELEASE_URL_MESSAGE = "()" - SLACK_CHANNEL = '#apm-serverless' - NOTIFY_TO = 'build-apm+apm-serverless@elastic.co' - DOCKER_REGISTRY = 'docker.elastic.co' - DOCKER_SECRET = 'secret/apm-team/ci/docker-registry/prod' - } - options { - timeout(time: 1, unit: 'HOURS') - buildDiscarder(logRotator(numToKeepStr: '20', artifactNumToKeepStr: '20', daysToKeepStr: '30')) - timestamps() - ansiColor('xterm') - disableResume() - durabilityHint('PERFORMANCE_OPTIMIZED') - rateLimitBuilds(throttle: [count: 60, durationName: 'hour', userBoost: true]) - quietPeriod(10) - } - triggers { - issueCommentTrigger("${obltGitHubComments()}") - } - stages { - stage('Checkout') { - steps { - whenTrue(isTag()) { - notifyStatus(slackStatus: 'good', subject: "[${env.REPO}] Release tag *${env.TAG_NAME}* has been created", body: "Build: (<${env.RUN_DISPLAY_URL}|here>) for further details.") - } - deleteDir() - gitCheckout(basedir: "${BASE_DIR}") - stash allowEmpty: true, name: 'source', useDefaultExcludes: false - setEnvVar('ELASTIC_LAYER_NAME', "elastic-apm-extension${getVersion()}") - } - } - stage('Lint'){ - steps { - withGithubNotify(context: "Lint") { - withGoEnv(){ - dir("${BASE_DIR}"){ - sh(label: 'lint-prep', script: 'go mod tidy && git diff --exit-code') - sh(label: 'lint-run', script: 'make lint') - sh(label: 'Go vet', script: 'go vet') - } - } - } - withGithubNotify(context: "NOTICE.txt") { - withGoEnv(){ - dir("${BASE_DIR}"){ - sh(label: 'notice', script: ''' - make NOTICE.txt - git diff --exit-code --quiet && exit 0 || echo "regenerate NOTICE.txt" && exit 1 - ''') - } - } - } - } - } - stage('Test') { - failFast false - matrix { - agent {label "${PLATFORM}"} - options { skipDefaultCheckout() } - axes { - axis { - name 'PLATFORM' - values 'macosx && x86_64', 'ubuntu-18 && immutable' - } - } - stages { - stage('Build'){ - steps { - withGithubNotify(context: "Build-${GO_VERSION}-${PLATFORM}") { - deleteDir() - unstash 'source' - withGoEnv(){ - dir("${BASE_DIR}"){ - cmd(label: 'make build', script: 'make build') - } - } - } - } - } - stage('Test') { - steps { - withGithubNotify(context: "Test-${GO_VERSION}-${PLATFORM}") { - withGoEnv(){ - dir("${BASE_DIR}"){ - goTestJUnit(options: '-v ./...', output: 'junit-report.xml') - } - } - } - } - post { - always { - junit(allowEmptyResults: true, keepLongStdio: true, testResults: '**/junit-report.xml') - } - } - } - } - } - } - stage('Release') { - options { skipDefaultCheckout() } - when { - tag pattern: 'v\\d+\\.\\d+\\.\\d+', comparator: 'REGEXP' - } - stages { - stage('BuildPublish') { - failFast false - matrix { - agent {label "${PLATFORM}"} - options { skipDefaultCheckout() } - environment { - HOME = "${env.WORKSPACE}" // required by "bash_standard_lib.sh" for "Push Docker Image" step - } - axes { - axis { - name 'PLATFORM' - values 'arm', 'ubuntu-18 && immutable' - } - } - stages { - stage('Dist') { - steps { - withGithubNotify(context: "Dist-${PLATFORM}") { - deleteDir() - unstash 'source' - withGoEnv(){ - dir("${BASE_DIR}"){ - cmd(label: 'make dist', script: 'make dist') - } - } - } - } - } - stage('PushDockerImage') { - steps { - withGithubNotify(context: "Push-Docker-${PLATFORM}") { - withGoEnv(){ - dir("${BASE_DIR}"){ - dockerLogin(secret: "${DOCKER_SECRET}", registry: "${DOCKER_REGISTRY}") - cmd(label: 'make push-docker', script: 'make push-docker') - } - } - } - } - } - stage('DeletePreviousLayer') { - steps { - withGithubNotify(context: "Delete-Layer-${PLATFORM}") { - withGoEnv(){ - withAWSEnv(secret: 'secret/observability-team/ci/service-account/apm-aws-lambda', forceInstallation: true, version: '2.4.10') { - dir("${BASE_DIR}"){ - cmd(label: 'make delete-in-all-aws-regions', script: 'make delete-in-all-aws-regions', returnStatus: true) - } - } - } - } - } - } - stage('PublishLayer') { - steps { - withGithubNotify(context: "Publish-Layer-${PLATFORM}") { - withGoEnv(){ - withAWSEnv(secret: 'secret/observability-team/ci/service-account/apm-aws-lambda', forceInstallation: true, version: '2.4.10') { - dir("${BASE_DIR}"){ - cmd(label: 'make publish-in-all-aws-regions', script: 'make publish-in-all-aws-regions') - cmd(label: 'make create-arn-file', script: 'make create-arn-file') - stash(includes: "*${SUFFIX_ARN_FILE}", name: "arn-${isArm() ? 'arm' : 'amd'}") - stash(includes: "bin/${BRANCH_NAME}-*.zip", name: "dist-${isArm() ? 'arm' : 'amd'}") - } - } - } - } - } - post { - always { - archiveArtifacts(allowEmptyArchive: true, artifacts: "${BASE_DIR}/.regions") - archiveArtifacts(allowEmptyArchive: true, artifacts: "${BASE_DIR}/*${SUFFIX_ARN_FILE}") - archiveArtifacts(allowEmptyArchive: true, artifacts: "${BASE_DIR}/.aws") - } - } - } - } - } - } - stage('Release Notes') { - steps { - withGhEnv(forceInstallation: true, version: '2.4.0') { - dir("${BASE_DIR}"){ - unstash "arn-arm" - unstash "arn-amd" - unstash "dist-arm" - unstash "dist-amd" - cmd(label: 'make release-notes', script: 'make release-notes') - } - } - } - } - } - } - } - post { - cleanup { - notifyBuildResult(prComment: true) - } - success { - whenTrue(isTag()) { - notifyStatus(slackStatus: 'good', subject: "[${env.REPO}] Release *${env.TAG_NAME}* published", body: "Build: (<${env.RUN_DISPLAY_URL}|here>)\nRelease URL: ${env.RELEASE_URL_MESSAGE}") - } - } - unsuccessful { - whenTrue(isTag()) { - notifyStatus(slackStatus: 'warning', subject: "[${env.REPO}] Release *${env.TAG_NAME}* could not be published.", body: "Build: (<${env.RUN_DISPLAY_URL}|here>)") - } - } - } -} - -/** -* Transform TAG releases from v{major}.{minor}.{patch} to -* ver-{major}-{minor}-{patch}. f.i: given v1.2.3 then -* -ver-1-2-3. -*/ -def getVersion() { - if (env.BRANCH_NAME?.trim() && env.BRANCH_NAME.startsWith('v')) { - return env.BRANCH_NAME.replaceAll('v', '-ver-').replaceAll('\\.', '-') - } - return '' -} - -def notifyStatus(def args = [:]) { - releaseNotification(slackChannel: "${env.SLACK_CHANNEL}", - slackColor: args.slackStatus, - slackCredentialsId: 'jenkins-slack-integration-token', - to: "${env.NOTIFY_TO}", - subject: args.subject, - body: args.body) -} diff --git a/.ci/jobs/apm-aws-lambda-mbp.yml b/.ci/jobs/apm-aws-lambda-mbp.yml deleted file mode 100644 index 79d3b6e7..00000000 --- a/.ci/jobs/apm-aws-lambda-mbp.yml +++ /dev/null @@ -1,45 +0,0 @@ ---- -- job: - name: library/apm-aws-lambda-mbp - display-name: AWS Lambda extension - description: AWS Lambda extension MBP - project-type: multibranch - concurrent: true - script-path: .ci/Jenkinsfile - scm: - - github: - branch-discovery: no-pr - discover-pr-forks-strategy: merge-current - discover-pr-forks-trust: permission - discover-pr-origin: merge-current - discover-tags: true - head-filter-regex: '(main|PR-.*|v\d+\.\d+\.\d+.*)' - notification-context: 'apm-ci' - repo: apm-aws-lambda - repo-owner: elastic - credentials-id: 2a9602aa-ab9f-4e52-baf3-b71ca88469c7-UserAndToken - ssh-checkout: - credentials: f6c7695a-671e-4f4f-a331-acdce44ff9ba - build-strategies: - - tags: - ignore-tags-older-than: -1 - ignore-tags-newer-than: -1 - - regular-branches: true - - change-request: - ignore-target-only-changes: true - clean: - after: true - before: true - prune: true - shallow-clone: true - depth: 3 - do-not-fetch-tags: true - submodule: - disable: false - recursive: true - parent-credentials: true - timeout: 100 - timeout: '15' - use-author: true - wipe-workspace: 'True' - prune-dead-branches: true diff --git a/.ci/publish-aws.sh b/.ci/publish-aws.sh new file mode 100755 index 00000000..2c8f22f0 --- /dev/null +++ b/.ci/publish-aws.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +set -euo pipefail + +# +# Publishes the created artifacts from GoReleaser to AWS as AWS Lambda Layers in every region. +# Finalized by generating an ARN table which will be used in the release notes. +# + +export AWS_FOLDER=${AWS_FOLDER:-.aws} +export SUFFIX_ARN_FILE=${SUFFIX_ARN_FILE:-arn-file.md} + +GOOS=${GOOS:?Please provide GOOS environment variable.} +GOARCH=${GOARCH:?Please provide GOARCH environment variable.} +ELASTIC_LAYER_NAME=${ELASTIC_LAYER_NAME:?Please provide ELASTIC_LAYER_NAME environment variable.} +ARCHITECTURE=${ARCHITECTURE:?Please provide ARCHITECTURE environment variable.} +VERSION=${VERSION:?Please provide VERSION environment variable. e.g. current git tag} + +FULL_LAYER_NAME="${ELASTIC_LAYER_NAME}-${ARCHITECTURE}" + +ALL_AWS_REGIONS=$(aws ec2 describe-regions --output json --no-cli-pager | jq -r '.Regions[].RegionName') + +rm -rf "${AWS_FOLDER}" + +# Delete previous layers +for region in $ALL_AWS_REGIONS; do + layer_versions=$(aws lambda list-layer-versions --region="${region}" --layer-name="${FULL_LAYER_NAME}" | jq '.LayerVersions[].Version') + echo "Found layer versions for ${FULL_LAYER_NAME} in ${region}: ${layer_versions:-none}" + for version_number in $layer_versions; do + echo "- Deleting ${FULL_LAYER_NAME}:${version_number} in ${region}" + aws lambda delete-layer-version \ + --region="${region}" \ + --layer-name="${FULL_LAYER_NAME}" \ + --version-number="${version_number}" + done +done + +mkdir -p "${AWS_FOLDER}" + +zip_file="./dist/${VERSION}-${GOOS}-${GOARCH}.zip" + +for region in $ALL_AWS_REGIONS; do + echo "Publish ${FULL_LAYER_NAME} in ${region}" + publish_output=$(aws lambda \ + --output json \ + publish-layer-version \ + --region="${region}" \ + --layer-name="${FULL_LAYER_NAME}" \ + --compatible-architectures="${ARCHITECTURE}" \ + --description="AWS Lambda Extension Layer for Elastic APM ${ARCHITECTURE}" \ + --license="Apache-2.0" \ + --zip-file="fileb://${zip_file}") + echo "${publish_output}" > "${AWS_FOLDER}/${region}" + layer_version=$(echo "${publish_output}" | jq '.Version') + echo "Grant public layer access ${FULL_LAYER_NAME}:${layer_version} in ${region}" + grant_access_output=$(aws lambda \ + --output json \ + add-layer-version-permission \ + --region="${region}" \ + --layer-name="${FULL_LAYER_NAME}" \ + --action="lambda:GetLayerVersion" \ + --principal='*' \ + --statement-id="${FULL_LAYER_NAME}" \ + --version-number="${layer_version}") + echo "${grant_access_output}" > "${AWS_FOLDER}/.${region}-public" +done + +sh -c "./.ci/create-arn-table.sh" diff --git a/.ci/push_docker.sh b/.ci/push_docker.sh deleted file mode 100755 index 61f2083c..00000000 --- a/.ci/push_docker.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -# This script is present on workers but may not be present in a development -# environment. - -if [ ${WORKSPACE+x} ] # We are on a CI worker -then - source /usr/local/bin/bash_standard_lib.sh -fi - -readonly RETRIES=3 - -readonly DOCKER_REGISTRY_URL="$1" - -readonly DOCKER_IMAGE_NAME="$2" - -readonly DOCKER_TAG="$3" - -readonly DOCKER_PUSH_IMAGE="$DOCKER_REGISTRY_URL/$DOCKER_IMAGE_NAME:$DOCKER_TAG" - -readonly DOCKER_PUSH_IMAGE_LATEST="$DOCKER_REGISTRY_URL/$DOCKER_IMAGE_NAME:latest" - -echo "INFO: Pushing image $DOCKER_PUSH_IMAGE to $DOCKER_REGISTRY_URL" - -if [ ${WORKERS+x} ] # We are on a CI worker -then - retry $RETRIES docker push $DOCKER_PUSH_IMAGE || echo "Push failed after $RETRIES retries" -else # We are in a local (non-CI) environment - docker push $DOCKER_PUSH_IMAGE || echo "You may need to run 'docker login' first and then re-run this script" -fi - -readonly LATEST_TAG=$(git tag --list --sort=version:refname "v*" | grep -v - | cut -c2- | tail -n1) - -if [ "$DOCKER_TAG" = "$LATEST_TAG" ] -then - echo "INFO: Current version ($DOCKER_TAG) is the latest version. Tagging and pushing $DOCKER_PUSH_IMAGE_LATEST ..." - docker tag $DOCKER_PUSH_IMAGE $DOCKER_PUSH_IMAGE_LATEST - - if [ ${WORKERS+x} ] # We are on a CI worker - then - retry $RETRIES docker push $DOCKER_PUSH_IMAGE_LATEST || echo "Push failed after $RETRIES retries" - else # We are in a local (non-CI) environment - docker push $DOCKER_PUSH_IMAGE_LATEST || echo "You may need to run 'docker login' first and then re-run this script" - fi -fi diff --git a/.ci/release-github.sh b/.ci/release-github.sh new file mode 100755 index 00000000..1193a2d6 --- /dev/null +++ b/.ci/release-github.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -euo pipefail + +# +# Collects all created ARN tables created by .ci/publish-aws.sh script +# and publishes a GitHub Release Note with the given information. +# + +export SUFFIX_ARN_FILE=${SUFFIX_ARN_FILE:-arn-file.md} +VERSION=${VERSION:?Please provide VERSION environment variable. e.g. current git tag} + +rm -rf "${SUFFIX_ARN_FILE}" + +cat ./*"-${SUFFIX_ARN_FILE}" >> "$SUFFIX_ARN_FILE" + +gh release \ + create "${VERSION}" \ + --title="${VERSION}" \ + --generate-notes \ + --notes-file="${SUFFIX_ARN_FILE}" \ + ./dist/${VERSION}*.zip + diff --git a/.github/workflows/opentelemetry.yml b/.github/workflows/opentelemetry.yml new file mode 100644 index 00000000..99b205c5 --- /dev/null +++ b/.github/workflows/opentelemetry.yml @@ -0,0 +1,23 @@ +--- +name: OpenTelemetry Export Trace + +on: + workflow_run: + workflows: + - Issue Labeler + - Add to obs-docs project + - Add to APM Project + - release + - test + - test-reporter + types: [completed] + +jobs: + otel-export-trace: + runs-on: ubuntu-latest + steps: + - uses: elastic/apm-pipeline-library/.github/actions/opentelemetry@current + with: + vaultUrl: ${{ secrets.VAULT_ADDR }} + vaultRoleId: ${{ secrets.VAULT_ROLE_ID }} + vaultSecretId: ${{ secrets.VAULT_SECRET_ID }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..f13436a2 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,75 @@ +name: release + +on: + push: + tags: + - v*.*.* + +env: + DOCKER_BUILDKIT: 1 + DOCKER_REGISTRY: docker.elastic.co + DOCKER_IMAGE_NAME: observability/apm-lambda-extension + +permissions: + contents: write + pull-requests: read + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - uses: actions/setup-go@v3 + with: + go-version-file: 'go.mod' + - uses: docker/setup-qemu-action@v2 + with: + platforms: linux/arm64, linux/amd64 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + - uses: elastic/apm-pipeline-library/.github/actions/docker-login@current + with: + registry: docker.elastic.co + secret: secret/observability-team/ci/docker-registry/prod + url: ${{ secrets.VAULT_ADDR }} + roleId: ${{ secrets.VAULT_ROLE_ID }} + secretId: ${{ secrets.VAULT_SECRET_ID }} + - uses: hashicorp/vault-action@v2.4.2 + with: + url: ${{ secrets.VAULT_ADDR }} + method: approle + roleId: ${{ secrets.VAULT_ROLE_ID }} + secretId: ${{ secrets.VAULT_SECRET_ID }} + secrets: | + secret/observability-team/ci/service-account/apm-aws-lambda access_key_id | AWS_ACCESS_KEY_ID ; + secret/observability-team/ci/service-account/apm-aws-lambda secret_access_key | AWS_SECRET_ACCESS_KEY + - name: Release + run: make release + - name: GitHub Release + run: make release-notes + env: + VERSION: ${{ github.ref_name }} + + - if: ${{ success() }} + uses: elastic/apm-pipeline-library/.github/actions/slack-message@current + with: + url: ${{ secrets.VAULT_ADDR }} + roleId: ${{ secrets.VAULT_ROLE_ID }} + secretId: ${{ secrets.VAULT_SECRET_ID }} + channel: "#apm-aws-lambda" + message: | + :large_green_circle: [${{ github.repository }}] Release *${{ github.ref_name }}* published." + Build: (<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|here>) + + - if: ${{ failure() }} + uses: elastic/apm-pipeline-library/.github/actions/slack-message@current + with: + url: ${{ secrets.VAULT_ADDR }} + roleId: ${{ secrets.VAULT_ROLE_ID }} + secretId: ${{ secrets.VAULT_SECRET_ID }} + channel: "#apm-aws-lambda" + message: | + :large_yellow_circle: [${{ github.repository }}] Release *${{ github.ref_name }}* could not be published." + Build: (<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|here>) diff --git a/.github/workflows/test-reporter.yml b/.github/workflows/test-reporter.yml new file mode 100644 index 00000000..33e13517 --- /dev/null +++ b/.github/workflows/test-reporter.yml @@ -0,0 +1,20 @@ +--- +## Workflow to process the JUnit test results and add a report to the checks. +name: test-reporter +on: + workflow_run: + workflows: + - test + types: + - completed + +jobs: + report: + runs-on: ubuntu-latest + steps: + - uses: elastic/apm-pipeline-library/.github/actions/test-report@current + with: + artifact: test-results # artifact name + name: JUnit Tests # Name of the check run which will be created + path: "*-junit-report.xml" # Path to test results (inside artifact .zip) + reporter: java-junit # Format of test results diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..c829af8d --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,65 @@ +name: test + +on: + push: + branches: + - main + pull_request: + +permissions: + contents: read + +jobs: + test: + strategy: + fail-fast: false + matrix: + platform: + - "ubuntu-latest" + - "macos-latest" + runs-on: ${{ matrix.platform }} + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v3 + with: + go-version-file: 'go.mod' + - name: Test + run: make test junitfile="${{ matrix.platform }}-junit-report.xml" + - uses: actions/upload-artifact@v3 + if: success() || failure() + with: + name: test-results + path: '*-junit-report.xml' + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v3 + with: + go-version-file: 'go.mod' + - uses: docker/setup-qemu-action@v2 + with: + platforms: linux/arm64, linux/amd64 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + - name: Build + run: make dist + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v3 + with: + go-version-file: 'go.mod' + - run: make lint-prep + - run: make lint + - run: go vet + notice: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: notice + run: make check-notice + + + diff --git a/.gitignore b/.gitignore index c0f0ed29..705eddae 100644 --- a/.gitignore +++ b/.gitignore @@ -20,11 +20,14 @@ # Dependency directories (remove the comment below to include it) # vendor/ -# AWS regions file -.regions -.aws/ - bin/ # local docs -html_docs \ No newline at end of file +html_docs + +# temporary release files +dist/ +.tmp +.aws-*/ +*arn-file.md + diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 00000000..2bc43fcf --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,92 @@ +project_name: apm-lambda-extension + +before: + hooks: + - make check-licenses + - make NOTICE.txt + +builds: + - env: + - CGO_ENABLED=0 + goos: + - linux + goarch: + - amd64 + - arm64 + flags: + - -trimpath + ldflags: + - -s -w + binary: "extensions/{{ .ProjectName }}" + mod_timestamp: '{{ .CommitTimestamp }}' + +archives: + - id: zip + format: zip + name_template: |- + {{ .Tag }}-{{ .Os }}-{{ .Arch }} + files: + - src: NOTICE.txt + info: + mtime: '{{ .CommitDate }}' + - src: dependencies.asciidoc + info: + mtime: '{{ .CommitDate }}' + builds_info: + mtime: "{{ .CommitTimestamp }}" + rlcp: true # https://goreleaser.com/deprecations/#archivesrlcp + +dockers: + + - &default-docker-image + id: linux-amd64-image + use: buildx + goos: linux + goarch: amd64 + image_templates: + - '{{ .Env.DOCKER_REGISTRY }}/{{ .Env.DOCKER_IMAGE_NAME }}-x86_64:{{ trimprefix .Tag "v" }}' + - "{{ .Env.DOCKER_REGISTRY }}/{{ .Env.DOCKER_IMAGE_NAME }}-x86_64:latest" + build_flag_templates: + - "--platform=linux/amd64" + - "--build-arg=EXTENSION_FILE={{ .ProjectName }}" + - "--build-arg=COMMIT_TIMESTAMP={{ .CommitTimestamp }}" + - "--label=org.opencontainers.image.created={{ .Date }}" + - "--label=org.opencontainers.image.title={{ .ProjectName }}" + - "--label=org.opencontainers.image.revision={{ .FullCommit }}" + - "--label=org.opencontainers.image.version={{ .Version }}" + extra_files: + - NOTICE.txt + - dependencies.asciidoc + + - <<: *default-docker-image + id: linux-arm64-image + goarch: arm64 + image_templates: + - '{{ .Env.DOCKER_REGISTRY }}/{{ .Env.DOCKER_IMAGE_NAME }}-arm64:{{ trimprefix .Tag "v" }}' + - "{{ .Env.DOCKER_REGISTRY }}/{{ .Env.DOCKER_IMAGE_NAME }}-arm64:latest" + build_flag_templates: + - "--platform=linux/arm64" + - "--build-arg=EXTENSION_FILE={{ .ProjectName }}" + - "--build-arg=COMMIT_TIMESTAMP={{ .CommitTimestamp }}" + - "--label=org.opencontainers.image.created={{ .Date }}" + - "--label=org.opencontainers.image.title={{ .ProjectName }}" + - "--label=org.opencontainers.image.revision={{ .FullCommit }}" + - "--label=org.opencontainers.image.version={{ .Version }}" + +publishers: + - name: publish-aws + cmd: ./.ci/publish-aws.sh + env: + - AWS_ACCESS_KEY_ID={{ .Env.AWS_ACCESS_KEY_ID }} + - AWS_SECRET_ACCESS_KEY={{ .Env.AWS_SECRET_ACCESS_KEY }} + - AWS_SECURITY_TOKEN={{ .Env.AWS_SECURITY_TOKEN }} + - AWS_SESSION_TOKEN={{ .Env.AWS_SESSION_TOKEN }} + - ELASTIC_LAYER_NAME=elastic-apm-extension-ver-{{ replace (trimprefix .Tag "v") "." "-" }} + - VERSION={{ .Tag }} + - ARCHITECTURE={{ if eq .Arch "amd64" }}x86_64{{ else }}{{ .Arch }}{{ end }} + - GOOS={{ .Os }} + - GOARCH={{ .Arch }} + - AWS_FOLDER=.aws-{{ .Os }}-{{ .Arch }} +release: + # Custom GitHub release + disable: true diff --git a/Dockerfile b/Dockerfile index 3963dfbb..85f4d049 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,12 @@ # Pin to Alpine 3.16.2 # For a complete list of hashes, see: # https://github.com/docker-library/repo-info/tree/master/repos/alpine/remote -FROM alpine@sha256:bc41182d7ef5ffc53a40b044e725193bc10142a1243f395ee852a8d9730fc2ad +FROM --platform=$BUILDPLATFORM alpine@sha256:b95359c2505145f16c6aa384f9cc74eeff78eb36d308ca4fd902eeeb0a0b161b ARG EXTENSION_FILE +ARG COMMIT_TIMESTAMP COPY ${EXTENSION_FILE} /opt/elastic-apm-extension -COPY /NOTICE.txt /opt/NOTICE.txt -COPY /dependencies.asciidoc /opt/dependencies.asciidoc +COPY NOTICE.txt dependencies.asciidoc /opt/ + +# Related to reproducible builds +RUN find /opt -exec touch -am -d $(date -u -d @"${COMMIT_TIMESTAMP}" "+%Y%m%d%H%M.%S") -t $(date -u -d @"${COMMIT_TIMESTAMP}" "+%Y%m%d%H%M.%S") {} \; + diff --git a/Makefile b/Makefile index 5cf4d7bc..4e26a4fb 100644 --- a/Makefile +++ b/Makefile @@ -1,194 +1,57 @@ SHELL = /bin/bash -eo pipefail -AWS_FOLDER = .aws -DOCKER_IMAGE_NAME = observability/apm-lambda-extension -DOCKER_REGISTRY = docker.elastic.co -AGENT_VERSION = $(shell echo $${BRANCH_NAME} | cut -f 2 -d 'v') - -# Add support for SOURCE_DATE_EPOCH and reproducble buils -# See https://reproducible-builds.org/specs/source-date-epoch/ -SOURCE_DATE_EPOCH ?= $(shell git log -1 --pretty=%ct) -DATE_FMT = +%Y%m%d%H%M.%S -# Fallback mechanism to support other systems: -# 1. 'date -d': Busybox and GNU coreutils. -# 2. 'date -r': BSD date. It does not support '-d'. -BUILD_DATE = $(shell date -u -d "@${SOURCE_DATE_EPOCH}" "${DATE_FMT}" 2>/dev/null || date -u -r "${SOURCE_DATE_EPOCH}" "${DATE_FMT}") - -GO_BUILDFLAGS ?= -trimpath -ldflags="-s -w" - -ifndef GOARCH - GOARCH=amd64 -endif - -# Transform GOARCH into the architecture of the extension layer -ifeq ($(GOARCH),amd64) - ARCHITECTURE=x86_64 -else - ARCHITECTURE=arm64 -endif - -export AWS_FOLDER GOARCH ARCHITECTURE DOCKER_IMAGE_NAME DOCKER_REGISTRY - -.PHONY: all -all: build +GORELEASER_VERSION = "v1.14.1" +GO_LICENSER_VERSION = "v0.4.0" +GOLANGCI_LINT_VERSION = "v1.48.0" +export DOCKER_IMAGE_NAME = observability/apm-lambda-extension +export DOCKER_REGISTRY = docker.elastic.co + +clean: + @rm -rf dist/ + @docker image ls "$(DOCKER_REGISTRY)/$(DOCKER_IMAGE_NAME)*" -aq | xargs -I {} docker rmi --force {} || true + +dist: + @go run github.com/goreleaser/goreleaser@$(GORELEASER_VERSION) release --snapshot --rm-dist + +.PHONY: zip +zip: + @go run github.com/goreleaser/goreleaser@$(GORELEASER_VERSION) release --snapshot --rm-dist --skip-docker + +.PHONY: release +release: + go run github.com/goreleaser/goreleaser@$(GORELEASER_VERSION) release --rm-dist + +.PHONY: release-notes +release-notes: + @./.ci/release-github.sh + +.PHONY: test +test: + @go run gotest.tools/gotestsum@v1.9.0 --format testname --junitfile $(junitfile) + +.PHONY: lint-prep +lint-prep: + @go mod tidy && git diff --exit-code + +.PHONY: lint +lint: + @go run github.com/golangci/golangci-lint/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION) version + @go run github.com/golangci/golangci-lint/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION) run NOTICE.txt: go.mod @bash ./scripts/notice.sh +.PHONY: check-linceses check-licenses: - go run github.com/elastic/go-licenser@v0.4.0 -d -exclude tf -exclude testing . - go run github.com/elastic/go-licenser@v0.4.0 -d -exclude tf -exclude testing -ext .java . - go run github.com/elastic/go-licenser@v0.4.0 -d -exclude tf -exclude testing -ext .js . + @go run github.com/elastic/go-licenser@$(GO_LICENSER_VERSION) -d -exclude tf -exclude testing . + @go run github.com/elastic/go-licenser@$(GO_LICENSER_VERSION) -d -exclude tf -exclude testing -ext .java . + @go run github.com/elastic/go-licenser@$(GO_LICENSER_VERSION) -d -exclude tf -exclude testing -ext .js . -update-licenses: - go run github.com/elastic/go-licenser@v0.4.0 -exclude tf -exclude testing . - go run github.com/elastic/go-licenser@v0.4.0 -exclude tf -exclude testing -ext .java . - go run github.com/elastic/go-licenser@v0.4.0 -exclude tf -exclude testing -ext .js . -lint: - go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.48.0 version - go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.48.0 run - -build: check-licenses NOTICE.txt - CGO_ENABLED=0 GOOS=linux go build ${GO_BUILDFLAGS} -o bin/extensions/apm-lambda-extension main.go - cp NOTICE.txt bin/NOTICE.txt - cp dependencies.asciidoc bin/dependencies.asciidoc - -build-and-publish: check-licenses validate-layer-name validate-aws-default-region -ifndef AWS_ACCESS_KEY_ID - $(error AWS_ACCESS_KEY_ID is undefined) -endif -ifndef AWS_SECRET_ACCESS_KEY - $(error AWS_SECRET_ACCESS_KEY is undefined) -endif - GOARCH=${GOARCH} make build - GOARCH=${GOARCH} make zip - $(MAKE) publish - -zip: build - cd bin \ - && rm -f extension.zip \ - && find extensions NOTICE.txt dependencies.asciidoc | xargs touch -t ${BUILD_DATE} \ - && zip -X -r extension.zip extensions NOTICE.txt dependencies.asciidoc \ - && cp extension.zip ${GOARCH}.zip - -test: - go test extension/*.go -v - -env: - env - -dist: validate-branch-name test zip - @cp ./bin/$(GOARCH).zip bin/$(BRANCH_NAME)-linux-$(GOARCH).zip - -build-docker: validate-version - docker build -t $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_NAME)-$(ARCHITECTURE):$(AGENT_VERSION) \ - --build-arg EXTENSION_FILE=bin/extensions/apm-lambda-extension . - -push-docker: build-docker - @./.ci/push_docker.sh $(DOCKER_REGISTRY) "$(DOCKER_IMAGE_NAME)-$(ARCHITECTURE)" $(AGENT_VERSION) - -# List all the AWS regions -get-all-aws-regions: - @aws \ - ec2 \ - describe-regions \ - --region us-east-1 \ - --output json \ - --no-cli-pager \ - | jq -r '.Regions[].RegionName' > .regions - -# Publish the given LAYER in all the AWS regions -publish-in-all-aws-regions: validate-layer-name get-all-aws-regions - @mkdir -p $(AWS_FOLDER) - @while read AWS_DEFAULT_REGION; do \ - echo "publish '$(ELASTIC_LAYER_NAME)-$(ARCHITECTURE)' in $${AWS_DEFAULT_REGION}"; \ - AWS_DEFAULT_REGION="$${AWS_DEFAULT_REGION}" ELASTIC_LAYER_NAME=$(ELASTIC_LAYER_NAME) $(MAKE) publish > $(AWS_FOLDER)/$${AWS_DEFAULT_REGION}; \ - AWS_DEFAULT_REGION="$${AWS_DEFAULT_REGION}" ELASTIC_LAYER_NAME=$(ELASTIC_LAYER_NAME) $(MAKE) grant-public-layer-access; \ - done <.regions - -# Publish the given LAYER in the given AWS region -publish: validate-layer-name validate-aws-default-region - @aws lambda \ - --output json \ - publish-layer-version \ - --layer-name "$(ELASTIC_LAYER_NAME)-$(ARCHITECTURE)" \ - --description "AWS Lambda Extension Layer for Elastic APM $(ARCHITECTURE)" \ - --license "Apache-2.0" \ - --zip-file "fileb://./bin/extension.zip" - -# Delete the given LAYER in all the AWS regions -delete-in-all-aws-regions: validate-layer-name get-all-aws-regions - @mkdir -p $(AWS_FOLDER) - @while read AWS_DEFAULT_REGION; do \ - echo "delete '$(ELASTIC_LAYER_NAME)-$(ARCHITECTURE)' in $${AWS_DEFAULT_REGION}"; \ - AWS_DEFAULT_REGION="$${AWS_DEFAULT_REGION}" ELASTIC_LAYER_NAME=$(ELASTIC_LAYER_NAME) $(MAKE) delete; \ - done <.regions - -# Delete the given LAYER in the given AWS region, it won't fail -delete: validate-layer-name validate-aws-default-region - @aws lambda \ - delete-layer-version \ - --layer-name "$(ELASTIC_LAYER_NAME)-$(ARCHITECTURE)" \ - --version-number 1 || echo "delete-layer-version $(ELASTIC_LAYER_NAME)-$(ARCHITECTURE) for $${AWS_DEFAULT_REGION} could not be found" - -# Grant public access to the given LAYER in the given AWS region -grant-public-layer-access: validate-layer-name validate-aws-default-region - @echo "[debug] $(ELASTIC_LAYER_NAME)-$(ARCHITECTURE) with version: $$($(MAKE) -s --no-print-directory get-version)" - @aws lambda \ - --output json \ - add-layer-version-permission \ - --layer-name "$(ELASTIC_LAYER_NAME)-$(ARCHITECTURE)" \ - --action lambda:GetLayerVersion \ - --principal '*' \ - --statement-id "$(ELASTIC_LAYER_NAME)-$(ARCHITECTURE)" \ - --version-number $$($(MAKE) -s --no-print-directory get-version) > $(AWS_FOLDER)/.$(AWS_DEFAULT_REGION)-public - -# Get the ARN Version for the AWS_REGIONS -# NOTE: jq -r .Version "$(AWS_FOLDER)/$(AWS_DEFAULT_REGION)" fails in the CI -# with 'parse error: Invalid numeric literal at line 1, column 5' -get-version: validate-aws-default-region - @grep '"Version"' "$(AWS_FOLDER)/$(AWS_DEFAULT_REGION)" | cut -d":" -f2 | sed 's/ //g' | cut -d"," -f1 - -# Generate the file with the ARN entries -create-arn-file: validate-suffix-arn-file - @./.ci/create-arn-table.sh - -release-notes: validate-branch-name validate-suffix-arn-file - @gh release list - cat *-$(SUFFIX_ARN_FILE) > $(SUFFIX_ARN_FILE) - @gh \ - release \ - create $(BRANCH_NAME) \ - --title '$(BRANCH_NAME)' \ - --generate-notes \ - --notes-file $(SUFFIX_ARN_FILE) \ - ./bin/$(BRANCH_NAME)*.zip - -validate-version: -ifndef AGENT_VERSION - $(error AGENT_VERSION is undefined) -endif - -validate-branch-name: -ifndef BRANCH_NAME - $(error BRANCH_NAME is undefined) -endif - -validate-suffix-arn-file: -ifndef SUFFIX_ARN_FILE - $(error SUFFIX_ARN_FILE is undefined) -endif - -validate-layer-name: -ifndef ELASTIC_LAYER_NAME - $(error ELASTIC_LAYER_NAME is undefined) -endif - -validate-aws-default-region: -ifndef AWS_DEFAULT_REGION - $(error AWS_DEFAULT_REGION is undefined) -endif +.PHONY: check-notice +check-notice: + $(MAKE) NOTICE.txt + @git diff --exit-code --quiet NOTICE.txt && exit 0 || echo "regenerate NOTICE.txt" && exit 1 ############################################################################## # Smoke tests -- Basic smoke tests for the APM Lambda extension diff --git a/apmproxy/receiver_test.go b/apmproxy/receiver_test.go index f8828725..9cdc5567 100644 --- a/apmproxy/receiver_test.go +++ b/apmproxy/receiver_test.go @@ -195,6 +195,8 @@ func Test_handleInfoRequest(t *testing.T) { req.Header.Add(name, value) } + time.Sleep(5 * time.Second) + // Send the request to the extension client := &http.Client{} resp, err := client.Do(req) diff --git a/scripts/notice.sh b/scripts/notice.sh old mode 100644 new mode 100755