From e6d80b646bb074006a63629fa1971cf8888fe999 Mon Sep 17 00:00:00 2001 From: Xavier Basty Date: Thu, 7 Dec 2023 17:43:47 +0100 Subject: [PATCH 1/5] remove legacy files --- .github/workflows/cd.yml | 97 - .github/workflows/ci.yml | 133 - .github/workflows/ci_terraform.yml | 97 - .github/workflows/deploy.yml | 86 - .github/workflows/intake.yml | 47 - .github/workflows/release.yml | 135 - .github/workflows/validate.yml | 39 - Cargo.lock | 740 +++--- terraform/backend.tf | 34 - terraform/ecs/analytics.tf | 65 - terraform/ecs/main.tf | 390 --- terraform/ecs/outputs.tf | 3 - terraform/ecs/variables.tf | 120 - terraform/main.tf | 125 - .../monitoring/grafana-dashboard.json.tpl | 2228 ----------------- terraform/monitoring/main.tf | 59 - terraform/monitoring/variables.tf | 11 - terraform/provider.tf | 22 - terraform/redis/main.tf | 52 - terraform/redis/outputs.tf | 7 - terraform/redis/variables.tf | 29 - terraform/variables.tf | 70 - terraform/vars/dev.tfvars | 5 - terraform/vars/prod.tfvars | 5 - terraform/vars/staging.tfvars | 5 - 25 files changed, 449 insertions(+), 4155 deletions(-) delete mode 100644 .github/workflows/cd.yml delete mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/ci_terraform.yml delete mode 100644 .github/workflows/deploy.yml delete mode 100644 .github/workflows/intake.yml delete mode 100644 .github/workflows/release.yml delete mode 100644 .github/workflows/validate.yml delete mode 100644 terraform/backend.tf delete mode 100644 terraform/ecs/analytics.tf delete mode 100644 terraform/ecs/main.tf delete mode 100644 terraform/ecs/outputs.tf delete mode 100644 terraform/ecs/variables.tf delete mode 100644 terraform/main.tf delete mode 100644 terraform/monitoring/grafana-dashboard.json.tpl delete mode 100644 terraform/monitoring/main.tf delete mode 100644 terraform/monitoring/variables.tf delete mode 100644 terraform/provider.tf delete mode 100644 terraform/redis/main.tf delete mode 100644 terraform/redis/outputs.tf delete mode 100644 terraform/redis/variables.tf delete mode 100644 terraform/variables.tf delete mode 100644 terraform/vars/dev.tfvars delete mode 100644 terraform/vars/prod.tfvars delete mode 100644 terraform/vars/staging.tfvars diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml deleted file mode 100644 index 44e137c..0000000 --- a/.github/workflows/cd.yml +++ /dev/null @@ -1,97 +0,0 @@ -name: cd -on: - workflow_dispatch: - workflow_call: - -concurrency: - # Only allow for one action to run at once, queue any others - group: cd - # Don't cancel existing - cancel-in-progress: false - -env: - TF_VAR_project_registry_auth_token: ${{ secrets.PROJECT_REGISTRY_AUTH_TOKEN }} - TF_VAR_data_api_auth_token: ${{ secrets.DATA_API_AUTH_TOKEN }} - TF_VAR_secret: ${{ secrets.SECRET }} - -jobs: - get-version: - runs-on: ubuntu-latest - outputs: - version: ${{ steps.clean_version.outputs.version }} - steps: - - id: latest_release - uses: pozetroninc/github-action-get-latest-release@master - if: github.event_name != 'release' - with: - repository: ${{ github.repository }} - excludes: draft - - - id: get - uses: actions/github-script@v6 - env: - LATEST_TAG: ${{ steps.latest_release.outputs.release }} - with: - result-encoding: string - script: | - if (context.eventName == "release") { - return context.payload.release.tag_name - } else { - return process.env.LATEST_TAG - } - - - id: clean_version - run: | - version=$(echo "${{ steps.get.outputs.result }}" | sed 's/v//g') - echo "version=$version" >> $GITHUB_OUTPUT - - deploy-infra-staging: - runs-on: ubuntu-latest - environment: - name: staging - url: https://verify.walletconnect.com/health - needs: - - get-version - steps: - - name: Checkout - uses: actions/checkout@v3 - - - id: deploy-staging - uses: WalletConnect/actions/actions/deploy-terraform/@v2 - env: - TF_VAR_image_version: ${{ needs.get-version.outputs.version }} - with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: eu-central-1 - environment: staging - app-name: ${{ github.event.repository.name }} - - validate-staging: - needs: [deploy-infra-staging] - uses: ./.github/workflows/validate.yml - with: - environment: 'staging' - - deploy-infra-prod: - runs-on: ubuntu-latest - environment: - name: prod - url: https://verify.walletconnect.com/health - needs: - - get-version - - validate-staging - steps: - - name: Checkout - uses: actions/checkout@v3 - - - id: deploy-staging - uses: WalletConnect/actions/actions/deploy-terraform/@v2 - env: - TF_VAR_image_version: ${{ needs.get-version.outputs.version }} - with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: eu-central-1 - environment: prod - app-name: ${{ github.event.repository.name }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index ab82f60..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,133 +0,0 @@ -name: ci - -on: - pull_request: - paths-ignore: - - "spec/**" - - "terraform/**" - - ".github/**" - - "*.md" - - "LICENSE" - - push: - branches: - - "main" - paths-ignore: - - "spec/**" - - "terraform/**" - - ".github/**" - - "*.md" - - "LICENSE" - -concurrency: - # Support push/pr as event types with different behaviors each: - # 1. push: queue up builds - # 2. pr: only allow one run per PR - group: ${{ github.workflow }}-${{ github.event.type }}${{ github.event.pull_request.number }} - # If there is already a workflow running for the same pull request, cancel it - cancel-in-progress: ${{ github.event_name == 'pull_request' }} - -jobs: - tasks: - name: "[${{ matrix.os }}/rust-${{matrix.rust}}] ${{ matrix.cargo.name }}" - runs-on: ubuntu-latest - services: - redis: - image: redis - options: >- - --health-cmd "redis-cli ping" - --health-interval 10s - --health-timeout 5s - --health-retries 5 - strategy: - fail-fast: false - matrix: - os: - - ubuntu-latest - rust: - - nightly - cargo: - # - name: "Clippy" - # cmd: clippy - # args: -- -D clippy::all - # cache: {} - - name: "Formatting" - cmd: fmt - args: -- --check - cache: {} - - name: "Unit Tests" - cmd: test - args: --all-features - cache: { sharedKey: "tests" } - include: - - os: ubuntu-latest - sccache-path: /home/runner/.cache/sccache - env: - RUST_BACKTRACE: full - RUSTC_WRAPPER: sccache - SCCACHE_CACHE_SIZE: 1G - SCCACHE_DIR: ${{ matrix.sccache-path }} - steps: - # Checkout code - - name: "Git checkout" - uses: actions/checkout@v2 - - # Install sccache - - name: "Install sccache" - if: matrix.os == 'ubuntu-latest' - env: - SCCACHE_URL: https://github.com/mozilla/sccache/releases/download - SCCACHE_VERSION: v0.2.15 - run: | - SCCACHE_FILE=sccache-$SCCACHE_VERSION-x86_64-unknown-linux-musl - curl -sSL "$SCCACHE_URL/$SCCACHE_VERSION/$SCCACHE_FILE.tar.gz" | tar xz - install -vDm 755 "$SCCACHE_FILE/sccache" "$HOME/.local/bin/sccache" - echo "$HOME/.local/bin" >> "$GITHUB_PATH" - - # Install Rust toolchain - - name: "Install Rust ${{ matrix.rust }}" - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.rust }} - profile: minimal - override: true - components: rustfmt, clippy - - # Rebuild cache - - name: Cache cargo registry - uses: Swatinem/rust-cache@3bb3a9a087029c7bc392586cdc88cb6f66b9c6ef - with: ${{ matrix.cargo.cache }} - continue-on-error: false - - - name: Cache sccache - uses: actions/cache@v2 - continue-on-error: false - with: - path: ${{ matrix.sccache-path }} - key: ${{ runner.os }}-sccache-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-sccache- - - # Run job - - name: "Start sccache server" - run: | - sccache --stop-server || true - sccache --start-server - - - name: "Task ${{ matrix.cargo.name }}" - uses: actions-rs/cargo@v1 - with: - command: ${{ matrix.cargo.cmd }} - args: ${{ matrix.cargo.args }} - - - name: "Print sccache stats" - run: sccache --show-stats - - - name: "Stop sccache server" - run: sccache --stop-server || true - - kick-off-release: - needs: [tasks] - if: github.event_name != 'pull_request' && !startsWith(github.event.head_commit.message, 'chore') - uses: ./.github/workflows/release.yml - secrets: inherit diff --git a/.github/workflows/ci_terraform.yml b/.github/workflows/ci_terraform.yml deleted file mode 100644 index 2f5d979..0000000 --- a/.github/workflows/ci_terraform.yml +++ /dev/null @@ -1,97 +0,0 @@ -name: ci_terraform -on: - push: - branches: - - main - paths: - - "terraform/**" - pull_request: - paths: - - "terraform/**" - -concurrency: - # Support push/pr as event types with different behaviors each: - # 1. push: queue up builds - # 2. pr: only allow one run per PR - group: ${{ github.workflow }}-${{ github.event.type }}${{ github.event.pull_request.number }} - # If there is already a workflow running for the same pull request, cancel it - cancel-in-progress: ${{ github.event.type == 'PullRequest' }} - -env: - TF_VAR_project_registry_auth_token: ${{ secrets.PROJECT_REGISTRY_AUTH_TOKEN }} - TF_VAR_data_api_auth_token: ${{ secrets.DATA_API_AUTH_TOKEN }} - TF_VAR_secret: ${{ secrets.SECRET }} - -jobs: - fmt: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - id: fmt - uses: WalletConnect/actions/actions/fmt-check-terraform/@master - - plan: - if: github.event_name == 'pull_request' - runs-on: ubuntu-latest - environment: - name: infra/staging - url: https://staging.verify.walletconnect.com/health - steps: - - name: Checkout - uses: actions/checkout@v3 - # Get latest release for image version - - id: latest_release - uses: pozetroninc/github-action-get-latest-release@master - if: github.event_name != 'release' - with: - repository: ${{ github.repository }} - excludes: draft - - name: Run Terraform Plan - id: plan - uses: WalletConnect/actions/actions/plan-terraform/@master - env: - TF_VAR_image_version: ${{ steps.latest_release.outputs.release }} - with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: eu-central-1 - environment: staging - app-name: ${{ github.event.repository.name }} - - uses: actions/upload-artifact@v3 - with: - name: plan.tfplan - path: ${{ steps.plan.outputs.plan-file }} - - uses: actions/upload-artifact@v3 - with: - name: plan.txt - path: ${{ steps.plan.outputs.output-file }} - - name: Add Plan to PR - uses: actions/github-script@v6 - env: - PLAN: "terraform\n${{ steps.plan.outputs.plan }}" - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const output = `
Show Plan - - \`\`\`\n - ${process.env.PLAN} - \`\`\` - -
- - *Action: \`${{ github.event_name }}\`*`; - - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: output - }) - - kick-off-cd: - needs: [fmt] - if: github.event_name != 'pull_request' - uses: ./.github/workflows/cd.yml - secrets: inherit diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml deleted file mode 100644 index dec0c25..0000000 --- a/.github/workflows/deploy.yml +++ /dev/null @@ -1,86 +0,0 @@ -name: deploy - -on: - workflow_dispatch: - inputs: - environment: - required: true - type: choice - options: - - dev - - staging - workflow_call: null - -concurrency: - group: cd - cancel-in-progress: false - -env: - TF_VAR_project_registry_auth_token: ${{ secrets.PROJECT_REGISTRY_AUTH_TOKEN }} - TF_VAR_data_api_auth_token: ${{ secrets.DATA_API_AUTH_TOKEN }} - TF_VAR_secret: ${{ secrets.SECRET }} - -jobs: - build-container: - runs-on: - group: ubuntu-runners - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v1 - with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: eu-central-1 - - # Authenticate with ECR - - name: Login to Amazon ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@v1 - - - name: Docker meta - id: meta - uses: docker/metadata-action@v4 - with: - images: | - ${{ steps.login-ecr.outputs.registry }}/bouncer - flavor: | - latest=auto - tags: | - type=raw,value=${{ github.sha }} - - # Setup Buildkit - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - - name: Build, tag, and push image - uses: docker/build-push-action@v3 - with: - context: . - push: true - tags: ${{ steps.meta.outputs.tags }} - cache-from: type=gha - cache-to: type=gha,mode=max - deploy: - runs-on: ubuntu-latest - needs: - - build-container - environment: - name: ${{ inputs.environment }} - url: https://${{ inputs.environment }}.verify.walletconnect.com/health - steps: - - name: Checkout - uses: actions/checkout@v3 - - id: terraform - uses: WalletConnect/actions/actions/deploy-terraform/@v2 - env: - TF_VAR_image_version: ${{ github.sha }} - with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: eu-central-1 - environment: ${{inputs.environment}} - app-name: ${{ github.event.repository.name }} diff --git a/.github/workflows/intake.yml b/.github/workflows/intake.yml deleted file mode 100644 index 6dabaa0..0000000 --- a/.github/workflows/intake.yml +++ /dev/null @@ -1,47 +0,0 @@ -# This workflow moves issues to the Project board when they receive the "accepted" label -# When WalletConnect Org members create issues they are automatically "accepted". -# Otherwise, they need to manually receive that label during intake. -name: intake - -on: - issues: - types: [opened, labeled] - pull_request: - types: [opened, labeled] - -jobs: - add-to-project: - name: Add issue to board - if: github.event_name == 'issues' && github.event.action == 'labeled' && github.event.label.name == 'accepted' - runs-on: ubuntu-latest - steps: - - uses: actions/add-to-project@v0.1.0 - with: - project-url: https://github.com/orgs/WalletConnect/projects/20 - github-token: ${{ secrets.ASSIGN_TO_PROJECT_GITHUB_TOKEN }} - labeled: accepted - label-operator: OR - - auto-promote: - name: auto-promote - if: github.event.action == 'opened' - runs-on: ubuntu-latest - steps: - - name: Check Core Team membership - uses: tspascoal/get-user-teams-membership@v1 - id: is-core-team - with: - username: ${{ github.event_name != 'pull_request' && github.event.issue.user.login || github.event.sender.login }} - team: "Core Team" - GITHUB_TOKEN: ${{ secrets.ASSIGN_TO_PROJECT_GITHUB_TOKEN }} - - name: Print result - env: - CREATOR: ${{ github.event_name != 'pull_request' && github.event.issue.user.login || github.event.sender.login }} - IS_TEAM_MEMBER: ${{ steps.is-core-team.outputs.isTeamMember }} - run: echo "$CREATOR (Core Team Member $IS_TEAM_MEMBER) created this issue/PR" - - name: Label issues - if: ${{ steps.is-core-team.outputs.isTeamMember == 'true' }} - uses: andymckay/labeler@e6c4322d0397f3240f0e7e30a33b5c5df2d39e90 - with: - add-labels: "accepted" - repo-token: ${{ secrets.ASSIGN_TO_PROJECT_GITHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index d684124..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,135 +0,0 @@ -name: release - -on: - workflow_dispatch: - workflow_call: - -permissions: - contents: write - packages: write - -jobs: - release: - runs-on: - group: ubuntu-runners - outputs: - version: ${{ steps.clean_version.outputs.version }} - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: "Cocogitto release" - id: release - uses: cocogitto/cocogitto-action@v3 - with: - check: true - check-latest-tag-only: true - release: true - git-user: 'github-actions[bot]' - git-user-email: "github-actions[bot]@users.noreply.github.com" - - - name: "Update version in Cargo.toml" - shell: bash - run: | - version=$(echo "${{ steps.release.outputs.version }}" | sed 's/v//g') - - sed "s/^version = \".*\"\$/version = \"$version\"/" ./Cargo.toml > /tmp/cargo.toml - mv /tmp/cargo.toml ./Cargo.toml - - - name: "Commit version bump" - uses: stefanzweifel/git-auto-commit-action@v4 - with: - commit_message: "chore: Bump version for release" - file_pattern: "Cargo.toml Cargo.lock" - commit_user_name: "github-actions[bot]" - commit_user_email: "github-actions[bot]@users.noreply.github.com" - - - name: "Install Rust toolchain (stable)" - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - profile: minimal - default: true - - - name: Cache cargo registry - uses: Swatinem/rust-cache@v2 - - - name: "Generate Changelog" - run: cog changelog --at ${{ steps.release.outputs.version }} -t full_hash > GITHUB_CHANGELOG.md - - - name: "Update Github release notes" - uses: softprops/action-gh-release@v1 - with: - body_path: GITHUB_CHANGELOG.md - tag_name: ${{ steps.release.outputs.version }} - token: ${{ secrets.PAT }} - - - id: clean_version - run: | - version=$(echo "${{ steps.release.outputs.version }}" | sed 's/v//g') - echo "version=$version" >> $GITHUB_OUTPUT - - build-container: - runs-on: - group: ubuntu-runners - needs: - - release - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v1 - with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: eu-central-1 - - # Authenticate with ECR - - name: Login to Amazon ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@v1 - - - name: Login to GitHub Container Registry - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - logout: false - - - name: Docker meta - id: meta - uses: docker/metadata-action@v4 - with: - images: | - ${{ steps.login-ecr.outputs.registry }}/bouncer - ghcr.io/${{ github.repository }} - walletconnect/bouncer,enable=false - flavor: | - latest=auto - tags: | - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=raw,value=${{ needs.release.outputs.version }} - - # Setup Buildkit - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - - name: Build, tag, and push image - uses: docker/build-push-action@v3 - with: - context: . - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - cache-from: type=gha - cache-to: type=gha,mode=max - - kick-off-cd: - needs: [build-container] - uses: ./.github/workflows/cd.yml - secrets: inherit diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml deleted file mode 100644 index 450f239..0000000 --- a/.github/workflows/validate.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: validate - -on: - workflow_dispatch: - inputs: - environment: - description: 'the environment to validate' - required: true - default: 'staging' - type: choice - options: - - prod - - staging - - dev - workflow_call: - inputs: - environment: - description: 'the environment to validate' - required: true - default: 'staging' - type: string - -jobs: - validate: - runs-on: ubuntu-latest - steps: - - name: checkout - uses: actions/checkout@v3 - - name: setup-node - uses: actions/setup-node@v3 - with: - node-version: 16.x - cache: "yarn" - cache-dependency-path: "**/yarn.lock" - - name: install - run: yarn install - - run: yarn integration:$ENVIRONMENT - env: - ENVIRONMENT: ${{ inputs.environment }} diff --git a/Cargo.lock b/Cargo.lock index 6ab4a26..9236d7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" dependencies = [ "getrandom", "once_cell", @@ -30,22 +30,23 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.3" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" dependencies = [ "cfg-if", "const-random", "getrandom", "once_cell", "version_check", + "zerocopy", ] [[package]] name = "aho-corasick" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] @@ -100,13 +101,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.73" +version = "0.1.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -137,7 +138,7 @@ dependencies = [ "hex", "http", "hyper", - "ring", + "ring 0.16.20", "time", "tokio", "tower", @@ -610,9 +611,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.4" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" [[package]] name = "base64-simd" @@ -641,9 +642,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "block-buffer" @@ -656,7 +657,7 @@ dependencies = [ [[package]] name = "bouncer" -version = "0.32.0" +version = "0.33.0" dependencies = [ "anyhow", "arrayvec", @@ -695,9 +696,9 @@ dependencies = [ [[package]] name = "build-info" -version = "0.0.33" +version = "0.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1add148a02352a8149c1ae42528988427aeb0438f808df66f393cb9948006ec" +checksum = "155eb070980e96aeb4ef3b8620b0febb2ae5e17451dc1b329681bdd4eb0a94e1" dependencies = [ "build-info-common", "build-info-proc", @@ -705,12 +706,12 @@ dependencies = [ [[package]] name = "build-info-build" -version = "0.0.33" +version = "0.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a9cddd0655ecb60d7a500f30d3ae4e69b798026d76c3d16b2f464a72913066e" +checksum = "b69d6331ec579144d39e1c128f343d23e9b837617df1bed4ed032e141f83f06a" dependencies = [ "anyhow", - "base64 0.21.4", + "base64 0.21.5", "bincode", "build-info-common", "cargo_metadata", @@ -720,14 +721,14 @@ dependencies = [ "pretty_assertions", "rustc_version", "serde_json", - "xz2", + "zstd", ] [[package]] name = "build-info-common" -version = "0.0.33" +version = "0.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3b772d111f4cc62aa9018ba5cad5a1748a6d9a286d91a69b748cd233e43cdbd" +checksum = "8209c0c2b13da7e5f7202e591b6d41b46c8f0e78d031dedf5cff71cc8c6ec773" dependencies = [ "chrono", "derive_more", @@ -737,12 +738,12 @@ dependencies = [ [[package]] name = "build-info-proc" -version = "0.0.33" +version = "0.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39fc1a191a93dd7d72c21007a3d65cbfd88204bc744ecccac21858812bc06988" +checksum = "5fc1874cb1995691fb01f9bb56e75f9660d2614e74607fa71c08a8b3bd7e30e4" dependencies = [ "anyhow", - "base64 0.21.4", + "base64 0.21.5", "bincode", "build-info-common", "chrono", @@ -752,8 +753,8 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.37", - "xz2", + "syn 2.0.39", + "zstd", ] [[package]] @@ -764,9 +765,9 @@ checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" @@ -776,9 +777,9 @@ checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "bytes-utils" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e47d3a8076e283f3acd27400535992edb3ba4b5bb72f8891ad8fbe7932a7d4b9" +checksum = "7dafe3a8757b027e2be6e4e5601ed563c55989fcf1546e933c66c8eb3a058d35" dependencies = [ "bytes", "either", @@ -795,18 +796,18 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cfa25e60aea747ec7e1124f238816749faa93759c6ff5b31f1ccdda137f4479" +checksum = "e34637b3140142bdf929fb439e8aa4ebad7651ebf7b1080b3930aa16ac1459ff" dependencies = [ "serde", ] [[package]] name = "cargo_metadata" -version = "0.18.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb9ac64500cc83ce4b9f8dafa78186aa008c8dea77a09b94cd307fd0cd5022a8" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" dependencies = [ "camino", "cargo-platform", @@ -857,7 +858,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -902,9 +903,9 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -912,15 +913,15 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" dependencies = [ "libc", ] @@ -1015,9 +1016,12 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" +checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc" +dependencies = [ + "powerfmt", +] [[package]] name = "derive_more" @@ -1074,24 +1078,19 @@ dependencies = [ ] [[package]] -name = "errno" -version = "0.3.3" +name = "equivalent" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys", -] +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] -name = "errno-dragonfly" -version = "0.1.2" +name = "errno" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ - "cc", "libc", + "windows-sys 0.52.0", ] [[package]] @@ -1133,9 +1132,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -1152,9 +1151,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" dependencies = [ "futures-channel", "futures-core", @@ -1167,9 +1166,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" dependencies = [ "futures-core", "futures-sink", @@ -1177,15 +1176,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" [[package]] name = "futures-executor" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" dependencies = [ "futures-core", "futures-task", @@ -1194,38 +1193,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" dependencies = [ "futures-channel", "futures-core", @@ -1256,7 +1255,7 @@ source = "git+https://github.com/WalletConnect/utils-rs.git?tag=v0.7.1#95b9936f0 dependencies = [ "aws-sdk-s3", "axum-client-ip", - "bitflags 2.4.0", + "bitflags 2.4.1", "bytes", "futures", "http-body", @@ -1270,9 +1269,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" dependencies = [ "cfg-if", "libc", @@ -1281,9 +1280,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "git2" @@ -1291,7 +1290,7 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbf97ba92db08df386e10c8ede66a2a0369bd277090afd8710e19e38de9ec0cd" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "libc", "libgit2-sys", "log", @@ -1306,9 +1305,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "h2" -version = "0.3.21" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" +checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" dependencies = [ "bytes", "fnv", @@ -1316,7 +1315,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 2.1.0", "slab", "tokio", "tokio-util", @@ -1335,7 +1334,7 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ff8ae62cd3a9102e5637afc8452c55acf3844001bd5374e0b0bd7b6616c038" dependencies = [ - "ahash 0.8.3", + "ahash 0.8.6", ] [[package]] @@ -1350,7 +1349,7 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" dependencies = [ - "base64 0.21.4", + "base64 0.21.5", "bytes", "headers-core", "http", @@ -1391,9 +1390,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" dependencies = [ "bytes", "fnv", @@ -1446,7 +1445,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.9", + "socket2 0.4.10", "tokio", "tower-service", "tracing", @@ -1455,9 +1454,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http", @@ -1484,16 +1483,16 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.57" +version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows", + "windows-core", ] [[package]] @@ -1507,9 +1506,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -1525,6 +1524,16 @@ dependencies = [ "hashbrown 0.12.3", ] +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", +] + [[package]] name = "integer-encoding" version = "3.0.4" @@ -1533,9 +1542,9 @@ checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" [[package]] name = "ipnet" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "ipnetwork" @@ -1554,18 +1563,18 @@ checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "jobserver" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.64" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" dependencies = [ "wasm-bindgen", ] @@ -1576,9 +1585,9 @@ version = "8.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" dependencies = [ - "base64 0.21.4", + "base64 0.21.5", "pem", - "ring", + "ring 0.16.20", "serde", "serde_json", "simple_asn1", @@ -1592,9 +1601,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.148" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "libgit2-sys" @@ -1622,15 +1631,15 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.7" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" [[package]] name = "lock_api" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -1642,17 +1651,6 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" -[[package]] -name = "lzma-sys" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - [[package]] name = "mach2" version = "0.4.1" @@ -1692,9 +1690,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.6.3" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "memoffset" @@ -1711,7 +1709,7 @@ version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b9b8653cec6897f73b519a43fba5ee3d50f62fe9af80b428accdcc093b4a849" dependencies = [ - "ahash 0.7.6", + "ahash 0.7.7", "metrics-macros 0.6.0", "portable-atomic 0.3.20", ] @@ -1722,9 +1720,9 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fde3af1a009ed76a778cb84fdef9e7dbbdf5775ae3e4cc1f434a6a307f6f76c5" dependencies = [ - "ahash 0.8.3", + "ahash 0.8.6", "metrics-macros 0.7.0", - "portable-atomic 1.4.3", + "portable-atomic 1.6.0", ] [[package]] @@ -1733,9 +1731,9 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a4964177ddfdab1e3a2b37aec7cf320e14169abb0ed73999f558136409178d5" dependencies = [ - "base64 0.21.4", + "base64 0.21.5", "hyper", - "indexmap", + "indexmap 1.9.3", "ipnet", "metrics 0.21.1", "metrics-util", @@ -1764,7 +1762,7 @@ checksum = "ddece26afd34c31585c74a4db0630c376df271c285d682d1e55012197830b6df" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -1799,13 +1797,13 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", "wasi", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1909,9 +1907,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] @@ -1937,17 +1935,17 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "openssl" -version = "0.10.57" +version = "0.10.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" +checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "cfg-if", "foreign-types", "libc", @@ -1964,7 +1962,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -1975,9 +1973,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.93" +version = "0.9.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" +checksum = "c3eaad34cdd97d81de97964fc7f29e2d104f483840d906ef56daa1912338460b" dependencies = [ "cc", "libc", @@ -2018,15 +2016,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -2034,7 +2032,7 @@ name = "parquet" version = "42.0.0" source = "git+https://github.com/WalletConnect/arrow-rs.git?rev=99a1cc3#99a1cc36bce8d55e411dd441f2219d0689a82bee" dependencies = [ - "ahash 0.8.3", + "ahash 0.8.6", "bytes", "chrono", "flate2", @@ -2055,7 +2053,7 @@ dependencies = [ "parquet", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -2075,9 +2073,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project" @@ -2096,7 +2094,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -2123,14 +2121,20 @@ version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e30165d31df606f5726b090ec7592c308a0eaf61721ff64c9a3018e344a8753e" dependencies = [ - "portable-atomic 1.4.3", + "portable-atomic 1.6.0", ] [[package]] name = "portable-atomic" -version = "1.4.3" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" + +[[package]] +name = "powerfmt" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31114a898e107c51bb1609ffaf55a0e011cf6a4d7f1170d0015a165082c0338b" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "pretty_assertions" @@ -2168,9 +2172,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.67" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" dependencies = [ "unicode-ident", ] @@ -2251,18 +2255,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.3.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "regex" -version = "1.9.5" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", @@ -2272,9 +2276,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.8" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", @@ -2283,17 +2287,17 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.5" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.11.20" +version = "0.11.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" +checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" dependencies = [ - "base64 0.21.4", + "base64 0.21.5", "bytes", "encoding_rs", "futures-core", @@ -2314,6 +2318,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "system-configuration", "tokio", "tokio-native-tls", "tower-service", @@ -2339,12 +2344,26 @@ dependencies = [ "cc", "libc", "once_cell", - "spin", - "untrusted", + "spin 0.5.2", + "untrusted 0.7.1", "web-sys", "winapi", ] +[[package]] +name = "ring" +version = "0.17.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" +dependencies = [ + "cc", + "getrandom", + "libc", + "spin 0.9.8", + "untrusted 0.9.0", + "windows-sys 0.48.0", +] + [[package]] name = "rmp" version = "0.8.12" @@ -2384,25 +2403,25 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.15" +version = "0.38.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f9da0cbd88f9f09e7814e388301c8414c51c62aa6ce1e4b5c551d49d96e531" +checksum = "9470c4bf8246c8daf25f9598dca807fb6510347b1e1cfa55749113850c79d88a" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "rustls" -version = "0.21.7" +version = "0.21.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" +checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9" dependencies = [ "log", - "ring", + "ring 0.17.7", "rustls-webpki", "sct", ] @@ -2421,21 +2440,21 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "base64 0.21.4", + "base64 0.21.5", ] [[package]] name = "rustls-webpki" -version = "0.101.6" +version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring", - "untrusted", + "ring 0.17.7", + "untrusted 0.9.0", ] [[package]] @@ -2456,7 +2475,7 @@ version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2467,12 +2486,12 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sct" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring", - "untrusted", + "ring 0.17.7", + "untrusted 0.9.0", ] [[package]] @@ -2500,9 +2519,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" dependencies = [ "serde", ] @@ -2515,29 +2534,29 @@ checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4" [[package]] name = "serde" -version = "1.0.188" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] name = "serde_json" -version = "1.0.107" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", @@ -2590,9 +2609,9 @@ dependencies = [ [[package]] name = "sharded-slab" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1b21f559e07218024e7e9f90f96f601825397de0e25420135f7f952453fed0b" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] @@ -2635,15 +2654,15 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.1" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" [[package]] name = "socket2" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" dependencies = [ "libc", "winapi", @@ -2651,12 +2670,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2665,6 +2684,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "static_assertions" version = "1.1.0" @@ -2690,9 +2715,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.37" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", @@ -2705,6 +2730,27 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tap" version = "1.0.1" @@ -2713,35 +2759,35 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.8.0" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" dependencies = [ "cfg-if", "fastrand", "redox_syscall", "rustix", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] name = "thiserror" -version = "1.0.49" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.49" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -2767,12 +2813,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" dependencies = [ "deranged", "itoa", + "powerfmt", "serde", "time-core", "time-macros", @@ -2819,9 +2866,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.32.0" +version = "1.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" +checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" dependencies = [ "backtrace", "bytes", @@ -2831,20 +2878,20 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.4", + "socket2 0.5.5", "tokio-macros", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -2880,9 +2927,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ "bytes", "futures-core", @@ -2933,7 +2980,7 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "bytes", "futures-core", "futures-util", @@ -2959,11 +3006,10 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "log", "pin-project-lite", "tracing-attributes", @@ -2972,20 +3018,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] name = "tracing-core" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", "valuable", @@ -2993,12 +3039,12 @@ dependencies = [ [[package]] name = "tracing-log" -version = "0.1.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" dependencies = [ - "lazy_static", "log", + "once_cell", "tracing-core", ] @@ -3014,9 +3060,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ "nu-ansi-term", "serde", @@ -3031,9 +3077,9 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "twox-hash" @@ -3053,9 +3099,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" [[package]] name = "unicode-ident" @@ -3078,11 +3124,17 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna", @@ -3097,9 +3149,9 @@ checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" [[package]] name = "uuid" -version = "1.4.1" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" [[package]] name = "valuable" @@ -3142,9 +3194,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -3152,24 +3204,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.37" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" dependencies = [ "cfg-if", "js-sys", @@ -3179,9 +3231,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3189,22 +3241,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" [[package]] name = "wc" @@ -3217,9 +3269,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.64" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" dependencies = [ "js-sys", "wasm-bindgen", @@ -3248,12 +3300,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows" -version = "0.48.0" +name = "windows-core" +version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -3262,7 +3314,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", ] [[package]] @@ -3271,13 +3332,28 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] [[package]] @@ -3286,42 +3362,84 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + [[package]] name = "winreg" version = "0.50.0" @@ -3329,32 +3447,72 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ "cfg-if", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] name = "xmlparser" -version = "0.13.5" +version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" [[package]] -name = "xz2" -version = "0.1.7" +name = "yansi" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + +[[package]] +name = "zerocopy" +version = "0.7.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d075cf85bbb114e933343e087b92f2146bac0d55b534cbb8188becf0039948e" dependencies = [ - "lzma-sys", + "zerocopy-derive", ] [[package]] -name = "yansi" -version = "0.5.1" +name = "zerocopy-derive" +version = "0.7.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +checksum = "86cd5ca076997b97ef09d3ad65efe811fa68c9e874cb636ccb211223a813b0c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] [[package]] name = "zeroize" -version = "1.6.0" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" + +[[package]] +name = "zstd" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "6.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.9+zstd.1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/terraform/backend.tf b/terraform/backend.tf deleted file mode 100644 index 8837c7f..0000000 --- a/terraform/backend.tf +++ /dev/null @@ -1,34 +0,0 @@ -# Terraform Configuration -terraform { - required_version = "~> 1.0" - required_providers { - assert = { - source = "bwoznicki/assert" - } - aws = { - source = "hashicorp/aws" - version = ">= 5.0.0" - } - grafana = { - source = "grafana/grafana" - version = "~> 1.28" - } - random = { - source = "hashicorp/random" - version = "3.4.3" - } - github = { - source = "integrations/github" - version = "5.7.0" - } - } - - backend "s3" { - region = "eu-central-1" - bucket = "opz" - workspace_key_prefix = "infra/env" - key = "apps/bouncer.tfstate" - - force_path_style = true - } -} diff --git a/terraform/ecs/analytics.tf b/terraform/ecs/analytics.tf deleted file mode 100644 index 90147da..0000000 --- a/terraform/ecs/analytics.tf +++ /dev/null @@ -1,65 +0,0 @@ -# Analytics Bucket Access -resource "aws_iam_policy" "analytics-data-lake_bucket_access" { - name = "${var.app_name}_analytics-data-lake_bucket_access" - path = "/" - description = "Allows ${var.app_name} to read/write from ${var.data_lake_bucket_name}" - - policy = jsonencode({ - "Version" : "2012-10-17", - "Statement" : [ - { - "Sid" : "ListObjectsInAnalyticsBucket", - "Effect" : "Allow", - "Action" : ["s3:ListBucket"], - "Resource" : ["arn:aws:s3:::${var.data_lake_bucket_name}"] - }, - { - "Sid" : "AllObjectActionsInAnalyticsBucket", - "Effect" : "Allow", - "Action" : "s3:PutObject", - "Resource" : ["arn:aws:s3:::${var.data_lake_bucket_name}/verify-server/*"] - }, - { - "Sid" : "AllGenerateDataKeyForAnalyticsBucket", - "Effect" : "Allow", - "Action" : ["kms:GenerateDataKey"], - "Resource" : var.data_lake_kms_key_arn, - } - ] - }) -} - -resource "aws_iam_role_policy_attachment" "analytics-data-lake_policy-attach" { - role = aws_iam_role.ecs_task_execution_role.name - policy_arn = aws_iam_policy.analytics-data-lake_bucket_access.arn -} - -# GeoIP Bucket Access -resource "aws_iam_policy" "geoip_bucket_access" { - name = "${var.app_name}_geoip_bucket_access" - path = "/" - description = "Allows ${var.app_name} to read from ${var.geoip_db_bucket_name}" - - policy = jsonencode({ - "Version" : "2012-10-17", - "Statement" : [ - { - "Sid" : "ListObjectsInGeoipBucket", - "Effect" : "Allow", - "Action" : ["s3:ListBucket"], - "Resource" : ["arn:aws:s3:::${var.geoip_db_bucket_name}"] - }, - { - "Sid" : "AllObjectActionsInGeoipBucket", - "Effect" : "Allow", - "Action" : ["s3:CopyObject", "s3:GetObject", "s3:HeadObject"], - "Resource" : ["arn:aws:s3:::${var.geoip_db_bucket_name}/*"] - }, - ] - }) -} - -resource "aws_iam_role_policy_attachment" "geoip-bucket-policy-attach" { - role = aws_iam_role.ecs_task_execution_role.name - policy_arn = aws_iam_policy.geoip_bucket_access.arn -} diff --git a/terraform/ecs/main.tf b/terraform/ecs/main.tf deleted file mode 100644 index db61658..0000000 --- a/terraform/ecs/main.tf +++ /dev/null @@ -1,390 +0,0 @@ -locals { - file_descriptor_soft_limit = pow(2, 18) - file_descriptor_hard_limit = local.file_descriptor_soft_limit * 2 -} - -# Log Group for our App -resource "aws_cloudwatch_log_group" "cluster_logs" { - name = "${var.app_name}_logs" - retention_in_days = 14 -} - -# ECS Cluster -resource "aws_ecs_cluster" "app_cluster" { - name = var.app_name - - configuration { - execute_command_configuration { - logging = "OVERRIDE" - - log_configuration { - cloud_watch_encryption_enabled = false - cloud_watch_log_group_name = aws_cloudwatch_log_group.cluster_logs.name - } - } - } -} - -## Task Definition -resource "aws_ecs_task_definition" "app_task_definition" { - family = var.app_name - cpu = var.cpu - memory = var.memory - requires_compatibilities = [ - "FARGATE" - ] - network_mode = "awsvpc" # Required because of fargate - execution_role_arn = aws_iam_role.ecs_task_execution_role.arn - task_role_arn = aws_iam_role.ecs_task_execution_role.arn - container_definitions = jsonencode([ - { - name = var.app_name, - image = var.image, - cpu = var.cpu - 128, # Remove sidecar memory/cpu so rest is assigned to primary container - ulimits = [{ - name : "nofile", - softLimit : local.file_descriptor_soft_limit, - hardLimit : local.file_descriptor_hard_limit - }], - memory = var.memory - 128, - essential = true, - portMappings = [ - { - containerPort = 8080, - hostPort = 8080 - }, - { - containerPort = 8081, - hostPort = 8081 - } - ], - environment = [ - { name = "PORT", value = "8080" }, - - { name = "PROMETHEUS_PORT", value = "8081" }, - - { name = "LOG_LEVEL", value = "INFO" }, - - { name = "ATTESTATION_CACHE_URL", value = "redis://${var.redis_url}/0" }, - - { name = "PROJECT_REGISTRY_CACHE_URL", value = "redis://${var.redis_url}/1" }, - { name = "PROJECT_REGISTRY_URL", value = var.project_registry_url }, - { name = "PROJECT_REGISTRY_AUTH_TOKEN", value = var.project_registry_auth_token }, - - { name = "DATA_API_URL", value = var.data_api_url }, - { name = "DATA_API_AUTH_TOKEN", value = var.data_api_auth_token }, - { name = "SCAM_GUARD_CACHE_URL", value = "redis://${var.redis_url}/2" }, - - { name = "SECRET", value = var.secret }, - - { "name" = "GEOIP_DB_BUCKET", "value" = var.geoip_db_bucket_name }, - { "name" = "GEOIP_DB_KEY", "value" = var.geoip_db_key }, - - { "name" = "DATA_LAKE_BUCKET", "value" = var.data_lake_bucket_name }, - - { "name" = "BLOCKED_COUNTRIES", "value" = "KP,IR,CU,SY" }, - ], - dependsOn = [ - { containerName = "aws-otel-collector", condition = "START" } - ], - logConfiguration = { - logDriver = "awslogs", - options = { - awslogs-group = aws_cloudwatch_log_group.cluster_logs.name, - awslogs-region = var.region, - awslogs-stream-prefix = "ecs" - } - } - }, - { - name = "aws-otel-collector", - image = "public.ecr.aws/aws-observability/aws-otel-collector:latest", - cpu = 128, - memory = 128, - environment = [ - { "name" : "AWS_PROMETHEUS_SCRAPING_ENDPOINT", "value" : "0.0.0.0:8081" }, - { name = "AWS_PROMETHEUS_ENDPOINT", value = "${var.prometheus_endpoint}api/v1/remote_write" }, - { name = "AWS_REGION", value = "eu-central-1" } - ], - essential = true, - command = [ - "--config=/etc/ecs/ecs-amp-xray-prometheus.yaml" - ], - logConfiguration = { - logDriver = "awslogs", - options = { - awslogs-create-group = "True", - awslogs-group = "/ecs/${var.app_name}-ecs-aws-otel-sidecar-collector", - awslogs-region = var.region, - awslogs-stream-prefix = "ecs" - } - } - } - ]) - - runtime_platform { - operating_system_family = "LINUX" - } -} - -## Service -resource "aws_ecs_service" "app_service" { - name = "${var.app_name}-service" - cluster = aws_ecs_cluster.app_cluster.id - task_definition = aws_ecs_task_definition.app_task_definition.arn - launch_type = "FARGATE" - desired_count = 2 - propagate_tags = "TASK_DEFINITION" - - # Wait for the service deployment to succeed - wait_for_steady_state = true - - # Allow external changes without Terraform plan difference - lifecycle { - ignore_changes = [desired_count] - } - - network_configuration { - subnets = var.private_subnets - assign_public_ip = true # We do public ingress through the LB - security_groups = [aws_security_group.app_ingress.id] # Setting the security group - } - - load_balancer { - target_group_arn = aws_lb_target_group.target_group.arn # Referencing our target group - container_name = var.app_name - container_port = 8080 # Specifying the container port - } -} - -# Load Balancers & Networking -resource "aws_lb" "application_load_balancer" { - name = "${var.app_name}-load-balancer" - load_balancer_type = "application" - subnets = var.public_subnets - - security_groups = [aws_security_group.lb_ingress.id] -} - - - -resource "aws_lb_target_group" "target_group" { - name = "${var.app_name}-target-group" - port = 8080 - protocol = "HTTP" - target_type = "ip" - vpc_id = var.vpc_id # Referencing the default VPC - slow_start = 30 # Give a 30 second delay to allow the service to startup - - health_check { - protocol = "HTTP" - path = "/health" # Echo Server's health path - port = 8080 - interval = 15 - timeout = 10 - healthy_threshold = 3 - unhealthy_threshold = 3 - } - - lifecycle { - create_before_destroy = true - } -} - -resource "aws_lb_listener" "listener" { - load_balancer_arn = aws_lb.application_load_balancer.arn # Referencing our load balancer - port = "443" - protocol = "HTTPS" - certificate_arn = var.acm_certificate_arn - ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06" - - default_action { - type = "forward" - target_group_arn = aws_lb_target_group.target_group.arn # Referencing our target group - } - - lifecycle { - create_before_destroy = true - } -} - -resource "aws_lb_listener" "listener-http" { - load_balancer_arn = aws_lb.application_load_balancer.arn - port = "80" - protocol = "HTTP" - - default_action { - type = "redirect" - - redirect { - port = "443" - protocol = "HTTPS" - status_code = "HTTP_301" - } - } -} - -# DNS Records -resource "aws_route53_record" "dns_load_balancer" { - zone_id = var.route53_zone_id - name = var.fqdn - type = "A" - - alias { - name = aws_lb.application_load_balancer.dns_name - zone_id = aws_lb.application_load_balancer.zone_id - evaluate_target_health = true - } -} - -resource "aws_route53_record" "backup_dns_load_balancer" { - zone_id = var.backup_route53_zone_id - name = var.backup_fqdn - type = "A" - - alias { - name = aws_lb.application_load_balancer.dns_name - zone_id = aws_lb.application_load_balancer.zone_id - evaluate_target_health = true - } -} - -resource "aws_lb_listener_certificate" "backup_cert" { - listener_arn = aws_lb_listener.listener.arn - certificate_arn = var.backup_acm_certificate_arn -} - -# IAM -resource "aws_iam_role" "ecs_task_execution_role" { - name = "${var.app_name}-ecs-task-execution-role" - assume_role_policy = data.aws_iam_policy_document.assume_role_policy.json -} - -data "aws_iam_policy_document" "assume_role_policy" { - statement { - actions = ["sts:AssumeRole"] - - principals { - type = "Service" - identifiers = ["ecs-tasks.amazonaws.com"] - } - } -} - -resource "aws_iam_role_policy_attachment" "ecs_task_execution_role_policy" { - role = aws_iam_role.ecs_task_execution_role.name - policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" -} - -resource "aws_iam_role_policy_attachment" "prometheus_write_policy" { - role = aws_iam_role.ecs_task_execution_role.name - policy_arn = "arn:aws:iam::aws:policy/AmazonPrometheusRemoteWriteAccess" -} - -resource "aws_iam_role_policy_attachment" "cloudwatch_write_policy" { - role = aws_iam_role.ecs_task_execution_role.name - policy_arn = "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess" -} - -resource "aws_iam_role_policy_attachment" "ssm_read_only_policy" { - role = aws_iam_role.ecs_task_execution_role.name - policy_arn = "arn:aws:iam::aws:policy/AmazonSSMReadOnlyAccess" -} - -resource "aws_iam_role_policy_attachment" "ecs_task_execution_role_xray_policy" { - role = aws_iam_role.ecs_task_execution_role.name - policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess" -} - -data "aws_iam_policy_document" "otel" { - statement { - actions = [ - "logs:PutLogEvents", - "logs:CreateLogGroup", - "logs:CreateLogStream", - "logs:DescribeLogStreams", - "logs:DescribeLogGroups", - "xray:PutTraceSegments", - "xray:PutTelemetryRecords", - "xray:GetSamplingRules", - "xray:GetSamplingTargets", - "xray:GetSamplingStatisticSummaries", - "ssm:GetParameters", - ] - resources = [ - "*" - ] - } -} -resource "aws_iam_policy" "otel" { - name = "${var.app_name}-otel" - path = "/" - policy = data.aws_iam_policy_document.otel.json -} -resource "aws_iam_role_policy_attachment" "ecs_task_execution_fetch_ghcr_secret_policy" { - role = aws_iam_role.ecs_task_execution_role.name - policy_arn = aws_iam_policy.otel.arn -} - -# Security Groups -resource "aws_security_group" "app_ingress" { - name = "${var.app_name}-ingress-to-app" - description = "Allow app port ingress" - vpc_id = var.vpc_id - - ingress { - from_port = 0 - to_port = 0 - protocol = "-1" - security_groups = [aws_security_group.lb_ingress.id] - } - - ingress { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = [var.vpc_cidr] - } - - egress { - from_port = 0 # Allowing any incoming port - to_port = 0 # Allowing any outgoing port - protocol = "-1" # Allowing any outgoing protocol - cidr_blocks = ["0.0.0.0/0"] # Allowing traffic out to all IP addresses - } - - lifecycle { - create_before_destroy = true - } -} - -resource "aws_security_group" "lb_ingress" { - name = "${var.app_name}-lb-ingress" - description = "Allow app port ingress from vpc" - vpc_id = var.vpc_id - - ingress { - from_port = 443 - to_port = 443 - protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] # Allowing traffic in from all sources - } - - ingress { - from_port = 80 - to_port = 80 - protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] # Allowing traffic in from all sources - } - - egress { - from_port = 0 # Allowing any incoming port - to_port = 0 # Allowing any outgoing port - protocol = "-1" # Allowing any outgoing protocol - cidr_blocks = [var.vpc_cidr] # Allowing traffic out to all VPC IP addresses - } - - lifecycle { - create_before_destroy = true - } -} diff --git a/terraform/ecs/outputs.tf b/terraform/ecs/outputs.tf deleted file mode 100644 index 9f2dbaf..0000000 --- a/terraform/ecs/outputs.tf +++ /dev/null @@ -1,3 +0,0 @@ -output "load_balancer_arn" { - value = aws_lb.application_load_balancer.arn -} diff --git a/terraform/ecs/variables.tf b/terraform/ecs/variables.tf deleted file mode 100644 index e0e0614..0000000 --- a/terraform/ecs/variables.tf +++ /dev/null @@ -1,120 +0,0 @@ -variable "region" { - type = string -} - -variable "app_name" { - type = string -} - -variable "environment" { - type = string -} - -variable "image" { - type = string -} - -variable "redis_url" { - type = string - sensitive = true -} - -variable "project_registry_url" { - type = string -} - -variable "project_registry_auth_token" { - type = string - sensitive = true -} - -variable "data_api_url" { - type = string -} - -variable "data_api_auth_token" { - type = string - sensitive = true -} - -variable "secret" { - type = string -} - -variable "prometheus_endpoint" { - type = string -} - -variable "vpc_id" { - type = string -} - -variable "vpc_cidr" { - type = string -} - -variable "route53_zone_id" { - type = string -} - -variable "fqdn" { - type = string -} - -variable "acm_certificate_arn" { - type = string -} - -variable "backup_acm_certificate_arn" { - type = string -} - -variable "backup_fqdn" { - type = string -} - -variable "backup_route53_zone_id" { - type = string -} - -variable "public_subnets" { - type = set(string) -} - -variable "private_subnets" { - type = set(string) -} - -variable "cpu" { - type = number -} - -variable "memory" { - type = number -} - -#--------------------------------------- -# GeoIP - -variable "geoip_db_bucket_name" { - description = "The name of the S3 bucket where the GeoIP database is stored" - type = string -} - -variable "geoip_db_key" { - description = "The key of the GeoIP database in the S3 bucket" - type = string -} - -#--------------------------------------- -# Analytics - -variable "data_lake_bucket_name" { - description = "The name of the S3 bucket where the analytics data is stored" - type = string -} - -variable "data_lake_kms_key_arn" { - description = "The ARN of the KMS encryption key for data-lake bucket." - type = string -} diff --git a/terraform/main.tf b/terraform/main.tf deleted file mode 100644 index 40edf69..0000000 --- a/terraform/main.tf +++ /dev/null @@ -1,125 +0,0 @@ -locals { - app_name = "verify" - fqdn = terraform.workspace == "prod" ? var.public_url : "${terraform.workspace}.${var.public_url}" - backup_fqdn = replace(local.fqdn, ".com", ".org") - latest_release_name = data.github_release.latest_release.name - version = coalesce(var.image_version, substr(local.latest_release_name, 1, length(local.latest_release_name))) - geoip_db_bucket_name = "${terraform.workspace}.relay.geo.ip.database.private.${terraform.workspace}.walletconnect" - data_lake_bucket_name = "walletconnect.data-lake.${terraform.workspace}" -} - -data "assert_test" "workspace" { - test = terraform.workspace != "default" - throw = "default workspace is not valid in this project" -} - -data "github_release" "latest_release" { - repository = "bouncer" - owner = "walletconnect" - retrieve_by = "latest" -} - -module "tags" { - source = "github.com/WalletConnect/terraform-modules.git//modules/tags" - - application = local.app_name - env = terraform.workspace -} - -module "dns" { - source = "github.com/WalletConnect/terraform-modules.git//modules/dns" - - hosted_zone_name = var.public_url - fqdn = local.fqdn -} - -module "backup_dns" { - source = "github.com/WalletConnect/terraform-modules.git//modules/dns" - - hosted_zone_name = replace(var.public_url, ".com", ".org") - fqdn = local.backup_fqdn -} - -resource "aws_prometheus_workspace" "prometheus" { - alias = "prometheus-${terraform.workspace}-${local.app_name}" -} - -module "o11y" { - source = "./monitoring" - - environment = terraform.workspace - app_name = local.app_name - prometheus_workspace_id = aws_prometheus_workspace.prometheus.id -} - -module "vpc" { - source = "terraform-aws-modules/vpc/aws" - name = "${terraform.workspace}-${local.app_name}" - - cidr = "10.0.0.0/16" - - azs = var.azs - private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"] - public_subnets = ["10.0.4.0/24", "10.0.5.0/24", "10.0.6.0/24"] - - private_subnet_tags = { - Visibility = "private" - } - public_subnet_tags = { - Visibility = "public" - } - - enable_dns_support = true - enable_dns_hostnames = true - enable_nat_gateway = true - single_nat_gateway = true - one_nat_gateway_per_az = false -} - -data "aws_ecr_repository" "repository" { - name = "bouncer" -} - -module "redis" { - source = "./redis" - - redis_name = "${terraform.workspace}-${local.app_name}" - app_name = local.app_name - node_type = "cache.t4g.micro" - vpc_id = module.vpc.vpc_id - allowed_egress_cidr_blocks = [module.vpc.vpc_cidr_block] - allowed_ingress_cidr_blocks = [module.vpc.vpc_cidr_block] - private_subnets = module.vpc.private_subnets -} - -module "ecs" { - source = "./ecs" - - app_name = "${terraform.workspace}-${local.app_name}" - environment = terraform.workspace - prometheus_endpoint = aws_prometheus_workspace.prometheus.prometheus_endpoint - image = "${data.aws_ecr_repository.repository.repository_url}:${local.version}" - acm_certificate_arn = module.dns.certificate_arn - cpu = var.cpu - fqdn = local.fqdn - memory = var.memory - private_subnets = module.vpc.private_subnets - public_subnets = module.vpc.public_subnets - region = var.region - route53_zone_id = module.dns.zone_id - backup_acm_certificate_arn = module.backup_dns.certificate_arn - backup_fqdn = local.backup_fqdn - backup_route53_zone_id = module.backup_dns.zone_id - vpc_cidr = module.vpc.vpc_cidr_block - vpc_id = module.vpc.vpc_id - redis_url = module.redis.endpoint - project_registry_url = var.project_registry_url - project_registry_auth_token = var.project_registry_auth_token - data_api_url = var.data_api_url - data_api_auth_token = var.data_api_auth_token - secret = var.secret - geoip_db_bucket_name = local.geoip_db_bucket_name - geoip_db_key = var.geoip_db_key - data_lake_bucket_name = local.data_lake_bucket_name - data_lake_kms_key_arn = var.data_lake_kms_key_arn -} diff --git a/terraform/monitoring/grafana-dashboard.json.tpl b/terraform/monitoring/grafana-dashboard.json.tpl deleted file mode 100644 index 6059080..0000000 --- a/terraform/monitoring/grafana-dashboard.json.tpl +++ /dev/null @@ -1,2228 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, - "type": "dashboard" - } - ] - }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 44, - "links": [], - "liveNow": false, - "panels": [ - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 16, - "panels": [], - "title": "HTTP Server", - "type": "row" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - } - }, - "mappings": [] - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "5XX" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "red", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Other" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "orange", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 8, - "w": 8, - "x": 0, - "y": 1 - }, - "id": 25, - "options": { - "legend": { - "displayMode": "list", - "placement": "bottom" - }, - "pieType": "pie", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "exemplar": true, - "expr": "sum(increase(axum_http_requests_total { status =~ \"^2.*\"}[$__range]))", - "format": "time_series", - "hide": false, - "interval": "", - "legendFormat": "2XX", - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "exemplar": true, - "expr": "sum(increase(axum_http_requests_total { status =~ \"^4.*\"}[$__range]))", - "format": "time_series", - "hide": false, - "interval": "", - "legendFormat": "4XX", - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "exemplar": true, - "expr": "sum(increase(axum_http_requests_total { status =~ \"^5.*\"}[$__range]))", - "format": "time_series", - "hide": false, - "interval": "", - "legendFormat": "5XX", - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "exemplar": true, - "expr": "sum(increase(axum_http_requests_total { status !~ \"^(2|4|5).*\"}[$__range]))", - "format": "time_series", - "hide": false, - "interval": "", - "legendFormat": "Other", - "refId": "D" - } - ], - "title": "Response Status Codes ", - "type": "piechart" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byFrameRefID", - "options": "A" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "green", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byFrameRefID", - "options": "B" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "yellow", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byFrameRefID", - "options": "C" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "red", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 8, - "w": 16, - "x": 8, - "y": 1 - }, - "id": 18, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "right" - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "exemplar": true, - "expr": "sum(rate(axum_http_requests_total{status=~\"^5.*\"}[1m])) by (status, method, endpoint)", - "hide": false, - "instant": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{status}} {{method}} {{endpoint}}", - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "exemplar": true, - "expr": "sum(rate(axum_http_requests_total{status=~\"^4.*\"}[1m])) by (status, method, endpoint)", - "hide": false, - "instant": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{status}} {{method}} {{endpoint}}", - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "exemplar": true, - "expr": "sum(rate(axum_http_requests_total{status!~\"^(2|4|5).*\"}[1m])) by (status, method, endpoint)", - "hide": false, - "instant": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{status}} {{method}} {{endpoint}}", - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "exemplar": true, - "expr": "sum(rate(axum_http_requests_total{status=~\"^2.*$\"}[1m])) by (status, method, endpoint)", - "instant": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{status}} {{method}} {{endpoint}}", - "refId": "A" - } - ], - "title": "Request-Response / s", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 8, - "x": 0, - "y": 9 - }, - "id": 23, - "options": { - "displayMode": "lcd", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showUnfilled": true - }, - "pluginVersion": "8.4.7", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "exemplar": true, - "expr": "histogram_quantile(0.5, sum by (le) (rate(axum_http_requests_duration_seconds_bucket[$__range]))) * 1000", - "format": "time_series", - "hide": false, - "interval": "", - "legendFormat": "0.5", - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "exemplar": true, - "expr": "histogram_quantile(0.9, sum by (le) (rate(axum_http_requests_duration_seconds_bucket[$__range]))) * 1000", - "format": "time_series", - "hide": false, - "interval": "", - "legendFormat": "0.9", - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "exemplar": true, - "expr": "histogram_quantile(0.99, sum by (le) (rate(axum_http_requests_duration_seconds_bucket[$__range]))) * 1000", - "format": "time_series", - "hide": false, - "interval": "", - "legendFormat": "0.99", - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "exemplar": true, - "expr": "histogram_quantile(0.999, sum by (le) (rate(axum_http_requests_duration_seconds_bucket[$__range]))) * 1000", - "format": "time_series", - "hide": false, - "interval": "", - "legendFormat": "0.999", - "refId": "E" - } - ], - "title": "Latency quantiles", - "type": "bargauge" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "line" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 100 - } - ] - }, - "unit": "ms" - }, - "overrides": [ - { - "matcher": { - "id": "byFrameRefID", - "options": "read" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "green", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byFrameRefID", - "options": "write" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "blue", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 8, - "w": 16, - "x": 8, - "y": 9 - }, - "id": 21, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "right" - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "8.4.7", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "exemplar": true, - "expr": "max(increase(axum_http_requests_duration_seconds_sum{method !~ \"GET|HEAD\"}[1m]) * 1000 / increase(axum_http_requests_duration_seconds_count{method !~ \"GET|HEAD\"}[1m])) by (method, endpoint)", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "{{method}} {{endpoint}}", - "refId": "write" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "exemplar": true, - "expr": "max(increase(axum_http_requests_duration_seconds_sum{method =~ \"GET|HEAD\"}[1m]) * 1000 / increase(axum_http_requests_duration_seconds_count{method =~ \"GET|HEAD\"}[1m])) by (status, method, endpoint)", - "format": "time_series", - "hide": false, - "interval": "", - "legendFormat": "{{method}} {{endpoint}}", - "refId": "read" - } - ], - "title": "Max avg latency [1m]", - "type": "timeseries" - }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 17 - }, - "id": 8, - "panels": [], - "title": "Project Registry", - "type": "row" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "errors" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "red", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "total" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "green", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 6, - "w": 8, - "x": 0, - "y": 18 - }, - "id": 10, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "exemplar": true, - "expr": "sum(rate(project_registry_errors[1m]))", - "hide": false, - "interval": "", - "legendFormat": "errors", - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "exemplar": true, - "expr": "sum(rate(project_registry_requests[1m]))", - "interval": "", - "legendFormat": "total", - "refId": "A" - } - ], - "title": "Requests / s", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "errors" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "red", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "hits" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "green", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 6, - "w": 8, - "x": 8, - "y": 18 - }, - "id": 12, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "exemplar": true, - "expr": "rate(project_registry_cache_errors[1m]) or vector(0)", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "errors", - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "exemplar": true, - "expr": "sum(rate(project_registry_cache_misses[1m]))", - "hide": false, - "interval": "", - "legendFormat": "misses", - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "exemplar": true, - "expr": "sum(rate(project_registry_cache_hits[1m]))", - "interval": "", - "legendFormat": "hits", - "refId": "A" - } - ], - "title": "Cache reads / s", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "errors" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "red", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "writes" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "green", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 6, - "w": 8, - "x": 16, - "y": 18 - }, - "id": 13, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "exemplar": true, - "expr": "rate(project_registry_cache_write_errors[1m]) or vector(0)", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "errors", - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "exemplar": true, - "expr": "sum(rate(project_registry_cache_writes[1m]))", - "interval": "", - "legendFormat": "writes", - "refId": "A" - } - ], - "title": "Cache writes / s", - "type": "timeseries" - }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 24 - }, - "id": 5, - "panels": [], - "title": "Redis", - "type": "row" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "total" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "dark-green", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "total errors" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "dark-red", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byFrameRefID", - "options": "per_database" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "green", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byFrameRefID", - "options": "errors_per_database" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "red", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 7, - "w": 12, - "x": 0, - "y": 25 - }, - "id": 2, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "exemplar": true, - "expr": "sum(rate(redis_read_errors{}[1m])) or vector(0)", - "hide": false, - "interval": "", - "legendFormat": "total errors", - "refId": "total_errors" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "exemplar": true, - "expr": "sum(rate(redis_reads{}[1m]))", - "hide": false, - "interval": "", - "legendFormat": "total", - "refId": "total" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "exemplar": true, - "expr": "sum(rate(redis_read_errors{}[1m]) or vector(0)) by (db)", - "hide": false, - "interval": "", - "legendFormat": "{{db}} errors", - "refId": "errors_per_database" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "exemplar": true, - "expr": "sum(rate(redis_reads{}[1m])) by (db)", - "interval": "", - "legendFormat": "{{db}}", - "refId": "per_database" - } - ], - "title": "Reads / s", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "total" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "dark-green", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "total errors" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "dark-red", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byFrameRefID", - "options": "per_database" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "green", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byFrameRefID", - "options": "errors_per_database" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "red", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 7, - "w": 12, - "x": 12, - "y": 25 - }, - "id": 19, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "exemplar": true, - "expr": "sum(rate(redis_write_errors{}[1m])) or vector(0)", - "hide": false, - "interval": "", - "legendFormat": "total errors", - "refId": "total_errors" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "exemplar": true, - "expr": "sum(rate(redis_writes{}[1m]))", - "hide": false, - "interval": "", - "legendFormat": "total", - "refId": "total" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "exemplar": true, - "expr": "sum(rate(redis_write_errors{}[1m]) or vector(0)) by (db)", - "hide": false, - "interval": "", - "legendFormat": "{{db}} errors", - "refId": "errors_per_database" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${prometheus_data_source_uid}" - - }, - "exemplar": true, - "expr": "sum(rate(redis_writes{}[1m])) by (db)", - "interval": "", - "legendFormat": "{{db}}", - "refId": "per_database" - } - ], - "title": "Writes / s", - "type": "timeseries" - }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 32 - }, - "id": 27, - "panels": [], - "title": "AWS/ECS", - "type": "row" - }, - { - "datasource": { - - "type": "cloudwatch", - "uid": "${cloudwatch_data_source_uid}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "decimals": 1, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "percent" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "max" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "red", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "min" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "green", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 33 - }, - "id": 29, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "alias": "max", - "datasource": { - "type": "cloudwatch", - "uid": "${cloudwatch_data_source_uid}" - }, - "dimensions": { - "ClusterName": "${environment}-verify" - }, - "expression": "", - "id": "", - "matchExact": false, - "metricEditorMode": 0, - "metricName": "CPUUtilization", - "metricQueryType": 0, - "namespace": "AWS/ECS", - "period": "", - "queryMode": "Metrics", - "refId": "A", - "region": "default", - "sqlExpression": "", - "statistic": "Maximum" - }, - { - "alias": "avg", - "datasource": { - "type": "cloudwatch", - "uid": "${cloudwatch_data_source_uid}" - }, - "dimensions": { - "ClusterName": "${environment}-verify" - }, - "expression": "", - "hide": false, - "id": "", - "matchExact": false, - "metricEditorMode": 0, - "metricName": "CPUUtilization", - "metricQueryType": 0, - "namespace": "AWS/ECS", - "period": "", - "queryMode": "Metrics", - "refId": "B", - "region": "default", - "sqlExpression": "", - "statistic": "Average" - }, - { - "alias": "min", - "datasource": { - "type": "cloudwatch", - "uid": "${cloudwatch_data_source_uid}" - }, - "dimensions": { - "ClusterName": "${environment}-verify" - }, - "expression": "", - "hide": false, - "id": "", - "matchExact": false, - "metricEditorMode": 0, - "metricName": "CPUUtilization", - "metricQueryType": 0, - "namespace": "AWS/ECS", - "period": "", - "queryMode": "Metrics", - "refId": "C", - "region": "default", - "sqlExpression": "", - "statistic": "Minimum" - } - ], - "title": "CPU Utilization", - "type": "timeseries" - }, - { - "datasource": { - "type": "cloudwatch", - "uid": "${cloudwatch_data_source_uid}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "decimals": 1, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "percent" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "max" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "red", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "min" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "green", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 33 - }, - "id": 30, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "alias": "max", - "datasource": { - "type": "cloudwatch", - "uid": "${cloudwatch_data_source_uid}" - }, - "dimensions": { - "ClusterName": "${environment}-verify" - }, - "expression": "", - "id": "", - "matchExact": false, - "metricEditorMode": 0, - "metricName": "MemoryUtilization", - "metricQueryType": 0, - "namespace": "AWS/ECS", - "period": "", - "queryMode": "Metrics", - "refId": "A", - "region": "default", - "sqlExpression": "", - "statistic": "Maximum" - }, - { - "alias": "avg", - "datasource": { - "type": "cloudwatch", - "uid": "${cloudwatch_data_source_uid}" - }, - "dimensions": { - "ClusterName": "${environment}-verify" - }, - "expression": "", - "hide": false, - "id": "", - "matchExact": false, - "metricEditorMode": 0, - "metricName": "MemoryUtilization", - "metricQueryType": 0, - "namespace": "AWS/ECS", - "period": "", - "queryMode": "Metrics", - "refId": "B", - "region": "default", - "sqlExpression": "", - "statistic": "Average" - }, - { - "alias": "min", - "datasource": { - "type": "cloudwatch", - "uid": "${cloudwatch_data_source_uid}" - }, - "dimensions": { - "ClusterName": "${environment}-verify" - }, - "expression": "", - "hide": false, - "id": "", - "matchExact": false, - "metricEditorMode": 0, - "metricName": "MemoryUtilization", - "metricQueryType": 0, - "namespace": "AWS/ECS", - "period": "", - "queryMode": "Metrics", - "refId": "C", - "region": "default", - "sqlExpression": "", - "statistic": "Minimum" - } - ], - "title": "Memory Utilization", - "type": "timeseries" - }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 41 - }, - "id": 33, - "panels": [], - "title": "AWS/ElastiCache", - "type": "row" - }, - { - "datasource": { - "type": "cloudwatch", - "uid": "${cloudwatch_data_source_uid}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "decimals": 1, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "percent" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "max" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "red", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "min" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "green", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 42 - }, - "id": 31, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "alias": "max", - "datasource": { - "type": "cloudwatch", - "uid": "${cloudwatch_data_source_uid}" - }, - "dimensions": { - "CacheClusterId": "verify-${environment}-verify", - "CacheNodeId": "*" - }, - "expression": "", - "hide": false, - "id": "", - "matchExact": false, - "metricEditorMode": 0, - "metricName": "CPUUtilization", - "metricQueryType": 0, - "namespace": "AWS/ElastiCache", - "period": "", - "queryMode": "Metrics", - "refId": "A", - "region": "default", - "sqlExpression": "", - "statistic": "Maximum" - }, - { - "alias": "avg", - "datasource": { - "type": "cloudwatch", - "uid": "${cloudwatch_data_source_uid}" - }, - "dimensions": { - "CacheClusterId": "verify-${environment}-verify", - "CacheNodeId": "*" - }, - "expression": "", - "hide": false, - "id": "", - "matchExact": false, - "metricEditorMode": 0, - "metricName": "CPUUtilization", - "metricQueryType": 0, - "namespace": "AWS/ElastiCache", - "period": "", - "queryMode": "Metrics", - "refId": "B", - "region": "default", - "sqlExpression": "", - "statistic": "Average" - }, - { - "alias": "min", - "datasource": { - "type": "cloudwatch", - "uid": "${cloudwatch_data_source_uid}" - }, - "dimensions": { - "CacheClusterId": "verify-${environment}-verify", - "CacheNodeId": "*" - }, - "expression": "", - "hide": false, - "id": "", - "matchExact": false, - "metricEditorMode": 0, - "metricName": "CPUUtilization", - "metricQueryType": 0, - "namespace": "AWS/ElastiCache", - "period": "", - "queryMode": "Metrics", - "refId": "C", - "region": "default", - "sqlExpression": "", - "statistic": "Minimum" - } - ], - "title": "CPU Utilization", - "type": "timeseries" - }, - { - "datasource": { - "type": "cloudwatch", - "uid": "${cloudwatch_data_source_uid}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "decimals": 1, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "percent" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "max" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "red", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "min" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "green", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 42 - }, - "id": 34, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "alias": "max", - "datasource": { - "type": "cloudwatch", - "uid": "${cloudwatch_data_source_uid}" - }, - "dimensions": { - "CacheClusterId": "verify-${environment}-verify", - "CacheNodeId": "*" - }, - "expression": "", - "hide": false, - "id": "", - "matchExact": false, - "metricEditorMode": 0, - "metricName": "DatabaseMemoryUsagePercentage", - "metricQueryType": 0, - "namespace": "AWS/ElastiCache", - "period": "", - "queryMode": "Metrics", - "refId": "A", - "region": "default", - "sqlExpression": "", - "statistic": "Maximum" - }, - { - "alias": "avg", - "datasource": { - "type": "cloudwatch", - "uid": "${cloudwatch_data_source_uid}" - }, - "dimensions": { - "CacheClusterId": "verify-${environment}-verify", - "CacheNodeId": "*" - }, - "expression": "", - "hide": false, - "id": "", - "matchExact": false, - "metricEditorMode": 0, - "metricName": "DatabaseMemoryUsagePercentage", - "metricQueryType": 0, - "namespace": "AWS/ElastiCache", - "period": "", - "queryMode": "Metrics", - "refId": "B", - "region": "default", - "sqlExpression": "", - "statistic": "Average" - }, - { - "alias": "min", - "datasource": { - "type": "cloudwatch", - "uid": "${cloudwatch_data_source_uid}" - }, - "dimensions": { - "CacheClusterId": "verify-${environment}-verify", - "CacheNodeId": "*" - }, - "expression": "", - "hide": false, - "id": "", - "matchExact": false, - "metricEditorMode": 0, - "metricName": "DatabaseMemoryUsagePercentage", - "metricQueryType": 0, - "namespace": "AWS/ElastiCache", - "period": "", - "queryMode": "Metrics", - "refId": "C", - "region": "default", - "sqlExpression": "", - "statistic": "Minimum" - } - ], - "title": "Memory Utilization", - "type": "timeseries" - } - ], - "refresh": false, - "schemaVersion": 35, - "style": "dark", - "tags": [], - "templating": { - "list": [] - }, - "time": { - "from": "now-6h", - "to": "now" - }, - "timepicker": {}, - "timezone": "", - "title": "${environment}_verify", - "uid": "${environment}_verify", - "version": 10, - "weekStart": "" -} \ No newline at end of file diff --git a/terraform/monitoring/main.tf b/terraform/monitoring/main.tf deleted file mode 100644 index a85c80a..0000000 --- a/terraform/monitoring/main.tf +++ /dev/null @@ -1,59 +0,0 @@ -terraform { - required_version = "~> 1.0" - - required_providers { - grafana = { - source = "grafana/grafana" - version = "~> 1.24" - } - } -} - -locals { - opsgenie_notification_channel = "NNOynGwVz" - notifications = ( - var.environment == "prod" ? - "[{\"uid\": \"${local.opsgenie_notification_channel}\"}]" : - "[]" - ) -} - -resource "grafana_data_source" "prometheus" { - type = "prometheus" - name = "${var.environment}-${var.app_name}-amp" - url = "https://aps-workspaces.eu-central-1.amazonaws.com/workspaces/${var.prometheus_workspace_id}/" - - json_data_encoded = jsonencode({ - httpMethod = "GET" - manageAlerts = false - sigV4Auth = true - sigV4AuthType = "ec2_iam_role" - sigV4Region = "eu-central-1" - }) -} - -resource "grafana_data_source" "cloudwatch" { - type = "cloudwatch" - name = "${var.environment}-${var.app_name}-cloudwatch" - - json_data { - default_region = "eu-central-1" - } -} - -# JSON Dashboard. When exporting from Grafana make sure that all -# variables are replaced properly using template syntax -data "template_file" "grafana_dashboard_template" { - template = file("monitoring/grafana-dashboard.json.tpl") - vars = { - environment = var.environment - prometheus_data_source_uid = grafana_data_source.prometheus.uid - cloudwatch_data_source_uid = grafana_data_source.cloudwatch.uid - } -} - -resource "grafana_dashboard" "at_a_glance" { - overwrite = true - message = "Updated by Terraform" - config_json = data.template_file.grafana_dashboard_template.rendered -} diff --git a/terraform/monitoring/variables.tf b/terraform/monitoring/variables.tf deleted file mode 100644 index 3342b51..0000000 --- a/terraform/monitoring/variables.tf +++ /dev/null @@ -1,11 +0,0 @@ -variable "environment" { - type = string -} - -variable "app_name" { - type = string -} - -variable "prometheus_workspace_id" { - type = string -} diff --git a/terraform/provider.tf b/terraform/provider.tf deleted file mode 100644 index 3a59b2b..0000000 --- a/terraform/provider.tf +++ /dev/null @@ -1,22 +0,0 @@ -provider "aws" { - region = var.region - - # Make it faster by skipping something - skip_metadata_api_check = true - skip_region_validation = true - skip_credentials_validation = true - skip_requesting_account_id = true - - default_tags { - tags = module.tags.tags - } -} - -# Expects GRAFANA_AUTH env variable to be set -provider "grafana" { - url = "https://${var.grafana_endpoint}" -} - -provider "random" {} - -provider "github" {} \ No newline at end of file diff --git a/terraform/redis/main.tf b/terraform/redis/main.tf deleted file mode 100644 index 914b6a3..0000000 --- a/terraform/redis/main.tf +++ /dev/null @@ -1,52 +0,0 @@ -terraform { - required_version = "~> 1.0" - - required_providers { - aws = { - source = "hashicorp/aws" - version = ">= 5.0.0" - } - } -} - -resource "aws_elasticache_cluster" "cache" { - cluster_id = replace("${var.app_name}-${var.redis_name}", "_", "-") - engine = "redis" - node_type = var.node_type - num_cache_nodes = 1 - parameter_group_name = "default.redis6.x" - engine_version = "6.x" - port = 6379 - subnet_group_name = aws_elasticache_subnet_group.private_subnets.name - security_group_ids = [ - aws_security_group.service_security_group.id - ] - snapshot_retention_limit = 2 -} - -resource "aws_elasticache_subnet_group" "private_subnets" { - name = replace("${var.app_name}-${var.redis_name}-private-subnet-group", "_", "-") - subnet_ids = var.private_subnets -} - -# Allow only the app to access Redis -resource "aws_security_group" "service_security_group" { - name = "${var.app_name}-${var.redis_name}-redis-service-ingress" - description = "Allow ingress from the application" - vpc_id = var.vpc_id - ingress { - description = "${var.app_name}-${var.redis_name} - ingress from application" - from_port = 6379 - to_port = 6379 - protocol = "TCP" - cidr_blocks = var.allowed_ingress_cidr_blocks - } - - egress { - description = "${var.app_name}-${var.redis_name} - egress to application" - from_port = 0 # Allowing any incoming port - to_port = 0 # Allowing any outgoing port - protocol = "-1" # Allowing any outgoing protocol - cidr_blocks = var.allowed_egress_cidr_blocks - } -} diff --git a/terraform/redis/outputs.tf b/terraform/redis/outputs.tf deleted file mode 100644 index b6dd02d..0000000 --- a/terraform/redis/outputs.tf +++ /dev/null @@ -1,7 +0,0 @@ -output "endpoint" { - value = aws_elasticache_cluster.cache.cache_nodes[0].address -} - -output "cluster_id" { - value = aws_elasticache_cluster.cache.id -} diff --git a/terraform/redis/variables.tf b/terraform/redis/variables.tf deleted file mode 100644 index 2e58ae2..0000000 --- a/terraform/redis/variables.tf +++ /dev/null @@ -1,29 +0,0 @@ -variable "redis_name" { - type = string -} - -variable "node_type" { - type = string -} - -variable "app_name" { - type = string -} - -variable "allowed_ingress_cidr_blocks" { - type = list(string) - default = null -} - -variable "allowed_egress_cidr_blocks" { - type = list(string) - default = null -} - -variable "private_subnets" { - type = set(string) -} - -variable "vpc_id" { - type = string -} diff --git a/terraform/variables.tf b/terraform/variables.tf deleted file mode 100644 index b42e1f2..0000000 --- a/terraform/variables.tf +++ /dev/null @@ -1,70 +0,0 @@ -variable "cpu" { - type = number -} - -variable "memory" { - type = number -} - -variable "project_registry_url" { - type = string -} - -variable "project_registry_auth_token" { - type = string - sensitive = true -} - -variable "data_api_url" { - type = string -} - -variable "data_api_auth_token" { - type = string - sensitive = true -} - -variable "region" { - type = string - default = "eu-central-1" -} - -variable "secret" { - type = string -} - -variable "azs" { - type = list(string) - default = ["eu-central-1a", "eu-central-1b", "eu-central-1c"] -} - -variable "public_url" { - type = string - default = "verify.walletconnect.com" -} - -variable "grafana_endpoint" { - type = string -} - -variable "image_version" { - type = string - default = "" -} - -#--------------------------------------- -# GeoIP - -variable "geoip_db_key" { - description = "The name to the GeoIP database" - type = string - default = "GeoLite2-City.mmdb" -} - -#--------------------------------------- -# Analytics - -variable "data_lake_kms_key_arn" { - description = "The ARN of the KMS encryption key for data-lake bucket." - type = string -} diff --git a/terraform/vars/dev.tfvars b/terraform/vars/dev.tfvars deleted file mode 100644 index 4ef4510..0000000 --- a/terraform/vars/dev.tfvars +++ /dev/null @@ -1,5 +0,0 @@ -cpu = 512 -memory = 1024 -project_registry_url = "https://registry-prod-cf.walletconnect.com" -data_api_url = "https://data.walletconnect.com" -data_lake_kms_key_arn = "arn:aws:kms:eu-central-1:898587786287:key/d1d2f047-b2a3-4f4a-8786-7c87ee83c954" diff --git a/terraform/vars/prod.tfvars b/terraform/vars/prod.tfvars deleted file mode 100644 index ccc0f42..0000000 --- a/terraform/vars/prod.tfvars +++ /dev/null @@ -1,5 +0,0 @@ -cpu = 4096 -memory = 8192 -project_registry_url = "https://registry-prod-cf.walletconnect.com" -data_api_url = "https://data.walletconnect.com" -data_lake_kms_key_arn = "arn:aws:kms:eu-central-1:898587786287:key/06e7c9fd-943d-47bf-bcf4-781b44411ba4" diff --git a/terraform/vars/staging.tfvars b/terraform/vars/staging.tfvars deleted file mode 100644 index 4ef4510..0000000 --- a/terraform/vars/staging.tfvars +++ /dev/null @@ -1,5 +0,0 @@ -cpu = 512 -memory = 1024 -project_registry_url = "https://registry-prod-cf.walletconnect.com" -data_api_url = "https://data.walletconnect.com" -data_lake_kms_key_arn = "arn:aws:kms:eu-central-1:898587786287:key/d1d2f047-b2a3-4f4a-8786-7c87ee83c954" From ec0dfbc9cc58837dfeb4992931147be2f5f7c60d Mon Sep 17 00:00:00 2001 From: Xavier Basty Date: Fri, 2 Feb 2024 14:44:04 +0100 Subject: [PATCH 2/5] account migration --- .github/codeowners | 2 + .github/workflows/debug_validate.yml | 35 + .github/workflows/dispatch_deploy.yml | 79 +++ .github/workflows/dispatch_publish.yml | 52 ++ .github/workflows/dispatch_validate.yml | 57 ++ .github/workflows/event_intake.yml | 47 ++ .github/workflows/event_pr.yml | 64 ++ .github/workflows/event_release.yml | 64 ++ .github/workflows/sub-cd.yml | 79 +++ .github/workflows/sub-validate.yml | 85 +++ .gitignore | 80 ++- .gitmodules | 3 + justfile | 623 ++++++++++++++---- ops/docker-compose.ot.yml | 24 + ops/docker-compose.storage.yml | 148 +++++ terraform/.terraform-docs.yml | 40 ++ terraform/.terraform.lock.hcl | 89 +++ terraform/.tflint.hcl | 19 + terraform/README.md | 56 ++ terraform/alerting/README.md | 43 ++ terraform/alerting/alarms_ecs.tf | 45 ++ terraform/alerting/alarms_redis.tf | 43 ++ terraform/alerting/context.tf | 179 +++++ terraform/alerting/main.tf | 39 ++ terraform/alerting/terraform.tf | 10 + terraform/alerting/variables.tf | 54 ++ terraform/context.tf | 23 + terraform/ecs/README.md | 87 +++ terraform/ecs/cluster.tf | 188 ++++++ terraform/ecs/cluster_autoscaling.tf | 43 ++ terraform/ecs/cluster_iam.tf | 118 ++++ terraform/ecs/cluster_logs.tf | 17 + terraform/ecs/context.tf | 179 +++++ terraform/ecs/dns.tf | 14 + terraform/ecs/main.tf | 3 + terraform/ecs/network.tf | 161 +++++ terraform/ecs/outputs.tf | 34 + terraform/ecs/terraform.tf | 14 + terraform/ecs/variables.tf | 231 +++++++ terraform/inputs.tf | 39 ++ terraform/main.tf | 48 ++ terraform/monitoring/context.tf | 179 +++++ terraform/monitoring/dashboard.jsonnet | 80 +++ terraform/monitoring/data_sources.tf | 34 + .../monitoring/grafana-dashboard.json.tpl | 480 ++++++++++++++ terraform/monitoring/grafonnet-lib | 1 + terraform/monitoring/main.tf | 23 + .../panels/ecs/availability.libsonnet | 55 ++ terraform/monitoring/panels/ecs/cpu.libsonnet | 73 ++ .../monitoring/panels/ecs/memory.libsonnet | 73 ++ .../panels/http/average_latency.libsonnet | 57 ++ .../panels/http/latency_quantiles.libsonnet | 59 ++ .../panels/http/request_response.libsonnet | 75 +++ .../panels/http/response_status.libsonnet | 65 ++ terraform/monitoring/panels/panels.libsonnet | 27 + .../monitoring/panels/redis/cpu.libsonnet | 75 +++ .../monitoring/panels/redis/memory.libsonnet | 75 +++ .../monitoring/panels/redis/reads.libsonnet | 78 +++ .../monitoring/panels/redis/writes.libsonnet | 78 +++ .../panels/registry/cache_read.libsonnet | 63 ++ .../panels/registry/cache_write.libsonnet | 56 ++ .../panels/registry/requests.libsonnet | 52 ++ terraform/monitoring/terraform.tf | 14 + terraform/monitoring/variables.tf | 24 + terraform/outputs.tf | 0 terraform/providers.tf | 12 + terraform/redis/README.md | 43 ++ terraform/redis/context.tf | 193 ++++++ terraform/redis/main.tf | 45 ++ terraform/redis/outputs.tf | 9 + terraform/redis/terraform.tf | 10 + terraform/redis/variables.tf | 45 ++ terraform/res_alerting.tf | 12 + terraform/res_dns.tf | 13 + terraform/res_ecs.tf | 82 +++ terraform/res_monitoring.tf | 10 + terraform/res_network.tf | 134 ++++ terraform/res_redis.tf | 7 + terraform/terraform.tf | 27 + terraform/variables.tf | 136 ++++ 80 files changed, 5703 insertions(+), 129 deletions(-) create mode 100644 .github/codeowners create mode 100644 .github/workflows/debug_validate.yml create mode 100644 .github/workflows/dispatch_deploy.yml create mode 100644 .github/workflows/dispatch_publish.yml create mode 100644 .github/workflows/dispatch_validate.yml create mode 100644 .github/workflows/event_intake.yml create mode 100644 .github/workflows/event_pr.yml create mode 100644 .github/workflows/event_release.yml create mode 100644 .github/workflows/sub-cd.yml create mode 100644 .github/workflows/sub-validate.yml create mode 100644 .gitmodules create mode 100644 ops/docker-compose.ot.yml create mode 100644 ops/docker-compose.storage.yml create mode 100644 terraform/.terraform-docs.yml create mode 100644 terraform/.terraform.lock.hcl create mode 100644 terraform/.tflint.hcl create mode 100644 terraform/README.md create mode 100644 terraform/alerting/README.md create mode 100644 terraform/alerting/alarms_ecs.tf create mode 100644 terraform/alerting/alarms_redis.tf create mode 100644 terraform/alerting/context.tf create mode 100644 terraform/alerting/main.tf create mode 100644 terraform/alerting/terraform.tf create mode 100644 terraform/alerting/variables.tf create mode 100644 terraform/context.tf create mode 100644 terraform/ecs/README.md create mode 100644 terraform/ecs/cluster.tf create mode 100644 terraform/ecs/cluster_autoscaling.tf create mode 100644 terraform/ecs/cluster_iam.tf create mode 100644 terraform/ecs/cluster_logs.tf create mode 100644 terraform/ecs/context.tf create mode 100644 terraform/ecs/dns.tf create mode 100644 terraform/ecs/main.tf create mode 100644 terraform/ecs/network.tf create mode 100644 terraform/ecs/outputs.tf create mode 100644 terraform/ecs/terraform.tf create mode 100644 terraform/ecs/variables.tf create mode 100644 terraform/inputs.tf create mode 100644 terraform/main.tf create mode 100644 terraform/monitoring/context.tf create mode 100644 terraform/monitoring/dashboard.jsonnet create mode 100644 terraform/monitoring/data_sources.tf create mode 100644 terraform/monitoring/grafana-dashboard.json.tpl create mode 160000 terraform/monitoring/grafonnet-lib create mode 100644 terraform/monitoring/main.tf create mode 100644 terraform/monitoring/panels/ecs/availability.libsonnet create mode 100644 terraform/monitoring/panels/ecs/cpu.libsonnet create mode 100644 terraform/monitoring/panels/ecs/memory.libsonnet create mode 100644 terraform/monitoring/panels/http/average_latency.libsonnet create mode 100644 terraform/monitoring/panels/http/latency_quantiles.libsonnet create mode 100644 terraform/monitoring/panels/http/request_response.libsonnet create mode 100644 terraform/monitoring/panels/http/response_status.libsonnet create mode 100644 terraform/monitoring/panels/panels.libsonnet create mode 100644 terraform/monitoring/panels/redis/cpu.libsonnet create mode 100644 terraform/monitoring/panels/redis/memory.libsonnet create mode 100644 terraform/monitoring/panels/redis/reads.libsonnet create mode 100644 terraform/monitoring/panels/redis/writes.libsonnet create mode 100644 terraform/monitoring/panels/registry/cache_read.libsonnet create mode 100644 terraform/monitoring/panels/registry/cache_write.libsonnet create mode 100644 terraform/monitoring/panels/registry/requests.libsonnet create mode 100644 terraform/monitoring/terraform.tf create mode 100644 terraform/monitoring/variables.tf create mode 100644 terraform/outputs.tf create mode 100644 terraform/providers.tf create mode 100644 terraform/redis/README.md create mode 100644 terraform/redis/context.tf create mode 100644 terraform/redis/main.tf create mode 100644 terraform/redis/outputs.tf create mode 100644 terraform/redis/terraform.tf create mode 100644 terraform/redis/variables.tf create mode 100644 terraform/res_alerting.tf create mode 100644 terraform/res_dns.tf create mode 100644 terraform/res_ecs.tf create mode 100644 terraform/res_monitoring.tf create mode 100644 terraform/res_network.tf create mode 100644 terraform/res_redis.tf create mode 100644 terraform/terraform.tf create mode 100644 terraform/variables.tf diff --git a/.github/codeowners b/.github/codeowners new file mode 100644 index 0000000..41a97be --- /dev/null +++ b/.github/codeowners @@ -0,0 +1,2 @@ +* @xDarksome +* @xav diff --git a/.github/workflows/debug_validate.yml b/.github/workflows/debug_validate.yml new file mode 100644 index 0000000..2039df8 --- /dev/null +++ b/.github/workflows/debug_validate.yml @@ -0,0 +1,35 @@ +name: ⚙️ Debug - Validate +on: + push: + branches: + - 58-migrate-ti-dedicated-aws-account + +permissions: + contents: read + checks: write + id-token: write + +jobs: + ci: + name: CI + uses: WalletConnect/ci_workflows/.github/workflows/ci.yml@0.1.3 + secrets: inherit + with: + check-infra: true + check-app: true + + validate-staging: + name: Validate - Staging + uses: ./.github/workflows/sub-validate.yml + secrets: inherit + with: + stage: staging + stage-url: https://staging.${{ vars.SUBDOMAIN_NAME }}.walletconnect.com + + validate-prod: + name: Validate - Prod + uses: ./.github/workflows/sub-validate.yml + secrets: inherit + with: + stage: prod + stage-url: https://${{ vars.SUBDOMAIN_NAME }}.walletconnect.com diff --git a/.github/workflows/dispatch_deploy.yml b/.github/workflows/dispatch_deploy.yml new file mode 100644 index 0000000..93ebd43 --- /dev/null +++ b/.github/workflows/dispatch_deploy.yml @@ -0,0 +1,79 @@ +name: ⚙️ Deploy +run-name: "Deploy: ${{ github.sha }} ➠ ${{ inputs.version }}${{ (!inputs.deploy-infra && !inputs.deploy-app) && ' 👀 deploy nothing' || ''}}${{ inputs.deploy-infra && ' ❱❱  infra' || '' }}${{ inputs.deploy-app && ' ❱❱  app' || '' }}" + +on: + workflow_dispatch: + inputs: + deploy-infra: + description: "Deploy Infra" + default: true + required: true + type: boolean + deploy-app: + description: "Deploy App" + default: true + required: true + type: boolean + stage: + description: 'Target Environment' + type: choice + options: + - staging + - prod + default: staging + required: true + version: + description: "Release Version" + type: string + default: '-current-' + +concurrency: deploy + +permissions: + contents: write + checks: write + id-token: write + packages: write + +jobs: + get_deployed_version: + name: Lookup Version + if: ${{ !inputs.deploy-app && inputs.version == '-current-' }} + secrets: inherit + uses: WalletConnect/ci_workflows/.github/workflows/release-get_deployed_version.yml@0.1.3 + with: + task-name-stage: prod + task-name: ${{ vars.TASK_NAME }} + aws-region: ${{ vars.AWS_REGION }} + aws-role-arn: ${{vars.AWS_ROLE_PROD}} + run-group: ${{ vars.RUN_GROUP }} + + select_version: + name: Select Version + needs: [ get_deployed_version ] + if: ${{ always() && !cancelled() && !failure() }} + runs-on: + group: ${{ vars.RUN_GROUP }} + steps: + - name: Select target version + id: select_version + run: | + if [ "${{ inputs.deploy-app }}" != "true" ] && [ "${{ inputs.version }}" == "-current-" ]; then + echo "version=${{ needs.get_deployed_version.outputs.version }}" >> "$GITHUB_OUTPUT" + else + echo "version=${{ inputs.version }}" >> "$GITHUB_OUTPUT" + fi + outputs: + version: ${{ steps.select_version.outputs.version }} + + cd: + name: CD + uses: ./.github/workflows/sub-cd.yml + needs: [ select_version ] + if: ${{ always() && !cancelled() && !failure() }} + secrets: inherit + with: + deploy-infra: ${{ inputs.deploy-infra }} + deploy-app: ${{ inputs.deploy-app }} + deploy-prod: ${{ inputs.stage == 'prod' }} + version: ${{ needs.select_version .outputs.version }} diff --git a/.github/workflows/dispatch_publish.yml b/.github/workflows/dispatch_publish.yml new file mode 100644 index 0000000..414dea8 --- /dev/null +++ b/.github/workflows/dispatch_publish.yml @@ -0,0 +1,52 @@ +name: ⚙️ Publish +run-name: "Publish: ${{ github.sha }}${{ inputs.deploy-to != 'none' && format(' ❱❱ {0}', inputs.deploy-to) || ''}}" + +on: + workflow_dispatch: + inputs: + deploy-to: + description: "Deploy published image to" + type: choice + options: + - none + - staging + - prod + default: staging + required: true + +concurrency: deploy + +permissions: + contents: write + checks: write + id-token: write + packages: write + +jobs: + ci: + name: CI + uses: WalletConnect/ci_workflows/.github/workflows/ci.yml@0.1.3 + secrets: inherit + with: + check-infra: false + check-app: true + + release: + name: Release + uses: WalletConnect/ci_workflows/.github/workflows/release.yml@0.1.3 + secrets: inherit + with: + infra-changed: false + app-changed: true + + cd: + name: CD + needs: [ release ] + if: ${{ inputs.deploy-to == 'staging' || inputs.deploy-to == 'prod' }} + secrets: inherit + uses: ./.github/workflows/sub-cd.yml + with: + deploy-infra: false + deploy-app: true + deploy-prod: ${{ inputs.deploy-to == 'prod' }} + version: ${{ needs.release.outputs.version }} diff --git a/.github/workflows/dispatch_validate.yml b/.github/workflows/dispatch_validate.yml new file mode 100644 index 0000000..f8ee6a3 --- /dev/null +++ b/.github/workflows/dispatch_validate.yml @@ -0,0 +1,57 @@ +name: ⚙️ Validate +run-name: "Validate: ${{ github.sha }}${{ (!inputs.check-infra && !inputs.check-app) && '👀 validate nothing' || ''}}${{ inputs.check-infra && ' ✓  infra' || '' }}${{ inputs.check-app && ' ✓  app' || '' }}" +on: + workflow_dispatch: + inputs: + check-infra: + description: "Validate Infra" + default: true + required: true + type: boolean + check-app: + description: "Validate App" + default: true + required: true + type: boolean + check-staging: + description: "Validate Staging" + default: false + required: true + type: boolean + check-prod: + description: "Validate Prod" + default: false + required: true + type: boolean + +permissions: + contents: read + checks: write + id-token: write + +jobs: + ci: + name: CI + uses: WalletConnect/ci_workflows/.github/workflows/ci.yml@0.1.3 + secrets: inherit + with: + check-infra: ${{ inputs.check-infra }} + check-app: ${{ inputs.check-app }} + + validate-staging: + name: Validate - Staging + if: ${{ inputs.check-staging }} + uses: ./.github/workflows/sub-validate.yml + secrets: inherit + with: + stage: staging + stage-url: https://staging.${{ vars.SUBDOMAIN_NAME }}.walletconnect.com + + validate-prod: + name: Validate - Prod + if: ${{ inputs.check-prod }} + uses: ./.github/workflows/sub-validate.yml + secrets: inherit + with: + stage: prod + stage-url: https://${{ vars.SUBDOMAIN_NAME }}.walletconnect.com diff --git a/.github/workflows/event_intake.yml b/.github/workflows/event_intake.yml new file mode 100644 index 0000000..36ff490 --- /dev/null +++ b/.github/workflows/event_intake.yml @@ -0,0 +1,47 @@ +# This workflow moves issues to the Project board when they receive the "accepted" label +# When WalletConnect Org members create issues they are automatically "accepted". +# Otherwise, they need to manually receive that label during intake. +name: ⚡ Intake + +on: + issues: + types: [ opened, labeled ] + +jobs: + add-to-project: + name: Add issue to board + if: github.event_name == 'issues' && github.event.action == 'labeled' && github.event.label.name == 'accepted' + runs-on: + group: ${{ vars.RUN_GROUP }} + steps: + - uses: actions/add-to-project@v0.1.0 + with: + project-url: https://github.com/orgs/WalletConnect/projects/20 + github-token: ${{ secrets.ASSIGN_TO_PROJECT_GITHUB_TOKEN }} + labeled: accepted + label-operator: OR + + auto-promote: + name: auto-promote + if: github.event.action == 'opened' + runs-on: + group: ${{ vars.RUN_GROUP }} + steps: + - name: Check Core Team membership + uses: tspascoal/get-user-teams-membership@v1 + id: is-core-team + with: + username: ${{ github.event_name != 'pull_request' && github.event.issue.user.login || github.event.sender.login }} + team: "Core Team" + GITHUB_TOKEN: ${{ secrets.ASSIGN_TO_PROJECT_GITHUB_TOKEN }} + - name: Print result + env: + CREATOR: ${{ github.event_name != 'pull_request' && github.event.issue.user.login || github.event.sender.login }} + IS_TEAM_MEMBER: ${{ steps.is-core-team.outputs.isTeamMember }} + run: echo "$CREATOR (Core Team Member $IS_TEAM_MEMBER) created this issue/PR" + - name: Label issues + if: ${{ steps.is-core-team.outputs.isTeamMember == 'true' }} + uses: andymckay/labeler@e6c4322d0397f3240f0e7e30a33b5c5df2d39e90 + with: + add-labels: "accepted" + repo-token: ${{ secrets.ASSIGN_TO_PROJECT_GITHUB_TOKEN }} diff --git a/.github/workflows/event_pr.yml b/.github/workflows/event_pr.yml new file mode 100644 index 0000000..56def43 --- /dev/null +++ b/.github/workflows/event_pr.yml @@ -0,0 +1,64 @@ +name: ⚡ Pull-Request +run-name: 'PR / ${{ github.event.pull_request.title }}' + +on: + pull_request: + types: + - opened # A pull request was created. + - reopened # A closed pull request was reopened. + - edited # A pull request's title, body, or labels are edited. + - synchronize # A pull request's branch was synchronized with its base branch. + - unlocked # Conversation on a pull request was unlocked. + +concurrency: + group: pr-${{ github.event.pull_request.number }} + cancel-in-progress: true + +permissions: + contents: read + id-token: write + issues: read + pull-requests: write + +jobs: + check_pr: + name: Check PR + runs-on: + group: ${{ vars.RUN_GROUP }} + permissions: + statuses: write + steps: + - name: Check PR Title + uses: aslafy-z/conventional-pr-title-action@v3 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + paths-filter: + name: Paths Filter + runs-on: + group: ${{ vars.RUN_GROUP }} + steps: + - uses: actions/checkout@v4 + - uses: WalletConnect/actions/github/paths-filter/@2.2.1 + id: filter + outputs: + infra: ${{ steps.filter.outputs.infra }} + app: ${{ steps.filter.outputs.app }} + + ci: + name: CI + needs: [ paths-filter ] + uses: WalletConnect/ci_workflows/.github/workflows/ci.yml@0.1.3 + secrets: inherit + with: + check-app: ${{ needs.paths-filter.outputs.app == 'true' }} + check-infra: ${{ needs.paths-filter.outputs.infra == 'true' }} + + merge_check: + name: Merge Check + needs: [ check_pr, ci ] + if: ${{ always() && !cancelled() && !failure() }} + runs-on: + group: ${{ vars.RUN_GROUP }} + steps: + - run: echo "CI is successful" diff --git a/.github/workflows/event_release.yml b/.github/workflows/event_release.yml new file mode 100644 index 0000000..40c2880 --- /dev/null +++ b/.github/workflows/event_release.yml @@ -0,0 +1,64 @@ +name: ⚡ Release +run-name: 'Release / ${{ github.event.head_commit.message }}' + +on: + push: + branches: + - main + - master + paths-ignore: + - '.github/**' + - 'docs/**' + - 'Cargo.toml' + - 'Cargo.lock' + - 'README.md' + - 'CHANGELOG.md' + - 'LICENSE' + - 'justfile' + - 'rustfmt.toml' + - '.editorconfig' + - '.pre-commit-config.yaml' + - '.terraformignore' + - '.env.example' + +concurrency: deploy + +permissions: + contents: write + id-token: write + packages: write + checks: write + +jobs: + paths_filter: + name: Paths Filter + runs-on: + group: ${{ vars.RUN_GROUP }} + steps: + - uses: actions/checkout@v4 + - uses: WalletConnect/actions/github/paths-filter/@2.2.1 + id: filter + outputs: + infra: ${{ steps.filter.outputs.infra }} + app: ${{ steps.filter.outputs.app }} + + release: + name: Release + needs: [ paths_filter ] + uses: WalletConnect/ci_workflows/.github/workflows/release.yml@0.1.3 + secrets: inherit + with: + task-name: ${{ vars.TASK_NAME }} + infra-changed: ${{ needs.paths_filter.outputs.infra == 'true' }} + app-changed: ${{ needs.paths_filter.outputs.app == 'true' }} + + cd: + name: CD + needs: [ paths_filter, release ] + secrets: inherit + uses: ./.github/workflows/sub-cd.yml + with: + deploy-infra: ${{ needs.paths_filter.outputs.infra == 'true' }} + deploy-app: ${{ needs.paths_filter.outputs.app == 'true' }} + deploy-prod: true + version: ${{ needs.release.outputs.version }} diff --git a/.github/workflows/sub-cd.yml b/.github/workflows/sub-cd.yml new file mode 100644 index 0000000..740edae --- /dev/null +++ b/.github/workflows/sub-cd.yml @@ -0,0 +1,79 @@ +name: ❖ CD + +on: + workflow_call: + inputs: + deploy-infra: + description: "Deploy infrastructure" + type: boolean + default: true + deploy-app: + description: "Deploy app" + type: boolean + default: true + deploy-prod: + description: "Deploy to production after successful deployment to staging" + type: boolean + default: false + version: + description: "The release version" + type: string + required: true + +concurrency: cd + +permissions: + contents: write + checks: write + id-token: write + +jobs: + cd-staging: + name: Staging + secrets: inherit + uses: WalletConnect/ci_workflows/.github/workflows/cd.yml@0.1.3 + with: + deploy-infra: ${{ inputs.deploy-infra }} + deploy-app: ${{ inputs.deploy-app && !inputs.deploy-infra }} + version: ${{ inputs.version }} + task-name: ${{ vars.TASK_NAME }} + stage: staging + stage-url: https://staging.${{ vars.SUBDOMAIN_NAME }}.walletconnect.com/health + tf-variables: | + ofac_blocked_countries: ${{ vars.OFAC_BLOCKED_ZONES }} + aws-role-arn: ${{ vars.AWS_ROLE_STAGING }} + + validate-staging: + name: Validate Staging + needs: [ cd-staging ] + uses: ./.github/workflows/sub-validate.yml + secrets: inherit + with: + stage: staging + stage-url: https://staging.${{ vars.SUBDOMAIN_NAME }}.walletconnect.com + + cd-prod: + name: Prod + needs: [ validate-staging ] + if: ${{ inputs.deploy-prod }} + secrets: inherit + uses: WalletConnect/ci_workflows/.github/workflows/cd.yml@0.1.3 + with: + deploy-infra: ${{ inputs.deploy-infra }} + deploy-app: ${{ inputs.deploy-app && !inputs.deploy-infra }} + version: ${{ inputs.version }} + task-name: ${{ vars.TASK_NAME }} + stage: prod + stage-url: https://${{ vars.SUBDOMAIN_NAME }}.walletconnect.com/health + tf-variables: | + ofac_blocked_countries: ${{ vars.OFAC_BLOCKED_ZONES }} + aws-role-arn: ${{ vars.AWS_ROLE_PROD }} + + validate-prod: + name: Validate Prod + needs: [ cd-prod ] + uses: ./.github/workflows/sub-validate.yml + secrets: inherit + with: + stage: prod + stage-url: https://${{ vars.SUBDOMAIN_NAME }}.walletconnect.com diff --git a/.github/workflows/sub-validate.yml b/.github/workflows/sub-validate.yml new file mode 100644 index 0000000..736532f --- /dev/null +++ b/.github/workflows/sub-validate.yml @@ -0,0 +1,85 @@ +name: ❖ Validate + +on: + workflow_call: + inputs: + stage: + description: 'the environment to validate' + type: string + default: 'staging' + stage-url: + description: 'the URL of the environment' + type: string + default: https://${{ vars.SUBDOMAIN_NAME }}.walletconnect.com + rust-toolchain: + description: 'The Rust version to use' + type: string + default: ${{ vars.RUST_VERSION }} + +permissions: + contents: read + checks: write + id-token: write + +jobs: + health-check: + name: Health Check - ${{ inputs.stage }} + runs-on: + group: ${{ vars.RUN_GROUP }} + environment: + name: ${{ inputs.stage }} + url: ${{ inputs.stage-url }} + steps: + - name: health-check + run: curl "${{ inputs.stage-url }}" + + integration-tests: + name: Integration Tests - ${{ inputs.stage }} + runs-on: + group: ${{ vars.RUN_GROUP }} + environment: + name: ${{ inputs.stage }} + url: ${{ inputs.stage-url }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Rust ${{ inputs.rust-toolchain }} + uses: WalletConnect/actions-rs/toolchain@1.0.0 + with: + toolchain: ${{ inputs.rust-toolchain }} + profile: 'default' + override: true + + - name: "Run Integration Tests" + uses: WalletConnect/actions-rs/cargo@1.0.0 + env: + PROJECT_ID: ${{ secrets.PROJECT_ID }} + RPC_URL: ${{ inputs.stage-url }} + with: + command: test + args: --test integration + + integration-tests-ts: + name: TS Integration Tests - ${{ inputs.stage }} + runs-on: + group: ${{ vars.RUN_GROUP }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: 18.x + cache: 'yarn' + cache-dependency-path: '**/yarn.lock' + + - name: Yarn Install + run: yarn install + + - name: Yarn Integration Tests + run: yarn integration + env: + PROJECT_ID: ${{ secrets.PROJECT_ID }} + RPC_URL: ${{ inputs.stage-url }} diff --git a/.gitignore b/.gitignore index d0e8a39..d63f40f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,83 @@ -/target +#--------------------------------------- +# General + +.DS_Store +.AppleDouble +.LSOverride +[Dd]esktop.ini + +#--------------------------------------- +# Environment + +.direnv +.envrc +.actrc .env +#--------------------------------------- # Editors -.idea -.vscode -# Terraform -terraform/.terraform* +# JetBrains +.idea/ +out/ +.fleet +*.iws + +# VSCode +.vscode/ +.history/ +*.code-workspace +#--------------------------------------- +# Rust/Cargo + +# Generated by Cargo, will have compiled files and executables +debug/ +target/ + +# Backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +#--------------------------------------- # Integration + node_modules *.log + + +#--------------------------------------- +# Terraform + +# Local .terraform directories +**/.terraform/* + +# .tfstate files +*.tfstate +*.tfstate.* + +# Exclude all .tfvars files, which are likely to contain sensitive data, such as +# password, private keys, and other secrets. These should not be part of version +# control as they are data points which are potentially sensitive and subject +# to change depending on the environment. +*.tfvars +*.tfvars.json + +# Ignore override files as they are usually used to override resources locally and so are not checked in +override.tf +override.tf.json +*_override.tf +*_override.tf.json + +# Include override files you do wish to add to version control using negated pattern +# +# !example_override.tf + +# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan +*tfplan* + +# Ignore CLI configuration files +.terraformrc +terraform.rc diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..b7368fb --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "terraform/monitoring/grafonnet-lib"] + path = terraform/monitoring/grafonnet-lib + url = git@github.com:WalletConnect/grafonnet-lib.git diff --git a/justfile b/justfile index d6d6f54..1794866 100644 --- a/justfile +++ b/justfile @@ -1,175 +1,550 @@ -binary-crate := "." - -export JUST_ROOT := justfile_directory() +binary-crate := "." +tf-dir := "terraform" +app-name := "verify" + +nocolor := '\033[0m' +black := '\033[0;30m' +red := '\033[0;31m' +green := '\033[0;32m' +brown := '\033[0;33m' +blue := '\033[0;34m' +purple := '\033[0;35m' +cyan := '\033[0;36m' +light-gray := '\033[0;37m' +dark-gray := '\033[1;30m' +light-red := '\033[1;31m' +light-green := '\033[1;32m' +yellow := '\033[1;33m' +light-blue := '\033[1;34m' +light-purple := '\033[1;35m' +light-cyan := '\033[1;36m' +white := '\033[1;37m' + +color-cmd := brown +color-arg := cyan +color-val := green +color-hint := brown +color-desc := blue +color-service := light-green + + +export JUST_ROOT := justfile_directory() # Default to listing recipes _default: - @just --list --list-prefix ' > ' + @just --list --unsorted + +alias build := cargo-build +alias run := cargo-run +alias test := cargo-test +alias clean := cargo-clean +alias check := cargo-check +alias clippy := cargo-clippy +alias udeps := cargo-udeps +alias checkfmt := cargo-checkfmt + +alias tfsec := tf-tfsec +alias tflint := tf-tflint + +deploy-dev: + #!/bin/bash + set -euo pipefail + + accountId="$(aws sts get-caller-identity | jq -r .Account)" + region="$(cat $TERRAFORM_DIR/variables.tf | grep -A 2 region | grep default | sed -nr 's/.+default = "(.+)"/\1/p')" + + imageRepo="$accountId.dkr.ecr.$region.amazonaws.com/{{ app-name }}" + aws ecr get-login-password --region $region | docker login --username AWS --password-stdin "$imageRepo" + docker build . -t "$imageRepo" --build-arg=release=true --platform=linux/amd64 $BUILD_ARGS + sha="$(docker inspect --format="{{ .Id }}" "$imageRepo" | cut -d: -f2)" + tag="$imageRepo:$sha" + docker tag "$imageRepo" "$tag" + docker push "$tag" + + cd {{ tf-dir }} + terraform workspace select dev + TF_VAR_ecr_app_version="$sha" terraform apply -auto-approve + +################################################################################ +# Meta recipes + +# Format the project code +fmt target='all': (_check-string-in-set target "all,rust,tf") + #!/bin/bash + set -euo pipefail + [[ '{{ target }}' == 'all' || '{{ target }}' == 'rust' ]] && { just cargo-fmt; } + [[ '{{ target }}' == 'all' || '{{ target }}' == 'tf' ]] && { just tf-fmt; } + +# Update project documentation +docs target='all': (_check-string-in-set target "all,rust,tf") + #!/bin/bash + set -euo pipefail + [[ '{{ target }}' == 'all' || '{{ target }}' == 'rust' ]] && { just cargo-build-docs; } + [[ '{{ target }}' == 'all' || '{{ target }}' == 'tf' ]] && { just tf-docs; } + +# Run linting and tests +amigood: lint cargo-test-default cargo-test-all -# Open project documentation in your local browser -docs: (_build-docs "open" "nodeps") +################################################################################ +# Linting recipes + +# Lint the project for quality issues +lint target='all': (_check-string-in-set target "all,rust,tf") + #!/bin/bash + set -euo pipefail + [[ '{{ target }}' == 'all' || '{{ target }}' == 'rust' ]] && { just lint-rust; } + [[ '{{ target }}' == 'all' || '{{ target }}' == 'tf' ]] && { just lint-tf; } + + +# Lint the rust project for any quality issues +lint-rust: cargo-check cargo-clippy cargo-udeps cargo-checkfmt + +# Lint the terrafrom project for any quality issues +lint-tf: tf-checkfmt tf-validate tf-tfsec tf-tflint + +################################################################################ +# Rust recipes + +# Run a Cargo command, choose target from open-docs, build-docs, fmt, build, run, test, clean, check, clippy, udeps, checkfmt +cargo target='' sub-target='': (_check-string-in-set target "open-docs,build-docs,fmt,build,run,test,clean,check,clippy,udeps,checkfmt" "allow_empty") + #!/bin/bash + set -euo pipefail + + [[ '{{ target }}' == 'help' || '{{ target }}' == 'h' || '{{ target }}' == '' ]] && { + printf "Available {{ color-cmd }}cargo{{ nocolor }} targets:\n" + printf " open-docs {{ color-desc }}# Open rust project documentation in your local browser{{ nocolor }}\n" + printf " build-docs {{ color-desc }}# Build rust project documentation{{ nocolor }}\n" + printf " fmt {{ color-desc }}# Format the application code{{ nocolor }}\n" + printf " build {{ color-desc }}# Build service for development{{ nocolor }}\n" + printf " run {{ color-desc }}# Run the service{{ nocolor }}\n" + printf " test target='default' {{ color-desc }}# Run project tests, choose target from default, all, doc{{ nocolor }}\n" + printf " clean {{ color-desc }}# Clean build artifacts{{ nocolor }}\n" + printf " check {{ color-desc }}# Fast check rust project for errors{{ nocolor }}\n" + printf " clippy {{ color-desc }}# Check rust project with clippy{{ nocolor }}\n" + printf " udeps {{ color-desc }}# Check unused dependencies{{ nocolor }}\n" + printf " checkfmt {{ color-desc }}# Check the rust code formatting{{ nocolor }}\n" + + exit 0 + } + + [[ '{{ target }}' == 'open-docs' ]] && { just cargo-open-docs; } + [[ '{{ target }}' == 'build-docs' ]] && { just cargo-build-docs; } + [[ '{{ target }}' == 'fmt' ]] && { just cargo-fmt; } + [[ '{{ target }}' == 'build' ]] && { just cargo-build; } + [[ '{{ target }}' == 'run' ]] && { just cargo-run; } + [[ '{{ target }}' == 'test' ]] && { just cargo-test {{ sub-target }}; } + [[ '{{ target }}' == 'clean' ]] && { just cargo-clean; } + [[ '{{ target }}' == 'check' ]] && { just cargo-check; } + [[ '{{ target }}' == 'clippy' ]] && { just cargo-clippy; } + [[ '{{ target }}' == 'udeps' ]] && { just cargo-udeps; } + [[ '{{ target }}' == 'checkfmt' ]] && { just cargo-checkfmt; } + +# Open rust project documentation in your local browser +cargo-open-docs: (_cargo-build-docs "open" "nodeps") @echo '==> Opening documentation in system browser' -# Fast check project for errors -check: - @echo '==> Checking project for compile errors' - cargo check +# Build rust project documentation +cargo-build-docs: (_cargo-build-docs "" "nodeps") + +@_cargo-build-docs $open="" $nodeps="": _check-cmd-cargo + echo "==> Building project documentation @$JUST_ROOT/target/doc" + cargo doc --all-features --document-private-items ${nodeps:+--no-deps} ${open:+--open} + +# Format the application code +@cargo-fmt: _check-cmd-cargo-fmt + printf '==> Running {{ color-cmd }}rustfmt{{ nocolor }}\n' + cargo +nightly fmt # Build service for development -build: +cargo-build: _check-cmd-cargo @echo '==> Building project' cargo build -# Build project documentation -build-docs: (_build-docs "" "nodeps") - # Run the service -run: build +cargo-run: _check-cmd-cargo cargo-build @echo '==> Running project (ctrl+c to exit)' cargo run -# Run project test suite, skipping storage tests -test: - @echo '==> Testing project (default)' +# Run project tests, choose target from default, all, doc +cargo-test target='default': (_check-string-in-set target "default,all,doc") + #!/bin/bash + set -euo pipefail + [[ "{{ target }}" == 'default' ]] && { just cargo-test-default; } + [[ "{{ target }}" == 'all' ]] && { just cargo-test-all; } + [[ "{{ target }}" == 'doc' ]] && { just cargo-test-doc; } + +# Run project default tests +cargo-test-default: _check-cmd-cargo + @printf '==> Testing project ({{ light-green }}default{{ nocolor }})\n' cargo test -# Run project test suite, including storage tests (requires storage docker services to be running) -test-all: - @echo '==> Testing project (all features)' +# Run project tests with all features activated +cargo-test-all: _check-cmd-cargo + @printf '==> Testing project ({{ light-green }}all features{{ nocolor }})\n' cargo test --all-features -# Run test from project documentation -test-doc: - @echo '==> Testing project docs' +# Run tests from project documentation +cargo-test-doc: _check-cmd-cargo + @printf '==> Testing project ({{ light-green }}docs{{ nocolor }})\n' cargo test --doc # Clean build artifacts -clean: - @echo '==> Cleaning project target/*' +cargo-clean: _check-cmd-cargo + @printf '==> Cleaning project target/*\n' cargo clean -# Build docker image -build-docker: - @echo '=> Build rs-relay docker image' - docker-compose -f ./ops/docker-compose.relay.yml -f ./ops/docker-compose.storage.yml build rs-relay-srv1 - -# Start relay & storage services on docker -run-docker: - @echo '==> Start services on docker' - @echo '==> Use run rs-relay app on docker with "cargo-watch"' - @echo '==> for more details check https://crates.io/crates/cargo-watch' - docker-compose -f ./ops/docker-compose.relay.yml -f ./ops/docker-compose.storage.yml up -d - -# Stop relay & storage services on docker -stop-docker: - @echo '==> Stop services on docker' - docker-compose -f ./ops/docker-compose.relay.yml -f ./ops/docker-compose.storage.yml down - -# Clean up docker relay & storage services -clean-docker: - @echo '==> Clean services on docker' - docker-compose -f ./ops/docker-compose.relay.yml -f ./ops/docker-compose.storage.yml stop - docker-compose -f ./ops/docker-compose.relay.yml -f ./ops/docker-compose.storage.yml rm -f - -# Start storage services on docker -run-storage-docker: - @echo '==> Start storage services on docker' +# Fast check project for errors +cargo-check: _check-cmd-cargo + @printf '==> Checking project for compile errors\n' + cargo check + +# Check rust project with clippy +cargo-clippy: _check-cmd-cargo-clippy + @printf '==> Running {{ color-cmd }}clippy{{ nocolor }}\n' + cargo +nightly clippy --all-features --tests -- -D clippy::all + +# Check unused dependencies +cargo-udeps: _check-cmd-cargo-udeps + @printf '==> Running {{ color-cmd }}udeps{{ nocolor }}\n' + cargo +nightly udeps + +# Check the rust code formatting +cargo-checkfmt: _check-cmd-cargo-fmt + @printf '==> Running {{ color-cmd }}rustfmt{{ nocolor }} --check\n' + cargo +nightly fmt --check + +################################################################################ +# Docker recipes + +# Run a docker command, choose target from build, run, stop, clean, ps, test +docker target='' sub-target='': (_check-string-in-set target "build,run,stop,clean,ps,test" "allow_empty") + #!/bin/bash + set -euo pipefail + + [[ '{{ target }}' == 'help' || '{{ target }}' == 'h' || '{{ target }}' == '' ]] && { + printf "Available {{ color-cmd }}docker{{ nocolor }} targets:\n" + printf " build {{ color-desc }}# Build the application docker image{{ nocolor }}\n" + printf " run {{ color-arg }}target{{ nocolor }}={{ color-val }}''{{ nocolor }} {{ color-desc }}# Run docker services{{ nocolor }}\n" + printf " stop {{ color-arg }}target{{ nocolor }}={{ color-val }}''{{ nocolor }} {{ color-desc }}# Stop docker services{{ nocolor }}\n" + printf " clean {{ color-arg }}target{{ nocolor }}={{ color-val }}''{{ nocolor }} {{ color-desc }}# Stop and clean docker services{{ nocolor }}\n" + printf " ps {{ color-desc }}# List running docker services{{ nocolor }}\n" + printf " test {{ color-desc }}# Run project test suite on docker containers{{ nocolor }}\n" + + exit 0 + } + + [[ '{{ target }}' == 'build' ]] && { just docker-build; } + [[ '{{ target }}' == 'run' ]] && { just docker-run {{ sub-target }}; } + [[ '{{ target }}' == 'stop' ]] && { just docker-stop {{ sub-target }}; } + [[ '{{ target }}' == 'clean' ]] && { just docker-clean {{ sub-target }}; } + [[ '{{ target }}' == 'ps' ]] && { just docker-ps; } + [[ '{{ target }}' == 'test' ]] && { just docker-test; } + +# Build the application docker image +docker-build: _check-cmd-docker-compose + @printf '=> Build {{ color-cmd }}application server{{ nocolor }} docker image\n' + docker-compose -f ./ops/docker-compose.relay.yml -f ./ops/docker-compose.storage.yml -f ./ops/docker-compose.ot.yml build {{ app-name }} + +# Run docker services, you can specify which services to run by passing a comma separated list of targets +docker-run target='': (_check-set-in-set target "all,server,storage,ot" "allow_empty") + #!/bin/bash + set -euo pipefail + + [[ '{{ target }}' == 'help' || '{{ target }}' == 'h' || '{{ target }}' == '' ]] && { + printf "Available {{ color-cmd }}run{{ nocolor }} targets:\n" + printf " server {{ color-desc }}# Run the Application Server docker container{{ nocolor }}\n" + printf " storage {{ color-desc }}# Run Storage Services docker containers{{ nocolor }}\n" + printf " ot {{ color-desc }}# Run OpenTelemetry docker container{{ nocolor }}\n" + + exit 0 + } + + IFS=',' read -ra items <<< "{{ target }}" + for item in "${items[@]}"; do + [[ "$item" == 'all' || "$item" == 'ot' ]] && { just docker-run-ot; } + [[ "$item" == 'all' || "$item" == 'storage' ]] && { just docker-run-storage; } + [[ "$item" == 'all' || "$item" == 'server' ]] && { just docker-run-server; } + done + +# Run the application server docker container +docker-run-server: _check-cmd-docker-compose + @printf '==> Start {{ color-service }}Application Server{{ nocolor }} docker container\n' + docker-compose -f ./ops/docker-compose.server.yml up -d + +# Run storage services docker containers +docker-run-storage: _check-cmd-docker-compose + @printf '==> Start {{ color-service }}Storage Services{{ nocolor }} docker containers\n' docker-compose -f ./ops/docker-compose.storage.yml up -d -# Stop relay & storage services on docker -stop-storage-docker: - @echo '==> Stop storage services on docker' + +# Run OpenTelemetry docker container +docker-run-ot: _check-cmd-docker-compose + @printf '==> Start {{ color-service }}OpenTelemetry{{ nocolor }} docker container\n' + docker-compose -f ./ops/docker-compose.ot.yml up -d + +# Stop docker services, you can specify which services to stop by passing a comma separated list of targets +docker-stop target='': (_check-set-in-set target "all,server,storage,ot" "allow_empty") + #!/bin/bash + set -euo pipefail + + [[ '{{ target }}' == 'help' || '{{ target }}' == 'h' || '{{ target }}' == '' ]] && { + printf "Available {{ color-cmd }}stop{{ nocolor }} targets:\n" + printf " server {{ color-desc }}# Stop the application server docker container{{ nocolor }}\n" + printf " storage {{ color-desc }}# Stop the storage services docker containers{{ nocolor }}\n" + printf " ot {{ color-desc }}# Stop the OpenTelemetry docker container{{ nocolor }}\n" + + exit 0 + } + + IFS=',' read -ra items <<< "{{ target }}" + for item in "${items[@]}"; do + [[ "$item" == 'all' || "$item" == 'server' ]] && { just docker-stop-server; } + [[ "$item" == 'all' || "$item" == 'storage' ]] && { just docker-stop-storage; } + [[ "$item" == 'all' || "$item" == 'ot' ]] && { just docker-stop-ot; } + done + +# Stop the application server docker container +docker-stop-server: _check-cmd-docker-compose + @printf '==> Stop {{ color-service }}application server{{ nocolor }} docker container\n' + docker-compose -f ./ops/docker-compose.server.yml down + +# Stop storage services docker containers +docker-stop-storage: _check-cmd-docker-compose + @printf '==> Stop {{ color-service }}storage services{{ nocolor }} docker containers\n' docker-compose -f ./ops/docker-compose.storage.yml down -# Clean up docker storage services -clean-storage-docker: - @echo '==> Clean storage services on docker' +# Stop OpenTelemetry docker container +docker-stop-ot: _check-cmd-docker-compose + @printf '==> Stop {{ color-cmd }}OpenTelemetry{{ nocolor }} docker container\n' + docker-compose -f ./ops/docker-compose.ot.yml down + +# Stop and clean docker services, you can specify which services to clean by passing a comma separated list of targets +docker-clean target='': (_check-set-in-set target "all,server,storage,ot" "allow_empty") + #!/bin/bash + set -euo pipefail + + [[ '{{ target }}' == 'help' || '{{ target }}' == 'h' || '{{ target }}' == '' ]] && { + printf "Available {{ color-cmd }}clean{{ nocolor }} targets:\n" + printf " server {{ color-desc }}# Stop and clean the application server docker container{{ nocolor }}\n" + printf " storage {{ color-desc }}# Stop and clean the storage services docker containers{{ nocolor }}\n" + printf " ot {{ color-desc }}# Stop and clean the OpenTelemetry docker container{{ nocolor }}\n" + + exit 0 + } + + IFS=',' read -ra items <<< "{{ target }}" + for item in "${items[@]}"; do + [[ "$item" == 'all' || "$item" == 'server' ]] && { just docker-clean-server; } + [[ "$item" == 'all' || "$item" == 'storage' ]] && { just docker-clean-storage; } + [[ "$item" == 'all' || "$item" == 'ot' ]] && { just docker-clean-ot; } + done + +# Stop and clean the application server docker container +docker-clean-server: _check-cmd-docker-compose + @printf '==> Clean {{ color-cmd }}application server{{ nocolor }} docker container\n' + docker-compose -f ./ops/docker-compose.server.yml stop + docker-compose -f ./ops/docker-compose.server.yml rm -f + +# Stop and clean storage services docker containers +docker-clean-storage: _check-cmd-docker-compose + @printf '==> Clean {{ color-cmd }}storage services{{ nocolor }} docker containers\n' docker-compose -f ./ops/docker-compose.storage.yml stop docker-compose -f ./ops/docker-compose.storage.yml rm -f -# List services running on docker -ps-docker: - @echo '==> List services on docker' - docker-compose -f ./ops/docker-compose.relay.yml -f ./ops/docker-compose.storage.yml ps +# Stop and clean OpenTelemetry docker container +docker-clean-ot: _check-cmd-docker-compose + @printf '==> Clean {{ color-cmd }}OpenTelemetry{{ nocolor }} docker container\n' + docker-compose -f ./ops/docker-compose.ot.yml stop + docker-compose -f ./ops/docker-compose.ot.yml rm -f -# Run project test suite on docker containers -test-docker: - @echo '==> Run tests on docker container' - docker-compose -f ./ops/docker-compose.storage.yml -f ./ops/docker-compose.test.yml run --rm rs-relay-test +# List running docker services +docker-ps: _check-cmd-docker-compose + @printf '==> List running docker services\n' + docker-compose -f ./ops/docker-compose.server.yml -f ./ops/docker-compose.storage.yml p -f ./ops/docker-compose.ot.yml ps -run-jaeger: - @echo '==> Run opentelemetry jaeger docker container' - docker run --rm -p4317:4317 -p16686:16686 jaegertracing/all-in-one:latest +# Run project test suite on docker containers +docker-test: _check-cmd-docker-compose + @printf '==> Run tests on docker container\n' + docker-compose -f ./ops/docker-compose.storage.yml -f ./ops/docker-compose.test.yml -f ./ops/docker-compose.ot.yml run --rm {{ app-name }}-test -# Bumps the binary version to the given version -bump-version to: (_bump-cargo-version to binary-crate + "/Cargo.toml") +################################################################################ +# Terraform recipes -# Lint the project for any quality issues -lint: check fmt clippy commit-check +# Run a Terraform command, choose target from build, run, stop, clean, ps, test +tf target='': (_check-string-in-set target "fmt,checkfmt,validate,tfsec,tflint,init,plan,apply,docs,clean" "allow_empty") + #!/bin/bash + set -euo pipefail -# Run project linter -clippy: + [[ '{{ target }}' == 'help' || '{{ target }}' == 'h' || '{{ target }}' == '' ]] && { + printf "Available {{ color-cmd }}Terraform{{ nocolor }} targets:\n" + printf " fmt {{ color-desc }}# Format the terraform code{{ nocolor }}\n" + printf " checkfmt {{ color-desc }}# Check Terraform formatting{{ nocolor }}\n" + printf " validate {{ color-desc }}# Run Terraform validation{{ nocolor }}\n" + printf " tfsec {{ color-desc }}# Check Terraform configuration for potential security issues{{ nocolor }}\n" + printf " tflint {{ color-desc }}# Run Terraform linter{{ nocolor }}\n" + printf " init {{ color-desc }}# Init Terraform project{{ nocolor }}\n" + printf " plan {{ color-desc }}# Perform a Terraform plan on the current workspace{{ nocolor }}\n" + printf " apply {{ color-desc }}# Perform a Terraform apply on the current workspace{{ nocolor }}\n" + printf " docs {{ color-desc }}# Update the Terraform documentation{{ nocolor }}\n" + printf " clean {{ color-desc }}# Clean the Terraform environment{{ nocolor }}\n" + + exit 0 + } + + [[ '{{ target }}' == 'fmt' ]] && { just tf-fmt; } + [[ '{{ target }}' == 'checkfmt' ]] && { just tf-checkfmt; } + [[ '{{ target }}' == 'validate' ]] && { just tf-validate; } + [[ '{{ target }}' == 'tfsec' ]] && { just tf-tfsec; } + [[ '{{ target }}' == 'tflint' ]] && { just tf-tflint; } + [[ '{{ target }}' == 'init' ]] && { just tf-init; } + [[ '{{ target }}' == 'plan' ]] && { just tf-plan; } + [[ '{{ target }}' == 'apply' ]] && { just tf-apply; } + [[ '{{ target }}' == 'docs' ]] && { just tf-docs; } + [[ '{{ target }}' == 'clean' ]] && { just tf-clean; } + +# Format the terraform code +@tf-fmt: _check-cmd-terraform + printf '==> Running {{ color-cmd }}terraform fmt{{ nocolor }}\n' + cd {{ tf-dir }}; terraform fmt -recursive + +# Check Terraform formatting +@tf-checkfmt: _check-cmd-terraform + printf '==> Running {{ color-cmd }}terraform fmt{{ nocolor }}\n' + cd {{ tf-dir }}; terraform fmt -check -recursive + +# Run Terraform validation +@tf-validate: _check-cmd-terraform + printf '==> Running {{ color-cmd }}terraform fmt{{ nocolor }}\n' + cd {{ tf-dir }}; terraform validate + +# Check Terraform configuration for potential security issues +@tf-tfsec: _check-cmd-tfsec + printf '==> Running {{ color-cmd }}tfsec{{ nocolor }}\n' + cd {{ tf-dir }}; tfsec + +# Run Terraform linter +@tf-tflint: _check-cmd-tflint + printf '==> Running {{ color-cmd }}tflint{{ nocolor }}\n' + cd {{ tf-dir }}; tflint --recursive + +# Init Terraform project +@tf-init: _check-cmd-terraform + printf '==> Running {{ color-cmd }}terraform init{{ nocolor }}\n' + cd {{ tf-dir }}; terraform init + +# Perform a Terraform plan on the current workspace +@tf-plan: _check-cmd-terraform + printf '==> Running {{ color-cmd }}terraform init{{ nocolor }}\n' + cd {{ tf-dir }}; terraform plan + +# Perform a Terraform apply on the current workspace +@tf-apply: _check-cmd-terraform + printf '==> Running {{ color-cmd }}terraform init{{ nocolor }}\n' + cd {{ tf-dir }}; terraform apply + +# Update the Terraform documentation +@tf-docs: _check-cmd-tfdocs + printf '==> Running {{ color-cmd }}terraform-docs{{ nocolor }}\n' + cd {{ tf-dir }}; terraform-docs . + +# Clean the Terraform environment +@tf-clean: + printf '==> Clean Terraform environment\n' + cd {{ tf-dir }}; rm -rf .terraform/ .terraform.lock.hcl + +################################################################################ +# Helper recipes + +_check-cmd-cargo: (_check-cmd 'cargo' 'To install see https://doc.rust-lang.org/cargo/getting-started/installation.html for details') +_check-cmd-cargo-fmt: (_check-cmd 'cargo-fmt' 'To install run ' + color-hint + '`rustup component add rustfmt`' + nocolor + ', see https://github.com/rust-lang/rustfmt for details') +_check-cmd-cargo-clippy: (_check-cmd 'cargo-clippy' 'To install run ' + color-hint + '`rustup component add clippy`' + nocolor + ', see https://github.com/rust-lang/rust-clippy for details') +_check-cmd-cargo-udeps: (_check-cmd 'cargo-udeps' 'To install run ' + color-hint + '`cargo install cargo-udeps --locked`' + nocolor + ', see https://github.com/est31/cargo-udeps for details') +_check-cmd-docker-compose: (_check-cmd 'docker-compose' 'To install see https://docs.docker.com/compose/install') +_check-cmd-terraform: (_check-cmd 'terraform' 'To install see https://developer.hashicorp.com/terraform/downloads') +_check-cmd-tfsec: (_check-cmd 'tfsec' 'To install see https://github.com/aquasecurity/tfsec#installation') +_check-cmd-tflint: (_check-cmd 'tflint' 'To install see https://github.com/terraform-linters/tflint#installation') +_check-cmd-tfdocs: (_check-cmd 'terraform-docs' 'To install see https://terraform-docs.io/user-guide/installation/') + +[no-exit-message] +_check-cmd cmd install: #!/bin/bash set -euo pipefail - if command -v cargo-clippy >/dev/null; then - echo '==> Running clippy' - cargo clippy --all-features --tests -- -D clippy::all -W clippy::style - else - echo '==> clippy not found in PATH, skipping' - echo ' ^^^^^^ To install `rustup component add clippy`, see https://github.com/rust-lang/rust-clippy for details' + cmd="{{ cmd }}" + numChars=${#cmd} + underline=$(printf '%*s' "$numChars" | tr ' ' '^') + + if ! command -v {{ cmd }} >/dev/null; then + printf '==> {{ color-cmd }}{{ cmd }}{{ nocolor }} not found in PATH\n' + printf ' %s {{ install }}\n' "$underline" + exit 1 fi -# Run code formatting check -fmt: +[no-exit-message] +_check-string-in-set target set options='': #!/bin/bash set -euo pipefail - if command -v cargo-fmt >/dev/null; then - echo '==> Running rustfmt' - cargo +nightly fmt -- --check - else - echo '==> rustfmt not found in PATH, skipping' - echo ' ^^^^^^ To install `rustup component add rustfmt`, see https://github.com/rust-lang/rustfmt for details' + target="{{ target }}" + set="{{ set }}" + options="{{ options }}" + + if ! [[ -z "$target" && "$options" == "allow_empty" ]]; then + # Convert the set into an array + IFS=',' read -ra setArray <<< "$set" + + # Check if target is in the setArray + found=false + for item in "${setArray[@]}"; do + if [[ "$item" == "$target" ]]; then + found=true + break + fi + done + + if [[ "$found" != true ]]; then + printf "{{red }}$target{{ nocolor }} is not a valid target, accepted values are {{ brown }}[${set}]{{ nocolor }}\n" + exit 1 + fi fi - if command -v terraform -version >/dev/null; then - echo '==> Running terraform fmt' - terraform -chdir=terraform fmt -recursive - else - echo '==> terraform not found in PATH, skipping' - echo ' ^^^^^^^^^ To install see https://developer.hashicorp.com/terraform/downloads' - fi - -# Run commit checker -commit-check: +[no-exit-message] +_check-set-in-set set1 set2 options='': #!/bin/bash set -euo pipefail - if command -v cog >/dev/null; then - echo '==> Running cog check' - cog check --from-latest-tag - else - echo '==> cog not found in PATH, skipping' - echo ' ^^^ To install `cargo install --locked cocogitto`, see https://github.com/cocogitto/cocogitto for details' + set1="{{ set1 }}" + set2="{{ set2 }}" + options="{{ options }}" + + # Exit with status 0 if the first set is empty and empty strings are allowed + if [[ -z "$set1" && "$options" == "allow_empty" ]]; then + exit 0 fi -# Update documentation with any changes detected -update-docs: (_regenerate-metrics "docs/Metrics.md") - -# Build project documentation -_build-docs $open="" $nodeps="": - @echo "==> Building project documentation @$JUST_ROOT/target/doc" - @cargo doc --all-features --document-private-items ${nodeps:+--no-deps} ${open:+--open} - -# Update the metrics documentation with current metrics -_regenerate-metrics file temp=`mktemp`: build - @echo '==> Regenerating metrics to @{{file}}' - @cd scripts && ./metrics-apply.awk <(./metrics-fetch.sh | ./metrics-doc.pl | ./metrics-format.pl) < $JUST_ROOT/{{file}} > {{temp}} - @mv -f {{temp}} {{file}} - -# Bump the version field of a given Cargo.toml file -_bump-cargo-version version file temp=`mktemp`: - @echo '==> Bumping {{file}} version to {{version}}' - @perl -spe 'if (/^version/) { s/("[\w.]+")/"$version"/ }' -- -version={{version}} < {{file}} > {{temp}} - @mv -f {{temp}} {{file}} + # Convert both sets into arrays + IFS=',' read -ra setArray1 <<< "$set1" + IFS=',' read -ra setArray2 <<< "$set2" + + # Function to check if an item is in the second set + is_in_set() { + local e match="$1" + for e in "${setArray2[@]}"; do [[ "$e" == "$match" ]] && return 0; done + return 1 + } + + # Check each item in the first set + all_found=true + for item in "${setArray1[@]}"; do + if [[ -n "$item" || "$options" == "allow_empty" ]]; then + if ! is_in_set "$item"; then + all_found=false + break + fi + fi + done + + if [[ "$all_found" != true ]]; then + printf "[{{ red }}$set1{{ nocolor }}] contains invalid targets, accepted values are {{ brown }}[${set2}]{{ nocolor }}\n" + exit 1 + fi diff --git a/ops/docker-compose.ot.yml b/ops/docker-compose.ot.yml new file mode 100644 index 0000000..b76506a --- /dev/null +++ b/ops/docker-compose.ot.yml @@ -0,0 +1,24 @@ +version: '3.9' + +services: + jaeger: + image: jaegertracing/all-in-one:latest + networks: + - walletconnect-server + ports: + - 4317:4317 + - 16686:16686 + volumes: + - server:/jaeger + environment: + - COLLECTOR_OTLP_ENABLED=true + +networks: + walletconnect-server: + ipam: + driver: default + config: + - subnet: 172.10.1.0/16 + +volumes: + server: diff --git a/ops/docker-compose.storage.yml b/ops/docker-compose.storage.yml new file mode 100644 index 0000000..0463765 --- /dev/null +++ b/ops/docker-compose.storage.yml @@ -0,0 +1,148 @@ +version: '3.9' + +services: + activemq: + build: ./ActiveMQ + networks: + - walletconnect-rs-relay + ports: + - 5672:5672 + - 8086:8161 + volumes: + - rs-relay-test-data-storage:/activemq + environment: + - ACTIVEMQ_USERNAME=admin + - ACTIVEMQ_PASSWORD=admin + - ACTIVEMQ_WEBADMIN_USERNAME=admin + - ACTIVEMQ_WEBADMIN_PASSWORD=admin + + mongo: + image: mongo:4 + networks: + - walletconnect-rs-relay + ports: + - 27017:27017 + volumes: + - rs-relay-test-data-storage:/mongo + healthcheck: + test: + [ + "CMD", + "mongo", + "--eval", + "'db.runCommand(\"ping\").ok'", + "localhost:27017/test", + "--quiet" + ] + interval: 5s + timeout: 5s + retries: 5 + environment: + - MONGO_INITDB_ROOT_USERNAME=admin + - MONGO_INITDB_ROOT_PASSWORD=admin + - MONGO_INITDB_DATABASE=relay + + mongo-express: + image: mongo-express + networks: + - walletconnect-rs-relay + ports: + - 8085:8081 + depends_on: + mongo: + condition: service_healthy + environment: + - ME_CONFIG_MONGODB_ADMINUSERNAME=admin + - ME_CONFIG_MONGODB_ADMINPASSWORD=admin + - ME_CONFIG_MONGODB_URL="mongodb://admin:admin@mongo:27017" + + redis: + image: redis:6-alpine + networks: + - walletconnect-rs-relay + ports: + - 6379:6379 + volumes: + - rs-relay-test-data-storage:/redis + healthcheck: + test: [ "CMD", "redis-cli", "ping" ] + interval: 5s + timeout: 5s + retries: 5 + + redisinsight: + image: redislabs/redisinsight:latest + networks: + - walletconnect-rs-relay + ports: + - 8001:8001 + + minio: + image: minio/minio + networks: + - walletconnect-rs-relay + ports: + - "9000:9000" + - "9090:9090" + volumes: + - rs-relay-test-data-storage:/minio + environment: + - "MINIO_ACCESS_KEY=AKIAIOSFODNN7EXAMPLE" + - "MINIO_SECRET_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" + command: server /data --console-address ":9090" + + createbuckets: + image: minio/mc + depends_on: + - minio + networks: + - walletconnect-rs-relay + entrypoint: > + /bin/sh -c " + /usr/bin/mc config host add myminio http://minio:9000 AKIAIOSFODNN7EXAMPLE wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY; + /usr/bin/mc mb myminio/datalake; + /usr/bin/mc anonymous set upload myminio/datalake; + /usr/bin/mc anonymous set download myminio/datalake; + /usr/bin/mc anonymous set public myminio/datalake; + exit 0; + " + + jaeger: + image: jaegertracing/all-in-one:latest + networks: + - walletconnect-rs-relay + ports: + - 4317:4317 + - 16686:16686 + volumes: + - rs-relay-test-data-storage:/jaeger + environment: + - COLLECTOR_OTLP_ENABLED=true + # aws-otel-collector: + # image: amazon/aws-otel-collector:latest + # command: --config=/otel-config.yaml + # networks: + # - walletconnect-rs-relay + # ports: + # - 4318:4317 + # volumes: + # - ./otel/config.yaml:/otel-config.yaml + # healthcheck: + # test: [ "CMD", "/healthcheck" ] + # interval: 5s + # timeout: 5s + # retries: 5 + # environment: + # - AWS_ACCESS_KEY_ID + # - AWS_SECRET_ACCESS_KEY + # - AWS_REGION + +networks: + walletconnect-rs-relay: + ipam: + driver: default + config: + - subnet: 172.10.1.0/16 + +volumes: + rs-relay-test-data-storage: diff --git a/terraform/.terraform-docs.yml b/terraform/.terraform-docs.yml new file mode 100644 index 0000000..f112e8b --- /dev/null +++ b/terraform/.terraform-docs.yml @@ -0,0 +1,40 @@ +formatter: 'markdown table' + +recursive: + enabled: true + path: . + +output: + file: README.md + mode: inject + template: |- + + {{ .Content }} + + +content: | + {{ .Header }} + {{ .Requirements }} + {{ .Providers }} + {{ .Modules }} + + ## Inputs + {{- $hideInputs := list "namespace" "region" "stage" "name" "delimiter" "attributes" "tags" "regex_replace_chars" "id_length_limit" "label_key_case" "label_value_case" "label_order" }} + {{- $filteredInputs := list -}} + {{- range .Module.Inputs -}} + {{- if not (has .Name $hideInputs) -}} + {{- $filteredInputs = append $filteredInputs . -}} + {{- end -}} + {{- end -}} + {{ if not $filteredInputs }} + + No inputs. + {{ else }} + | Name | Description | Type | Default | Required | + |------|-------------|------|---------|:--------:| + {{- range $filteredInputs }} + | {{ anchorNameMarkdown "input" .Name }} | {{ tostring .Description | sanitizeMarkdownTbl }} | {{ printf " " }}
{{ tostring .Type | sanitizeMarkdownTbl }}
| {{ printf " " }}
{{ .GetValue | sanitizeMarkdownTbl }}
| {{ printf " " }}{{ ternary .Required "yes" "no" }} | + {{- end }} + {{- end }} + {{ .Outputs }} + {{/** End of file fixer */}} diff --git a/terraform/.terraform.lock.hcl b/terraform/.terraform.lock.hcl new file mode 100644 index 0000000..4655c55 --- /dev/null +++ b/terraform/.terraform.lock.hcl @@ -0,0 +1,89 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/alxrem/jsonnet" { + version = "2.2.0" + constraints = "~> 2.2.0" + hashes = [ + "h1:618oQ4FUqJKIihf/Tmxl3Tu9MsiuUpHYwn5BH79SJ0Y=", + "zh:36d073bffcbdc47a3e3d5b19f5c511f38e4075026b467d98395d27436aeb0234", + "zh:3e252ca26d6a6dbad61a10d3a9231daf0cf565d418efbd651d4e67afe1d6f500", + "zh:3e275f0ff014e7d32b3cc7d655b14a1ba82781757f65830d4e5b6349a82d1062", + "zh:42ddeed65087338ec73724e5b211157a804d9ab9ef6913cbb48e362d30c6b5c0", + "zh:5034cd7aaa3f27d914813eb3fb9c344a4670f3226476123379d9ec95d8a5381f", + "zh:6a0650d9f4302f0b6107612b149ea55c22eb9d19a1483e08dacb2ba22b5be5d3", + "zh:97e9f0b32e33d33d109b5e751342a6ba5949165c23d8a88dd147a6b082fee109", + "zh:a10faaf69352ee9ecb9a68a7b4ceea647f6a10840ecdf0b1a4edd59fe755d600", + "zh:c6bb0612a6eb489b74aa74dc5ff740b601bbdf2335a29f87b571c19cd232a62d", + "zh:d061a59d5c11e6e7b167d0cf6795cdf7de199c926fe4cc96edd434de71374c61", + "zh:da49f78a7ec1f598c2e4b5c3f84c2785fdb210ef61cfd70ae6d48b03143a416b", + "zh:e5bb54e1737c196dc6f8a585251f51fdd717fdc24a480816e1b86958693b993b", + "zh:f10ef2044905b08d9ed7c74a8b778f41ce48e86afd62c4119ab54a80810b795a", + "zh:f787d511f302d5714cb6349fae5f44053c14ebf6cb0435c733e7c822c929fe36", + ] +} + +provider "registry.terraform.io/grafana/grafana" { + version = "2.10.0" + constraints = ">= 2.1.0, ~> 2.1" + hashes = [ + "h1:coquRbqr1W9eLDGjkfya6zA7Zlirs9/23419URZoNSE=", + "zh:0e1132bc5c0b572f44af2b9336627447d8e6e2be3e4178c24b574bcd3b61f442", + "zh:32ecadcd77f1bdb4ccb456adecd126d28f82f3fcecee51366378c2a40289a558", + "zh:603580330d884b00b379c5407acd1f0d6b87fde36efeda52ab00132cb9347c1f", + "zh:61f40b67c4b3c021847e7163f0e96907089bdcd454c05f9625b6b7324ad2a908", + "zh:93d9d97998a210512acb1f11aea4dac2c02cefbcdb7f706bc1580bb977cb0a51", + "zh:9a4fac8681e2d0374edfb8f4df9876c112cf07a737f896f79b296cf5b531ac32", + "zh:bba533e6e0a16869090a55ba232945e79ce482ea370900ec93e8e843eeedc212", + "zh:c3c68199be5b98d2eca410d559b66543ad828d30e16968453e4fe4eac687137e", + "zh:c71358c30f3d02ee587253e4b152f159d20ee01865b39084d5b2ee00eb681fed", + "zh:d131827d98a1365b9e00ef7562f27d65ef98a15550e390141573bec512f171f7", + "zh:d5a7541508ba46d35e1bd5f21bea03a7dd536b0eab585be89f8b8d9662b3eb78", + "zh:eaa1f64856995aa1da61ae1b202e74253d473bb7486c8d16424f007e7fa9d0ef", + "zh:f71a4aa023d3f43e19ebcec7f6856a64235d12cf400c24c139945833d07c6b25", + "zh:faa014f97f8515653a596c76b9455f762c2a4ae637a3753786612c623d5571af", + ] +} + +provider "registry.terraform.io/hashicorp/aws" { + version = "5.34.0" + constraints = ">= 4.9.0, >= 4.50.0, >= 5.0.0, >= 5.7.0, ~> 5.7, >= 5.20.0" + hashes = [ + "h1:1Y1JgV1z99QqAK06+atyfNqreZxyGZKbm4mZO4VhhT8=", + "zh:01bb20ae12b8c66f0cacec4f417a5d6741f018009f3a66077008e67cce127aa4", + "zh:3b0c9bdbbf846beef2c9573fc27898ceb71b69cf9d2f4b1dd2d0c2b539eab114", + "zh:5226ecb9c21c2f6fbf1d662ac82459ffcd4ad058a9ea9c6200750a21a80ca009", + "zh:6021b905d9b3cd3d7892eb04d405c6fa20112718de1d6ef7b9f1db0b0c97721a", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:9e61b8e0ccf923979cd2dc1f1140dbcb02f92248578e10c1996f560b6306317c", + "zh:ad6bf62cdcf531f2f92f6416822918b7ba2af298e4a0065c6baf44991fda982d", + "zh:b698b041ef38837753bbe5265dddbc70b76e8b8b34c5c10876e6aab0eb5eaf63", + "zh:bb799843c534f6a3f072a99d93a3b53ff97c58a96742be15518adf8127706784", + "zh:cebee0d942c37cd3b21e9050457cceb26d0a6ea886b855dab64bb67d78f863d1", + "zh:e061fdd1cb99e7c81fb4485b41ae000c6792d38f73f9f50aed0d3d5c2ce6dcfb", + "zh:eeb4943f82734946362696928336357cd1d36164907ae5905da0316a67e275e1", + "zh:ef09b6ad475efa9300327a30cbbe4373d817261c8e41e5b7391750b16ef4547d", + "zh:f01aab3881cd90b3f56da7c2a75f83da37fd03cc615fc5600a44056a7e0f9af7", + "zh:fcd0f724ebc4b56a499eb6c0fc602de609af18a0d578befa2f7a8df155c55550", + ] +} + +provider "registry.terraform.io/hashicorp/random" { + version = "3.5.1" + constraints = "3.5.1" + hashes = [ + "h1:IL9mSatmwov+e0+++YX2V6uel+dV6bn+fC/cnGDK3Ck=", + "zh:04e3fbd610cb52c1017d282531364b9c53ef72b6bc533acb2a90671957324a64", + "zh:119197103301ebaf7efb91df8f0b6e0dd31e6ff943d231af35ee1831c599188d", + "zh:4d2b219d09abf3b1bb4df93d399ed156cadd61f44ad3baf5cf2954df2fba0831", + "zh:6130bdde527587bbe2dcaa7150363e96dbc5250ea20154176d82bc69df5d4ce3", + "zh:6cc326cd4000f724d3086ee05587e7710f032f94fc9af35e96a386a1c6f2214f", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:b6d88e1d28cf2dfa24e9fdcc3efc77adcdc1c3c3b5c7ce503a423efbdd6de57b", + "zh:ba74c592622ecbcef9dc2a4d81ed321c4e44cddf7da799faa324da9bf52a22b2", + "zh:c7c5cde98fe4ef1143bd1b3ec5dc04baf0d4cc3ca2c5c7d40d17c0e9b2076865", + "zh:dac4bad52c940cd0dfc27893507c1e92393846b024c5a9db159a93c534a3da03", + "zh:de8febe2a2acd9ac454b844a4106ed295ae9520ef54dc8ed2faf29f12716b602", + "zh:eab0d0495e7e711cca367f7d4df6e322e6c562fc52151ec931176115b83ed014", + ] +} diff --git a/terraform/.tflint.hcl b/terraform/.tflint.hcl new file mode 100644 index 0000000..6432d3b --- /dev/null +++ b/terraform/.tflint.hcl @@ -0,0 +1,19 @@ +config { + format = "default" + module = true +} + +plugin "terraform" { + enabled = true + preset = "all" +} + +plugin "aws" { + enabled = true + version = "0.18.0" + source = "github.com/terraform-linters/tflint-ruleset-aws" +} + +rule "terraform_workspace_remote" { + enabled = false +} diff --git a/terraform/README.md b/terraform/README.md new file mode 100644 index 0000000..8af30e9 --- /dev/null +++ b/terraform/README.md @@ -0,0 +1,56 @@ +# Terraform Infrastructure + +You need to be authenticated to Terraform Cloud to manage the infrastructure +from your computer. + +To authenticate, run `terraform login` and follow the instructions. + + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.0 | +| [aws](#requirement\_aws) | >= 5.7 | +| [grafana](#requirement\_grafana) | >= 2.1 | +| [random](#requirement\_random) | 3.5.1 | +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | 5.30.0 | +| [random](#provider\_random) | 3.5.1 | +| [terraform](#provider\_terraform) | n/a | +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [this](#module\_this) | app.terraform.io/wallet-connect/label/null | 0.3.2 | + +## Inputs +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [app\_autoscaling\_desired\_count](#input\_app\_autoscaling\_desired\_count) | The desired number of tasks to run |
number
|
1
| no | +| [app\_autoscaling\_max\_capacity](#input\_app\_autoscaling\_max\_capacity) | The maximum number of tasks to run when autoscaling |
number
|
1
| no | +| [app\_autoscaling\_min\_capacity](#input\_app\_autoscaling\_min\_capacity) | The minimum number of tasks to run when autoscaling |
number
|
1
| no | +| [geoip\_db\_key](#input\_geoip\_db\_key) | The name to the GeoIP database |
string
|
n/a
| yes | +| [grafana\_auth](#input\_grafana\_auth) | The API Token for the Grafana instance |
string
|
""
| no | +| [image\_version](#input\_image\_version) | The ECS tag of the image to deploy |
string
|
n/a
| yes | +| [infura\_project\_id](#input\_infura\_project\_id) | The project ID for Infura |
string
|
n/a
| yes | +| [log\_level](#input\_log\_level) | Defines logging level for the application |
string
|
n/a
| yes | +| [notification\_channels](#input\_notification\_channels) | The notification channels to send alerts to |
list(any)
|
[]
| no | +| [ofac\_blocked\_countries](#input\_ofac\_blocked\_countries) | The list of countries to block |
string
|
""
| no | +| [pokt\_project\_id](#input\_pokt\_project\_id) | The project ID for POKT |
string
|
n/a
| yes | +| [project\_cache\_ttl](#input\_project\_cache\_ttl) | The TTL for project data cache |
number
|
300
| no | +| [registry\_api\_auth\_token](#input\_registry\_api\_auth\_token) | The auth token for the registry API |
string
|
n/a
| yes | +| [registry\_api\_endpoint](#input\_registry\_api\_endpoint) | The endpoint of the registry API |
string
|
n/a
| yes | +| [webhook\_cloudwatch\_p2](#input\_webhook\_cloudwatch\_p2) | The webhook to send CloudWatch P2 alerts to |
string
|
""
| no | +| [webhook\_prometheus\_p2](#input\_webhook\_prometheus\_p2) | The webhook to send Prometheus P2 alerts to |
string
|
""
| no | +| [zerion\_api\_key](#input\_zerion\_api\_key) | The API key for Zerion |
string
|
n/a
| yes | +## Outputs + +No outputs. + + + diff --git a/terraform/alerting/README.md b/terraform/alerting/README.md new file mode 100644 index 0000000..d2909be --- /dev/null +++ b/terraform/alerting/README.md @@ -0,0 +1,43 @@ +# `cloudwatch` module + +This module configures the cloudwatch alarms and webhook forwarding. + + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | ~> 1.0 | +| [aws](#requirement\_aws) | ~> 5.7 | +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | ~> 5.7 | +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [cloudwatch](#module\_cloudwatch) | app.terraform.io/wallet-connect/cloudwatch-constants/aws | 1.0.0 | +| [this](#module\_this) | app.terraform.io/wallet-connect/label/null | 0.3.2 | + +## Inputs +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [context](#input\_context) | Single object for setting entire context at once.
See description of individual variables for details.
Leave string and numeric variables as `null` to use default value.
Individual variable settings (non-null) override settings in context object,
except for attributes and tags, which are merged. |
any
|
n/a
| yes | +| [ecs\_cluster\_name](#input\_ecs\_cluster\_name) | The name of the ECS cluster running the application |
string
|
n/a
| yes | +| [ecs\_cpu\_threshold](#input\_ecs\_cpu\_threshold) | The ECS CPU utilization alarm threshold in percents |
number
|
80
| no | +| [ecs\_memory\_threshold](#input\_ecs\_memory\_threshold) | The ECS memory utilization alarm threshold in percents |
number
|
80
| no | +| [ecs\_service\_name](#input\_ecs\_service\_name) | The name of the ECS service running the application |
string
|
n/a
| yes | +| [redis\_cluster\_id](#input\_redis\_cluster\_id) | The Redis cluster ID |
string
|
n/a
| yes | +| [redis\_cpu\_threshold](#input\_redis\_cpu\_threshold) | The Redis CPU utilization alarm threshold in percents |
number
|
80
| no | +| [redis\_memory\_threshold](#input\_redis\_memory\_threshold) | The Redis available memory alarm threshold in GiB |
number
|
3
| no | +| [webhook\_cloudwatch\_p2](#input\_webhook\_cloudwatch\_p2) | The URL of the webhook to be called on CloudWatch P2 alarms |
string
|
n/a
| yes | +| [webhook\_prometheus\_p2](#input\_webhook\_prometheus\_p2) | The URL of the webhook to be called on Prometheus P2 alarms |
string
|
n/a
| yes | +## Outputs + +No outputs. + + + diff --git a/terraform/alerting/alarms_ecs.tf b/terraform/alerting/alarms_ecs.tf new file mode 100644 index 0000000..0317297 --- /dev/null +++ b/terraform/alerting/alarms_ecs.tf @@ -0,0 +1,45 @@ +resource "aws_cloudwatch_metric_alarm" "ecs_cpu_utilization" { + alarm_name = "${local.alarm_prefix} - ECS CPU Utilization" + alarm_description = "${local.alarm_prefix} - ECS CPU utilization is high (over ${var.ecs_cpu_threshold}%)" + + namespace = module.cloudwatch.namespaces.ECS + dimensions = { + ClusterName = var.ecs_cluster_name + ServiceName = var.ecs_service_name + } + metric_name = module.cloudwatch.metrics.ECS.CPUUtilization + + evaluation_periods = local.evaluation_periods + period = local.period + + statistic = module.cloudwatch.statistics.Average + comparison_operator = module.cloudwatch.operators.GreaterThanOrEqualToThreshold + threshold = var.ecs_cpu_threshold + treat_missing_data = "breaching" + + alarm_actions = [aws_sns_topic.cloudwatch_webhook.arn] + insufficient_data_actions = [aws_sns_topic.cloudwatch_webhook.arn] +} + +resource "aws_cloudwatch_metric_alarm" "ecs_mem_utilization" { + alarm_name = "${local.alarm_prefix} - ECS Memory Utilization" + alarm_description = "${local.alarm_prefix} - ECS Memory utilization is high (over ${var.ecs_memory_threshold}%)" + + namespace = module.cloudwatch.namespaces.ECS + dimensions = { + ClusterName = var.ecs_cluster_name + ServiceName = var.ecs_service_name + } + metric_name = module.cloudwatch.metrics.ECS.MemoryUtilization + + evaluation_periods = local.evaluation_periods + period = local.period + + statistic = module.cloudwatch.statistics.Average + comparison_operator = module.cloudwatch.operators.GreaterThanOrEqualToThreshold + threshold = var.ecs_memory_threshold + treat_missing_data = "breaching" + + alarm_actions = [aws_sns_topic.cloudwatch_webhook.arn] + insufficient_data_actions = [aws_sns_topic.cloudwatch_webhook.arn] +} diff --git a/terraform/alerting/alarms_redis.tf b/terraform/alerting/alarms_redis.tf new file mode 100644 index 0000000..89b4ebe --- /dev/null +++ b/terraform/alerting/alarms_redis.tf @@ -0,0 +1,43 @@ +resource "aws_cloudwatch_metric_alarm" "redis_cpu_utilization" { + alarm_name = "${local.alarm_prefix} - Redis CPU Utilization" + alarm_description = "${local.alarm_prefix} - Redis CPU utilization is high (over ${var.redis_cpu_threshold}%)" + + namespace = module.cloudwatch.namespaces.ElastiCache + dimensions = { + CacheClusterId = var.redis_cluster_id + } + metric_name = module.cloudwatch.metrics.ElastiCache.CPUUtilization + + evaluation_periods = local.evaluation_periods + period = local.period + + statistic = module.cloudwatch.statistics.Average + comparison_operator = module.cloudwatch.operators.GreaterThanOrEqualToThreshold + threshold = var.redis_cpu_threshold + treat_missing_data = "breaching" + + alarm_actions = [aws_sns_topic.cloudwatch_webhook.arn] + insufficient_data_actions = [aws_sns_topic.cloudwatch_webhook.arn] +} + +resource "aws_cloudwatch_metric_alarm" "redis_available_memory" { + alarm_name = "${local.alarm_prefix} - Redis Available Memory" + alarm_description = "${local.alarm_prefix} - Redis available memory is low (less than ${var.redis_memory_threshold}GiB)" + + namespace = module.cloudwatch.namespaces.ElastiCache + dimensions = { + CacheClusterId = var.redis_cluster_id + } + metric_name = module.cloudwatch.metrics.ElastiCache.FreeableMemory + + evaluation_periods = local.evaluation_periods + period = local.period + + statistic = module.cloudwatch.statistics.Average + comparison_operator = module.cloudwatch.operators.LessThanOrEqualToThreshold + threshold = var.redis_memory_threshold * pow(1000, 3) + treat_missing_data = "breaching" + + alarm_actions = [aws_sns_topic.cloudwatch_webhook.arn] + insufficient_data_actions = [aws_sns_topic.cloudwatch_webhook.arn] +} diff --git a/terraform/alerting/context.tf b/terraform/alerting/context.tf new file mode 100644 index 0000000..da1c290 --- /dev/null +++ b/terraform/alerting/context.tf @@ -0,0 +1,179 @@ +module "this" { + source = "app.terraform.io/wallet-connect/label/null" + version = "0.3.2" + + namespace = var.namespace + region = var.region + stage = var.stage + name = var.name + delimiter = var.delimiter + attributes = var.attributes + tags = var.tags + label_order = var.label_order + regex_replace_chars = var.regex_replace_chars + id_length_limit = var.id_length_limit + label_key_case = var.label_key_case + label_value_case = var.label_value_case + + context = var.context +} + +################################################################################ +# Copy contents of label/variables.tf here + +#tflint-ignore: terraform_standard_module_structure +variable "context" { + type = any + description = <<-EOT + Single object for setting entire context at once. + See description of individual variables for details. + Leave string and numeric variables as `null` to use default value. + Individual variable settings (non-null) override settings in context object, + except for attributes and tags, which are merged. + EOT + + validation { + condition = lookup(var.context, "label_key_case", null) == null ? true : contains(["lower", "title", "upper"], var.context["label_key_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`." + } + + validation { + condition = lookup(var.context, "label_value_case", null) == null ? true : contains(["lower", "title", "upper", "none"], var.context["label_value_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} + +#tflint-ignore: terraform_standard_module_structure +variable "namespace" { + type = string + default = null + description = "ID element. Usually the organization name, i.e. 'walletconnect' to help ensure generated IDs are globally unique." +} + +#tflint-ignore: terraform_standard_module_structure +variable "region" { + type = string + default = null + description = "ID element. Usually used for region e.g. 'uw2', 'us-west-2'." +} + +#tflint-ignore: terraform_standard_module_structure +variable "stage" { + type = string + default = null + description = "ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release'." +} + +#tflint-ignore: terraform_standard_module_structure +variable "name" { + type = string + default = null + description = <<-EOT + ID element. Usually the component name. + This is the only ID element not also included as a `tag`. + The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. + EOT +} + +#tflint-ignore: terraform_standard_module_structure +variable "delimiter" { + type = string + default = null + description = <<-EOT + Delimiter to be used between ID elements. + Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. + EOT +} + +#tflint-ignore: terraform_standard_module_structure +variable "attributes" { + type = list(string) + default = [] + description = <<-EOT + ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`, + in the order they appear in the list. New attributes are appended to the + end of the list. The elements of the list are joined by the `delimiter` + and treated as a single ID element. + EOT +} + +#tflint-ignore: terraform_standard_module_structure +variable "tags" { + type = map(string) + default = {} + description = "Additional tags." +} + +#tflint-ignore: terraform_standard_module_structure +variable "label_order" { + type = list(string) + default = null + description = <<-EOT + The order in which the labels (ID elements) appear in the `id`. + Defaults to ["namespace", "region", "stage", "name", "attributes"]. + You can omit any of the 5 labels, but at least one must be present. + EOT +} + +#tflint-ignore: terraform_standard_module_structure +variable "regex_replace_chars" { + type = string + default = null + description = <<-EOT + Terraform regular expression (regex) string. + Characters matching the regex will be removed from the ID elements. + If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. + EOT +} + +#tflint-ignore: terraform_standard_module_structure +variable "id_length_limit" { + type = number + default = null + description = <<-EOT + Limit `id` to this many characters (minimum 6). + Set to `0` for unlimited length. + Set to `null` for keep the existing setting, which defaults to `0`. + Does not affect `id_full`. + EOT + validation { + condition = var.id_length_limit == null ? true : var.id_length_limit >= 6 || var.id_length_limit == 0 + error_message = "The id_length_limit must be >= 6 if supplied (not null), or 0 for unlimited length." + } +} + +#tflint-ignore: terraform_standard_module_structure +variable "label_key_case" { + type = string + default = null + description = <<-EOT + Controls the letter case of the `tags` keys (label names) for tags generated by this module. + Does not affect keys of tags passed in via the `tags` input. + Possible values: `lower`, `title`, `upper`. + Default value: `title`. + EOT + + validation { + condition = var.label_key_case == null ? true : contains(["lower", "title", "upper"], var.label_key_case) + error_message = "Allowed values: `lower`, `title`, `upper`." + } +} + +#tflint-ignore: terraform_standard_module_structure +variable "label_value_case" { + type = string + default = null + description = <<-EOT + Controls the letter case of ID elements (labels) as included in `id`, + set as tag values, and output by this module individually. + Does not affect values of tags passed in via the `tags` input. + Possible values: `lower`, `title`, `upper` and `none` (no transformation). + Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs. + Default value: `lower`. + EOT + + validation { + condition = var.label_value_case == null ? true : contains(["lower", "title", "upper", "none"], var.label_value_case) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} diff --git a/terraform/alerting/main.tf b/terraform/alerting/main.tf new file mode 100644 index 0000000..58da3d8 --- /dev/null +++ b/terraform/alerting/main.tf @@ -0,0 +1,39 @@ +module "cloudwatch" { + source = "app.terraform.io/wallet-connect/cloudwatch-constants/aws" + version = "1.0.0" +} + +locals { + alarm_prefix = "${title(module.this.name)} - ${title(module.this.stage)}" + evaluation_periods = 1 + period = 60 * 5 +} + + +#tfsec:ignore:aws-sns-enable-topic-encryption +resource "aws_sns_topic" "cloudwatch_webhook" { + name = "cloudwatch-webhook" + display_name = "CloudWatch Webhook forwarding to BetterUptime" +} + +resource "aws_sns_topic_subscription" "cloudwatch_webhook" { + count = var.webhook_cloudwatch_p2 != "" ? 1 : 0 + + endpoint = var.webhook_cloudwatch_p2 + protocol = "https" + topic_arn = aws_sns_topic.cloudwatch_webhook.arn +} + + +#tfsec:ignore:aws-sns-enable-topic-encryption +resource "aws_sns_topic" "prometheus_webhook" { + name = "prometheus-webhook" + display_name = "Prometheus Webhook forwarding to BetterUptime" +} + +resource "aws_sns_topic_subscription" "prometheus_webhook" { + count = var.webhook_prometheus_p2 != "" ? 1 : 0 + endpoint = var.webhook_prometheus_p2 + protocol = "https" + topic_arn = aws_sns_topic.prometheus_webhook.arn +} diff --git a/terraform/alerting/terraform.tf b/terraform/alerting/terraform.tf new file mode 100644 index 0000000..f4c0a25 --- /dev/null +++ b/terraform/alerting/terraform.tf @@ -0,0 +1,10 @@ +terraform { + required_version = "~> 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.7" + } + } +} diff --git a/terraform/alerting/variables.tf b/terraform/alerting/variables.tf new file mode 100644 index 0000000..1b603f4 --- /dev/null +++ b/terraform/alerting/variables.tf @@ -0,0 +1,54 @@ +variable "webhook_cloudwatch_p2" { + description = "The URL of the webhook to be called on CloudWatch P2 alarms" + type = string +} + +variable "webhook_prometheus_p2" { + description = "The URL of the webhook to be called on Prometheus P2 alarms" + type = string +} + +#------------------------------------------------------------------------------- +# ECS + +variable "ecs_cluster_name" { + description = "The name of the ECS cluster running the application" + type = string +} + +variable "ecs_service_name" { + description = "The name of the ECS service running the application" + type = string +} + +variable "ecs_cpu_threshold" { + description = "The ECS CPU utilization alarm threshold in percents" + type = number + default = 80 +} + +variable "ecs_memory_threshold" { + description = "The ECS memory utilization alarm threshold in percents" + type = number + default = 80 +} + +#------------------------------------------------------------------------------- +# Redis + +variable "redis_cluster_id" { + description = "The Redis cluster ID" + type = string +} + +variable "redis_cpu_threshold" { + description = "The Redis CPU utilization alarm threshold in percents" + type = number + default = 80 +} + +variable "redis_memory_threshold" { + description = "The Redis available memory alarm threshold in GiB" + type = number + default = 3 +} diff --git a/terraform/context.tf b/terraform/context.tf new file mode 100644 index 0000000..c678334 --- /dev/null +++ b/terraform/context.tf @@ -0,0 +1,23 @@ +module "stage" { + source = "app.terraform.io/wallet-connect/stage/null" + version = "0.1.0" + project = "verify-server" +} + +locals { + stage = module.stage.stage +} + +module "this" { + source = "app.terraform.io/wallet-connect/label/null" + version = "0.3.2" + + namespace = "wc" + region = var.region + stage = local.stage + name = var.name + + tags = { + Application = var.name + } +} diff --git a/terraform/ecs/README.md b/terraform/ecs/README.md new file mode 100644 index 0000000..a5bc239 --- /dev/null +++ b/terraform/ecs/README.md @@ -0,0 +1,87 @@ +# `ecs` module + +This module creates an ECS cluster and an autoscaling group of EC2 instances to run the application. + + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | ~> 1.0 | +| [aws](#requirement\_aws) | ~> 5.7 | +| [random](#requirement\_random) | 3.5.1 | +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | ~> 5.7 | +| [random](#provider\_random) | 3.5.1 | +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [ecs\_cpu\_mem](#module\_ecs\_cpu\_mem) | app.terraform.io/wallet-connect/ecs_cpu_mem/aws | 1.0.0 | +| [this](#module\_this) | app.terraform.io/wallet-connect/label/null | 0.3.2 | + +## Inputs +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [allowed\_app\_ingress\_cidr\_blocks](#input\_allowed\_app\_ingress\_cidr\_blocks) | A list of CIDR blocks to allow ingress access to the application. |
string
|
n/a
| yes | +| [allowed\_lb\_ingress\_cidr\_blocks](#input\_allowed\_lb\_ingress\_cidr\_blocks) | A list of CIDR blocks to allow ingress access to the load-balancer. |
string
|
n/a
| yes | +| [analytics\_datalake\_bucket\_name](#input\_analytics\_datalake\_bucket\_name) | The name of the S3 bucket to use for the analytics datalake |
string
|
n/a
| yes | +| [analytics\_datalake\_kms\_key\_arn](#input\_analytics\_datalake\_kms\_key\_arn) | The ARN of the KMS key to use with the datalake bucket |
string
|
n/a
| yes | +| [autoscaling\_cpu\_scale\_in\_cooldown](#input\_autoscaling\_cpu\_scale\_in\_cooldown) | The cooldown period (in seconds) before a scale in is possible |
number
|
180
| no | +| [autoscaling\_cpu\_scale\_out\_cooldown](#input\_autoscaling\_cpu\_scale\_out\_cooldown) | The cooldown period (in seconds) before a scale out is possible |
number
|
180
| no | +| [autoscaling\_cpu\_target](#input\_autoscaling\_cpu\_target) | The target CPU utilization for the autoscaling group |
number
|
50
| no | +| [autoscaling\_desired\_count](#input\_autoscaling\_desired\_count) | Minimum number of instances in the autoscaling group |
number
|
2
| no | +| [autoscaling\_max\_capacity](#input\_autoscaling\_max\_capacity) | Maximum number of instances in the autoscaling group |
number
|
8
| no | +| [autoscaling\_memory\_scale\_in\_cooldown](#input\_autoscaling\_memory\_scale\_in\_cooldown) | The cooldown period (in seconds) before a scale in is possible |
number
|
180
| no | +| [autoscaling\_memory\_scale\_out\_cooldown](#input\_autoscaling\_memory\_scale\_out\_cooldown) | The cooldown period (in seconds) before a scale out is possible |
number
|
180
| no | +| [autoscaling\_memory\_target](#input\_autoscaling\_memory\_target) | The target memory utilization for the autoscaling group |
number
|
50
| no | +| [autoscaling\_min\_capacity](#input\_autoscaling\_min\_capacity) | Minimum number of instances in the autoscaling group |
number
|
2
| no | +| [cloudwatch\_logs\_key\_arn](#input\_cloudwatch\_logs\_key\_arn) | The ARN of the KMS key to use for encrypting CloudWatch logs |
string
|
n/a
| yes | +| [cloudwatch\_retention\_in\_days](#input\_cloudwatch\_retention\_in\_days) | The number of days to retain CloudWatch logs for the DB instance |
number
|
14
| no | +| [context](#input\_context) | Single object for setting entire context at once.
See description of individual variables for details.
Leave string and numeric variables as `null` to use default value.
Individual variable settings (non-null) override settings in context object,
except for attributes and tags, which are merged. |
any
|
n/a
| yes | +| [ecr\_repository\_url](#input\_ecr\_repository\_url) | The URL of the ECR repository where the app image is stored |
string
|
n/a
| yes | +| [geoip\_db\_bucket\_name](#input\_geoip\_db\_bucket\_name) | The name of the S3 bucket where the GeoIP database is stored |
string
|
n/a
| yes | +| [geoip\_db\_key](#input\_geoip\_db\_key) | The key of the GeoIP database in the S3 bucket |
string
|
n/a
| yes | +| [identity\_cache\_endpoint\_read](#input\_identity\_cache\_endpoint\_read) | The endpoint of the identity cache (read) |
string
|
n/a
| yes | +| [identity\_cache\_endpoint\_write](#input\_identity\_cache\_endpoint\_write) | The endpoint of the identity cache (write) |
string
|
n/a
| yes | +| [image\_version](#input\_image\_version) | The version of the app image to deploy |
string
|
n/a
| yes | +| [infura\_project\_id](#input\_infura\_project\_id) | The project ID for Infura |
string
|
n/a
| yes | +| [log\_level](#input\_log\_level) | The log level for the app |
string
|
n/a
| yes | +| [ofac\_blocked\_countries](#input\_ofac\_blocked\_countries) | The list of countries under OFAC sanctions |
string
|
n/a
| yes | +| [pokt\_project\_id](#input\_pokt\_project\_id) | The project ID for POKT |
string
|
n/a
| yes | +| [port](#input\_port) | The port the app listens on |
number
|
n/a
| yes | +| [private\_subnets](#input\_private\_subnets) | The IDs of the private subnets |
list(string)
|
n/a
| yes | +| [project\_cache\_endpoint\_read](#input\_project\_cache\_endpoint\_read) | The endpoint of the project cache (read) |
string
|
n/a
| yes | +| [project\_cache\_endpoint\_write](#input\_project\_cache\_endpoint\_write) | The endpoint of the project cache (write) |
string
|
n/a
| yes | +| [project\_cache\_ttl](#input\_project\_cache\_ttl) | The TTL for project data cache |
number
|
n/a
| yes | +| [prometheus\_endpoint](#input\_prometheus\_endpoint) | The endpoint of the Prometheus server to use for monitoring |
string
|
n/a
| yes | +| [prometheus\_workspace\_id](#input\_prometheus\_workspace\_id) | The workspace ID of the Prometheus server used for monitoring |
string
|
n/a
| yes | +| [public\_subnets](#input\_public\_subnets) | The IDs of the public subnets |
list(string)
|
n/a
| yes | +| [redis\_max\_connections](#input\_redis\_max\_connections) | The maximum number of connections to the Redis server |
number
|
128
| no | +| [registry\_api\_auth\_token](#input\_registry\_api\_auth\_token) | The auth token for the registry API |
string
|
n/a
| yes | +| [registry\_api\_endpoint](#input\_registry\_api\_endpoint) | The endpoint of the registry API |
string
|
n/a
| yes | +| [route53\_zones](#input\_route53\_zones) | The FQDNs to use for the app |
map(string)
|
n/a
| yes | +| [route53\_zones\_certificates](#input\_route53\_zones\_certificates) | The ARNs of the ACM certificates to use for HTTPS |
map(string)
|
n/a
| yes | +| [task\_cpu](#input\_task\_cpu) | The number of CPU units to reserve for the container. |
number
|
n/a
| yes | +| [task\_execution\_role\_name](#input\_task\_execution\_role\_name) | The name of the task execution role |
string
|
n/a
| yes | +| [task\_memory](#input\_task\_memory) | The amount of memory (in MiB) to reserve for the container. |
number
|
n/a
| yes | +| [vpc\_id](#input\_vpc\_id) | The ID of the VPC to deploy to |
string
|
n/a
| yes | +| [zerion\_api\_key](#input\_zerion\_api\_key) | The API key for Zerion |
string
|
n/a
| yes | +## Outputs + +| Name | Description | +|------|-------------| +| [ecs\_cluster\_name](#output\_ecs\_cluster\_name) | The name of the ECS cluster | +| [ecs\_service\_name](#output\_ecs\_service\_name) | The name of the ECS service | +| [ecs\_task\_family](#output\_ecs\_task\_family) | The family of the task definition | +| [load\_balancer\_arn](#output\_load\_balancer\_arn) | The ARN of the load balancer | +| [load\_balancer\_arn\_suffix](#output\_load\_balancer\_arn\_suffix) | The ARN suffix of the load balancer | +| [service\_security\_group\_id](#output\_service\_security\_group\_id) | The ID of the security group for the service | +| [target\_group\_arn](#output\_target\_group\_arn) | The ARN of the target group | + + + diff --git a/terraform/ecs/cluster.tf b/terraform/ecs/cluster.tf new file mode 100644 index 0000000..2b0ed8f --- /dev/null +++ b/terraform/ecs/cluster.tf @@ -0,0 +1,188 @@ +locals { + image = "${var.ecr_repository_url}:${var.image_version}" + + otel_port = var.port + 1 + otel_cpu = 128 + otel_memory = 128 + + file_descriptor_soft_limit = pow(2, 18) + file_descriptor_hard_limit = local.file_descriptor_soft_limit * 2 +} + +module "ecs_cpu_mem" { + source = "app.terraform.io/wallet-connect/ecs_cpu_mem/aws" + version = "1.0.0" + cpu = var.task_cpu + local.otel_cpu + memory = var.task_memory + local.otel_memory +} + +#------------------------------------------------------------------------------- +# ECS Cluster + +resource "aws_ecs_cluster" "app_cluster" { + name = "${module.this.id}-cluster" + + configuration { + execute_command_configuration { + logging = "OVERRIDE" + + log_configuration { + cloud_watch_encryption_enabled = false + cloud_watch_log_group_name = aws_cloudwatch_log_group.cluster.name + } + } + } + + # Exposes metrics such as the number of running tasks in CloudWatch + setting { + name = "containerInsights" + value = "enabled" + } +} + +#------------------------------------------------------------------------------- +# ECS Task definition + +resource "aws_ecs_task_definition" "app_task" { + family = module.this.id + + requires_compatibilities = ["FARGATE"] + network_mode = "awsvpc" + cpu = module.ecs_cpu_mem.cpu + memory = module.ecs_cpu_mem.memory + execution_role_arn = data.aws_iam_role.ecs_task_execution_role.arn + task_role_arn = data.aws_iam_role.ecs_task_execution_role.arn + + runtime_platform { + operating_system_family = "LINUX" + } + + container_definitions = jsonencode([ + { + name = module.this.id, + image = local.image, + cpu = var.task_cpu, + memory = var.task_memory, + essential = true, + + environment = [ + { name = "SECRET", value = var.app_secret }, + + { name = "PORT", value = tostring(var.port) }, + { name = "PROMETHEUS_PORT", value = tostring(local.otel_port) }, + + { name = "LOG_LEVEL", value = var.log_level }, + + { name = "GEOIP_DB_BUCKET", value = var.geoip_db_bucket_name }, + { name = "GEOIP_DB_KEY", value = var.geoip_db_key }, + + { name = "PROJECT_REGISTRY_URL", value = var.project_registry_api_url }, + { name = "PROJECT_REGISTRY_AUTH_TOKEN", value = var.project_registry_api_auth_token }, + + { name = "DATA_API_URL", value = var.data_api_url }, + { name = "DATA_API_AUTH_TOKEN", value = var.data_api_auth_token }, + + { name = "ATTESTATION_CACHE_URL", value = var.attestation_cache_url }, + { name = "PROJECT_REGISTRY_CACHE_URL", value = var.project_registry_cache_url }, + { name = "SCAM_GUARD_CACHE_URL", value = var.scam_guard_cache_url }, + + { name = "DATA_LAKE_BUCKET", value = var.analytics_datalake_bucket_name }, + + { name = "BLOCKED_COUNTRIES", value = var.ofac_blocked_countries }, + ], + + ulimits = [{ + name : "nofile", + softLimit : local.file_descriptor_soft_limit, + hardLimit : local.file_descriptor_hard_limit + }], + + portMappings = [ + { + containerPort = var.port, + hostPort = var.port + }, + { + containerPort = local.otel_port, + hostPort = local.otel_port + } + ], + + logConfiguration : { + logDriver = "awslogs", + options = { + "awslogs-group" = aws_cloudwatch_log_group.cluster.name, + "awslogs-region" = module.this.region, + "awslogs-stream-prefix" = "ecs" + } + }, + + dependsOn = [ + { containerName : "aws-otel-collector", condition : "START" }, + ] + }, + + # Forward telemetry data to AWS CloudWatch + { + name = "aws-otel-collector", + image = "public.ecr.aws/aws-observability/aws-otel-collector:v0.31.0", + cpu = local.otel_cpu, + memory = local.otel_memory, + essential = true, + + command = [ + "--config=/etc/ecs/ecs-amp-xray-prometheus.yaml" + # Uncomment to enable debug logging in otel-collector + # "--set=service.telemetry.logs.level=DEBUG" + ], + + environment = [ + { name : "AWS_PROMETHEUS_SCRAPING_ENDPOINT", value : "0.0.0.0:${local.otel_port}" }, + { name : "AWS_PROMETHEUS_ENDPOINT", value : "${var.prometheus_endpoint}api/v1/remote_write" }, + { name : "AWS_REGION", value : module.this.region }, + ], + + logConfiguration = { + logDriver = "awslogs", + options = { + "awslogs-group" = aws_cloudwatch_log_group.otel.name, + "awslogs-region" = module.this.region, + "awslogs-stream-prefix" = "ecs" + } + } + }, + ]) +} + + +#------------------------------------------------------------------------------- +# ECS Service + +resource "aws_ecs_service" "app_service" { + name = "${module.this.id}-service" + cluster = aws_ecs_cluster.app_cluster.id + task_definition = aws_ecs_task_definition.app_task.arn + launch_type = "FARGATE" + desired_count = var.autoscaling_desired_count + propagate_tags = "TASK_DEFINITION" + + # Wait for the service deployment to succeed + wait_for_steady_state = true + + network_configuration { + subnets = var.private_subnets + assign_public_ip = false + security_groups = [aws_security_group.app_ingress.id] + } + + load_balancer { + target_group_arn = aws_lb_target_group.target_group.arn + container_name = aws_ecs_task_definition.app_task.family + container_port = var.port + } + + # Allow external changes without Terraform plan difference + lifecycle { + ignore_changes = [desired_count] + } +} diff --git a/terraform/ecs/cluster_autoscaling.tf b/terraform/ecs/cluster_autoscaling.tf new file mode 100644 index 0000000..d20ed76 --- /dev/null +++ b/terraform/ecs/cluster_autoscaling.tf @@ -0,0 +1,43 @@ +resource "aws_appautoscaling_target" "ecs_target" { + min_capacity = var.autoscaling_min_capacity + max_capacity = var.autoscaling_max_capacity + resource_id = "service/${aws_ecs_cluster.app_cluster.name}/${aws_ecs_service.app_service.name}" + scalable_dimension = "ecs:service:DesiredCount" + service_namespace = "ecs" +} + +resource "aws_appautoscaling_policy" "ecs_target_cpu" { + name = "${module.this.id}-scaling-policy-cpu" + policy_type = "TargetTrackingScaling" + resource_id = aws_appautoscaling_target.ecs_target.resource_id + scalable_dimension = aws_appautoscaling_target.ecs_target.scalable_dimension + service_namespace = aws_appautoscaling_target.ecs_target.service_namespace + + target_tracking_scaling_policy_configuration { + predefined_metric_specification { + predefined_metric_type = "ECSServiceAverageCPUUtilization" + } + target_value = var.autoscaling_cpu_target + scale_in_cooldown = var.autoscaling_cpu_scale_in_cooldown + scale_out_cooldown = var.autoscaling_cpu_scale_out_cooldown + } + depends_on = [aws_appautoscaling_target.ecs_target] +} + +resource "aws_appautoscaling_policy" "ecs_target_memory" { + name = "${module.this.id}-scaling-policy-memory" + policy_type = "TargetTrackingScaling" + resource_id = aws_appautoscaling_target.ecs_target.resource_id + scalable_dimension = aws_appautoscaling_target.ecs_target.scalable_dimension + service_namespace = aws_appautoscaling_target.ecs_target.service_namespace + + target_tracking_scaling_policy_configuration { + predefined_metric_specification { + predefined_metric_type = "ECSServiceAverageMemoryUtilization" + } + target_value = var.autoscaling_memory_target + scale_in_cooldown = var.autoscaling_memory_scale_in_cooldown + scale_out_cooldown = var.autoscaling_memory_scale_out_cooldown + } + depends_on = [aws_appautoscaling_target.ecs_target] +} diff --git a/terraform/ecs/cluster_iam.tf b/terraform/ecs/cluster_iam.tf new file mode 100644 index 0000000..572c4ef --- /dev/null +++ b/terraform/ecs/cluster_iam.tf @@ -0,0 +1,118 @@ +#------------------------------------------------------------------------------- +# Task execution role + +data "aws_iam_role" "ecs_task_execution_role" { + name = var.task_execution_role_name +} + +# GeoIP Bucket Access +resource "aws_iam_policy" "geoip_bucket_access" { + name = "${module.this.id}-geoip-bucket_access" + path = "/" + description = "Allows ${module.this.id} to read from ${var.geoip_db_bucket_name}" + + policy = jsonencode({ + "Version" : "2012-10-17", + "Statement" : [ + { + "Sid" : "ListObjectsInGeoipBucket", + "Effect" : "Allow", + "Action" : ["s3:ListBucket"], + "Resource" : ["arn:aws:s3:::${var.geoip_db_bucket_name}"] + }, + { + "Sid" : "AllObjectActionsInGeoipBucket", + "Effect" : "Allow", + "Action" : ["s3:CopyObject", "s3:GetObject", "s3:HeadObject"], + "Resource" : ["arn:aws:s3:::${var.geoip_db_bucket_name}/*"] + }, + ] + }) +} +resource "aws_iam_role_policy_attachment" "geoip_bucket_access" { + role = data.aws_iam_role.ecs_task_execution_role.name + policy_arn = aws_iam_policy.geoip_bucket_access.arn +} + +# Analytics Bucket Access +resource "aws_iam_policy" "datalake_bucket_access" { + name = "${module.this.id}-analytics-bucket_access" + path = "/" + description = "Allows ${module.this.id} to read/write from ${var.analytics_datalake_bucket_name}" + + policy = jsonencode({ + "Version" : "2012-10-17", + "Statement" : [ + { + "Sid" : "ListObjectsInAnalyticsBucket", + "Effect" : "Allow", + "Action" : ["s3:ListBucket"], + "Resource" : ["arn:aws:s3:::${var.analytics_datalake_bucket_name}"] + }, + { + "Sid" : "AllObjectActionsInAnalyticsBucket", + "Effect" : "Allow", + "Action" : "s3:*Object", + "Resource" : ["arn:aws:s3:::${var.analytics_datalake_bucket_name}/${module.this.name}}/*"] + }, + { + "Sid" : "AllGenerateDataKeyForAnalyticsBucket", + "Effect" : "Allow", + "Action" : ["kms:GenerateDataKey"], + "Resource" : [var.analytics_datalake_kms_key_arn] + } + ] + }) +} +resource "aws_iam_role_policy_attachment" "datalake_bucket_access" { + role = data.aws_iam_role.ecs_task_execution_role.name + policy_arn = aws_iam_policy.datalake_bucket_access.arn +} + +resource "aws_iam_role_policy_attachment" "ecs_task_execution_role_policy" { + role = data.aws_iam_role.ecs_task_execution_role.name + policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" +} + +resource "aws_iam_role_policy_attachment" "cloudwatch_write_policy" { + role = data.aws_iam_role.ecs_task_execution_role.name + policy_arn = "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess" +} + +resource "aws_iam_role_policy_attachment" "prometheus_write_policy" { + role = data.aws_iam_role.ecs_task_execution_role.name + policy_arn = "arn:aws:iam::aws:policy/AmazonPrometheusRemoteWriteAccess" +} + +resource "aws_iam_role_policy_attachment" "ssm_read_only_policy" { + role = data.aws_iam_role.ecs_task_execution_role.name + policy_arn = "arn:aws:iam::aws:policy/AmazonSSMReadOnlyAccess" +} + +#tfsec:ignore:aws-iam-no-policy-wildcards +resource "aws_iam_policy" "otel" { + name = "${module.this.id}-otel" + path = "/" + + policy = jsonencode({ + "Version" : "2012-10-17", + "Statement" : [ + { + "Effect" : "Allow", + "Action" : [ + "logs:PutLogEvents", + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:DescribeLogStreams", + "logs:DescribeLogGroups", + "ssm:GetParameters", + ], + "Resource" : ["*"] + }, + ] + }) +} +resource "aws_iam_role_policy_attachment" "ecs_task_execution_fetch_ghcr_secret_policy" { + role = data.aws_iam_role.ecs_task_execution_role.name + policy_arn = aws_iam_policy.otel.arn +} diff --git a/terraform/ecs/cluster_logs.tf b/terraform/ecs/cluster_logs.tf new file mode 100644 index 0000000..f5ef4c4 --- /dev/null +++ b/terraform/ecs/cluster_logs.tf @@ -0,0 +1,17 @@ +resource "aws_cloudwatch_log_group" "cluster" { + name = "${module.this.id}-app-logs" + kms_key_id = var.cloudwatch_logs_key_arn + retention_in_days = var.cloudwatch_retention_in_days +} + +resource "aws_cloudwatch_log_group" "otel" { + name = "${module.this.id}-aws-otel-sidecar-collector" + kms_key_id = var.cloudwatch_logs_key_arn + retention_in_days = var.cloudwatch_retention_in_days +} + +resource "aws_cloudwatch_log_group" "prometheus_proxy" { + name = "${module.this.id}-sigv4-prometheus-proxy" + kms_key_id = var.cloudwatch_logs_key_arn + retention_in_days = var.cloudwatch_retention_in_days +} diff --git a/terraform/ecs/context.tf b/terraform/ecs/context.tf new file mode 100644 index 0000000..da1c290 --- /dev/null +++ b/terraform/ecs/context.tf @@ -0,0 +1,179 @@ +module "this" { + source = "app.terraform.io/wallet-connect/label/null" + version = "0.3.2" + + namespace = var.namespace + region = var.region + stage = var.stage + name = var.name + delimiter = var.delimiter + attributes = var.attributes + tags = var.tags + label_order = var.label_order + regex_replace_chars = var.regex_replace_chars + id_length_limit = var.id_length_limit + label_key_case = var.label_key_case + label_value_case = var.label_value_case + + context = var.context +} + +################################################################################ +# Copy contents of label/variables.tf here + +#tflint-ignore: terraform_standard_module_structure +variable "context" { + type = any + description = <<-EOT + Single object for setting entire context at once. + See description of individual variables for details. + Leave string and numeric variables as `null` to use default value. + Individual variable settings (non-null) override settings in context object, + except for attributes and tags, which are merged. + EOT + + validation { + condition = lookup(var.context, "label_key_case", null) == null ? true : contains(["lower", "title", "upper"], var.context["label_key_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`." + } + + validation { + condition = lookup(var.context, "label_value_case", null) == null ? true : contains(["lower", "title", "upper", "none"], var.context["label_value_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} + +#tflint-ignore: terraform_standard_module_structure +variable "namespace" { + type = string + default = null + description = "ID element. Usually the organization name, i.e. 'walletconnect' to help ensure generated IDs are globally unique." +} + +#tflint-ignore: terraform_standard_module_structure +variable "region" { + type = string + default = null + description = "ID element. Usually used for region e.g. 'uw2', 'us-west-2'." +} + +#tflint-ignore: terraform_standard_module_structure +variable "stage" { + type = string + default = null + description = "ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release'." +} + +#tflint-ignore: terraform_standard_module_structure +variable "name" { + type = string + default = null + description = <<-EOT + ID element. Usually the component name. + This is the only ID element not also included as a `tag`. + The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. + EOT +} + +#tflint-ignore: terraform_standard_module_structure +variable "delimiter" { + type = string + default = null + description = <<-EOT + Delimiter to be used between ID elements. + Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. + EOT +} + +#tflint-ignore: terraform_standard_module_structure +variable "attributes" { + type = list(string) + default = [] + description = <<-EOT + ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`, + in the order they appear in the list. New attributes are appended to the + end of the list. The elements of the list are joined by the `delimiter` + and treated as a single ID element. + EOT +} + +#tflint-ignore: terraform_standard_module_structure +variable "tags" { + type = map(string) + default = {} + description = "Additional tags." +} + +#tflint-ignore: terraform_standard_module_structure +variable "label_order" { + type = list(string) + default = null + description = <<-EOT + The order in which the labels (ID elements) appear in the `id`. + Defaults to ["namespace", "region", "stage", "name", "attributes"]. + You can omit any of the 5 labels, but at least one must be present. + EOT +} + +#tflint-ignore: terraform_standard_module_structure +variable "regex_replace_chars" { + type = string + default = null + description = <<-EOT + Terraform regular expression (regex) string. + Characters matching the regex will be removed from the ID elements. + If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. + EOT +} + +#tflint-ignore: terraform_standard_module_structure +variable "id_length_limit" { + type = number + default = null + description = <<-EOT + Limit `id` to this many characters (minimum 6). + Set to `0` for unlimited length. + Set to `null` for keep the existing setting, which defaults to `0`. + Does not affect `id_full`. + EOT + validation { + condition = var.id_length_limit == null ? true : var.id_length_limit >= 6 || var.id_length_limit == 0 + error_message = "The id_length_limit must be >= 6 if supplied (not null), or 0 for unlimited length." + } +} + +#tflint-ignore: terraform_standard_module_structure +variable "label_key_case" { + type = string + default = null + description = <<-EOT + Controls the letter case of the `tags` keys (label names) for tags generated by this module. + Does not affect keys of tags passed in via the `tags` input. + Possible values: `lower`, `title`, `upper`. + Default value: `title`. + EOT + + validation { + condition = var.label_key_case == null ? true : contains(["lower", "title", "upper"], var.label_key_case) + error_message = "Allowed values: `lower`, `title`, `upper`." + } +} + +#tflint-ignore: terraform_standard_module_structure +variable "label_value_case" { + type = string + default = null + description = <<-EOT + Controls the letter case of ID elements (labels) as included in `id`, + set as tag values, and output by this module individually. + Does not affect values of tags passed in via the `tags` input. + Possible values: `lower`, `title`, `upper` and `none` (no transformation). + Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs. + Default value: `lower`. + EOT + + validation { + condition = var.label_value_case == null ? true : contains(["lower", "title", "upper", "none"], var.label_value_case) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} diff --git a/terraform/ecs/dns.tf b/terraform/ecs/dns.tf new file mode 100644 index 0000000..aaa95cd --- /dev/null +++ b/terraform/ecs/dns.tf @@ -0,0 +1,14 @@ +# DNS Records +resource "aws_route53_record" "dns_load_balancer" { + for_each = var.route53_zones + + zone_id = each.key + name = each.value + type = "A" + + alias { + name = aws_lb.load_balancer.dns_name + zone_id = aws_lb.load_balancer.zone_id + evaluate_target_health = true + } +} diff --git a/terraform/ecs/main.tf b/terraform/ecs/main.tf new file mode 100644 index 0000000..fb5425f --- /dev/null +++ b/terraform/ecs/main.tf @@ -0,0 +1,3 @@ +resource "random_pet" "this" { + length = 2 +} diff --git a/terraform/ecs/network.tf b/terraform/ecs/network.tf new file mode 100644 index 0000000..f488cdd --- /dev/null +++ b/terraform/ecs/network.tf @@ -0,0 +1,161 @@ +locals { + lb_name = trimsuffix(substr(replace("${module.this.id}-${random_pet.this.id}", "_", "-"), 0, 32), "-") +} + +#tfsec:ignore:aws-elb-drop-invalid-headers +#tfsec:ignore:aws-elb-alb-not-public +resource "aws_lb" "load_balancer" { + name = local.lb_name + load_balancer_type = "application" + subnets = var.public_subnets + + security_groups = [aws_security_group.lb_ingress.id] + + lifecycle { + create_before_destroy = true + } +} + +locals { + main_certificate_key = keys(var.route53_zones_certificates)[0] + main_certificate = var.route53_zones_certificates[local.main_certificate_key] + additional_certificates = { for k, v in var.route53_zones_certificates : k => v if k != local.main_certificate_key } +} + +resource "aws_lb_listener" "listener-https" { + load_balancer_arn = aws_lb.load_balancer.arn + port = "443" + protocol = "HTTPS" + certificate_arn = local.main_certificate + ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06" + + default_action { + type = "forward" + target_group_arn = aws_lb_target_group.target_group.arn + } + + lifecycle { + create_before_destroy = true + } +} + +resource "aws_lb_listener_certificate" "listener-https" { + for_each = local.additional_certificates + listener_arn = aws_lb_listener.listener-https.arn + certificate_arn = each.value +} + +resource "aws_lb_listener" "listener-http" { + load_balancer_arn = aws_lb.load_balancer.arn + port = "80" + protocol = "HTTP" + + default_action { + type = "redirect" + + redirect { + port = "443" + protocol = "HTTPS" + status_code = "HTTP_301" + } + } + + lifecycle { + create_before_destroy = true + } +} + +resource "aws_lb_target_group" "target_group" { + name = local.lb_name + port = var.port + protocol = "HTTP" + target_type = "ip" + vpc_id = var.vpc_id + slow_start = 30 + + health_check { + protocol = "HTTP" + path = "/health" # KeysServer's health path + port = var.port + interval = 15 + timeout = 10 + healthy_threshold = 2 + unhealthy_threshold = 2 + } + + lifecycle { + create_before_destroy = true + } +} + +# Security Groups + +#tfsec:ignore:aws-ec2-no-public-ingress-sgr +resource "aws_security_group" "lb_ingress" { + name = "${local.lb_name}-lb-ingress" + description = "Allow app port ingress from vpc" + vpc_id = var.vpc_id + + ingress { + from_port = 443 + to_port = 443 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + description = "Allow HTTPS traffic from anywhere" + } + + ingress { + from_port = 80 + to_port = 80 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + description = "Allow HTTP traffic from anywhere" + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = [var.allowed_lb_ingress_cidr_blocks] + description = "Allow traffic out to all VPC IP addresses" + } + + lifecycle { + create_before_destroy = true + } +} + +#tfsec:ignore:aws-ec2-no-public-egress-sgr +resource "aws_security_group" "app_ingress" { + name = "${local.lb_name}-app-ingress" + description = "Allow app port ingress" + vpc_id = var.vpc_id + + ingress { + from_port = 0 + to_port = 0 + protocol = "-1" + security_groups = [aws_security_group.lb_ingress.id] + description = "Allow traffic from load balancer" + } + + ingress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = [var.allowed_app_ingress_cidr_blocks] + description = "Allow traffic from allowed CIDR blocks" + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + description = "Allow traffic out to all IP addresses" + } + + lifecycle { + create_before_destroy = true + } +} diff --git a/terraform/ecs/outputs.tf b/terraform/ecs/outputs.tf new file mode 100644 index 0000000..8036e7a --- /dev/null +++ b/terraform/ecs/outputs.tf @@ -0,0 +1,34 @@ +output "ecs_cluster_name" { + description = "The name of the ECS cluster" + value = aws_ecs_cluster.app_cluster.name +} + +output "ecs_service_name" { + description = "The name of the ECS service" + value = aws_ecs_service.app_service.name +} + +output "ecs_task_family" { + description = "The family of the task definition" + value = aws_ecs_task_definition.app_task.family +} + +output "service_security_group_id" { + description = "The ID of the security group for the service" + value = aws_security_group.app_ingress.id +} + +output "target_group_arn" { + description = "The ARN of the target group" + value = aws_lb_target_group.target_group.arn +} + +output "load_balancer_arn" { + description = "The ARN of the load balancer" + value = aws_lb.load_balancer.arn +} + +output "load_balancer_arn_suffix" { + description = "The ARN suffix of the load balancer" + value = aws_lb.load_balancer.arn_suffix +} diff --git a/terraform/ecs/terraform.tf b/terraform/ecs/terraform.tf new file mode 100644 index 0000000..692cc58 --- /dev/null +++ b/terraform/ecs/terraform.tf @@ -0,0 +1,14 @@ +terraform { + required_version = "~> 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.7" + } + random = { + source = "hashicorp/random" + version = "3.5.1" + } + } +} diff --git a/terraform/ecs/variables.tf b/terraform/ecs/variables.tf new file mode 100644 index 0000000..2205d2a --- /dev/null +++ b/terraform/ecs/variables.tf @@ -0,0 +1,231 @@ +#------------------------------------------------------------------------------- +# Cluster + +variable "ecr_repository_url" { + description = "The URL of the ECR repository where the app image is stored" + type = string +} + +variable "image_version" { + description = "The version of the app image to deploy" + type = string +} + +variable "task_execution_role_name" { + description = "The name of the task execution role" + type = string +} + +variable "task_cpu" { + description = "The number of CPU units to reserve for the container." + type = number +} + +variable "task_memory" { + description = "The amount of memory (in MiB) to reserve for the container." + type = number +} + +variable "autoscaling_desired_count" { + description = "Minimum number of instances in the autoscaling group" + type = number + default = 2 +} + +variable "autoscaling_min_capacity" { + description = "Minimum number of instances in the autoscaling group" + type = number + default = 2 +} + +variable "autoscaling_max_capacity" { + description = "Maximum number of instances in the autoscaling group" + type = number + default = 8 +} + +variable "cloudwatch_logs_key_arn" { + description = "The ARN of the KMS key to use for encrypting CloudWatch logs" + type = string +} + +variable "cloudwatch_retention_in_days" { + description = "The number of days to retain CloudWatch logs for the DB instance" + type = number + default = 14 +} + +#------------------------------------------------------------------------------- +# DNS + +variable "route53_zones" { + description = "The FQDNs to use for the app" + type = map(string) +} + +variable "route53_zones_certificates" { + description = "The ARNs of the ACM certificates to use for HTTPS" + type = map(string) +} + +#------------------------------------------------------------------------------- +# Network + +variable "vpc_id" { + description = "The ID of the VPC to deploy to" + type = string +} + +variable "public_subnets" { + description = "The IDs of the public subnets" + type = list(string) +} + +variable "private_subnets" { + description = "The IDs of the private subnets" + type = list(string) +} + +variable "allowed_app_ingress_cidr_blocks" { + description = "A list of CIDR blocks to allow ingress access to the application." + type = string +} + +variable "allowed_lb_ingress_cidr_blocks" { + description = "A list of CIDR blocks to allow ingress access to the load-balancer." + type = string +} + +#------------------------------------------------------------------------------- +# Application + +variable "app_secret" { + description = "The application secret" + type = string + sensitive = true +} + +variable "port" { + description = "The port the app listens on" + type = number +} + +variable "log_level" { + description = "The log level for the app" + type = string +} + +variable "project_registry_api_url" { + description = "The url of the project registry API" + type = string +} + +variable "project_registry_api_auth_token" { + description = "The auth token for the project registry API" + type = string + sensitive = true +} + +variable "data_api_url" { + description = "The url of the data API" + type = string +} + +variable "data_api_auth_token" { + description = "The auth token for the data API" + type = string + sensitive = true +} + +variable "attestation_cache_url" { + description = "The endpoint of the attestation cache" + type = string +} + +variable "project_registry_cache_url" { + description = "The url of the project registry cache" + type = string +} + +variable "scam_guard_cache_url" { + description = "The url of the scam guard cache" + type = string +} + +variable "ofac_blocked_countries" { + description = "The list of countries under OFAC sanctions" + type = string +} + +#------------------------------------------------------------------------------- +# Analytics + +variable "analytics_datalake_bucket_name" { + description = "The name of the S3 bucket to use for the analytics datalake" + type = string +} + +variable "analytics_datalake_kms_key_arn" { + description = "The ARN of the KMS key to use with the datalake bucket" + type = string +} + +#------------------------------------------------------------------------------- +# Autoscaling + +variable "autoscaling_cpu_target" { + description = "The target CPU utilization for the autoscaling group" + type = number + default = 50 +} + +variable "autoscaling_cpu_scale_in_cooldown" { + description = "The cooldown period (in seconds) before a scale in is possible" + type = number + default = 180 +} + +variable "autoscaling_cpu_scale_out_cooldown" { + description = "The cooldown period (in seconds) before a scale out is possible" + type = number + default = 180 +} + +variable "autoscaling_memory_target" { + description = "The target memory utilization for the autoscaling group" + type = number + default = 50 +} + +variable "autoscaling_memory_scale_in_cooldown" { + description = "The cooldown period (in seconds) before a scale in is possible" + type = number + default = 180 +} + +variable "autoscaling_memory_scale_out_cooldown" { + description = "The cooldown period (in seconds) before a scale out is possible" + type = number + default = 180 +} + +#------------------------------------------------------------------------------- +# Monitoring + +variable "prometheus_endpoint" { + description = "The endpoint of the Prometheus server to use for monitoring" + type = string +} + +#--------------------------------------- +# GeoIP + +variable "geoip_db_bucket_name" { + description = "The name of the S3 bucket where the GeoIP database is stored" + type = string +} + +variable "geoip_db_key" { + description = "The key of the GeoIP database in the S3 bucket" + type = string +} diff --git a/terraform/inputs.tf b/terraform/inputs.tf new file mode 100644 index 0000000..a5298e4 --- /dev/null +++ b/terraform/inputs.tf @@ -0,0 +1,39 @@ +data "terraform_remote_state" "org" { + backend = "remote" + config = { + organization = "wallet-connect" + workspaces = { + name = "aws-org" + } + } +} + +data "terraform_remote_state" "datalake" { + backend = "remote" + config = { + organization = "wallet-connect" + workspaces = { + name = "datalake-${module.stage.dev ? "staging" : local.stage}" + } + } +} + +data "terraform_remote_state" "infra_aws" { + backend = "remote" + config = { + organization = "wallet-connect" + workspaces = { + name = "infra-aws" + } + } +} + +data "terraform_remote_state" "monitoring" { + backend = "remote" + config = { + organization = "wallet-connect" + workspaces = { + name = "monitoring" + } + } +} diff --git a/terraform/main.tf b/terraform/main.tf new file mode 100644 index 0000000..e950eaa --- /dev/null +++ b/terraform/main.tf @@ -0,0 +1,48 @@ +data "aws_caller_identity" "this" {} + +resource "random_pet" "this" { + length = 2 +} + +locals { + ecr_repository_url = module.stage.dev ? data.terraform_remote_state.org.outputs.accounts.sdlc.dev.ecr-urls.verify : data.terraform_remote_state.org.outputs.accounts.wl.verify[local.stage].ecr-url +} + +resource "aws_kms_key" "cloudwatch_logs" { + description = "KMS key for encrypting CloudWatch Logs" + enable_key_rotation = true + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Sid = "Enable IAM User Permissions" + Effect = "Allow" + Principal = { + AWS = data.aws_caller_identity.this.account_id + } + Action = "kms:*" + Resource = "*" + }, + { + Sid = "AllowCloudWatchLogs" + Effect = "Allow" + Principal = { + Service = "logs.${module.this.region}.amazonaws.com" + } + Action = [ + "kms:Encrypt*", + "kms:Decrypt*", + "kms:ReEncrypt*", + "kms:GenerateDataKey*", + "kms:Describe*" + ] + Resource = "*" + }, + ] + }) +} + +resource "aws_kms_alias" "cloudwatch_logs" { + name = "alias/${module.this.id}-cloudwatch-logs" + target_key_id = aws_kms_key.cloudwatch_logs.key_id +} diff --git a/terraform/monitoring/context.tf b/terraform/monitoring/context.tf new file mode 100644 index 0000000..da1c290 --- /dev/null +++ b/terraform/monitoring/context.tf @@ -0,0 +1,179 @@ +module "this" { + source = "app.terraform.io/wallet-connect/label/null" + version = "0.3.2" + + namespace = var.namespace + region = var.region + stage = var.stage + name = var.name + delimiter = var.delimiter + attributes = var.attributes + tags = var.tags + label_order = var.label_order + regex_replace_chars = var.regex_replace_chars + id_length_limit = var.id_length_limit + label_key_case = var.label_key_case + label_value_case = var.label_value_case + + context = var.context +} + +################################################################################ +# Copy contents of label/variables.tf here + +#tflint-ignore: terraform_standard_module_structure +variable "context" { + type = any + description = <<-EOT + Single object for setting entire context at once. + See description of individual variables for details. + Leave string and numeric variables as `null` to use default value. + Individual variable settings (non-null) override settings in context object, + except for attributes and tags, which are merged. + EOT + + validation { + condition = lookup(var.context, "label_key_case", null) == null ? true : contains(["lower", "title", "upper"], var.context["label_key_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`." + } + + validation { + condition = lookup(var.context, "label_value_case", null) == null ? true : contains(["lower", "title", "upper", "none"], var.context["label_value_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} + +#tflint-ignore: terraform_standard_module_structure +variable "namespace" { + type = string + default = null + description = "ID element. Usually the organization name, i.e. 'walletconnect' to help ensure generated IDs are globally unique." +} + +#tflint-ignore: terraform_standard_module_structure +variable "region" { + type = string + default = null + description = "ID element. Usually used for region e.g. 'uw2', 'us-west-2'." +} + +#tflint-ignore: terraform_standard_module_structure +variable "stage" { + type = string + default = null + description = "ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release'." +} + +#tflint-ignore: terraform_standard_module_structure +variable "name" { + type = string + default = null + description = <<-EOT + ID element. Usually the component name. + This is the only ID element not also included as a `tag`. + The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. + EOT +} + +#tflint-ignore: terraform_standard_module_structure +variable "delimiter" { + type = string + default = null + description = <<-EOT + Delimiter to be used between ID elements. + Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. + EOT +} + +#tflint-ignore: terraform_standard_module_structure +variable "attributes" { + type = list(string) + default = [] + description = <<-EOT + ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`, + in the order they appear in the list. New attributes are appended to the + end of the list. The elements of the list are joined by the `delimiter` + and treated as a single ID element. + EOT +} + +#tflint-ignore: terraform_standard_module_structure +variable "tags" { + type = map(string) + default = {} + description = "Additional tags." +} + +#tflint-ignore: terraform_standard_module_structure +variable "label_order" { + type = list(string) + default = null + description = <<-EOT + The order in which the labels (ID elements) appear in the `id`. + Defaults to ["namespace", "region", "stage", "name", "attributes"]. + You can omit any of the 5 labels, but at least one must be present. + EOT +} + +#tflint-ignore: terraform_standard_module_structure +variable "regex_replace_chars" { + type = string + default = null + description = <<-EOT + Terraform regular expression (regex) string. + Characters matching the regex will be removed from the ID elements. + If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. + EOT +} + +#tflint-ignore: terraform_standard_module_structure +variable "id_length_limit" { + type = number + default = null + description = <<-EOT + Limit `id` to this many characters (minimum 6). + Set to `0` for unlimited length. + Set to `null` for keep the existing setting, which defaults to `0`. + Does not affect `id_full`. + EOT + validation { + condition = var.id_length_limit == null ? true : var.id_length_limit >= 6 || var.id_length_limit == 0 + error_message = "The id_length_limit must be >= 6 if supplied (not null), or 0 for unlimited length." + } +} + +#tflint-ignore: terraform_standard_module_structure +variable "label_key_case" { + type = string + default = null + description = <<-EOT + Controls the letter case of the `tags` keys (label names) for tags generated by this module. + Does not affect keys of tags passed in via the `tags` input. + Possible values: `lower`, `title`, `upper`. + Default value: `title`. + EOT + + validation { + condition = var.label_key_case == null ? true : contains(["lower", "title", "upper"], var.label_key_case) + error_message = "Allowed values: `lower`, `title`, `upper`." + } +} + +#tflint-ignore: terraform_standard_module_structure +variable "label_value_case" { + type = string + default = null + description = <<-EOT + Controls the letter case of ID elements (labels) as included in `id`, + set as tag values, and output by this module individually. + Does not affect values of tags passed in via the `tags` input. + Possible values: `lower`, `title`, `upper` and `none` (no transformation). + Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs. + Default value: `lower`. + EOT + + validation { + condition = var.label_value_case == null ? true : contains(["lower", "title", "upper", "none"], var.label_value_case) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} diff --git a/terraform/monitoring/dashboard.jsonnet b/terraform/monitoring/dashboard.jsonnet new file mode 100644 index 0000000..3075689 --- /dev/null +++ b/terraform/monitoring/dashboard.jsonnet @@ -0,0 +1,80 @@ +local grafana = import 'grafonnet-lib/grafana.libsonnet'; +local panels = import 'panels/panels.libsonnet'; + +local dashboard = grafana.dashboard; +local row = grafana.row; +local annotation = grafana.annotation; +local layout = grafana.layout; + +local ds = { + prometheus: { + type: 'prometheus', + uid: std.extVar('prometheus_uid'), + }, + cloudwatch: { + type: 'cloudwatch', + uid: std.extVar('cloudwatch_uid'), + }, +}; +local vars = { + namespace: 'Verify', + environment: std.extVar('environment'), + notifications: std.parseJson(std.extVar('notifications')), + + ecs_service_name: std.extVar('ecs_service_name'), + redis_cluster_id: std.extVar('redis_cluster_id'), + load_balancer: std.extVar('load_balancer'), + target_group: std.extVar('target_group'), +}; + +//////////////////////////////////////////////////////////////////////////////// + +local height = 8; +local pos = grafana.layout.pos(height); + +//////////////////////////////////////////////////////////////////////////////// + +dashboard.new( + title = std.extVar('dashboard_title'), + uid = std.extVar('dashboard_uid'), + editable = true, + graphTooltip = dashboard.graphTooltips.sharedCrosshair, + timezone = dashboard.timezones.utc, +) +.addAnnotation( + annotation.new( + target = { + limit: 100, + matchAny: false, + tags: [], + type: 'dashboard', + }, + ) +) + +.addPanels(layout.generate_grid([ + ////////////////////////////////////////////////////////////////////////////// + row.new('HTTP Server'), + panels.http.response_status(ds, vars) { gridPos: pos.one_third }, + panels.http.request_response(ds, vars) { gridPos: pos.two_thirds }, + panels.http.latency_quantiles(ds, vars) { gridPos: pos.one_third }, + panels.http.average_latency(ds, vars) { gridPos: pos.two_thirds }, + + ////////////////////////////////////////////////////////////////////////////// + row.new('ECS'), + panels.ecs.cpu(ds, vars) { gridPos: pos._2 }, + panels.ecs.memory(ds, vars) { gridPos: pos._2 }, + + ////////////////////////////////////////////////////////////////////////////// + row.new('Project Registry'), + panels.registry.requests(ds, vars) { gridPos: pos._3 }, + panels.registry.cache_read(ds, vars) { gridPos: pos._3 }, + panels.registry.cache_write(ds, vars) { gridPos: pos._3 }, + + ////////////////////////////////////////////////////////////////////////////// + row.new('Redis'), + panels.redis.cpu(ds, vars) { gridPos: pos._2 }, + panels.redis.reads(ds, vars) { gridPos: pos._2 }, + panels.redis.memory(ds, vars) { gridPos: pos._2 }, + panels.redis.writes(ds, vars) { gridPos: pos._2 }, +])) diff --git a/terraform/monitoring/data_sources.tf b/terraform/monitoring/data_sources.tf new file mode 100644 index 0000000..473b245 --- /dev/null +++ b/terraform/monitoring/data_sources.tf @@ -0,0 +1,34 @@ +module "monitoring-role" { + source = "app.terraform.io/wallet-connect/monitoring-role/aws" + version = "1.0.2" + context = module.this + remote_role_arn = var.monitoring_role_arn +} + +resource "grafana_data_source" "prometheus" { + type = "prometheus" + name = "${module.this.stage}-${module.this.name}-amp" + url = var.prometheus_endpoint + + json_data_encoded = jsonencode({ + httpMethod = "GET" + sigV4Auth = true + sigV4AuthType = "ec2_iam_role" + sigV4Region = module.this.region + sigV4AssumeRoleArn = module.monitoring-role.iam_role_arn + }) + + depends_on = [module.monitoring-role] +} + +resource "grafana_data_source" "cloudwatch" { + type = "cloudwatch" + name = "${module.this.stage}-${module.this.name}-cloudwatch" + + json_data_encoded = jsonencode({ + defaultRegion = module.this.region + assumeRoleArn = module.monitoring-role.iam_role_arn + }) + + depends_on = [module.monitoring-role] +} diff --git a/terraform/monitoring/grafana-dashboard.json.tpl b/terraform/monitoring/grafana-dashboard.json.tpl new file mode 100644 index 0000000..8f851c3 --- /dev/null +++ b/terraform/monitoring/grafana-dashboard.json.tpl @@ -0,0 +1,480 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 44, + "links": [], + "liveNow": false, + "panels": [ +// + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 17 + }, + "id": 8, + "panels": [], + "title": "Project Registry", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${prometheus_data_source_uid}" + + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "errors" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "total" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "green", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 6, + "w": 8, + "x": 0, + "y": 18 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${prometheus_data_source_uid}" + + }, + "exemplar": true, + "expr": "sum(rate(project_registry_errors[1m]))", + "hide": false, + "interval": "", + "legendFormat": "errors", + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${prometheus_data_source_uid}" + + }, + "exemplar": true, + "expr": "sum(rate(project_registry_requests[1m]))", + "interval": "", + "legendFormat": "total", + "refId": "A" + } + ], + "title": "Requests / s", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${prometheus_data_source_uid}" + + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "errors" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "hits" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "green", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 6, + "w": 8, + "x": 8, + "y": 18 + }, + "id": 12, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${prometheus_data_source_uid}" + + }, + "exemplar": true, + "expr": "rate(project_registry_cache_errors[1m]) or vector(0)", + "hide": false, + "instant": false, + "interval": "", + "legendFormat": "errors", + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${prometheus_data_source_uid}" + + }, + "exemplar": true, + "expr": "sum(rate(project_registry_cache_misses[1m]))", + "hide": false, + "interval": "", + "legendFormat": "misses", + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${prometheus_data_source_uid}" + + }, + "exemplar": true, + "expr": "sum(rate(project_registry_cache_hits[1m]))", + "interval": "", + "legendFormat": "hits", + "refId": "A" + } + ], + "title": "Cache reads / s", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${prometheus_data_source_uid}" + + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "errors" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "writes" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "green", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 6, + "w": 8, + "x": 16, + "y": 18 + }, + "id": 13, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${prometheus_data_source_uid}" + + }, + "exemplar": true, + "expr": "rate(project_registry_cache_write_errors[1m]) or vector(0)", + "hide": false, + "instant": false, + "interval": "", + "legendFormat": "errors", + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${prometheus_data_source_uid}" + + }, + "exemplar": true, + "expr": "sum(rate(project_registry_cache_writes[1m]))", + "interval": "", + "legendFormat": "writes", + "refId": "A" + } + ], + "title": "Cache writes / s", + "type": "timeseries" + }, + ], + "refresh": false, + "schemaVersion": 35, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "${environment}_verify", + "uid": "${environment}_verify", + "version": 10, + "weekStart": "" +} diff --git a/terraform/monitoring/grafonnet-lib b/terraform/monitoring/grafonnet-lib new file mode 160000 index 0000000..b085843 --- /dev/null +++ b/terraform/monitoring/grafonnet-lib @@ -0,0 +1 @@ +Subproject commit b085843dd64a0c8426f6f5cbd616fc1438379235 diff --git a/terraform/monitoring/main.tf b/terraform/monitoring/main.tf new file mode 100644 index 0000000..dbbd06a --- /dev/null +++ b/terraform/monitoring/main.tf @@ -0,0 +1,23 @@ +data "jsonnet_file" "dashboard" { + source = "${path.module}/dashboard.jsonnet" + + ext_str = { + dashboard_title = "Verify Server - ${title(module.this.stage)}" + dashboard_uid = "verify-${module.this.stage}" + + prometheus_uid = grafana_data_source.prometheus.uid + cloudwatch_uid = grafana_data_source.cloudwatch.uid + + environment = module.this.stage + notifications = jsonencode(var.notification_channels) + + ecs_service_name = var.ecs_service_name + redis_cluster_id = var.redis_cluster_id + } +} + +resource "grafana_dashboard" "at_a_glance" { + overwrite = true + message = "Updated by Terraform" + config_json = data.jsonnet_file.dashboard.rendered +} diff --git a/terraform/monitoring/panels/ecs/availability.libsonnet b/terraform/monitoring/panels/ecs/availability.libsonnet new file mode 100644 index 0000000..e13d670 --- /dev/null +++ b/terraform/monitoring/panels/ecs/availability.libsonnet @@ -0,0 +1,55 @@ +local grafana = import '../../grafonnet-lib/grafana.libsonnet'; +local defaults = import '../../grafonnet-lib/defaults.libsonnet'; + +local panels = grafana.panels; +local targets = grafana.targets; +local alert = grafana.alert; +local alertCondition = grafana.alertCondition; + +local error_alert(vars) = alert.new( + namespace = vars.namespace, + name = "%s - Availability" % vars.environment, + message = "%s - Availability" % vars.environment, + period = '5m', + frequency = '1m', + noDataState = 'alerting', + notifications = vars.notifications, + alertRuleTags = { + 'og_priority': 'P3', + }, + + conditions = [ + alertCondition.new( + evaluatorParams = [ 95 ], + evaluatorType = 'lt', + operatorType = 'or', + queryRefId = 'availability', + queryTimeStart = '5m', + reducerType = 'avg', + ), + ] +); + +{ + new(ds, vars):: + panels.timeseries( + title = 'Availability', + datasource = ds.prometheus, + ) + .configure( + defaults.configuration.timeseries + .withUnit('percent') + .withSoftLimit( + axisSoftMin = 98, + axisSoftMax = 100, + ) + ) + .setAlert(vars.environment, error_alert(vars)) + + .addTarget(targets.prometheus( + datasource = ds.prometheus, + expr = '(1-(sum(rate(http_call_counter_total{code=~"5.+"}[5m])) or vector(0))/(sum(rate(http_call_counter_total{}[5m]))))*100', + refId = "availability", + exemplar = false, + )) +} diff --git a/terraform/monitoring/panels/ecs/cpu.libsonnet b/terraform/monitoring/panels/ecs/cpu.libsonnet new file mode 100644 index 0000000..52ec396 --- /dev/null +++ b/terraform/monitoring/panels/ecs/cpu.libsonnet @@ -0,0 +1,73 @@ +local grafana = import '../../grafonnet-lib/grafana.libsonnet'; +local defaults = import '../../grafonnet-lib/defaults.libsonnet'; + +local panels = grafana.panels; +local targets = grafana.targets; + +local thresholds = { + warning: defaults.values.resource.thresholds.warning, + critical: defaults.values.resource.thresholds.critical, +}; + +local _configuration = defaults.configuration.timeseries + .withUnit('percent') + .withThresholdStyle(grafana.fieldConfig.thresholdStyle.Area) + .withThresholds( + baseColor = defaults.values.colors.ok, + steps = [ + { value: thresholds.warning, color: defaults.values.colors.warn }, + { value: thresholds.critical, color: defaults.values.colors.critical }, + ] + ) + .withSoftLimit( + axisSoftMin = 0, + axisSoftMax = thresholds.warning, + ) + .addOverrides([ + grafana.override.newColorOverride( + name = 'CPU_Avg', + color = defaults.values.colors.cpu + ), + grafana.override.newColorOverride( + name = 'CPU_Max', + color = defaults.values.colors.cpu_alt + ) + ]); + +{ + new(ds, vars):: + panels.timeseries( + title = 'CPU Utilization', + datasource = ds.cloudwatch, + ) + .configure(_configuration) + .setAlert(vars.environment, defaults.alerts.cpu( + namespace = vars.namespace, + env = vars.environment, + title = 'ECS', + notifications = vars.notifications, + )) + + .addTarget(targets.cloudwatch( + alias = 'CPU (Max)', + datasource = ds.cloudwatch, + namespace = 'AWS/ECS', + metricName = 'CPUUtilization', + dimensions = { + ServiceName: vars.ecs_service_name + }, + statistic = 'Maximum', + refId = 'CPU_Max', + )) + .addTarget(targets.cloudwatch( + alias = 'CPU (Avg)', + datasource = ds.cloudwatch, + namespace = 'AWS/ECS', + metricName = 'CPUUtilization', + dimensions = { + ServiceName: vars.ecs_service_name + }, + statistic = 'Average', + refId = 'CPU_Avg', + )) +} diff --git a/terraform/monitoring/panels/ecs/memory.libsonnet b/terraform/monitoring/panels/ecs/memory.libsonnet new file mode 100644 index 0000000..82e9963 --- /dev/null +++ b/terraform/monitoring/panels/ecs/memory.libsonnet @@ -0,0 +1,73 @@ +local grafana = import '../../grafonnet-lib/grafana.libsonnet'; +local defaults = import '../../grafonnet-lib/defaults.libsonnet'; + +local panels = grafana.panels; +local targets = grafana.targets; + +local thresholds = { + warning: defaults.values.resource.thresholds.warning, + critical: defaults.values.resource.thresholds.critical, +}; + +local _configuration = defaults.configuration.timeseries + .withUnit('percent') + .withThresholdStyle(grafana.fieldConfig.thresholdStyle.Area) + .withThresholds( + baseColor = defaults.values.colors.ok, + steps = [ + { value: thresholds.warning, color: defaults.values.colors.warn }, + { value: thresholds.critical, color: defaults.values.colors.critical }, + ] + ) + .withSoftLimit( + axisSoftMin = 0, + axisSoftMax = thresholds.warning, + ) + .addOverrides([ + grafana.override.newColorOverride( + name = 'Mem_Avg', + color = defaults.values.colors.memory + ), + grafana.override.newColorOverride( + name = 'Mem_Max', + color = defaults.values.colors.memory_alt + ) + ]); + +{ + new(ds, vars):: + panels.timeseries( + title = 'Memory Utilization', + datasource = ds.cloudwatch, + ) + .configure(_configuration) + .setAlert(vars.environment, defaults.alerts.memory( + namespace = vars.namespace, + env = vars.environment, + title = 'ECS', + notifications = vars.notifications, + )) + + .addTarget(targets.cloudwatch( + alias = 'Memory (Max)', + datasource = ds.cloudwatch, + namespace = 'AWS/ECS', + metricName = 'MemoryUtilization', + dimensions = { + ServiceName: vars.ecs_service_name + }, + statistic = 'Maximum', + refId = 'Mem_Max', + )) + .addTarget(targets.cloudwatch( + alias = 'Memory (Avg)', + datasource = ds.cloudwatch, + namespace = 'AWS/ECS', + metricName = 'MemoryUtilization', + dimensions = { + ServiceName: vars.ecs_service_name + }, + statistic = 'Average', + refId = 'Mem_Avg', + )) +} diff --git a/terraform/monitoring/panels/http/average_latency.libsonnet b/terraform/monitoring/panels/http/average_latency.libsonnet new file mode 100644 index 0000000..c04a45c --- /dev/null +++ b/terraform/monitoring/panels/http/average_latency.libsonnet @@ -0,0 +1,57 @@ +local grafana = import '../../grafonnet-lib/grafana.libsonnet'; +local defaults = import '../../grafonnet-lib/defaults.libsonnet'; + +local panels = grafana.panels; +local targets = grafana.targets; + +local thresholds = { + warning: 100, +}; + +local _configuration = defaults.configuration.timeseries + .withUnit(grafana.fieldConfig.units.Milliseconds) + .withThresholdStyle(grafana.fieldConfig.thresholdStyle.Area) + .withThresholds( + baseColor = defaults.values.colors.ok, + steps = [ + { value: thresholds.warning, color: defaults.values.colors.critical }, + ] + ) + .withSoftLimit( + axisSoftMin = 0, + axisSoftMax = thresholds.warning, + ) + .addOverrides([ + grafana.override.newColorOverride( + name = 'read', + color = 'green' + ), + grafana.override.newColorOverride( + name = 'write', + color = 'blue' + ) + ]); + +{ + new(ds, vars):: + panels.timeseries( + title = 'Max avg latency [1m]', + datasource = ds.prometheus, + ) + .configure(_configuration) + + .addTarget(targets.prometheus( + datasource = ds.prometheus, + legendFormat = '{{method}} {{endpoint}}', + refId = 'write', + expr = 'max(increase(axum_http_requests_duration_seconds_sum{method !~ "GET|HEAD"}[1m]) * 1000 / increase(axum_http_requests_duration_seconds_count{method !~ "GET|HEAD"}[1m])) by (method, endpoint)', + exemplar = true, + )) + .addTarget(targets.prometheus( + datasource = ds.prometheus, + legendFormat = '{{method}} {{endpoint}}', + refId = 'read', + expr = 'max(increase(axum_http_requests_duration_seconds_sum{method =~ "GET|HEAD"}[1m]) * 1000 / increase(axum_http_requests_duration_seconds_count{method =~ "GET|HEAD"}[1m])) by (status, method, endpoint)', + exemplar = true, + )) +} diff --git a/terraform/monitoring/panels/http/latency_quantiles.libsonnet b/terraform/monitoring/panels/http/latency_quantiles.libsonnet new file mode 100644 index 0000000..815521a --- /dev/null +++ b/terraform/monitoring/panels/http/latency_quantiles.libsonnet @@ -0,0 +1,59 @@ +local grafana = import '../../grafonnet-lib/grafana.libsonnet'; +local defaults = import '../../grafonnet-lib/defaults.libsonnet'; + +local panels = grafana.panels; +local targets = grafana.targets; + +local thresholds = { + warning: 80, +}; + +local _configuration = defaults.configuration.timeseries + .withUnit(grafana.fieldConfig.units.Milliseconds) + .withThresholdStyle(grafana.fieldConfig.thresholdStyle.Area) + .withThresholds( + baseColor = defaults.values.colors.ok, + steps = [ + { value: thresholds.warning, color: defaults.values.colors.critical }, + ] + ); + +{ + new(ds, vars):: + panels.barGauge( + title = 'Latency quantiles', + datasource = ds.prometheus, + ) + + .addTarget(targets.prometheus( + datasource = ds.prometheus, + legendFormat = '0.5', + refId = 'A', + expr = 'histogram_quantile(0.5, sum by (le) (rate(axum_http_requests_duration_seconds_bucket[$__range]))) * 1000', + exemplar = true, + )) + .addTarget(targets.prometheus( + datasource = ds.prometheus, + legendFormat = '0.9', + refId = 'B', + expr = 'histogram_quantile(0.9, sum by (le) (rate(axum_http_requests_duration_seconds_bucket[$__range]))) * 1000', + exemplar = true, + )) + .addTarget(targets.prometheus( + datasource = ds.prometheus, + legendFormat = '0.99', + refId = 'C', + expr = 'histogram_quantile(0.99, sum by (le) (rate(axum_http_requests_duration_seconds_bucket[$__range]))) * 1000', + exemplar = true, + )) + .addTarget(targets.prometheus( + datasource = ds.prometheus, + legendFormat = '0.999', + refId = 'D', + expr = 'histogram_quantile(0.999, sum by (le) (rate(axum_http_requests_duration_seconds_bucket[$__range]))) * 1000', + exemplar = true, + )) + { + fieldConfig+: _configuration.fieldConfig, + options+: _configuration.options, + } +} diff --git a/terraform/monitoring/panels/http/request_response.libsonnet b/terraform/monitoring/panels/http/request_response.libsonnet new file mode 100644 index 0000000..e480bb1 --- /dev/null +++ b/terraform/monitoring/panels/http/request_response.libsonnet @@ -0,0 +1,75 @@ +local grafana = import '../../grafonnet-lib/grafana.libsonnet'; +local defaults = import '../../grafonnet-lib/defaults.libsonnet'; + +local panels = grafana.panels; +local targets = grafana.targets; + +local thresholds = { + warning: 80, +}; + +local _configuration = defaults.configuration.timeseries + .withUnit(grafana.fieldConfig.units.Milliseconds) + .withThresholdStyle(grafana.fieldConfig.thresholdStyle.Area) + .withThresholds( + baseColor = defaults.values.colors.ok, + steps = [ + { value: thresholds.warning, color: defaults.values.colors.critical }, + ] + ) + .withSoftLimit( + axisSoftMin = 0, + axisSoftMax = thresholds.warning, + ) + .addOverrides([ + grafana.override.newColorOverride( + name = 'status_2xx', + color = 'green' + ), + grafana.override.newColorOverride( + name = 'status_4xx', + color = 'yellow' + ), + grafana.override.newColorOverride( + name = 'status_5xx', + color = 'red' + ), + ]); + +{ + new(ds, vars):: + panels.timeseries( + title = 'Request-Response / s', + datasource = ds.prometheus, + ) + .configure(_configuration) + + .addTarget(targets.prometheus( + datasource = ds.prometheus, + legendFormat = '{{status}} {{method}} {{endpoint}}', + refId = 'status_2xx', + expr = 'sum(rate(axum_http_requests_total{status=~"^2.*$"}[1m])) by (status, method, endpoint)', + exemplar = true, + )) + .addTarget(targets.prometheus( + datasource = ds.prometheus, + legendFormat = '{{status}} {{method}} {{endpoint}}', + refId = 'status_4xx', + expr = 'sum(rate(axum_http_requests_total{status=~"^4.*"}[1m])) by (status, method, endpoint)', + exemplar = true, + )) + .addTarget(targets.prometheus( + datasource = ds.prometheus, + legendFormat = '{{status}} {{method}} {{endpoint}}', + refId = 'status_5xx', + expr = 'sum(rate(axum_http_requests_total{status=~"^5.*"}[1m])) by (status, method, endpoint)', + exemplar = true, + )) + .addTarget(targets.prometheus( + datasource = ds.prometheus, + legendFormat = '{{status}} {{method}} {{endpoint}}', + refId = 'status_all', + expr = 'sum(rate(axum_http_requests_total{status!~"^(2|4|5).*"}[1m])) by (status, method, endpoint)', + exemplar = true, + )) +} diff --git a/terraform/monitoring/panels/http/response_status.libsonnet b/terraform/monitoring/panels/http/response_status.libsonnet new file mode 100644 index 0000000..525daf5 --- /dev/null +++ b/terraform/monitoring/panels/http/response_status.libsonnet @@ -0,0 +1,65 @@ +local grafana = import '../../grafonnet-lib/grafana.libsonnet'; +local defaults = import '../../grafonnet-lib/defaults.libsonnet'; + +local panels = grafana.panels; +local targets = grafana.targets; + +local _configuration = defaults.configuration.timeseries + .addOverrides([ + grafana.override.newColorOverride( + name = 'status_2XX', + color = 'green' + ), + grafana.override.newColorOverride( + name = 'status_4XX', + color = 'orange' + ), + grafana.override.newColorOverride( + name = 'status_5XX', + color = 'red' + ), + grafana.override.newColorOverride( + name = 'status_other', + color = 'blue' + ), + ]); + +{ + new(ds, vars):: + panels.pieChart( + title = 'Response Status Codes', + datasource = ds.prometheus, + ) + + .addTarget(targets.prometheus( + datasource = ds.prometheus, + legendFormat ='2XX', + refId = 'status_2XX', + expr = 'sum(increase(axum_http_requests_total { status =~ "^2.*"}[$__range]))', + exemplar = true, + )) + .addTarget(targets.prometheus( + datasource = ds.prometheus, + legendFormat ='4XX', + refId = 'status_4XX', + expr = 'sum(increase(axum_http_requests_total { status =~ "^4.*"}[$__range]))', + exemplar = true, + )) + .addTarget(targets.prometheus( + datasource = ds.prometheus, + legendFormat ='5XX', + refId = 'status_5XX', + expr = 'sum(increase(axum_http_requests_total { status =~ "^5.*"}[$__range]))', + exemplar = true, + )) + .addTarget(targets.prometheus( + datasource = ds.prometheus, + legendFormat ='Other', + refId = 'status_other', + expr = 'sum(increase(axum_http_requests_total { status !~ "^(2|4|5).*"}[$__range]))', + exemplar = true, + )) + { + fieldConfig+: _configuration.fieldConfig, + options+: _configuration.options, + } +} diff --git a/terraform/monitoring/panels/panels.libsonnet b/terraform/monitoring/panels/panels.libsonnet new file mode 100644 index 0000000..66bc7ce --- /dev/null +++ b/terraform/monitoring/panels/panels.libsonnet @@ -0,0 +1,27 @@ +{ + ecs: { + availability: (import 'ecs/availability.libsonnet' ).new, + cpu: (import 'ecs/cpu.libsonnet' ).new, + memory: (import 'ecs/memory.libsonnet' ).new, + }, + + http: { + response_status: (import 'http/response_status.libsonnet' ).new, + request_response: (import 'http/request_response.libsonnet' ).new, + latency_quantiles: (import 'http/latency_quantiles.libsonnet' ).new, + average_latency: (import 'http/average_latency.libsonnet' ).new, + }, + + redis: { + reads: (import 'redis/reads.libsonnet' ).new, + writes: (import 'redis/writes.libsonnet' ).new, + cpu: (import 'redis/cpu.libsonnet' ).new, + memory: (import 'redis/memory.libsonnet' ).new, + }, + + registry: { + requests: (import 'registry/requests.libsonnet' ).new, + cache_read: (import 'registry/cache_read.libsonnet' ).new, + cache_write: (import 'registry/cache_write.libsonnet' ).new, + } +} diff --git a/terraform/monitoring/panels/redis/cpu.libsonnet b/terraform/monitoring/panels/redis/cpu.libsonnet new file mode 100644 index 0000000..4c826a3 --- /dev/null +++ b/terraform/monitoring/panels/redis/cpu.libsonnet @@ -0,0 +1,75 @@ +local grafana = import '../../grafonnet-lib/grafana.libsonnet'; +local defaults = import '../../grafonnet-lib/defaults.libsonnet'; + +local panels = grafana.panels; +local targets = grafana.targets; + +local thresholds = { + warning: defaults.values.resource.thresholds.warning, + critical: defaults.values.resource.thresholds.critical, +}; + +local _configuration = defaults.configuration.timeseries + .withUnit('percent') + .withThresholdStyle(grafana.fieldConfig.thresholdStyle.Area) + .withThresholds( + baseColor = defaults.values.colors.ok, + steps = [ + { value: thresholds.warning, color: defaults.values.colors.warn }, + { value: thresholds.critical, color: defaults.values.colors.critical }, + ] + ) + .withSoftLimit( + axisSoftMin = 0, + axisSoftMax = thresholds.warning, + ) + .addOverrides([ + grafana.override.newColorOverride( + name = 'CPU_Avg', + color = defaults.values.colors.cpu + ), + grafana.override.newColorOverride( + name = 'CPU_Max', + color = defaults.values.colors.cpu_alt + ) + ]); + +{ + new(ds, vars):: + panels.timeseries( + title = 'Redis CPU', + datasource = ds.cloudwatch, + ) + .configure(_configuration) + .setAlert(vars.environment, defaults.alerts.cpu( + namespace = vars.namespace, + env = vars.environment, + title = 'Redis', + notifications = vars.notifications, + )) + + .addTarget(targets.cloudwatch( + alias = 'CPU (Max)', + datasource = ds.cloudwatch, + dimensions = { + CacheClusterId: vars.redis_cluster_id, + }, + matchExact = true, + metricName = 'CPUUtilization', + namespace = 'AWS/ElastiCache', + statistic = 'Maximum', + refId = 'CPU_Max', + )) + .addTarget(targets.cloudwatch( + alias = 'CPU (Avg)', + datasource = ds.cloudwatch, + dimensions = { + CacheClusterId: vars.redis_cluster_id, + }, + matchExact = true, + metricName = 'CPUUtilization', + namespace = 'AWS/ElastiCache', + statistic = 'Average', + refId = 'CPU_Avg', + )) +} diff --git a/terraform/monitoring/panels/redis/memory.libsonnet b/terraform/monitoring/panels/redis/memory.libsonnet new file mode 100644 index 0000000..d1d08cb --- /dev/null +++ b/terraform/monitoring/panels/redis/memory.libsonnet @@ -0,0 +1,75 @@ +local grafana = import '../../grafonnet-lib/grafana.libsonnet'; +local defaults = import '../../grafonnet-lib/defaults.libsonnet'; + +local panels = grafana.panels; +local targets = grafana.targets; + +local thresholds = { + warning: defaults.values.resource.thresholds.warning, + critical: defaults.values.resource.thresholds.critical, +}; + +local _configuration = defaults.configuration.timeseries + .withUnit('percent') + .withThresholdStyle(grafana.fieldConfig.thresholdStyle.Area) + .withThresholds( + baseColor = defaults.values.colors.ok, + steps = [ + { value: thresholds.warning, color: defaults.values.colors.warn }, + { value: thresholds.critical, color: defaults.values.colors.critical }, + ] + ) + .withSoftLimit( + axisSoftMin = 0, + axisSoftMax = thresholds.warning, + ) + .addOverrides([ + grafana.override.newColorOverride( + name = 'Mem_Avg', + color = defaults.values.colors.memory + ), + grafana.override.newColorOverride( + name = 'Mem_Max', + color = defaults.values.colors.memory_alt + ) + ]); + +{ + new(ds, vars):: + panels.timeseries( + title = 'Redis Memory', + datasource = ds.cloudwatch, + ) + .configure(_configuration) + .setAlert(vars.environment, defaults.alerts.memory( + namespace = vars.namespace, + env = vars.environment, + title = 'Redis', + notifications = vars.notifications, + )) + + .addTarget(targets.cloudwatch( + alias = 'Memory (Max)', + datasource = ds.cloudwatch, + dimensions = { + CacheClusterId: vars.redis_cluster_id, + }, + matchExact = true, + metricName = 'DatabaseMemoryUsagePercentage', + namespace = 'AWS/ElastiCache', + statistic = 'Maximum', + refId = 'Mem_Max', + )) + .addTarget(targets.cloudwatch( + alias = 'Memory (Avg)', + datasource = ds.cloudwatch, + dimensions = { + CacheClusterId: vars.redis_cluster_id, + }, + matchExact = true, + metricName = 'DatabaseMemoryUsagePercentage', + namespace = 'AWS/ElastiCache', + statistic = 'Average', + refId = 'Mem_Avg', + )) +} diff --git a/terraform/monitoring/panels/redis/reads.libsonnet b/terraform/monitoring/panels/redis/reads.libsonnet new file mode 100644 index 0000000..8afa656 --- /dev/null +++ b/terraform/monitoring/panels/redis/reads.libsonnet @@ -0,0 +1,78 @@ +local grafana = import '../../grafonnet-lib/grafana.libsonnet'; +local defaults = import '../../grafonnet-lib/defaults.libsonnet'; + +local panels = grafana.panels; +local targets = grafana.targets; + +local thresholds = { + warning: 80, +}; + +local _configuration = defaults.configuration.timeseries + .withThresholdStyle(grafana.fieldConfig.thresholdStyle.Area) + .withThresholds( + baseColor = defaults.values.colors.ok, + steps = [ + { value: thresholds.warning, color: defaults.values.colors.critical }, + ] + ) + .withSoftLimit( + axisSoftMin = 0, + axisSoftMax = thresholds.warning, + ) + .addOverrides([ + grafana.override.newColorOverride( + name = 'total_errors', + color = 'dark-red' + ), + grafana.override.newColorOverride( + name = 'total', + color = 'dark-green' + ), + grafana.override.newColorOverride( + name = 'errors_per_database', + color = 'red' + ), + grafana.override.newColorOverride( + name = 'per_database', + color = 'green' + ), + ]); + +{ + new(ds, vars):: + panels.timeseries( + title = 'Redis Reads / sec', + datasource = ds.prometheus, + ) + .configure(_configuration) + + .addTarget(targets.prometheus( + datasource = ds.prometheus, + legendFormat ='total errors', + refId = 'total_errors', + expr = 'sum(rate(redis_read_errors{}[1m])) or vector(0)', + exemplar = true, + )) + .addTarget(targets.prometheus( + datasource = ds.prometheus, + legendFormat ='total', + refId = 'total', + expr = 'sum(rate(redis_reads{}[1m]))', + exemplar = true, + )) + .addTarget(targets.prometheus( + datasource = ds.prometheus, + legendFormat ='{{db}} errors', + refId = 'errors_per_database', + expr = 'sum(rate(redis_read_errors{}[1m]) or vector(0)) by (db)', + exemplar = true, + )) + .addTarget(targets.prometheus( + datasource = ds.prometheus, + legendFormat ='{{db}}', + refId = 'per_database', + expr = 'sum(rate(redis_reads{}[1m])) by (db)', + exemplar = true, + )) +} diff --git a/terraform/monitoring/panels/redis/writes.libsonnet b/terraform/monitoring/panels/redis/writes.libsonnet new file mode 100644 index 0000000..fc5805c --- /dev/null +++ b/terraform/monitoring/panels/redis/writes.libsonnet @@ -0,0 +1,78 @@ +local grafana = import '../../grafonnet-lib/grafana.libsonnet'; +local defaults = import '../../grafonnet-lib/defaults.libsonnet'; + +local panels = grafana.panels; +local targets = grafana.targets; + +local thresholds = { + warning: 80, +}; + +local _configuration = defaults.configuration.timeseries + .withThresholdStyle(grafana.fieldConfig.thresholdStyle.Area) + .withThresholds( + baseColor = defaults.values.colors.ok, + steps = [ + { value: thresholds.warning, color: defaults.values.colors.critical }, + ] + ) + .withSoftLimit( + axisSoftMin = 0, + axisSoftMax = thresholds.warning, + ) + .addOverrides([ + grafana.override.newColorOverride( + name = 'total_errors', + color = 'dark-red' + ), + grafana.override.newColorOverride( + name = 'total', + color = 'dark-green' + ), + grafana.override.newColorOverride( + name = 'errors_per_database', + color = 'red' + ), + grafana.override.newColorOverride( + name = 'per_database', + color = 'green' + ), + ]); + +{ + new(ds, vars):: + panels.timeseries( + title = 'Redis Writes / sec', + datasource = ds.prometheus, + ) + .configure(_configuration) + + .addTarget(targets.prometheus( + datasource = ds.prometheus, + legendFormat ='total errors', + refId = 'total_errors', + expr = 'sum(rate(redis_write_errors{}[1m])) or vector(0)', + exemplar = true, + )) + .addTarget(targets.prometheus( + datasource = ds.prometheus, + legendFormat ='total', + refId = 'total', + expr = 'sum(rate(redis_writes{}[1m]))', + exemplar = true, + )) + .addTarget(targets.prometheus( + datasource = ds.prometheus, + legendFormat ='{{db}} errors', + refId = 'errors_per_database', + expr = 'sum(rate(redis_write_errors{}[1m]) or vector(0)) by (db)', + exemplar = true, + )) + .addTarget(targets.prometheus( + datasource = ds.prometheus, + legendFormat ='{{db}}', + refId = 'per_database', + expr = 'sum(rate(redis_writes{}[1m])) by (db)', + exemplar = true, + )) +} diff --git a/terraform/monitoring/panels/registry/cache_read.libsonnet b/terraform/monitoring/panels/registry/cache_read.libsonnet new file mode 100644 index 0000000..2be600c --- /dev/null +++ b/terraform/monitoring/panels/registry/cache_read.libsonnet @@ -0,0 +1,63 @@ +local grafana = import '../../grafonnet-lib/grafana.libsonnet'; +local defaults = import '../../grafonnet-lib/defaults.libsonnet'; + +local panels = grafana.panels; +local targets = grafana.targets; + +local thresholds = { + warning: 80, +}; + +local _configuration = defaults.configuration.timeseries + .withThresholdStyle(grafana.fieldConfig.thresholdStyle.Area) + .withThresholds( + baseColor = defaults.values.colors.ok, + steps = [ + { value: thresholds.warning, color: defaults.values.colors.critical }, + ] + ) + .withSoftLimit( + axisSoftMin = 0, + axisSoftMax = thresholds.warning, + ) + .addOverrides([ + grafana.override.newColorOverride( + name = 'errors', + color = 'red' + ), + grafana.override.newColorOverride( + name = 'hits', + color = 'green' + ), + ]); + +{ + new(ds, vars):: + panels.timeseries( + title = 'Cache Weads / s', + datasource = ds.prometheus, + ) + .configure(_configuration) + + .addTarget(targets.prometheus( + datasource = ds.prometheus, + legendFormat = 'errors', + refId = 'errors', + expr = 'rate(project_registry_cache_errors[1m]) or vector(0)', + exemplar = true, + )) + .addTarget(targets.prometheus( + datasource = ds.prometheus, + legendFormat = 'misses', + refId = 'misses', + expr = 'sum(rate(project_registry_cache_misses[1m]))', + exemplar = true, + )) + .addTarget(targets.prometheus( + datasource = ds.prometheus, + legendFormat = 'hits', + refId = 'hits', + expr = 'sum(rate(project_registry_cache_hits[1m]))', + exemplar = true, + )) +} diff --git a/terraform/monitoring/panels/registry/cache_write.libsonnet b/terraform/monitoring/panels/registry/cache_write.libsonnet new file mode 100644 index 0000000..7c1f7cc --- /dev/null +++ b/terraform/monitoring/panels/registry/cache_write.libsonnet @@ -0,0 +1,56 @@ +local grafana = import '../../grafonnet-lib/grafana.libsonnet'; +local defaults = import '../../grafonnet-lib/defaults.libsonnet'; + +local panels = grafana.panels; +local targets = grafana.targets; + +local thresholds = { + warning: 80, +}; + +local _configuration = defaults.configuration.timeseries + .withThresholdStyle(grafana.fieldConfig.thresholdStyle.Area) + .withThresholds( + baseColor = defaults.values.colors.ok, + steps = [ + { value: thresholds.warning, color: defaults.values.colors.critical }, + ] + ) + .withSoftLimit( + axisSoftMin = 0, + axisSoftMax = thresholds.warning, + ) + .addOverrides([ + grafana.override.newColorOverride( + name = 'errors', + color = 'red' + ), + grafana.override.newColorOverride( + name = 'writes', + color = 'green' + ), + ]); + +{ + new(ds, vars):: + panels.timeseries( + title = 'Cache Writes / s', + datasource = ds.prometheus, + ) + .configure(_configuration) + + .addTarget(targets.prometheus( + datasource = ds.prometheus, + legendFormat = 'errors', + refId = 'errors', + expr = 'rate(project_registry_cache_write_errors[1m]) or vector(0)', + exemplar = true, + )) + .addTarget(targets.prometheus( + datasource = ds.prometheus, + legendFormat = 'writes', + refId = 'writes', + expr = 'sum(rate(project_registry_cache_writes[1m]))', + exemplar = true, + )) +} diff --git a/terraform/monitoring/panels/registry/requests.libsonnet b/terraform/monitoring/panels/registry/requests.libsonnet new file mode 100644 index 0000000..8b06d89 --- /dev/null +++ b/terraform/monitoring/panels/registry/requests.libsonnet @@ -0,0 +1,52 @@ +local grafana = import '../../grafonnet-lib/grafana.libsonnet'; +local defaults = import '../../grafonnet-lib/defaults.libsonnet'; + +local panels = grafana.panels; +local targets = grafana.targets; + +local thresholds = { + warning: 80, +}; + +local _configuration = defaults.configuration.timeseries + .withThresholdStyle(grafana.fieldConfig.thresholdStyle.Area) + .withThresholds( + baseColor = defaults.values.colors.ok, + steps = [ + { value: thresholds.warning, color: defaults.values.colors.critical }, + ] + ) + .addOverrides([ + grafana.override.newColorOverride( + name = 'errors', + color = 'red' + ), + grafana.override.newColorOverride( + name = 'fixed', + color = 'green' + ), + ]); + +{ + new(ds, vars):: + panels.timeseries( + title = 'Requests / s', + datasource = ds.prometheus, + ) + .configure(_configuration) + + .addTarget(targets.prometheus( + datasource = ds.prometheus, + legendFormat = 'errors', + refId = 'errors', + expr = 'sum(rate(project_registry_errors[1m]))', + exemplar = true, + )) + .addTarget(targets.prometheus( + datasource = ds.prometheus, + legendFormat = 'total', + refId = 'total', + expr = 'sum(rate(project_registry_requests[1m]))', + exemplar = true, + )) +} diff --git a/terraform/monitoring/terraform.tf b/terraform/monitoring/terraform.tf new file mode 100644 index 0000000..d733dd6 --- /dev/null +++ b/terraform/monitoring/terraform.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + grafana = { + source = "grafana/grafana" + version = "~> 2.0" + } + jsonnet = { + source = "alxrem/jsonnet" + version = "~> 2.2.0" + } + } +} diff --git a/terraform/monitoring/variables.tf b/terraform/monitoring/variables.tf new file mode 100644 index 0000000..4cf69c2 --- /dev/null +++ b/terraform/monitoring/variables.tf @@ -0,0 +1,24 @@ +variable "monitoring_role_arn" { + description = "The ARN of the monitoring role." + type = string +} + +variable "notification_channels" { + description = "The notification channels to send alerts to" + type = list(any) +} + +variable "prometheus_endpoint" { + description = "The endpoint for the Prometheus server." + type = string +} + +variable "ecs_service_name" { + description = "The name of the ECS service." + type = string +} + +variable "redis_cluster_id" { + description = "The ID of the Redis cluster." + type = string +} diff --git a/terraform/outputs.tf b/terraform/outputs.tf new file mode 100644 index 0000000..e69de29 diff --git a/terraform/providers.tf b/terraform/providers.tf new file mode 100644 index 0000000..f7d191e --- /dev/null +++ b/terraform/providers.tf @@ -0,0 +1,12 @@ +provider "aws" { + region = var.region + + default_tags { + tags = module.this.tags + } +} + +provider "grafana" { + url = "https://${data.terraform_remote_state.monitoring.outputs.grafana_workspaces.central.grafana_endpoint}" + auth = var.grafana_auth +} diff --git a/terraform/redis/README.md b/terraform/redis/README.md new file mode 100644 index 0000000..bd3eda3 --- /dev/null +++ b/terraform/redis/README.md @@ -0,0 +1,43 @@ +# `redis` module + +This module creates a Redis database. + + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | ~> 1.0 | +| [aws](#requirement\_aws) | ~> 5.7 | +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | ~> 5.7 | +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [this](#module\_this) | app.terraform.io/wallet-connect/label/null | 0.3.2 | + +## Inputs +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [context](#input\_context) | Single object for setting entire context at once.
See description of individual variables for details.
Leave string and numeric variables as `null` to use default value.
Individual variable settings (non-null) override settings in context object,
except for attributes and tags, which are merged. |
any
|
{
"attributes": [],
"delimiter": null,
"id\_length\_limit": null,
"label\_key\_case": null,
"label\_order": [],
"label\_value\_case": null,
"name": null,
"namespace": null,
"regex\_replace\_chars": null,
"region": null,
"stage": null,
"tags": {}
}
| no | +| [egress\_cidr\_blocks](#input\_egress\_cidr\_blocks) | The CIDR blocks to allow egress to, default to VPC only. |
set(string)
|
null
| no | +| [ingress\_cidr\_blocks](#input\_ingress\_cidr\_blocks) | The CIDR blocks to allow ingress from, default to VPC only. |
set(string)
|
null
| no | +| [node\_engine\_version](#input\_node\_engine\_version) | The version of Redis to use |
string
|
"6.x"
| no | +| [node\_type](#input\_node\_type) | The instance type to use for the database nodes |
string
|
"cache.t4g.micro"
| no | +| [num\_cache\_nodes](#input\_num\_cache\_nodes) | The number of nodes to create in the cluster |
number
|
1
| no | +| [subnets\_ids](#input\_subnets\_ids) | The list of subnet IDs to create the cluster in |
set(string)
|
n/a
| yes | +| [vpc\_id](#input\_vpc\_id) | The VPC ID to create the security group in |
string
|
n/a
| yes | +## Outputs + +| Name | Description | +|------|-------------| +| [cluster\_id](#output\_cluster\_id) | The ID of the cluster | +| [endpoint](#output\_endpoint) | The endpoint of the Redis cluster | + + + diff --git a/terraform/redis/context.tf b/terraform/redis/context.tf new file mode 100644 index 0000000..9871e49 --- /dev/null +++ b/terraform/redis/context.tf @@ -0,0 +1,193 @@ +module "this" { + source = "app.terraform.io/wallet-connect/label/null" + version = "0.3.2" + + namespace = var.namespace + region = var.region + stage = var.stage + name = var.name + delimiter = var.delimiter + attributes = var.attributes + tags = var.tags + label_order = var.label_order + regex_replace_chars = var.regex_replace_chars + id_length_limit = var.id_length_limit + label_key_case = var.label_key_case + label_value_case = var.label_value_case + + context = var.context +} + +################################################################################ +# Copy contents of label/variables.tf here + +#tflint-ignore: terraform_standard_module_structure +variable "context" { + type = any + default = { + namespace = null + region = null + stage = null + name = null + delimiter = null + attributes = [] + tags = {} + label_order = [] + regex_replace_chars = null + id_length_limit = null + label_key_case = null + label_value_case = null + } + description = <<-EOT + Single object for setting entire context at once. + See description of individual variables for details. + Leave string and numeric variables as `null` to use default value. + Individual variable settings (non-null) override settings in context object, + except for attributes and tags, which are merged. + EOT + + validation { + condition = lookup(var.context, "label_key_case", null) == null ? true : contains(["lower", "title", "upper"], var.context["label_key_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`." + } + + validation { + condition = lookup(var.context, "label_value_case", null) == null ? true : contains(["lower", "title", "upper", "none"], var.context["label_value_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} + +#tflint-ignore: terraform_standard_module_structure +variable "namespace" { + type = string + default = null + description = "ID element. Usually the organization name, i.e. 'walletconnect' to help ensure generated IDs are globally unique." +} + +#tflint-ignore: terraform_standard_module_structure +variable "region" { + type = string + default = null + description = "ID element. Usually used for region e.g. 'uw2', 'us-west-2'." +} + +#tflint-ignore: terraform_standard_module_structure +variable "stage" { + type = string + default = null + description = "ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release'." +} + +#tflint-ignore: terraform_standard_module_structure +variable "name" { + type = string + default = null + description = <<-EOT + ID element. Usually the component name. + This is the only ID element not also included as a `tag`. + The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. + EOT +} + +#tflint-ignore: terraform_standard_module_structure +variable "delimiter" { + type = string + default = null + description = <<-EOT + Delimiter to be used between ID elements. + Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. + EOT +} + +#tflint-ignore: terraform_standard_module_structure +variable "attributes" { + type = list(string) + default = [] + description = <<-EOT + ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`, + in the order they appear in the list. New attributes are appended to the + end of the list. The elements of the list are joined by the `delimiter` + and treated as a single ID element. + EOT +} + +#tflint-ignore: terraform_standard_module_structure +variable "tags" { + type = map(string) + default = {} + description = "Additional tags." +} + +#tflint-ignore: terraform_standard_module_structure +variable "label_order" { + type = list(string) + default = null + description = <<-EOT + The order in which the labels (ID elements) appear in the `id`. + Defaults to ["namespace", "region", "stage", "name", "attributes"]. + You can omit any of the 5 labels, but at least one must be present. + EOT +} + +#tflint-ignore: terraform_standard_module_structure +variable "regex_replace_chars" { + type = string + default = null + description = <<-EOT + Terraform regular expression (regex) string. + Characters matching the regex will be removed from the ID elements. + If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. + EOT +} + +#tflint-ignore: terraform_standard_module_structure +variable "id_length_limit" { + type = number + default = null + description = <<-EOT + Limit `id` to this many characters (minimum 6). + Set to `0` for unlimited length. + Set to `null` for keep the existing setting, which defaults to `0`. + Does not affect `id_full`. + EOT + validation { + condition = var.id_length_limit == null ? true : var.id_length_limit >= 6 || var.id_length_limit == 0 + error_message = "The id_length_limit must be >= 6 if supplied (not null), or 0 for unlimited length." + } +} + +#tflint-ignore: terraform_standard_module_structure +variable "label_key_case" { + type = string + default = null + description = <<-EOT + Controls the letter case of the `tags` keys (label names) for tags generated by this module. + Does not affect keys of tags passed in via the `tags` input. + Possible values: `lower`, `title`, `upper`. + Default value: `title`. + EOT + + validation { + condition = var.label_key_case == null ? true : contains(["lower", "title", "upper"], var.label_key_case) + error_message = "Allowed values: `lower`, `title`, `upper`." + } +} + +#tflint-ignore: terraform_standard_module_structure +variable "label_value_case" { + type = string + default = null + description = <<-EOT + Controls the letter case of ID elements (labels) as included in `id`, + set as tag values, and output by this module individually. + Does not affect values of tags passed in via the `tags` input. + Possible values: `lower`, `title`, `upper` and `none` (no transformation). + Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs. + Default value: `lower`. + EOT + + validation { + condition = var.label_value_case == null ? true : contains(["lower", "title", "upper", "none"], var.label_value_case) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} diff --git a/terraform/redis/main.tf b/terraform/redis/main.tf new file mode 100644 index 0000000..dcdeaa0 --- /dev/null +++ b/terraform/redis/main.tf @@ -0,0 +1,45 @@ +data "aws_vpc" "vpc" { + id = var.vpc_id +} + +resource "aws_elasticache_cluster" "cache" { + cluster_id = module.this.id + engine = "redis" + node_type = var.node_type + num_cache_nodes = var.num_cache_nodes + parameter_group_name = "default.redis6.x" + engine_version = var.node_engine_version + port = 6379 + subnet_group_name = aws_elasticache_subnet_group.private_subnets.name + security_group_ids = [ + aws_security_group.service_security_group.id + ] + snapshot_retention_limit = 2 +} + +resource "aws_elasticache_subnet_group" "private_subnets" { + name = "${module.this.id}-private-subnet-group" + subnet_ids = var.subnets_ids +} + +# Allow only the app to access Redis +resource "aws_security_group" "service_security_group" { + name = "${module.this.id}-redis-service-ingress" + description = "Allow ingress from the application" + vpc_id = var.vpc_id + ingress { + description = "${module.this.id} - ingress from application" + from_port = 6379 + to_port = 6379 + protocol = "TCP" + cidr_blocks = var.ingress_cidr_blocks == null ? [data.aws_vpc.vpc.cidr_block] : var.ingress_cidr_blocks + } + + egress { + description = "${module.this.id} - egress to application" + from_port = 0 # Allowing any incoming port + to_port = 0 # Allowing any outgoing port + protocol = "-1" # Allowing any outgoing protocol + cidr_blocks = var.egress_cidr_blocks == null ? [data.aws_vpc.vpc.cidr_block] : var.egress_cidr_blocks + } +} diff --git a/terraform/redis/outputs.tf b/terraform/redis/outputs.tf new file mode 100644 index 0000000..88c618c --- /dev/null +++ b/terraform/redis/outputs.tf @@ -0,0 +1,9 @@ +output "cluster_id" { + description = "The ID of the cluster" + value = aws_elasticache_cluster.cache.id +} + +output "endpoint" { + description = "The endpoint of the Redis cluster" + value = "${aws_elasticache_cluster.cache.cache_nodes[0].address}:${aws_elasticache_cluster.cache.cache_nodes[0].port}" +} diff --git a/terraform/redis/terraform.tf b/terraform/redis/terraform.tf new file mode 100644 index 0000000..f4c0a25 --- /dev/null +++ b/terraform/redis/terraform.tf @@ -0,0 +1,10 @@ +terraform { + required_version = "~> 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.7" + } + } +} diff --git a/terraform/redis/variables.tf b/terraform/redis/variables.tf new file mode 100644 index 0000000..cf2f70c --- /dev/null +++ b/terraform/redis/variables.tf @@ -0,0 +1,45 @@ +#------------------------------------------------------------------------------- +# Nodes Configuration + +variable "node_type" { + description = "The instance type to use for the database nodes" + type = string + default = "cache.t4g.micro" # https://aws.amazon.com/elasticache/pricing/?nc=sn&loc=5#On-demand_nodes +} + +variable "num_cache_nodes" { + description = "The number of nodes to create in the cluster" + type = number + default = 1 +} + +variable "node_engine_version" { + description = "The version of Redis to use" + type = string + default = "6.x" +} + +#------------------------------------------------------------------------------- +# Networking + +variable "vpc_id" { + description = "The VPC ID to create the security group in" + type = string +} + +variable "subnets_ids" { + description = "The list of subnet IDs to create the cluster in" + type = set(string) +} + +variable "ingress_cidr_blocks" { + description = "The CIDR blocks to allow ingress from, default to VPC only." + type = set(string) + default = null +} + +variable "egress_cidr_blocks" { + description = "The CIDR blocks to allow egress to, default to VPC only." + type = set(string) + default = null +} diff --git a/terraform/res_alerting.tf b/terraform/res_alerting.tf new file mode 100644 index 0000000..82c1087 --- /dev/null +++ b/terraform/res_alerting.tf @@ -0,0 +1,12 @@ +module "alerting" { + source = "./alerting" + context = module.this + + webhook_cloudwatch_p2 = var.webhook_cloudwatch_p2 + webhook_prometheus_p2 = var.webhook_prometheus_p2 + + ecs_cluster_name = module.ecs.ecs_cluster_name + ecs_service_name = module.ecs.ecs_service_name + + redis_cluster_id = module.redis.cluster_id +} diff --git a/terraform/res_dns.tf b/terraform/res_dns.tf new file mode 100644 index 0000000..fb4750d --- /dev/null +++ b/terraform/res_dns.tf @@ -0,0 +1,13 @@ +locals { + zones = { for k, v in tomap(data.terraform_remote_state.infra_aws.outputs.zones.verify[local.stage]) : v.id => v.name } + zones_certificates = { for k, v in module.dns_certificate : v.zone_id => v.certificate_arn } +} + +module "dns_certificate" { + for_each = local.zones + source = "app.terraform.io/wallet-connect/dns/aws" + version = "0.1.3" + context = module.this + hosted_zone_name = each.value + fqdn = each.value +} diff --git a/terraform/res_ecs.tf b/terraform/res_ecs.tf new file mode 100644 index 0000000..d93e4a2 --- /dev/null +++ b/terraform/res_ecs.tf @@ -0,0 +1,82 @@ +data "aws_s3_bucket" "geoip" { + bucket = data.terraform_remote_state.infra_aws.outputs.geoip_bucked_id +} + +resource "aws_prometheus_workspace" "prometheus" { + alias = "prometheus-${module.this.id}" +} + +resource "aws_iam_role" "application_role" { + name = "${module.this.id}-ecs-task-execution" + assume_role_policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "ecs-tasks.amazonaws.com" + } + } + ] + }) +} + +# ECS Cluster, Task, Service, and Load Balancer for our app +module "ecs" { + source = "./ecs" + context = module.this + + # Cluster + ecr_repository_url = local.ecr_repository_url + image_version = var.image_version + task_execution_role_name = aws_iam_role.application_role.name + task_cpu = var.task_cpu + task_memory = var.task_memory + autoscaling_desired_count = var.app_autoscaling_desired_count + autoscaling_min_capacity = var.app_autoscaling_min_capacity + autoscaling_max_capacity = var.app_autoscaling_max_capacity + cloudwatch_logs_key_arn = aws_kms_key.cloudwatch_logs.arn + + # DNS + route53_zones = local.zones + route53_zones_certificates = local.zones_certificates + + # Network + vpc_id = module.vpc.vpc_id + public_subnets = module.vpc.public_subnets + private_subnets = module.vpc.private_subnets + allowed_app_ingress_cidr_blocks = module.vpc.vpc_cidr_block + allowed_lb_ingress_cidr_blocks = module.vpc.vpc_cidr_block + + # Application + app_secret = var.app_secret + + port = 8080 + log_level = var.log_level + + project_registry_api_url = var.project_registry_api_url + project_registry_api_auth_token = var.project_registry_api_auth_token + + data_api_url = var.data_api_url + data_api_auth_token = var.data_api_auth_token + + attestation_cache_url = "redis://${module.redis.endpoint}/0" + project_registry_cache_url = "redis://${module.redis.endpoint}/1" + scam_guard_cache_url = "redis://${module.redis.endpoint}/2" + + ofac_blocked_countries = var.ofac_blocked_countries + + # Analytics + analytics_datalake_bucket_name = data.terraform_remote_state.datalake.outputs.datalake_bucket_id + analytics_datalake_kms_key_arn = data.terraform_remote_state.datalake.outputs.datalake_kms_key_arn + + # Monitoring + prometheus_endpoint = aws_prometheus_workspace.prometheus.prometheus_endpoint + + # GeoIP + geoip_db_bucket_name = data.aws_s3_bucket.geoip.id + geoip_db_key = var.geoip_db_key + + depends_on = [aws_iam_role.application_role] +} diff --git a/terraform/res_monitoring.tf b/terraform/res_monitoring.tf new file mode 100644 index 0000000..563290d --- /dev/null +++ b/terraform/res_monitoring.tf @@ -0,0 +1,10 @@ +module "monitoring" { + source = "./monitoring" + context = module.this + + monitoring_role_arn = data.terraform_remote_state.monitoring.outputs.grafana_workspaces.central.iam_role_arn + notification_channels = var.notification_channels + prometheus_endpoint = aws_prometheus_workspace.prometheus.prometheus_endpoint + ecs_service_name = module.ecs.ecs_service_name + redis_cluster_id = module.redis.cluster_id +} diff --git a/terraform/res_network.tf b/terraform/res_network.tf new file mode 100644 index 0000000..05c96a7 --- /dev/null +++ b/terraform/res_network.tf @@ -0,0 +1,134 @@ +locals { + vpc_cidr = "10.0.0.0/16" + vpc_azs = slice(data.aws_availability_zones.available.names, 0, 3) + vpc_flow_s3_bucket_name = substr("vpc-flow-logs-${module.this.id}-${random_pet.this.id}", 0, 63) +} + +#------------------------------------------------------------------------------- +# VPC + +data "aws_availability_zones" "available" {} + +#tfsec:ignore:aws-ec2-no-public-ingress-acl +#tfsec:ignore:aws-ec2-require-vpc-flow-logs-for-all-vpcs +#tfsec:ignore:aws-ec2-no-excessive-port-access +module "vpc" { + source = "terraform-aws-modules/vpc/aws" + version = "~> 5.0" + + name = module.this.id + cidr = local.vpc_cidr + azs = local.vpc_azs + + database_subnets = [for k, v in local.vpc_azs : cidrsubnet(local.vpc_cidr, 8, k)] + intra_subnets = [for k, v in local.vpc_azs : cidrsubnet(local.vpc_cidr, 8, k + 4)] + public_subnets = [for k, v in local.vpc_azs : cidrsubnet(local.vpc_cidr, 8, k + 8)] + private_subnets = [for k, v in local.vpc_azs : cidrsubnet(local.vpc_cidr, 8, k + 12)] + + enable_dns_support = true + enable_dns_hostnames = true + enable_nat_gateway = true + single_nat_gateway = true + one_nat_gateway_per_az = false + + enable_flow_log = true + flow_log_file_format = "parquet" + flow_log_destination_type = "s3" + flow_log_destination_arn = module.vpc_flow_s3_bucket.s3_bucket_arn + vpc_flow_log_tags = module.this.tags +} + +module "vpc_endpoints" { + source = "terraform-aws-modules/vpc/aws//modules/vpc-endpoints" + version = "5.1" + + vpc_id = module.vpc.vpc_id + + endpoints = { + cloudwatch = { + service = "monitoring" + }, + cloudwatch-events = { + service = "events" + }, + cloudwatch-logs = { + service = "logs" + }, + ecs = { + service = "ecs" + }, + ecs-agent = { + service = "ecs-agent" + }, + ecs-telemetry = { + service = "ecs-telemetry" + }, + elastic-load-balancing = { + service = "elasticloadbalancing" + }, + kms = { + service = "kms" + }, + s3 = { + service = "s3" + }, + } +} + +#------------------------------------------------------------------------------- +# VPC Flow S3 Bucket + +#TODO: Enable bucket logging and send logs to bucket on security account. +#tfsec:ignore:aws-s3-enable-versioning +#tfsec:ignore:aws-s3-enable-bucket-logging +#tfsec:ignore:aws-s3-enable-bucket-encryption +#tfsec:ignore:aws-s3-encryption-customer-key +module "vpc_flow_s3_bucket" { + source = "terraform-aws-modules/s3-bucket/aws" + version = "~> 3.14" + + bucket = local.vpc_flow_s3_bucket_name + force_destroy = true + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Sid = "AWSLogDeliveryAclCheck" + Effect = "Allow" + Principal = { + Service = "delivery.logs.amazonaws.com" + } + Action = "s3:GetBucketAcl" + Resource = "arn:aws:s3:::${local.vpc_flow_s3_bucket_name}" + }, + { + Sid = "AWSLogDeliveryWrite" + Effect = "Allow" + Principal = { + Service = "delivery.logs.amazonaws.com" + } + Action = "s3:PutObject" + Resource = "arn:aws:s3:::${local.vpc_flow_s3_bucket_name}/AWSLogs/*" + } + ] + }) + + lifecycle_rule = [ + { + id = "transition-old-logs" + enabled = true + + transition = [ + { + days = 30 + storage_class = "ONEZONE_IA" + }, + { + days = 60 + storage_class = "GLACIER" + } + ] + } + ] +} diff --git a/terraform/res_redis.tf b/terraform/res_redis.tf new file mode 100644 index 0000000..e997b2d --- /dev/null +++ b/terraform/res_redis.tf @@ -0,0 +1,7 @@ +module "redis" { + source = "./redis" + context = module.this + + vpc_id = module.vpc.vpc_id + subnets_ids = module.vpc.intra_subnets +} diff --git a/terraform/terraform.tf b/terraform/terraform.tf new file mode 100644 index 0000000..11c6c1f --- /dev/null +++ b/terraform/terraform.tf @@ -0,0 +1,27 @@ +# Terraform Configuration +terraform { + required_version = ">= 1.0" + + backend "remote" { + hostname = "app.terraform.io" + organization = "wallet-connect" + workspaces { + prefix = "verify-server-" + } + } + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.7" + } + grafana = { + source = "grafana/grafana" + version = ">= 2.1" + } + random = { + source = "hashicorp/random" + version = "3.5.1" + } + } +} diff --git a/terraform/variables.tf b/terraform/variables.tf new file mode 100644 index 0000000..e364e94 --- /dev/null +++ b/terraform/variables.tf @@ -0,0 +1,136 @@ +#------------------------------------------------------------------------------- +# Configuration + +variable "grafana_auth" { + description = "The API Token for the Grafana instance" + type = string + default = "" +} + + +#------------------------------------------------------------------------------- +# Service + +variable "name" { + description = "The name of the application" + type = string + default = "verify-server" +} + +variable "region" { + description = "AWS region to deploy to" + type = string +} + +variable "image_version" { + description = "The ECS tag of the image to deploy" + type = string +} + +variable "task_cpu" { + description = "The number of CPU units to allocate to the task" + type = number +} + +variable "task_memory" { + description = "The amount of memory to allocate to the task" + type = number +} + +variable "app_autoscaling_desired_count" { + description = "The desired number of tasks to run" + type = number + default = 1 +} + +variable "app_autoscaling_min_capacity" { + description = "The minimum number of tasks to run when autoscaling" + type = number + default = 1 +} + +variable "app_autoscaling_max_capacity" { + description = "The maximum number of tasks to run when autoscaling" + type = number + default = 1 +} + +#------------------------------------------------------------------------------- +# Application + +variable "app_secret" { + description = "The application secret" + type = string + sensitive = true +} + +variable "log_level" { + description = "Defines logging level for the application" + type = string +} + +variable "ofac_blocked_countries" { + description = "The list of countries to block" + type = string + default = "" +} + + +#------------------------------------------------------------------------------- +# Project Registry + +variable "project_registry_api_url" { + description = "The url of the project registry API" + type = string +} + +variable "project_registry_api_auth_token" { + description = "The auth token for the project registry API" + type = string + sensitive = true +} + +#------------------------------------------------------------------------------- +# Data API + +variable "data_api_url" { + description = "The url of the data API" + type = string +} + +variable "data_api_auth_token" { + description = "The auth token for the data API" + type = string + sensitive = true +} + + +#------------------------------------------------------------------------------- +# Analytics + +variable "geoip_db_key" { + description = "The name to the GeoIP database" + type = string +} + + +#------------------------------------------------------------------------------- +# Alerting / Monitoring + +variable "notification_channels" { + description = "The notification channels to send alerts to" + type = list(any) + default = [] +} + +variable "webhook_cloudwatch_p2" { + description = "The webhook to send CloudWatch P2 alerts to" + type = string + default = "" +} + +variable "webhook_prometheus_p2" { + description = "The webhook to send Prometheus P2 alerts to" + type = string + default = "" +} From 887199bca0ee72193ea7455c11b1efab771b026a Mon Sep 17 00:00:00 2001 From: Xavier Basty Date: Fri, 2 Feb 2024 16:23:16 +0100 Subject: [PATCH 3/5] updated CI versions --- .github/workflows/debug_validate.yml | 2 +- .github/workflows/dispatch_deploy.yml | 2 +- .github/workflows/dispatch_publish.yml | 4 +-- .github/workflows/dispatch_validate.yml | 2 +- .github/workflows/event_intake.yml | 4 +-- .github/workflows/event_pr.yml | 4 +-- .github/workflows/event_release.yml | 4 +-- .github/workflows/sub-cd.yml | 4 +-- .github/workflows/sub-validate.yml | 34 ++----------------------- 9 files changed, 15 insertions(+), 45 deletions(-) diff --git a/.github/workflows/debug_validate.yml b/.github/workflows/debug_validate.yml index 2039df8..53db19b 100644 --- a/.github/workflows/debug_validate.yml +++ b/.github/workflows/debug_validate.yml @@ -12,7 +12,7 @@ permissions: jobs: ci: name: CI - uses: WalletConnect/ci_workflows/.github/workflows/ci.yml@0.1.3 + uses: WalletConnect/ci_workflows/.github/workflows/ci.yml@0.2.0 secrets: inherit with: check-infra: true diff --git a/.github/workflows/dispatch_deploy.yml b/.github/workflows/dispatch_deploy.yml index 93ebd43..40c1f22 100644 --- a/.github/workflows/dispatch_deploy.yml +++ b/.github/workflows/dispatch_deploy.yml @@ -40,7 +40,7 @@ jobs: name: Lookup Version if: ${{ !inputs.deploy-app && inputs.version == '-current-' }} secrets: inherit - uses: WalletConnect/ci_workflows/.github/workflows/release-get_deployed_version.yml@0.1.3 + uses: WalletConnect/ci_workflows/.github/workflows/release-get_deployed_version.yml@0.2.0 with: task-name-stage: prod task-name: ${{ vars.TASK_NAME }} diff --git a/.github/workflows/dispatch_publish.yml b/.github/workflows/dispatch_publish.yml index 414dea8..11105d3 100644 --- a/.github/workflows/dispatch_publish.yml +++ b/.github/workflows/dispatch_publish.yml @@ -25,7 +25,7 @@ permissions: jobs: ci: name: CI - uses: WalletConnect/ci_workflows/.github/workflows/ci.yml@0.1.3 + uses: WalletConnect/ci_workflows/.github/workflows/ci.yml@0.2.0 secrets: inherit with: check-infra: false @@ -33,7 +33,7 @@ jobs: release: name: Release - uses: WalletConnect/ci_workflows/.github/workflows/release.yml@0.1.3 + uses: WalletConnect/ci_workflows/.github/workflows/release.yml@0.2.0 secrets: inherit with: infra-changed: false diff --git a/.github/workflows/dispatch_validate.yml b/.github/workflows/dispatch_validate.yml index f8ee6a3..1476975 100644 --- a/.github/workflows/dispatch_validate.yml +++ b/.github/workflows/dispatch_validate.yml @@ -32,7 +32,7 @@ permissions: jobs: ci: name: CI - uses: WalletConnect/ci_workflows/.github/workflows/ci.yml@0.1.3 + uses: WalletConnect/ci_workflows/.github/workflows/ci.yml@0.2.0 secrets: inherit with: check-infra: ${{ inputs.check-infra }} diff --git a/.github/workflows/event_intake.yml b/.github/workflows/event_intake.yml index 36ff490..f715af1 100644 --- a/.github/workflows/event_intake.yml +++ b/.github/workflows/event_intake.yml @@ -14,7 +14,7 @@ jobs: runs-on: group: ${{ vars.RUN_GROUP }} steps: - - uses: actions/add-to-project@v0.1.0 + - uses: actions/add-to-project@v0.5.0 with: project-url: https://github.com/orgs/WalletConnect/projects/20 github-token: ${{ secrets.ASSIGN_TO_PROJECT_GITHUB_TOKEN }} @@ -28,7 +28,7 @@ jobs: group: ${{ vars.RUN_GROUP }} steps: - name: Check Core Team membership - uses: tspascoal/get-user-teams-membership@v1 + uses: tspascoal/get-user-teams-membership@v3 id: is-core-team with: username: ${{ github.event_name != 'pull_request' && github.event.issue.user.login || github.event.sender.login }} diff --git a/.github/workflows/event_pr.yml b/.github/workflows/event_pr.yml index 56def43..17a51be 100644 --- a/.github/workflows/event_pr.yml +++ b/.github/workflows/event_pr.yml @@ -39,7 +39,7 @@ jobs: group: ${{ vars.RUN_GROUP }} steps: - uses: actions/checkout@v4 - - uses: WalletConnect/actions/github/paths-filter/@2.2.1 + - uses: WalletConnect/actions/github/paths-filter/@2.4.0 id: filter outputs: infra: ${{ steps.filter.outputs.infra }} @@ -48,7 +48,7 @@ jobs: ci: name: CI needs: [ paths-filter ] - uses: WalletConnect/ci_workflows/.github/workflows/ci.yml@0.1.3 + uses: WalletConnect/ci_workflows/.github/workflows/ci.yml@0.2.0 secrets: inherit with: check-app: ${{ needs.paths-filter.outputs.app == 'true' }} diff --git a/.github/workflows/event_release.yml b/.github/workflows/event_release.yml index 40c2880..83af153 100644 --- a/.github/workflows/event_release.yml +++ b/.github/workflows/event_release.yml @@ -36,7 +36,7 @@ jobs: group: ${{ vars.RUN_GROUP }} steps: - uses: actions/checkout@v4 - - uses: WalletConnect/actions/github/paths-filter/@2.2.1 + - uses: WalletConnect/actions/github/paths-filter/@2.4.0 id: filter outputs: infra: ${{ steps.filter.outputs.infra }} @@ -45,7 +45,7 @@ jobs: release: name: Release needs: [ paths_filter ] - uses: WalletConnect/ci_workflows/.github/workflows/release.yml@0.1.3 + uses: WalletConnect/ci_workflows/.github/workflows/release.yml@0.2.0 secrets: inherit with: task-name: ${{ vars.TASK_NAME }} diff --git a/.github/workflows/sub-cd.yml b/.github/workflows/sub-cd.yml index 740edae..d56a2cf 100644 --- a/.github/workflows/sub-cd.yml +++ b/.github/workflows/sub-cd.yml @@ -31,7 +31,7 @@ jobs: cd-staging: name: Staging secrets: inherit - uses: WalletConnect/ci_workflows/.github/workflows/cd.yml@0.1.3 + uses: WalletConnect/ci_workflows/.github/workflows/cd.yml@0.2.0 with: deploy-infra: ${{ inputs.deploy-infra }} deploy-app: ${{ inputs.deploy-app && !inputs.deploy-infra }} @@ -57,7 +57,7 @@ jobs: needs: [ validate-staging ] if: ${{ inputs.deploy-prod }} secrets: inherit - uses: WalletConnect/ci_workflows/.github/workflows/cd.yml@0.1.3 + uses: WalletConnect/ci_workflows/.github/workflows/cd.yml@0.2.0 with: deploy-infra: ${{ inputs.deploy-infra }} deploy-app: ${{ inputs.deploy-app && !inputs.deploy-infra }} diff --git a/.github/workflows/sub-validate.yml b/.github/workflows/sub-validate.yml index 736532f..dc5900a 100644 --- a/.github/workflows/sub-validate.yml +++ b/.github/workflows/sub-validate.yml @@ -33,33 +33,6 @@ jobs: - name: health-check run: curl "${{ inputs.stage-url }}" - integration-tests: - name: Integration Tests - ${{ inputs.stage }} - runs-on: - group: ${{ vars.RUN_GROUP }} - environment: - name: ${{ inputs.stage }} - url: ${{ inputs.stage-url }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Install Rust ${{ inputs.rust-toolchain }} - uses: WalletConnect/actions-rs/toolchain@1.0.0 - with: - toolchain: ${{ inputs.rust-toolchain }} - profile: 'default' - override: true - - - name: "Run Integration Tests" - uses: WalletConnect/actions-rs/cargo@1.0.0 - env: - PROJECT_ID: ${{ secrets.PROJECT_ID }} - RPC_URL: ${{ inputs.stage-url }} - with: - command: test - args: --test integration - integration-tests-ts: name: TS Integration Tests - ${{ inputs.stage }} runs-on: @@ -69,7 +42,7 @@ jobs: uses: actions/checkout@v4 - name: Setup Node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 18.x cache: 'yarn' @@ -79,7 +52,4 @@ jobs: run: yarn install - name: Yarn Integration Tests - run: yarn integration - env: - PROJECT_ID: ${{ secrets.PROJECT_ID }} - RPC_URL: ${{ inputs.stage-url }} + run: yarn integration:${{ inputs.stage }} From 63d384ba320dd565f3d8e2367b3105d2b2f2d596 Mon Sep 17 00:00:00 2001 From: Xavier Basty Date: Fri, 2 Feb 2024 17:07:33 +0100 Subject: [PATCH 4/5] fix CI errors --- Cargo.lock | 22 ---------------------- Cargo.toml | 1 - deny.toml | 27 +++++++++++++++++++++++++++ src/http_server/mod.rs | 4 ++-- 4 files changed, 29 insertions(+), 25 deletions(-) create mode 100644 deny.toml diff --git a/Cargo.lock b/Cargo.lock index 9236d7b..9b70711 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -680,7 +680,6 @@ dependencies = [ "metrics 0.20.1", "parquet", "parquet_derive", - "prometheus", "reqwest", "rmp-serde", "serde", @@ -2179,27 +2178,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "prometheus" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c" -dependencies = [ - "cfg-if", - "fnv", - "lazy_static", - "memchr", - "parking_lot", - "protobuf", - "thiserror", -] - -[[package]] -name = "protobuf" -version = "2.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" - [[package]] name = "quanta" version = "0.11.1" diff --git a/Cargo.toml b/Cargo.toml index 7235b49..14cfcf1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,6 @@ tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["json"] } metrics = "0.20" axum-prometheus = "0.3" -prometheus = "0.13" # Serialisation serde = { version = "1.0", features = ["derive"] } diff --git a/deny.toml b/deny.toml new file mode 100644 index 0000000..a295713 --- /dev/null +++ b/deny.toml @@ -0,0 +1,27 @@ +[licenses] +unused-allowed-license = "deny" +copyleft = "deny" +allow = [ + "Apache-2.0", + "MIT", + "Unlicense", + "BSD-3-Clause", + "0BSD", + "ISC" +] + +exceptions = [{ name = "unicode-ident", allow = ["Unicode-DFS-2016"] }] + +[licenses.private] +ignore = true + +# TODO We should be able to remove `ignore-sources` once we add `publish = false` to all the crates sourced from here. +ignore-sources = [ + "https://github.com/WalletConnect/utils-rs.git", + "https://github.com/WalletConnect/WalletConnectRust.git", +] + +[[licenses.clarify]] +name = "ring" +expression = "ISC" +license-files = [{ path = "LICENSE", hash = 0xbd0eed23 }] diff --git a/src/http_server/mod.rs b/src/http_server/mod.rs index 9b407e0..fd11a29 100644 --- a/src/http_server/mod.rs +++ b/src/http_server/mod.rs @@ -318,7 +318,7 @@ fn build_content_security_header(domains: Vec) -> String { #[test] fn test_build_content_security_header() { fn case(domains: &[&str], expected: &str) { - let domains = domains.into_iter().map(|s| Domain::from(s.to_string())); + let domains = domains.iter().map(|s| Domain::from(s.to_string())); let got = build_content_security_header(domains.collect()); assert_eq!(&got, expected); } @@ -348,5 +348,5 @@ fn csrf_validation_checks_jwt_header_and_payload() { let valid_header_invalid_payload = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9."; - assert!(!CsrfToken::validate_format(&valid_header_invalid_payload)) + assert!(!CsrfToken::validate_format(valid_header_invalid_payload)) } From 9b1189fb7e828b2e6b7978f2d78b6269b558bfc4 Mon Sep 17 00:00:00 2001 From: Xavier Basty Date: Mon, 5 Feb 2024 14:09:34 +0100 Subject: [PATCH 5/5] remove test flow --- .github/workflows/debug_validate.yml | 35 ---------------------------- 1 file changed, 35 deletions(-) delete mode 100644 .github/workflows/debug_validate.yml diff --git a/.github/workflows/debug_validate.yml b/.github/workflows/debug_validate.yml deleted file mode 100644 index 53db19b..0000000 --- a/.github/workflows/debug_validate.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: ⚙️ Debug - Validate -on: - push: - branches: - - 58-migrate-ti-dedicated-aws-account - -permissions: - contents: read - checks: write - id-token: write - -jobs: - ci: - name: CI - uses: WalletConnect/ci_workflows/.github/workflows/ci.yml@0.2.0 - secrets: inherit - with: - check-infra: true - check-app: true - - validate-staging: - name: Validate - Staging - uses: ./.github/workflows/sub-validate.yml - secrets: inherit - with: - stage: staging - stage-url: https://staging.${{ vars.SUBDOMAIN_NAME }}.walletconnect.com - - validate-prod: - name: Validate - Prod - uses: ./.github/workflows/sub-validate.yml - secrets: inherit - with: - stage: prod - stage-url: https://${{ vars.SUBDOMAIN_NAME }}.walletconnect.com