name: Test and Deploy on: push: branches: - main - release pull_request: concurrency: # Allow only one workflow per any non-`main` branch. group: ${{ github.workflow }}-${{ github.ref }}-${{ github.ref == 'refs/heads/main' && github.sha || 'anysha' }} cancel-in-progress: true env: RUST_BACKTRACE: 1 COPT: '-Werror' jobs: tag: runs-on: dev container: 369495373322.dkr.ecr.eu-central-1.amazonaws.com/base:latest outputs: build-tag: ${{steps.build-tag.outputs.tag}} steps: - name: Checkout uses: actions/checkout@v3 with: fetch-depth: 0 - name: Get build tag run: | echo run:$GITHUB_RUN_ID echo ref:$GITHUB_REF_NAME echo rev:$(git rev-list --count HEAD) if [[ "$GITHUB_REF_NAME" == "main" ]]; then echo "::set-output name=tag::$(git rev-list --count HEAD)" elif [[ "$GITHUB_REF_NAME" == "release" ]]; then echo "::set-output name=tag::release-$(git rev-list --count HEAD)" else echo "GITHUB_REF_NAME (value '$GITHUB_REF_NAME') is not set to either 'main' or 'release'" echo "::set-output name=tag::$GITHUB_RUN_ID" fi shell: bash id: build-tag build-neon: runs-on: dev container: image: 369495373322.dkr.ecr.eu-central-1.amazonaws.com/rust:pinned options: --init strategy: fail-fast: false matrix: build_type: [ debug, release ] rust_toolchain: [ 1.58 ] env: BUILD_TYPE: ${{ matrix.build_type }} GIT_VERSION: ${{ github.sha }} steps: - name: Fix git ownership run: | # Workaround for `fatal: detected dubious ownership in repository at ...` # # Use both ${{ github.workspace }} and ${GITHUB_WORKSPACE} because they're different on host and in containers # Ref https://github.com/actions/checkout/issues/785 # git config --global --add safe.directory ${{ github.workspace }} git config --global --add safe.directory ${GITHUB_WORKSPACE} - name: Checkout uses: actions/checkout@v3 with: submodules: true fetch-depth: 1 - name: Set pg revision for caching id: pg_ver run: echo ::set-output name=pg_rev::$(git rev-parse HEAD:vendor/postgres) shell: bash -euxo pipefail {0} # Set some environment variables used by all the steps. # # CARGO_FLAGS is extra options to pass to "cargo build", "cargo test" etc. # It also includes --features, if any # # CARGO_FEATURES is passed to "cargo metadata". It is separate from CARGO_FLAGS, # because "cargo metadata" doesn't accept --release or --debug options # - name: Set env variables run: | if [[ $BUILD_TYPE == "debug" ]]; then cov_prefix="scripts/coverage --profraw-prefix=$GITHUB_JOB --dir=/tmp/coverage run" CARGO_FEATURES="" CARGO_FLAGS="" elif [[ $BUILD_TYPE == "release" ]]; then cov_prefix="" CARGO_FEATURES="--features profiling" CARGO_FLAGS="--release $CARGO_FEATURES" fi echo "cov_prefix=${cov_prefix}" >> $GITHUB_ENV echo "CARGO_FEATURES=${CARGO_FEATURES}" >> $GITHUB_ENV echo "CARGO_FLAGS=${CARGO_FLAGS}" >> $GITHUB_ENV shell: bash -euxo pipefail {0} # Don't include the ~/.cargo/registry/src directory. It contains just # uncompressed versions of the crates in ~/.cargo/registry/cache # directory, and it's faster to let 'cargo' to rebuild it from the # compressed crates. - name: Cache cargo deps id: cache_cargo uses: actions/cache@v3 with: path: | ~/.cargo/registry/ !~/.cargo/registry/src ~/.cargo/git/ target/ # Fall back to older versions of the key, if no cache for current Cargo.lock was found key: | v6-${{ runner.os }}-${{ matrix.build_type }}-cargo-${{ matrix.rust_toolchain }}-${{ hashFiles('Cargo.lock') }} v6-${{ runner.os }}-${{ matrix.build_type }}-cargo-${{ matrix.rust_toolchain }}- - name: Cache postgres build id: cache_pg uses: actions/cache@v3 with: path: tmp_install/ key: v1-${{ runner.os }}-${{ matrix.build_type }}-pg-${{ steps.pg_ver.outputs.pg_rev }}-${{ hashFiles('Makefile') }} - name: Build postgres if: steps.cache_pg.outputs.cache-hit != 'true' run: mold -run make postgres -j$(nproc) shell: bash -euxo pipefail {0} - name: Run cargo build run: | ${cov_prefix} mold -run cargo build $CARGO_FLAGS --features failpoints --bins --tests shell: bash -euxo pipefail {0} - name: Run cargo test run: | ${cov_prefix} cargo test $CARGO_FLAGS shell: bash -euxo pipefail {0} - name: Install rust binaries run: | # Install target binaries mkdir -p /tmp/neon/bin/ binaries=$( ${cov_prefix} cargo metadata $CARGO_FEATURES --format-version=1 --no-deps | jq -r '.packages[].targets[] | select(.kind | index("bin")) | .name' ) for bin in $binaries; do SRC=target/$BUILD_TYPE/$bin DST=/tmp/neon/bin/$bin cp "$SRC" "$DST" done # Install test executables and write list of all binaries (for code coverage) if [[ $BUILD_TYPE == "debug" ]]; then # Keep bloated coverage data files away from the rest of the artifact mkdir -p /tmp/coverage/ mkdir -p /tmp/neon/test_bin/ test_exe_paths=$( ${cov_prefix} cargo test $CARGO_FLAGS --message-format=json --no-run | jq -r '.executable | select(. != null)' ) for bin in $test_exe_paths; do SRC=$bin DST=/tmp/neon/test_bin/$(basename $bin) # We don't need debug symbols for code coverage, so strip them out to make # the artifact smaller. strip "$SRC" -o "$DST" echo "$DST" >> /tmp/coverage/binaries.list done for bin in $binaries; do echo "/tmp/neon/bin/$bin" >> /tmp/coverage/binaries.list done fi shell: bash -euxo pipefail {0} - name: Install postgres binaries run: cp -a tmp_install /tmp/neon/pg_install shell: bash -euxo pipefail {0} - name: Upload Neon artifact uses: ./.github/actions/upload with: name: neon-${{ runner.os }}-${{ matrix.build_type }}-${{ matrix.rust_toolchain }}-artifact path: /tmp/neon # XXX: keep this after the binaries.list is formed, so the coverage can properly work later - name: Merge and upload coverage data if: matrix.build_type == 'debug' uses: ./.github/actions/save-coverage-data pg_regress-tests: runs-on: dev container: image: 369495373322.dkr.ecr.eu-central-1.amazonaws.com/rust:pinned options: --init needs: [ build-neon ] strategy: fail-fast: false matrix: build_type: [ debug, release ] rust_toolchain: [ 1.58 ] steps: - name: Checkout uses: actions/checkout@v3 with: submodules: true fetch-depth: 2 - name: Pytest regress tests uses: ./.github/actions/run-python-test-set with: build_type: ${{ matrix.build_type }} rust_toolchain: ${{ matrix.rust_toolchain }} test_selection: batch_pg_regress needs_postgres_source: true - name: Merge and upload coverage data if: matrix.build_type == 'debug' uses: ./.github/actions/save-coverage-data other-tests: runs-on: dev container: image: 369495373322.dkr.ecr.eu-central-1.amazonaws.com/rust:pinned options: --init needs: [ build-neon ] strategy: fail-fast: false matrix: build_type: [ debug, release ] rust_toolchain: [ 1.58 ] steps: - name: Checkout uses: actions/checkout@v3 with: submodules: true fetch-depth: 2 - name: Pytest other tests uses: ./.github/actions/run-python-test-set with: build_type: ${{ matrix.build_type }} rust_toolchain: ${{ matrix.rust_toolchain }} test_selection: batch_others run_with_real_s3: true real_s3_bucket: ci-tests-s3 real_s3_region: us-west-2 real_s3_access_key_id: "${{ secrets.AWS_ACCESS_KEY_ID_CI_TESTS_S3 }}" real_s3_secret_access_key: "${{ secrets.AWS_SECRET_ACCESS_KEY_CI_TESTS_S3 }}" - name: Merge and upload coverage data if: matrix.build_type == 'debug' uses: ./.github/actions/save-coverage-data benchmarks: runs-on: dev container: image: 369495373322.dkr.ecr.eu-central-1.amazonaws.com/rust:pinned options: --init needs: [ build-neon ] if: github.ref_name == 'main' || contains(github.event.pull_request.labels.*.name, 'run-benchmarks') strategy: fail-fast: false matrix: build_type: [ release ] rust_toolchain: [ 1.58 ] steps: - name: Checkout uses: actions/checkout@v3 with: submodules: true fetch-depth: 2 - name: Pytest benchmarks uses: ./.github/actions/run-python-test-set with: build_type: ${{ matrix.build_type }} rust_toolchain: ${{ matrix.rust_toolchain }} test_selection: performance run_in_parallel: false save_perf_report: true env: VIP_VAP_ACCESS_TOKEN: "${{ secrets.VIP_VAP_ACCESS_TOKEN }}" PERF_TEST_RESULT_CONNSTR: "${{ secrets.PERF_TEST_RESULT_CONNSTR }}" # XXX: no coverage data handling here, since benchmarks are run on release builds, # while coverage is currently collected for the debug ones coverage-report: runs-on: dev container: image: 369495373322.dkr.ecr.eu-central-1.amazonaws.com/rust:pinned options: --init needs: [ other-tests, pg_regress-tests ] strategy: fail-fast: false matrix: build_type: [ debug ] rust_toolchain: [ 1.58 ] steps: - name: Checkout uses: actions/checkout@v3 with: submodules: true fetch-depth: 1 - name: Restore cargo deps cache id: cache_cargo uses: actions/cache@v3 with: path: | ~/.cargo/registry/ !~/.cargo/registry/src ~/.cargo/git/ target/ key: v5-${{ runner.os }}-${{ matrix.build_type }}-cargo-${{ matrix.rust_toolchain }}-${{ hashFiles('Cargo.lock') }} - name: Get Neon artifact uses: ./.github/actions/download with: name: neon-${{ runner.os }}-${{ matrix.build_type }}-${{ matrix.rust_toolchain }}-artifact path: /tmp/neon - name: Get coverage artifact uses: ./.github/actions/download with: name: coverage-data-artifact path: /tmp/coverage - name: Merge coverage data run: scripts/coverage "--profraw-prefix=$GITHUB_JOB" --dir=/tmp/coverage merge shell: bash -euxo pipefail {0} - name: Build and upload coverage report run: | COMMIT_SHA=${{ github.event.pull_request.head.sha }} COMMIT_SHA=${COMMIT_SHA:-${{ github.sha }}} COMMIT_URL=https://github.com/${{ github.repository }}/commit/$COMMIT_SHA scripts/coverage \ --dir=/tmp/coverage report \ --input-objects=/tmp/coverage/binaries.list \ --commit-url=$COMMIT_URL \ --format=github REPORT_URL=https://${{ github.repository_owner }}.github.io/zenith-coverage-data/$COMMIT_SHA scripts/git-upload \ --repo=https://${{ secrets.VIP_VAP_ACCESS_TOKEN }}@github.com/${{ github.repository_owner }}/zenith-coverage-data.git \ --message="Add code coverage for $COMMIT_URL" \ copy /tmp/coverage/report $COMMIT_SHA # COPY FROM TO_RELATIVE # Add link to the coverage report to the commit curl -f -X POST \ https://api.github.com/repos/${{ github.repository }}/statuses/$COMMIT_SHA \ -H "Accept: application/vnd.github.v3+json" \ --user "${{ secrets.CI_ACCESS_TOKEN }}" \ --data \ "{ \"state\": \"success\", \"context\": \"neon-coverage\", \"description\": \"Coverage report is ready\", \"target_url\": \"$REPORT_URL\" }" shell: bash -euxo pipefail {0} trigger-e2e-tests: runs-on: dev container: image: 369495373322.dkr.ecr.eu-central-1.amazonaws.com/rust:pinned options: --init needs: [ build-neon ] steps: - name: Set PR's status to pending and request a remote CI test run: | COMMIT_SHA=${{ github.event.pull_request.head.sha }} COMMIT_SHA=${COMMIT_SHA:-${{ github.sha }}} REMOTE_REPO="${{ github.repository_owner }}/cloud" curl -f -X POST \ https://api.github.com/repos/${{ github.repository }}/statuses/$COMMIT_SHA \ -H "Accept: application/vnd.github.v3+json" \ --user "${{ secrets.CI_ACCESS_TOKEN }}" \ --data \ "{ \"state\": \"pending\", \"context\": \"neon-cloud-e2e\", \"description\": \"[$REMOTE_REPO] Remote CI job is about to start\" }" curl -f -X POST \ https://api.github.com/repos/$REMOTE_REPO/actions/workflows/testing.yml/dispatches \ -H "Accept: application/vnd.github.v3+json" \ --user "${{ secrets.CI_ACCESS_TOKEN }}" \ --data \ "{ \"ref\": \"main\", \"inputs\": { \"ci_job_name\": \"neon-cloud-e2e\", \"commit_hash\": \"$COMMIT_SHA\", \"remote_repo\": \"${{ github.repository }}\" } }" dockerfile-check: if: github.event_name != 'workflow_dispatch' runs-on: dev container: 369495373322.dkr.ecr.eu-central-1.amazonaws.com/base:latest outputs: value: ${{ steps.dockerfile-check.outputs.any_changed }} steps: - name: Checkout uses: actions/checkout@v3 - name: Get specific changed files id: dockerfile-check uses: tj-actions/changed-files@802732316a11c01531ea72773ec7998155238e31 # v25 with: files: | Dockerfile Dockerfile.compute-tools ./vendor/postgres/Dockerfile neon-image: # force building for all 3 images if: needs.dockerfile-check.outputs.value != 'true' runs-on: dev needs: [ dockerfile-check ] container: gcr.io/kaniko-project/executor:v1.9.0-debug environment: dev steps: - name: Checkout uses: actions/checkout@v1 # v3 won't work with kaniko with: submodules: true fetch-depth: 0 - name: Configure ECR login run: echo "{\"credsStore\":\"ecr-login\"}" > /kaniko/.docker/config.json - name: Kaniko build console run: /kaniko/executor --snapshotMode=redo --cache=true --cache-repo 369495373322.dkr.ecr.eu-central-1.amazonaws.com/cache --snapshotMode=redo --context . --destination 369495373322.dkr.ecr.eu-central-1.amazonaws.com/neon:$GITHUB_RUN_ID compute-tools-image: if: needs.dockerfile-check.outputs.value != 'true' runs-on: dev needs: [ dockerfile-check ] container: gcr.io/kaniko-project/executor:v1.9.0-debug environment: dev steps: - name: Checkout uses: actions/checkout@v1 # v3 won't work with kaniko - name: Configure ECR login run: echo "{\"credsStore\":\"ecr-login\"}" > /kaniko/.docker/config.json - name: Kaniko build console run: /kaniko/executor --snapshotMode=redo --cache=true --cache-repo 369495373322.dkr.ecr.eu-central-1.amazonaws.com/cache --snapshotMode=redo --context . --dockerfile Dockerfile.compute-tools --destination 369495373322.dkr.ecr.eu-central-1.amazonaws.com/compute-tools:$GITHUB_RUN_ID compute-node-image: if: needs.dockerfile-check.outputs.value != 'true' runs-on: dev needs: [ dockerfile-check ] container: gcr.io/kaniko-project/executor:v1.9.0-debug environment: dev steps: - name: Checkout uses: actions/checkout@v1 # v3 won't work with kaniko with: submodules: true fetch-depth: 0 - name: Configure ECR login run: echo "{\"credsStore\":\"ecr-login\"}" > /kaniko/.docker/config.json - name: Kaniko build console working-directory: ./vendor/postgres/ run: /kaniko/executor --snapshotMode=redo --cache=true --cache-repo 369495373322.dkr.ecr.eu-central-1.amazonaws.com/cache --snapshotMode=redo --context . --destination 369495373322.dkr.ecr.eu-central-1.amazonaws.com/compute-node:$GITHUB_RUN_ID promote-images: runs-on: dev needs: [ neon-image, compute-tools-image, compute-node-image ] if: github.event_name != 'workflow_dispatch' container: amazon/aws-cli strategy: fail-fast: false matrix: name: [ neon, compute-tools, compute-node ] steps: - name: Promote image to latest run: MANIFEST=$(aws ecr batch-get-image --repository-name ${{ matrix.name }} --image-ids imageTag=$GITHUB_RUN_ID --query 'images[].imageManifest' --output text) && aws ecr put-image --repository-name ${{ matrix.name }} --image-tag latest --image-manifest "$MANIFEST" push-docker-hub: runs-on: dev needs: [ promote-images, tag ] container: golang:1.19-bullseye environment: dev steps: - name: Install Crane & ECR helper run: | go install github.com/google/go-containerregistry/cmd/crane@31786c6cbb82d6ec4fb8eb79cd9387905130534e # v0.11.0 go install github.com/awslabs/amazon-ecr-credential-helper/ecr-login/cli/docker-credential-ecr-login@69c85dc22db6511932bbf119e1a0cc5c90c69a7f # v0.6.0 # - name: Get build tag # run: | # if [[ "$GITHUB_REF_NAME" == "main" ]]; then # echo "::set-output name=tag::$(git rev-list --count HEAD)" # elif [[ "$GITHUB_REF_NAME" == "release" ]]; then # echo "::set-output name=tag::release-$(git rev-list --count HEAD)" # else # echo "GITHUB_REF_NAME (value '$GITHUB_REF_NAME') is not set to either 'main' or 'release' " # echo "::set-output name=tag::$GITHUB_RUN_ID" # fi # id: build-tag - name: Configure ECR login run: | mkdir /github/home/.docker/ echo "{\"credsStore\":\"ecr-login\"}" > /github/home/.docker/config.json - name: Pull neon image from ECR run: crane pull 369495373322.dkr.ecr.eu-central-1.amazonaws.com/neon:latest neon - name: Pull compute tools image from ECR run: crane pull 369495373322.dkr.ecr.eu-central-1.amazonaws.com/compute-tools:latest compute-tools - name: Pull compute node image from ECR run: crane pull 369495373322.dkr.ecr.eu-central-1.amazonaws.com/compute-node:latest compute-node - name: Configure docker login run: | # ECR Credential Helper & Docker Hub don't work together in config, hence reset echo "" > /github/home/.docker/config.json crane auth login -u ${{ secrets.NEON_DOCKERHUB_USERNAME }} -p ${{ secrets.NEON_DOCKERHUB_PASSWORD }} index.docker.io - name: Push neon image to Docker Hub run: crane push neon neondatabase/neon:${{needs.tag.outputs.build-tag}} - name: Push compute tools image to Docker Hub run: crane push compute-tools neondatabase/compute-tools:${{needs.tag.outputs.build-tag}} - name: Push compute node image to Docker Hub run: crane push compute-node neondatabase/compute-node:${{needs.tag.outputs.build-tag}} - name: Add latest tag to images if: | (github.ref_name == 'main' || github.ref_name == 'release') && github.event_name != 'workflow_dispatch' run: | crane tag neondatabase/neon:${{needs.tag.outputs.build-tag}} latest crane tag neondatabase/compute-tools:${{needs.tag.outputs.build-tag}} latest crane tag neondatabase/compute-node:${{needs.tag.outputs.build-tag}} latest calculate-deploy-targets: runs-on: [ self-hosted, Linux, k8s-runner ] if: | (github.ref_name == 'main' || github.ref_name == 'release') && github.event_name != 'workflow_dispatch' outputs: matrix-include: ${{ steps.set-matrix.outputs.include }} steps: - id: set-matrix run: | if [[ "$GITHUB_REF_NAME" == "main" ]]; then STAGING='{"env_name": "staging", "proxy_job": "neon-proxy", "proxy_config": "staging.proxy", "kubeconfig_secret": "STAGING_KUBECONFIG_DATA"}' NEON_STRESS='{"env_name": "neon-stress", "proxy_job": "neon-stress-proxy", "proxy_config": "neon-stress.proxy", "kubeconfig_secret": "NEON_STRESS_KUBECONFIG_DATA"}' echo "::set-output name=include::[$STAGING, $NEON_STRESS]" elif [[ "$GITHUB_REF_NAME" == "release" ]]; then PRODUCTION='{"env_name": "production", "proxy_job": "neon-proxy", "proxy_config": "production.proxy", "kubeconfig_secret": "PRODUCTION_KUBECONFIG_DATA"}' echo "::set-output name=include::[$PRODUCTION]" else echo "GITHUB_REF_NAME (value '$GITHUB_REF_NAME') is not set to either 'main' or 'release'" exit 1 fi deploy: runs-on: [ self-hosted, Linux, k8s-runner ] #container: 369495373322.dkr.ecr.eu-central-1.amazonaws.com/base:latest # We need both storage **and** compute images for deploy, because control plane picks the compute version based on the storage version. # If it notices a fresh storage it may bump the compute version. And if compute image failed to build it may break things badly needs: [ push-docker-hub, calculate-deploy-targets, tag ] if: | (github.ref_name == 'main' || github.ref_name == 'release') && github.event_name != 'workflow_dispatch' defaults: run: shell: bash strategy: matrix: include: ${{fromJSON(needs.calculate-deploy-targets.outputs.matrix-include)}} steps: - name: Checkout uses: actions/checkout@v3 with: submodules: true fetch-depth: 0 - name: Setup python uses: actions/setup-python@v4 with: python-version: '3.10' - name: Setup ansible run: | export PATH="/root/.local/bin:$PATH" pip install --progress-bar off --user ansible boto3 - name: Redeploy run: | export DOCKER_TAG=${{needs.tag.outputs.build-tag}} cd "$(pwd)/.github/ansible" if [[ "$GITHUB_REF_NAME" == "main" ]]; then ./get_binaries.sh elif [[ "$GITHUB_REF_NAME" == "release" ]]; then RELEASE=true ./get_binaries.sh else echo "GITHUB_REF_NAME (value '$GITHUB_REF_NAME') is not set to either 'main' or 'release'" exit 1 fi eval $(ssh-agent) echo "${{ secrets.TELEPORT_SSH_KEY }}" | tr -d '\n'| base64 --decode >ssh-key echo "${{ secrets.TELEPORT_SSH_CERT }}" | tr -d '\n'| base64 --decode >ssh-key-cert.pub chmod 0600 ssh-key ssh-add ssh-key rm -f ssh-key ssh-key-cert.pub ansible-playbook deploy.yaml -i ${{ matrix.env_name }}.hosts rm -f neon_install.tar.gz .neon_current_version deploy-proxy: runs-on: dev container: 369495373322.dkr.ecr.eu-central-1.amazonaws.com/base:latest # Compute image isn't strictly required for proxy deploy, but let's still wait for it to run all deploy jobs consistently. needs: [ push-docker-hub, calculate-deploy-targets, tag ] if: | (github.ref_name == 'main' || github.ref_name == 'release') && github.event_name != 'workflow_dispatch' defaults: run: shell: bash strategy: matrix: include: ${{fromJSON(needs.calculate-deploy-targets.outputs.matrix-include)}} env: KUBECONFIG: .kubeconfig steps: - name: Checkout uses: actions/checkout@v3 with: submodules: true fetch-depth: 0 - name: Add curl run: apt update && apt install curl -y - name: Store kubeconfig file run: | echo "${{ secrets[matrix.kubeconfig_secret] }}" | base64 --decode > ${KUBECONFIG} chmod 0600 ${KUBECONFIG} - name: Setup helm v3 run: | curl -s https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash helm repo add neondatabase https://neondatabase.github.io/helm-charts - name: Re-deploy proxy run: | DOCKER_TAG=${{needs.tag.outputs.build-tag}} helm upgrade ${{ matrix.proxy_job }} neondatabase/neon-proxy --namespace default --install -f .github/helm-values/${{ matrix.proxy_config }}.yaml --set image.tag=${DOCKER_TAG} --wait --timeout 15m0s helm upgrade ${{ matrix.proxy_job }}-scram neondatabase/neon-proxy --namespace default --install -f .github/helm-values/${{ matrix.proxy_config }}-scram.yaml --set image.tag=${DOCKER_TAG} --wait --timeout 15m0s