From 9a9a42134fde2d4951e10d917071115d9f7c8272 Mon Sep 17 00:00:00 2001 From: Rachel Rogers Date: Tue, 24 Mar 2026 07:56:01 -0700 Subject: [PATCH 1/8] Remove files related to hosting infrastructure on Cloud.gov --- notifications-admin/.profile | 9 - .../app/cloudfoundry_config.py | 30 --- notifications-admin/deploy-config/demo.yml | 11 - .../egress_proxy/notify-admin-demo.allow.acl | 4 - .../egress_proxy/notify-admin-demo.deny.acl | 0 .../egress_proxy/notify-admin-demo.deploy.acl | 3 - .../notify-admin-production.allow.acl | 4 - .../notify-admin-production.deny.acl | 0 .../notify-admin-production.deploy.acl | 1 - .../notify-admin-staging.allow.acl | 4 - .../notify-admin-staging.deny.acl | 0 .../notify-admin-staging.deploy.acl | 3 - .../deploy-config/production.yml | 11 - notifications-admin/deploy-config/sandbox.yml | 15 -- notifications-admin/deploy-config/staging.yml | 11 - notifications-admin/manifest.yml | 67 ----- notifications-admin/print_vcap.sh | 17 -- notifications-admin/terraform/README.md | 3 - .../terraform/bootstrap/import.sh | 12 - .../terraform/bootstrap/main.tf | 20 -- .../terraform/bootstrap/providers.tf | 16 -- .../terraform/bootstrap/run.sh | 12 - .../terraform/bootstrap/teardown_creds.sh | 5 - .../terraform/bootstrap/variables.tf | 4 - .../terraform/create_service_account.sh | 92 ------- notifications-admin/terraform/demo/main.tf | 44 ---- .../terraform/demo/providers.tf | 37 --- .../terraform/demo/variables.tf | 5 - .../terraform/destroy_service_account.sh | 53 ---- .../terraform/development/main.tf | 58 ----- .../terraform/development/providers.tf | 16 -- .../terraform/development/reset.sh | 59 ----- .../terraform/development/run.sh | 73 ------ .../terraform/development/variables.tf | 5 - .../terraform/production/main.tf | 68 ----- .../terraform/production/providers.tf | 37 --- .../terraform/production/variables.tf | 5 - notifications-admin/terraform/sandbox/main.tf | 44 ---- .../terraform/sandbox/providers.tf | 37 --- .../terraform/sandbox/variables.tf | 5 - .../terraform/set_space_egress.sh | 67 ----- .../shared/container_networking/main.tf | 22 -- .../shared/container_networking/providers.tf | 9 - .../shared/container_networking/variables.tf | 10 - notifications-admin/terraform/staging/main.tf | 65 ----- .../terraform/staging/providers.tf | 37 --- .../terraform/staging/variables.tf | 5 - .../tests/app/test_cloudfoundry_config.py | 70 ----- notifications-api/.profile | 9 - notifications-api/app/cloudfoundry_config.py | 110 -------- notifications-api/deploy-config/demo.yml | 10 - .../egress_proxy/notify-api-demo.allow.acl | 12 - .../egress_proxy/notify-api-demo.deny.acl | 0 .../egress_proxy/notify-api-demo.deploy.acl | 3 - .../notify-api-production.allow.acl | 11 - .../notify-api-production.deny.acl | 0 .../notify-api-production.deploy.acl | 1 - .../egress_proxy/notify-api-staging.allow.acl | 12 - .../egress_proxy/notify-api-staging.deny.acl | 0 .../notify-api-staging.deploy.acl | 4 - .../deploy-config/production.yml | 10 - notifications-api/deploy-config/sandbox.yml | 18 -- notifications-api/deploy-config/staging.yml | 10 - notifications-api/manifest.yml | 59 ----- notifications-api/paas-failwhale/README.md | 27 -- notifications-api/paas-failwhale/manifest.yml | 8 - notifications-api/paas-failwhale/nginx.conf | 31 --- .../scripts/deploy_to_sandbox.sh | 15 -- notifications-api/terraform/README.md | 245 ------------------ .../terraform/bootstrap/import.sh | 12 - notifications-api/terraform/bootstrap/main.tf | 20 -- .../terraform/bootstrap/providers.tf | 16 -- notifications-api/terraform/bootstrap/run.sh | 12 - .../terraform/bootstrap/teardown_creds.sh | 5 - .../terraform/bootstrap/variables.tf | 4 - .../terraform/create_service_account.sh | 92 ------- notifications-api/terraform/demo/main.tf | 76 ------ notifications-api/terraform/demo/providers.tf | 37 --- notifications-api/terraform/demo/variables.tf | 4 - .../terraform/destroy_service_account.sh | 53 ---- .../terraform/development/main.tf | 77 ------ .../terraform/development/providers.tf | 16 -- .../terraform/development/reset.sh | 65 ----- .../terraform/development/run.sh | 76 ------ .../terraform/development/variables.tf | 9 - .../terraform/ops/cloudgov_user_report.py | 85 ------ .../terraform/production/main.tf | 76 ------ .../terraform/production/providers.tf | 37 --- .../terraform/production/variables.tf | 4 - notifications-api/terraform/sandbox/main.tf | 75 ------ .../terraform/sandbox/providers.tf | 24 -- .../terraform/sandbox/variables.tf | 4 - .../terraform/set_space_egress.sh | 67 ----- .../terraform/shared/egress_space/main.tf | 36 --- .../shared/egress_space/providers.tf | 9 - .../shared/egress_space/variables.tf | 5 - .../terraform/shared/ses/main.tf | 29 --- .../terraform/shared/ses/providers.tf | 9 - .../terraform/shared/ses/variables.tf | 36 --- .../terraform/shared/sns/main.tf | 26 -- .../terraform/shared/sns/providers.tf | 9 - .../terraform/shared/sns/variables.tf | 24 -- notifications-api/terraform/staging/main.tf | 85 ------ .../terraform/staging/providers.tf | 37 --- .../terraform/staging/variables.tf | 4 - .../tests/app/test_cloudfoundry_config.py | 82 ------ 106 files changed, 3117 deletions(-) delete mode 100644 notifications-admin/.profile delete mode 100644 notifications-admin/app/cloudfoundry_config.py delete mode 100644 notifications-admin/deploy-config/demo.yml delete mode 100644 notifications-admin/deploy-config/egress_proxy/notify-admin-demo.allow.acl delete mode 100644 notifications-admin/deploy-config/egress_proxy/notify-admin-demo.deny.acl delete mode 100644 notifications-admin/deploy-config/egress_proxy/notify-admin-demo.deploy.acl delete mode 100644 notifications-admin/deploy-config/egress_proxy/notify-admin-production.allow.acl delete mode 100644 notifications-admin/deploy-config/egress_proxy/notify-admin-production.deny.acl delete mode 100644 notifications-admin/deploy-config/egress_proxy/notify-admin-production.deploy.acl delete mode 100644 notifications-admin/deploy-config/egress_proxy/notify-admin-staging.allow.acl delete mode 100644 notifications-admin/deploy-config/egress_proxy/notify-admin-staging.deny.acl delete mode 100644 notifications-admin/deploy-config/egress_proxy/notify-admin-staging.deploy.acl delete mode 100644 notifications-admin/deploy-config/production.yml delete mode 100644 notifications-admin/deploy-config/sandbox.yml delete mode 100644 notifications-admin/deploy-config/staging.yml delete mode 100644 notifications-admin/manifest.yml delete mode 100755 notifications-admin/print_vcap.sh delete mode 100644 notifications-admin/terraform/README.md delete mode 100755 notifications-admin/terraform/bootstrap/import.sh delete mode 100644 notifications-admin/terraform/bootstrap/main.tf delete mode 100644 notifications-admin/terraform/bootstrap/providers.tf delete mode 100755 notifications-admin/terraform/bootstrap/run.sh delete mode 100755 notifications-admin/terraform/bootstrap/teardown_creds.sh delete mode 100644 notifications-admin/terraform/bootstrap/variables.tf delete mode 100755 notifications-admin/terraform/create_service_account.sh delete mode 100644 notifications-admin/terraform/demo/main.tf delete mode 100644 notifications-admin/terraform/demo/providers.tf delete mode 100644 notifications-admin/terraform/demo/variables.tf delete mode 100755 notifications-admin/terraform/destroy_service_account.sh delete mode 100644 notifications-admin/terraform/development/main.tf delete mode 100644 notifications-admin/terraform/development/providers.tf delete mode 100755 notifications-admin/terraform/development/reset.sh delete mode 100755 notifications-admin/terraform/development/run.sh delete mode 100644 notifications-admin/terraform/development/variables.tf delete mode 100644 notifications-admin/terraform/production/main.tf delete mode 100644 notifications-admin/terraform/production/providers.tf delete mode 100644 notifications-admin/terraform/production/variables.tf delete mode 100644 notifications-admin/terraform/sandbox/main.tf delete mode 100644 notifications-admin/terraform/sandbox/providers.tf delete mode 100644 notifications-admin/terraform/sandbox/variables.tf delete mode 100755 notifications-admin/terraform/set_space_egress.sh delete mode 100644 notifications-admin/terraform/shared/container_networking/main.tf delete mode 100644 notifications-admin/terraform/shared/container_networking/providers.tf delete mode 100644 notifications-admin/terraform/shared/container_networking/variables.tf delete mode 100644 notifications-admin/terraform/staging/main.tf delete mode 100644 notifications-admin/terraform/staging/providers.tf delete mode 100644 notifications-admin/terraform/staging/variables.tf delete mode 100644 notifications-admin/tests/app/test_cloudfoundry_config.py delete mode 100644 notifications-api/.profile delete mode 100644 notifications-api/app/cloudfoundry_config.py delete mode 100644 notifications-api/deploy-config/demo.yml delete mode 100644 notifications-api/deploy-config/egress_proxy/notify-api-demo.allow.acl delete mode 100644 notifications-api/deploy-config/egress_proxy/notify-api-demo.deny.acl delete mode 100644 notifications-api/deploy-config/egress_proxy/notify-api-demo.deploy.acl delete mode 100644 notifications-api/deploy-config/egress_proxy/notify-api-production.allow.acl delete mode 100644 notifications-api/deploy-config/egress_proxy/notify-api-production.deny.acl delete mode 100644 notifications-api/deploy-config/egress_proxy/notify-api-production.deploy.acl delete mode 100644 notifications-api/deploy-config/egress_proxy/notify-api-staging.allow.acl delete mode 100644 notifications-api/deploy-config/egress_proxy/notify-api-staging.deny.acl delete mode 100644 notifications-api/deploy-config/egress_proxy/notify-api-staging.deploy.acl delete mode 100644 notifications-api/deploy-config/production.yml delete mode 100644 notifications-api/deploy-config/sandbox.yml delete mode 100644 notifications-api/deploy-config/staging.yml delete mode 100644 notifications-api/manifest.yml delete mode 100644 notifications-api/paas-failwhale/README.md delete mode 100644 notifications-api/paas-failwhale/manifest.yml delete mode 100644 notifications-api/paas-failwhale/nginx.conf delete mode 100755 notifications-api/scripts/deploy_to_sandbox.sh delete mode 100644 notifications-api/terraform/README.md delete mode 100755 notifications-api/terraform/bootstrap/import.sh delete mode 100644 notifications-api/terraform/bootstrap/main.tf delete mode 100644 notifications-api/terraform/bootstrap/providers.tf delete mode 100755 notifications-api/terraform/bootstrap/run.sh delete mode 100755 notifications-api/terraform/bootstrap/teardown_creds.sh delete mode 100644 notifications-api/terraform/bootstrap/variables.tf delete mode 100755 notifications-api/terraform/create_service_account.sh delete mode 100644 notifications-api/terraform/demo/main.tf delete mode 100644 notifications-api/terraform/demo/providers.tf delete mode 100644 notifications-api/terraform/demo/variables.tf delete mode 100755 notifications-api/terraform/destroy_service_account.sh delete mode 100644 notifications-api/terraform/development/main.tf delete mode 100644 notifications-api/terraform/development/providers.tf delete mode 100755 notifications-api/terraform/development/reset.sh delete mode 100755 notifications-api/terraform/development/run.sh delete mode 100644 notifications-api/terraform/development/variables.tf delete mode 100644 notifications-api/terraform/ops/cloudgov_user_report.py delete mode 100644 notifications-api/terraform/production/main.tf delete mode 100644 notifications-api/terraform/production/providers.tf delete mode 100644 notifications-api/terraform/production/variables.tf delete mode 100644 notifications-api/terraform/sandbox/main.tf delete mode 100644 notifications-api/terraform/sandbox/providers.tf delete mode 100644 notifications-api/terraform/sandbox/variables.tf delete mode 100755 notifications-api/terraform/set_space_egress.sh delete mode 100644 notifications-api/terraform/shared/egress_space/main.tf delete mode 100644 notifications-api/terraform/shared/egress_space/providers.tf delete mode 100644 notifications-api/terraform/shared/egress_space/variables.tf delete mode 100644 notifications-api/terraform/shared/ses/main.tf delete mode 100644 notifications-api/terraform/shared/ses/providers.tf delete mode 100644 notifications-api/terraform/shared/ses/variables.tf delete mode 100644 notifications-api/terraform/shared/sns/main.tf delete mode 100644 notifications-api/terraform/shared/sns/providers.tf delete mode 100644 notifications-api/terraform/shared/sns/variables.tf delete mode 100644 notifications-api/terraform/staging/main.tf delete mode 100644 notifications-api/terraform/staging/providers.tf delete mode 100644 notifications-api/terraform/staging/variables.tf delete mode 100644 notifications-api/tests/app/test_cloudfoundry_config.py diff --git a/notifications-admin/.profile b/notifications-admin/.profile deleted file mode 100644 index eddc47e..0000000 --- a/notifications-admin/.profile +++ /dev/null @@ -1,9 +0,0 @@ -## -# Cloud Foundry app initialization script -# https://docs.cloudfoundry.org/devguide/deploy-apps/deploy-app.html#profile -## - -export NEW_RELIC_PROXY_HOST=$egress_proxy -export http_proxy=$egress_proxy -export https_proxy=$egress_proxy -export no_proxy="apps.internal,s3-fips.us-gov-west-1.amazonaws.com" diff --git a/notifications-admin/app/cloudfoundry_config.py b/notifications-admin/app/cloudfoundry_config.py deleted file mode 100644 index 9ab433e..0000000 --- a/notifications-admin/app/cloudfoundry_config.py +++ /dev/null @@ -1,30 +0,0 @@ -import json -import os - - -class CloudfoundryConfig: - def __init__(self): - self.parsed_services = json.loads(os.environ.get("VCAP_SERVICES") or "{}") - buckets = self.parsed_services.get("s3") or [] - self.s3_buckets = {bucket["name"]: bucket["credentials"] for bucket in buckets} - self._empty_bucket_credentials = { - "bucket": "", - "access_key_id": "", - "secret_access_key": "", # nosec B105 - empty default, not a real password - "region": "", - } - - @property - def redis_url(self): - try: - return self.parsed_services["aws-elasticache-redis"][0]["credentials"][ - "uri" - ] - except KeyError: - return os.environ.get("REDIS_URL") - - def s3_credentials(self, service_name): - return self.s3_buckets.get(service_name) or self._empty_bucket_credentials - - -cloud_config = CloudfoundryConfig() diff --git a/notifications-admin/deploy-config/demo.yml b/notifications-admin/deploy-config/demo.yml deleted file mode 100644 index e09776d..0000000 --- a/notifications-admin/deploy-config/demo.yml +++ /dev/null @@ -1,11 +0,0 @@ -env: demo -instances: 1 -memory: 1G -command: newrelic-admin run-program gunicorn -c /home/vcap/app/gunicorn_config.py gunicorn_entry:application -public_admin_route: notify-demo.app.cloud.gov -cloud_dot_gov_route: notify-demo.app.cloud.gov -redis_enabled: 1 -nr_agent_id: '1134302465' -nr_app_id: '1083160688' -API_PUBLIC_URL: https://notify-api-demo.app.cloud.gov -ORGANIZATION_DASHBOARD_ENABLED: False diff --git a/notifications-admin/deploy-config/egress_proxy/notify-admin-demo.allow.acl b/notifications-admin/deploy-config/egress_proxy/notify-admin-demo.allow.acl deleted file mode 100644 index 41bf3a3..0000000 --- a/notifications-admin/deploy-config/egress_proxy/notify-admin-demo.allow.acl +++ /dev/null @@ -1,4 +0,0 @@ -gov-collector.newrelic.com -egress-proxy-notify-admin-demo.apps.internal -idp.int.identitysandbox.gov -secure.login.gov diff --git a/notifications-admin/deploy-config/egress_proxy/notify-admin-demo.deny.acl b/notifications-admin/deploy-config/egress_proxy/notify-admin-demo.deny.acl deleted file mode 100644 index e69de29..0000000 diff --git a/notifications-admin/deploy-config/egress_proxy/notify-admin-demo.deploy.acl b/notifications-admin/deploy-config/egress_proxy/notify-admin-demo.deploy.acl deleted file mode 100644 index a05cd40..0000000 --- a/notifications-admin/deploy-config/egress_proxy/notify-admin-demo.deploy.acl +++ /dev/null @@ -1,3 +0,0 @@ -Update this file to force a re-deploy of the egress proxy even when notify-admin-demo..acl haven't changed - -20230412: Redeploy to re-calculate the list of allowed s3 buckets diff --git a/notifications-admin/deploy-config/egress_proxy/notify-admin-production.allow.acl b/notifications-admin/deploy-config/egress_proxy/notify-admin-production.allow.acl deleted file mode 100644 index 8103b09..0000000 --- a/notifications-admin/deploy-config/egress_proxy/notify-admin-production.allow.acl +++ /dev/null @@ -1,4 +0,0 @@ -gov-collector.newrelic.com -egress-proxy-notify-admin-production.apps.internal -idp.int.identitysandbox.gov -secure.login.gov diff --git a/notifications-admin/deploy-config/egress_proxy/notify-admin-production.deny.acl b/notifications-admin/deploy-config/egress_proxy/notify-admin-production.deny.acl deleted file mode 100644 index e69de29..0000000 diff --git a/notifications-admin/deploy-config/egress_proxy/notify-admin-production.deploy.acl b/notifications-admin/deploy-config/egress_proxy/notify-admin-production.deploy.acl deleted file mode 100644 index ecbb75b..0000000 --- a/notifications-admin/deploy-config/egress_proxy/notify-admin-production.deploy.acl +++ /dev/null @@ -1 +0,0 @@ -Update this file to force a re-deploy of the egress proxy even when notify-admin-production..acl haven't changed diff --git a/notifications-admin/deploy-config/egress_proxy/notify-admin-staging.allow.acl b/notifications-admin/deploy-config/egress_proxy/notify-admin-staging.allow.acl deleted file mode 100644 index 65c7931..0000000 --- a/notifications-admin/deploy-config/egress_proxy/notify-admin-staging.allow.acl +++ /dev/null @@ -1,4 +0,0 @@ -gov-collector.newrelic.com -egress-proxy-notify-admin-staging.apps.internal -idp.int.identitysandbox.gov -secure.login.gov diff --git a/notifications-admin/deploy-config/egress_proxy/notify-admin-staging.deny.acl b/notifications-admin/deploy-config/egress_proxy/notify-admin-staging.deny.acl deleted file mode 100644 index e69de29..0000000 diff --git a/notifications-admin/deploy-config/egress_proxy/notify-admin-staging.deploy.acl b/notifications-admin/deploy-config/egress_proxy/notify-admin-staging.deploy.acl deleted file mode 100644 index ee2d68a..0000000 --- a/notifications-admin/deploy-config/egress_proxy/notify-admin-staging.deploy.acl +++ /dev/null @@ -1,3 +0,0 @@ -Update this file to force a re-deploy of the egress proxy even when notify-admin-staging..acl haven't changed - -20230412: Redeploy to re-calculate the list of allowed s3 buckets diff --git a/notifications-admin/deploy-config/production.yml b/notifications-admin/deploy-config/production.yml deleted file mode 100644 index 836f457..0000000 --- a/notifications-admin/deploy-config/production.yml +++ /dev/null @@ -1,11 +0,0 @@ -env: production -instances: 2 -memory: 2G -command: newrelic-admin run-program gunicorn -c /home/vcap/app/gunicorn_config.py gunicorn_entry:application -public_admin_route: beta.notify.gov -cloud_dot_gov_route: notify.app.cloud.gov -redis_enabled: 1 -nr_agent_id: '1050708682' -nr_app_id: '1050708682' -API_PUBLIC_URL: https://notify-api-production.app.cloud.gov -ORGANIZATION_DASHBOARD_ENABLED: false diff --git a/notifications-admin/deploy-config/sandbox.yml b/notifications-admin/deploy-config/sandbox.yml deleted file mode 100644 index 7a67ac0..0000000 --- a/notifications-admin/deploy-config/sandbox.yml +++ /dev/null @@ -1,15 +0,0 @@ -env: sandbox -instances: 1 -memory: 1G -command: gunicorn -c /home/vcap/app/gunicorn_config.py gunicorn_entry:application -public_admin_route: notify-sandbox.app.cloud.gov -cloud_dot_gov_route: notify-sandbox.app.cloud.gov -redis_enabled: 1 -ADMIN_CLIENT_USERNAME: notify-admin -ADMIN_CLIENT_SECRET: sandbox-notify-secret-key -DANGEROUS_SALT: sandbox-notify-salt -SECRET_KEY: sandbox-notify-secret-key -nr_agent_id: '' -nr_app_id: '' -NR_BROWSER_KEY: '' -ORGANIZATION_DASHBOARD_ENABLED: False diff --git a/notifications-admin/deploy-config/staging.yml b/notifications-admin/deploy-config/staging.yml deleted file mode 100644 index e234942..0000000 --- a/notifications-admin/deploy-config/staging.yml +++ /dev/null @@ -1,11 +0,0 @@ -env: staging -instances: 1 -memory: 1G -command: newrelic-admin run-program gunicorn -c /home/vcap/app/gunicorn_config.py gunicorn_entry:application -public_admin_route: notify-staging.app.cloud.gov -cloud_dot_gov_route: notify-staging.app.cloud.gov -redis_enabled: 1 -nr_agent_id: '1134291385' -nr_app_id: '1031640326' -API_PUBLIC_URL: https://notify-api-staging.app.cloud.gov -ORGANIZATION_DASHBOARD_ENABLED: true diff --git a/notifications-admin/manifest.yml b/notifications-admin/manifest.yml deleted file mode 100644 index 510af34..0000000 --- a/notifications-admin/manifest.yml +++ /dev/null @@ -1,67 +0,0 @@ ---- -applications: - - name: notify-admin-((env)) - buildpack: python_buildpack - stack: cflinuxfs4 - instances: ((instances)) - memory: ((memory)) - command: ((command)) - health-check-type: port - health-check-invocation-timeout: 10 - routes: - - route: ((cloud_dot_gov_route)) - - services: - - notify-admin-redis-v70-((env)) - - notify-api-csv-upload-bucket-((env)) - - notify-admin-logo-upload-bucket-((env)) - - env: - NOTIFY_ENVIRONMENT: ((env)) - NOTIFY_APP_NAME: admin - NOTIFY_LOG_LEVEL: INFO - FLASK_APP: application.py - FLASK_DEBUG: 'false' - NEW_RELIC_CONFIG_FILE: newrelic.ini - NEW_RELIC_ENVIRONMENT: ((env)) - - NR_ACCOUNT_ID: 3389907 - NR_TRUST_KEY: 562946 - NR_AGENT_ID: ((nr_agent_id)) - NR_APP_ID: ((nr_app_id)) - NR_BROWSER_KEY: ((NR_BROWSER_KEY)) - - REDIS_ENABLED: ((redis_enabled)) - ADMIN_BASE_URL: https://((public_admin_route)) - API_HOST_NAME: https://notify-api-((env)).apps.internal:61443 - - # Credentials variables - ADMIN_CLIENT_SECRET: ((ADMIN_CLIENT_SECRET)) - ADMIN_CLIENT_USERNAME: ((ADMIN_CLIENT_USERNAME)) - DANGEROUS_SALT: ((DANGEROUS_SALT)) - SECRET_KEY: ((SECRET_KEY)) - NEW_RELIC_LICENSE_KEY: ((NEW_RELIC_LICENSE_KEY)) - - NOTIFY_BILLING_DETAILS: '[]' - - SSL_CERT_FILE: '/etc/ssl/certs/ca-certificates.crt' - REQUESTS_CA_BUNDLE: '/etc/ssl/certs/ca-certificates.crt' - NEW_RELIC_CA_BUNDLE_PATH: '/etc/ssl/certs/ca-certificates.crt' - - COMMIT_HASH: ((COMMIT_HASH)) - - # login.gov variables - - LOGIN_PEM: ((LOGIN_PEM)) - LOGIN_DOT_GOV_CLIENT_ID: ((LOGIN_DOT_GOV_CLIENT_ID)) - LOGIN_DOT_GOV_USER_INFO_URL: ((LOGIN_DOT_GOV_USER_INFO_URL)) - LOGIN_DOT_GOV_ACCESS_TOKEN_URL: ((LOGIN_DOT_GOV_ACCESS_TOKEN_URL)) - LOGIN_DOT_GOV_LOGOUT_URL: ((LOGIN_DOT_GOV_LOGOUT_URL)) - LOGIN_DOT_GOV_BASE_LOGOUT_URL: ((LOGIN_DOT_GOV_BASE_LOGOUT_URL)) - LOGIN_DOT_GOV_SIGNOUT_REDIRECT: ((LOGIN_DOT_GOV_SIGNOUT_REDIRECT)) - LOGIN_DOT_GOV_INITIAL_SIGNIN_URL: ((LOGIN_DOT_GOV_INITIAL_SIGNIN_URL)) - LOGIN_DOT_GOV_CERTS_URL: ((LOGIN_DOT_GOV_CERTS_URL)) - - API_PUBLIC_URL: ((API_PUBLIC_URL)) - - ORGANIZATION_DASHBOARD_ENABLED: ((ORGANIZATION_DASHBOARD_ENABLED)) diff --git a/notifications-admin/print_vcap.sh b/notifications-admin/print_vcap.sh deleted file mode 100755 index c814145..0000000 --- a/notifications-admin/print_vcap.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -STAGING_APP_NAME="notify-admin-staging" - -# Fetch the environment variables of the staging app -env_var_value=$(cf env "$STAGING_APP_NAME" | awk '/'"VCAP_SERVICES"':/,/^}/') - - -# Check if the environment variable was found" -if [ -z "$env_var_value" ]; then - echo "Environment variable VCAP_SERVICES not found in the staging environment" -else - env_var_json=$(echo "$env_var_value" | sed '1s/^[^:]*: //' | tr -d '\n') - stringified_value=$(python3 -c "import json, sys; print(json.dumps(json.loads(sys.stdin.read())))" <<< "$env_var_json") - echo "VCAP_SERVICES:" - echo "$stringified_value" -fi diff --git a/notifications-admin/terraform/README.md b/notifications-admin/terraform/README.md deleted file mode 100644 index 74f1815..0000000 --- a/notifications-admin/terraform/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Terraform - -The instructions for how to use the Terraform in this repo are the same [as those in the API repo](https://github.com/GSA/notifications-api/tree/main/terraform#terraform). diff --git a/notifications-admin/terraform/bootstrap/import.sh b/notifications-admin/terraform/bootstrap/import.sh deleted file mode 100755 index 9140711..0000000 --- a/notifications-admin/terraform/bootstrap/import.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -read -p "Are you sure you want to import terraform state (y/n)? " verify - -if [[ $verify == "y" ]]; then - echo "Importing bootstrap state" - ./run.sh import module.s3.cloudfoundry_service_instance.bucket 6b759c13-6253-4a64-9bda-dd1f620185b0 - ./run.sh import cloudfoundry_service_key.bucket_creds a8e40295-68b7-42ba-8955-d82ba262e948 - ./run.sh plan -else - echo "Not importing bootstrap state" -fi diff --git a/notifications-admin/terraform/bootstrap/main.tf b/notifications-admin/terraform/bootstrap/main.tf deleted file mode 100644 index a518530..0000000 --- a/notifications-admin/terraform/bootstrap/main.tf +++ /dev/null @@ -1,20 +0,0 @@ -locals { - s3_service_name = "notify-terraform-state" -} - -module "s3" { - source = "github.com/GSA-TTS/terraform-cloudgov//s3?ref=v1.0.0" - - cf_org_name = "gsa-tts-benefits-studio" - cf_space_name = "notify-management" - name = local.s3_service_name -} - -resource "cloudfoundry_service_key" "bucket_creds" { - name = "${local.s3_service_name}-access" - service_instance = module.s3.bucket_id - - lifecycle { - prevent_destroy = true - } -} diff --git a/notifications-admin/terraform/bootstrap/providers.tf b/notifications-admin/terraform/bootstrap/providers.tf deleted file mode 100644 index 7b9ce5c..0000000 --- a/notifications-admin/terraform/bootstrap/providers.tf +++ /dev/null @@ -1,16 +0,0 @@ -terraform { - required_version = "~> 1.7" - required_providers { - cloudfoundry = { - source = "cloudfoundry-community/cloudfoundry" - version = "0.53.1" - } - } -} - -provider "cloudfoundry" { - api_url = "https://api.fr.cloud.gov" - user = var.cf_user - password = var.cf_password - app_logs_max = 30 -} diff --git a/notifications-admin/terraform/bootstrap/run.sh b/notifications-admin/terraform/bootstrap/run.sh deleted file mode 100755 index 1ac3954..0000000 --- a/notifications-admin/terraform/bootstrap/run.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -if [[ ! -f "secrets.auto.tfvars" ]]; then - ../create_service_account.sh -s notify-management -u config-bootstrap-deployer > secrets.auto.tfvars -fi - -if [[ $# -gt 0 ]]; then - echo "Running terraform $@" - terraform $@ -else - echo "Not running terraform" -fi diff --git a/notifications-admin/terraform/bootstrap/teardown_creds.sh b/notifications-admin/terraform/bootstrap/teardown_creds.sh deleted file mode 100755 index 77207a6..0000000 --- a/notifications-admin/terraform/bootstrap/teardown_creds.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -../destroy_service_account.sh -s notify-management -u config-bootstrap-deployer - -rm secrets.auto.tfvars diff --git a/notifications-admin/terraform/bootstrap/variables.tf b/notifications-admin/terraform/bootstrap/variables.tf deleted file mode 100644 index a24f2f3..0000000 --- a/notifications-admin/terraform/bootstrap/variables.tf +++ /dev/null @@ -1,4 +0,0 @@ -variable "cf_password" { - sensitive = true -} -variable "cf_user" {} diff --git a/notifications-admin/terraform/create_service_account.sh b/notifications-admin/terraform/create_service_account.sh deleted file mode 100755 index 44c5e2a..0000000 --- a/notifications-admin/terraform/create_service_account.sh +++ /dev/null @@ -1,92 +0,0 @@ -#!/usr/bin/env bash - -org="gsa-tts-benefits-studio" - -usage=" -$0: Create a Service User Account for a given space - -Usage: - $0 -h - $0 -s -u [-r ] [-o ] [-m] - -Options: --h: show help and exit --s : configure the space to act on. Required --u : set the service user name. Required --r : set the service user's role to either space-deployer or space-auditor. Default: space-deployer --m: If provided, make the service user an OrgManager --o : configure the organization to act on. Default: $org - -Notes: -* OrgManager is required for terraform to create -egress spaces -* Requires cf-cli@8 & jq -" - -cf_version=`cf --version | cut -d " " -f 3` -if [[ $cf_version != 8.* ]]; then - echo "$usage" >&2 - exit 1 -fi -command -v jq >/dev/null || { echo "$usage" >&2; exit 1; } - -set -e -set -o pipefail - -space="" -service="" -role="space-deployer" -org_manager="false" - -while getopts ":hms:u:r:o:" opt; do - case "$opt" in - s) - space=${OPTARG} - ;; - u) - service=${OPTARG} - ;; - r) - role=${OPTARG} - ;; - o) - org=${OPTARG} - ;; - m) - org_manager="true" - ;; - h) - echo "$usage" - exit 0 - ;; - esac -done - -if [[ $space = "" || $service = "" ]]; then - echo "$usage" >&2 - exit 1 -fi - -cf target -o $org -s $space >&2 - -# create user account service -cf create-service cloud-gov-service-account $role $service >&2 - -# create service key -cf create-service-key $service service-account-key >&2 - -# output service key to stdout in secrets.auto.tfvars format -creds=`cf service-key $service service-account-key | tail -n +2 | jq '.credentials'` -username=`echo $creds | jq -r '.username'` -password=`echo $creds | jq -r '.password'` - -if [[ $org_manager = "true" ]]; then - cf set-org-role $username $org OrgManager >&2 -fi - -cat << EOF -# generated with $0 -s $space -u $service -r $role -o $org -# revoke with $(dirname $0)/destroy_service_account.sh -s $space -u $service -o $org - -cf_user = "$username" -cf_password = "$password" -EOF diff --git a/notifications-admin/terraform/demo/main.tf b/notifications-admin/terraform/demo/main.tf deleted file mode 100644 index d0b8ef9..0000000 --- a/notifications-admin/terraform/demo/main.tf +++ /dev/null @@ -1,44 +0,0 @@ -locals { - cf_org_name = "gsa-tts-benefits-studio" - cf_space_name = "notify-demo" - env = "demo" - app_name = "notify-admin" -} - -resource "null_resource" "prevent_destroy" { - - lifecycle { - prevent_destroy = true # never destroy demo - } -} - -module "redis-v70" { - source = "github.com/GSA-TTS/terraform-cloudgov//redis?ref=v1.0.0" - - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-redis-v70-${local.env}" - redis_plan_name = "redis-dev" - json_params = jsonencode( - { - "engineVersion" : "7.0", - } - ) -} - -module "logo_upload_bucket" { - source = "github.com/GSA-TTS/terraform-cloudgov//s3?ref=v1.0.0" - - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-logo-upload-bucket-${local.env}" -} - -module "api_network_route" { - source = "../shared/container_networking" - - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - source_app_name = "${local.app_name}-${local.env}" - destination_app_name = "notify-api-${local.env}" -} diff --git a/notifications-admin/terraform/demo/providers.tf b/notifications-admin/terraform/demo/providers.tf deleted file mode 100644 index a35684a..0000000 --- a/notifications-admin/terraform/demo/providers.tf +++ /dev/null @@ -1,37 +0,0 @@ -terraform { - required_version = "~> 1.7" - required_providers { - cloudfoundry = { - source = "cloudfoundry/cloudfoundry" - version = "1.9.0" - } - cfcommunity = { - source = "cloudfoundry-community/cloudfoundry" - version = "0.53.1" - } - } - - backend "s3" { - bucket = "cg-6b759c13-6253-4a64-9bda-dd1f620185b0" - key = "admin.tfstate.demo" - encrypt = "true" - region = "us-gov-west-1" - use_lockfile = "true" - } -} - -# Official provider (should be default but aliased for now) -provider "cloudfoundry" { - alias = "official" - api_url = "https://api.fr.cloud.gov" - user = var.cf_user - password = var.cf_password -} - -# Community provider (should be aliased but default for now) -provider "cfcommunity" { - api_url = "https://api.fr.cloud.gov" - user = var.cf_user - password = var.cf_password - app_logs_max = 30 -} diff --git a/notifications-admin/terraform/demo/variables.tf b/notifications-admin/terraform/demo/variables.tf deleted file mode 100644 index bd8f741..0000000 --- a/notifications-admin/terraform/demo/variables.tf +++ /dev/null @@ -1,5 +0,0 @@ -variable "cf_password" { - type = string - sensitive = true -} -variable "cf_user" {} diff --git a/notifications-admin/terraform/destroy_service_account.sh b/notifications-admin/terraform/destroy_service_account.sh deleted file mode 100755 index 9a4c250..0000000 --- a/notifications-admin/terraform/destroy_service_account.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env bash - -org="gsa-tts-benefits-studio" - -usage=" -$0: Destroy a Service User Account in a given space - -Usage: - $0 -h - $0 -s -u [-o ] - -Options: --h: show help and exit --s : configure the space to act on. Required --u : configure the service user name to destroy. Required --o : configure the organization to act on. Default: $org -" - -set -e - -space="" -service="" - -while getopts ":hs:u:o:" opt; do - case "$opt" in - s) - space=${OPTARG} - ;; - u) - service=${OPTARG} - ;; - o) - org=${OPTARG} - ;; - h) - echo "$usage" - exit 0 - ;; - esac -done - -if [[ $space = "" || $service = "" ]]; then - echo "$usage" - exit 1 -fi - -cf target -o $org -s $space - -# destroy service key -cf delete-service-key $service service-account-key -f - -# destroy service -cf delete-service $service -f diff --git a/notifications-admin/terraform/development/main.tf b/notifications-admin/terraform/development/main.tf deleted file mode 100644 index 88e1113..0000000 --- a/notifications-admin/terraform/development/main.tf +++ /dev/null @@ -1,58 +0,0 @@ -locals { - cf_org_name = "gsa-tts-benefits-studio" - cf_space_name = "notify-local-dev" - key_name = "${var.username}-admin-dev-key" -} - -data "cloudfoundry_space" "dev" { - org_name = local.cf_org_name - name = local.cf_space_name -} - -module "logo_upload_bucket" { - source = "github.com/GSA-TTS/terraform-cloudgov//s3?ref=v1.0.0" - - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${var.username}-logo-upload-bucket" -} -resource "cloudfoundry_service_key" "logo_key" { - name = local.key_name - service_instance = module.logo_upload_bucket.bucket_id -} - -data "cloudfoundry_service_instance" "csv_bucket" { - name_or_id = "${var.username}-csv-upload-bucket" - space = data.cloudfoundry_space.dev.id -} -resource "cloudfoundry_service_key" "csv_key" { - name = local.key_name - service_instance = data.cloudfoundry_service_instance.csv_bucket.id -} - -locals { - credentials = <> .env" - } -} diff --git a/notifications-admin/terraform/development/providers.tf b/notifications-admin/terraform/development/providers.tf deleted file mode 100644 index 7b9ce5c..0000000 --- a/notifications-admin/terraform/development/providers.tf +++ /dev/null @@ -1,16 +0,0 @@ -terraform { - required_version = "~> 1.7" - required_providers { - cloudfoundry = { - source = "cloudfoundry-community/cloudfoundry" - version = "0.53.1" - } - } -} - -provider "cloudfoundry" { - api_url = "https://api.fr.cloud.gov" - user = var.cf_user - password = var.cf_password - app_logs_max = 30 -} diff --git a/notifications-admin/terraform/development/reset.sh b/notifications-admin/terraform/development/reset.sh deleted file mode 100755 index 57c2f4d..0000000 --- a/notifications-admin/terraform/development/reset.sh +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env bash - -username=`whoami` -org="gsa-tts-benefits-studio" - -usage=" -$0: Reset terraform state so run.sh can be run again or for a new username - -Usage: - $0 -h - $0 [-u ] - -Options: --h: show help and exit --u : your username. Default: $username - -Notes: -* Requires cf-cli@8 -" - -while getopts ":hu:" opt; do - case "$opt" in - u) - username=${OPTARG} - ;; - h) - echo "$usage" - exit 0 - ;; - esac -done - -read -p "Are you sure you want to import terraform state and remove existing service keys for $username (y/n)? " verify - -if [[ $verify != "y" ]]; then - exit 0 -fi - -# ensure we're in the correct directory -cd $(dirname $0) - -service_account="$username-terraform" - -if [[ ! -s "secrets.auto.tfvars" ]]; then - # create user in notify-local-dev space to create s3 buckets - ../create_service_account.sh -s notify-local-dev -u $service_account > secrets.auto.tfvars -fi - -echo "Importing terraform state for $username" -terraform init -upgrade - -key_name=$username-admin-dev-key - -cf t -s notify-local-dev -terraform import -var "username=$username" module.logo_upload_bucket.cloudfoundry_service_instance.bucket $(cf service --guid $username-csv-upload-bucket) -cf delete-service-key -f $username-logo-upload-bucket $key_name -cf delete-service-key -f $username-csv-upload-bucket $key_name - -./run.sh -u $username diff --git a/notifications-admin/terraform/development/run.sh b/notifications-admin/terraform/development/run.sh deleted file mode 100755 index f8739d9..0000000 --- a/notifications-admin/terraform/development/run.sh +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env bash - -username=`whoami` -org="gsa-tts-benefits-studio" - -usage=" -$0: Create development infrastructure - -Usage: - $0 -h - $0 [-u ] [-k] [-d] - -Options: --h: show help and exit --u : your username. Default: $username --k: keep service user. Default is to remove them after run --d: Destroy development resources. Default is to create them - -Notes: -* Requires cf-cli@8 -* Requires terraform/development to be run on API app first, with the same [-u ] -" - -action="apply" -creds="remove" - -while getopts ":hkdu:" opt; do - case "$opt" in - u) - username=${OPTARG} - ;; - k) - creds="keep" - ;; - d) - action="destroy" - ;; - h) - echo "$usage" - exit 0 - ;; - esac -done - -set -e - -service_account="$username-terraform" - -# ensure we're in the correct directory -cd $(dirname $0) - -if [[ ! -s "secrets.auto.tfvars" ]]; then - # create user in notify-local-dev space to create s3 buckets - ../create_service_account.sh -s notify-local-dev -u $service_account > secrets.auto.tfvars -fi - -if [[ ! -f "../../.env" ]]; then - cp ../../sample.env ../../.env -fi - -set +e - -terraform init -terraform $action -var="username=$username" - -set -e - -if [[ $creds = "remove" ]]; then - ../destroy_service_account.sh -s notify-local-dev -u $service_account - rm secrets.auto.tfvars -fi - -exit 0 diff --git a/notifications-admin/terraform/development/variables.tf b/notifications-admin/terraform/development/variables.tf deleted file mode 100644 index 2f51379..0000000 --- a/notifications-admin/terraform/development/variables.tf +++ /dev/null @@ -1,5 +0,0 @@ -variable "cf_password" { - sensitive = true -} -variable "cf_user" {} -variable "username" {} diff --git a/notifications-admin/terraform/production/main.tf b/notifications-admin/terraform/production/main.tf deleted file mode 100644 index a0d12fd..0000000 --- a/notifications-admin/terraform/production/main.tf +++ /dev/null @@ -1,68 +0,0 @@ -locals { - cf_org_name = "gsa-tts-benefits-studio" - cf_space_name = "notify-production" - env = "production" - app_name = "notify-admin" -} - -resource "null_resource" "prevent_destroy" { - - lifecycle { - prevent_destroy = true # never destroy production - } -} - -module "redis-v70" { - source = "github.com/GSA-TTS/terraform-cloudgov//redis?ref=v1.0.0" - - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-redis-v70-${local.env}" - redis_plan_name = "redis-3node-large" - json_params = jsonencode( - { - "engineVersion" : "7.0", - } - ) -} - -module "logo_upload_bucket" { - source = "github.com/GSA-TTS/terraform-cloudgov//s3?ref=v1.0.0" - - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-logo-upload-bucket-${local.env}" -} - -module "api_network_route" { - source = "../shared/container_networking" - - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - source_app_name = "${local.app_name}-${local.env}" - destination_app_name = "notify-api-${local.env}" -} - -# ########################################################################## -# This governs the name of our main website. Because domain names are unique, -# it only lives here in this one location in production. Resulting problem: -# it is hard to test. Create a temporary, similar code block (with a different -# subdomain) in Sandbox, test your changes there, and bring them here. -# -# Dependencies: -# 1) an app named notify-admin-production in Cloud.gov -# 2) this route, manually created by an OrgManager: -# `cf create-domain gsa-tts-benefits-studio beta.notify.gov` -# 3) the acme-challenge CNAME record -# https://cloud.gov/docs/services/external-domain-service/#how-to-create-an-instance-of-this-service -########################################################################### -module "domain" { - source = "github.com/GSA-TTS/terraform-cloudgov//domain?ref=v1.0.0" - - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - app_name_or_id = "${local.app_name}-${local.env}" - name = "${local.app_name}-domain-${local.env}" - cdn_plan_name = "domain" - domain_name = "beta.notify.gov" -} diff --git a/notifications-admin/terraform/production/providers.tf b/notifications-admin/terraform/production/providers.tf deleted file mode 100644 index 6f08347..0000000 --- a/notifications-admin/terraform/production/providers.tf +++ /dev/null @@ -1,37 +0,0 @@ -terraform { - required_version = "~> 1.7" - required_providers { - cloudfoundry = { - source = "cloudfoundry/cloudfoundry" - version = "1.9.0" - } - cfcommunity = { - source = "cloudfoundry-community/cloudfoundry" - version = "0.53.1" - } - } - - backend "s3" { - bucket = "cg-6b759c13-6253-4a64-9bda-dd1f620185b0" - key = "admin.tfstate.prod" - encrypt = "true" - region = "us-gov-west-1" - use_lockfile = "true" - } -} - -# Official provider (should be default but aliased for now) -provider "cloudfoundry" { - alias = "official" - api_url = "https://api.fr.cloud.gov" - user = var.cf_user - password = var.cf_password -} - -# Community provider (should be aliased but default for now) -provider "cfcommunity" { - api_url = "https://api.fr.cloud.gov" - user = var.cf_user - password = var.cf_password - app_logs_max = 30 -} diff --git a/notifications-admin/terraform/production/variables.tf b/notifications-admin/terraform/production/variables.tf deleted file mode 100644 index bd8f741..0000000 --- a/notifications-admin/terraform/production/variables.tf +++ /dev/null @@ -1,5 +0,0 @@ -variable "cf_password" { - type = string - sensitive = true -} -variable "cf_user" {} diff --git a/notifications-admin/terraform/sandbox/main.tf b/notifications-admin/terraform/sandbox/main.tf deleted file mode 100644 index 76495b4..0000000 --- a/notifications-admin/terraform/sandbox/main.tf +++ /dev/null @@ -1,44 +0,0 @@ -locals { - cf_org_name = "gsa-tts-benefits-studio" - cf_space_name = "notify-sandbox" - env = "sandbox" - app_name = "notify-admin" -} - -resource "null_resource" "prevent_destroy" { - - lifecycle { - prevent_destroy = false # destroying sandbox is allowed - } -} - -module "redis-v70" { - source = "github.com/GSA-TTS/terraform-cloudgov//redis?ref=v1.0.0" - - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-redis-v70-${local.env}" - redis_plan_name = "redis-dev" - json_params = jsonencode( - { - "engineVersion" : "7.0", - } - ) -} - -module "logo_upload_bucket" { - source = "github.com/GSA-TTS/terraform-cloudgov//s3?ref=v1.0.0" - - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-logo-upload-bucket-${local.env}" -} - -module "api_network_route" { # API and Admin apps must both exist in Cloud - source = "../shared/container_networking" - - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - source_app_name = "${local.app_name}-${local.env}" - destination_app_name = "notify-api-${local.env}" -} diff --git a/notifications-admin/terraform/sandbox/providers.tf b/notifications-admin/terraform/sandbox/providers.tf deleted file mode 100644 index 247c47b..0000000 --- a/notifications-admin/terraform/sandbox/providers.tf +++ /dev/null @@ -1,37 +0,0 @@ -terraform { - required_version = "~> 1.7" - required_providers { - cloudfoundry = { - source = "cloudfoundry/cloudfoundry" - version = "1.9.0" - } - cfcommunity = { - source = "cloudfoundry-community/cloudfoundry" - version = "0.53.1" - } - } - - backend "s3" { - bucket = "cg-6b759c13-6253-4a64-9bda-dd1f620185b0" - key = "admin.tfstate.sandbox" - encrypt = "true" - region = "us-gov-west-1" - use_lockfile = "true" - } -} - -# Official provider (should be default but aliased for now) -provider "cloudfoundry" { - alias = "official" - api_url = "https://api.fr.cloud.gov" - user = var.cf_user - password = var.cf_password -} - -# Community provider (should be aliased but default for now) -provider "cfcommunity" { - api_url = "https://api.fr.cloud.gov" - user = var.cf_user - password = var.cf_password - app_logs_max = 30 -} diff --git a/notifications-admin/terraform/sandbox/variables.tf b/notifications-admin/terraform/sandbox/variables.tf deleted file mode 100644 index bd8f741..0000000 --- a/notifications-admin/terraform/sandbox/variables.tf +++ /dev/null @@ -1,5 +0,0 @@ -variable "cf_password" { - type = string - sensitive = true -} -variable "cf_user" {} diff --git a/notifications-admin/terraform/set_space_egress.sh b/notifications-admin/terraform/set_space_egress.sh deleted file mode 100755 index ee2b6b3..0000000 --- a/notifications-admin/terraform/set_space_egress.sh +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env bash - -org="gsa-tts-benefits-studio" - -usage=" -$0: Set egress rules for given space - -Usage: - $0 -h - $0 -s [-o ] [-p] [-t] - -Options: --h: show help and exit --s : configure the space to act on. Required --o : configure the organization to act on. Default: $org --p: Add the public egress rules --t: Add the trusted egress rules - -Notes: -* If -p or -t are not passed, the related security groups will be removed, if they were present -" - -set -e - -space="" -public=false -trusted=false - -while getopts ":hs:o:pt" opt; do - case "$opt" in - s) - space=${OPTARG} - ;; - o) - org=${OPTARG} - ;; - p) - public=true - ;; - t) - trusted=true - ;; - h) - echo "$usage" - exit 0 - ;; - esac -done - -if [[ $space = "" ]]; then - echo "$usage" - exit 1 -fi - -if [[ $public = true ]]; then - cf bind-security-group public_networks_egress $org --space $space -else - cf unbind-security-group public_networks_egress $org $space -fi - -if [[ $trusted = true ]]; then - cf bind-security-group trusted_local_networks_egress $org --space $space -else - cf unbind-security-group trusted_local_networks_egress $org $space -fi - -echo "Done" diff --git a/notifications-admin/terraform/shared/container_networking/main.tf b/notifications-admin/terraform/shared/container_networking/main.tf deleted file mode 100644 index da8e6f7..0000000 --- a/notifications-admin/terraform/shared/container_networking/main.tf +++ /dev/null @@ -1,22 +0,0 @@ -data "cloudfoundry_space" "space" { - org_name = var.cf_org_name - name = var.cf_space_name -} - -data "cloudfoundry_app" "source_app" { - name_or_id = var.source_app_name - space = data.cloudfoundry_space.space.id -} - -data "cloudfoundry_app" "destination_app" { - name_or_id = var.destination_app_name - space = data.cloudfoundry_space.space.id -} - -resource "cloudfoundry_network_policy" "internal_route" { - policy { - source_app = data.cloudfoundry_app.source_app.id - destination_app = data.cloudfoundry_app.destination_app.id - port = var.destination_port - } -} diff --git a/notifications-admin/terraform/shared/container_networking/providers.tf b/notifications-admin/terraform/shared/container_networking/providers.tf deleted file mode 100644 index dec8379..0000000 --- a/notifications-admin/terraform/shared/container_networking/providers.tf +++ /dev/null @@ -1,9 +0,0 @@ -terraform { - required_version = "~> 1.7" - required_providers { - cloudfoundry = { - source = "cloudfoundry-community/cloudfoundry" - version = "0.53.1" - } - } -} diff --git a/notifications-admin/terraform/shared/container_networking/variables.tf b/notifications-admin/terraform/shared/container_networking/variables.tf deleted file mode 100644 index 960ee72..0000000 --- a/notifications-admin/terraform/shared/container_networking/variables.tf +++ /dev/null @@ -1,10 +0,0 @@ -variable "cf_org_name" {} -variable "cf_space_name" {} -variable "source_app_name" {} -variable "destination_app_name" {} -variable "destination_port" { - type = string - # 61443 is the port to use to enable automatic TLS termination as specified at - # https://cloud.gov/docs/management/container-to-container/#configuring-secure-container-to-container-networking - default = "61443" -} diff --git a/notifications-admin/terraform/staging/main.tf b/notifications-admin/terraform/staging/main.tf deleted file mode 100644 index 6fe1255..0000000 --- a/notifications-admin/terraform/staging/main.tf +++ /dev/null @@ -1,65 +0,0 @@ -locals { - cf_org_name = "gsa-tts-benefits-studio" - cf_space_name = "notify-staging" - env = "staging" - app_name = "notify-admin" -} - -resource "null_resource" "prevent_destroy" { - - lifecycle { - prevent_destroy = false # destroying staging is allowed - } -} - -module "redis-v70" { - source = "github.com/GSA-TTS/terraform-cloudgov//redis?ref=v2.4.0" - # Right now the default is cfcommunity, remove this when default is cloudfoundry - providers = { - cloudfoundry = cloudfoundry.official - } - cf_space_id = data.cloudfoundry_space.space.id - name = "${local.app_name}-redis-v70-${local.env}" - redis_plan_name = "redis-dev" - json_params = jsonencode( - { - "engineVersion" : "7.0", - } - ) -} - -data "cloudfoundry_space" "space" { - provider = cloudfoundry.official - org = "9e428562-a2d9-41b4-9c23-1ef5237fb44e" - name = local.cf_space_name -} - -module "logo_upload_bucket" { - source = "github.com/GSA-TTS/terraform-cloudgov//s3?ref=v2.4.0" - # Right now the default is cfcommunity, remove this when default is cloudfoundry - providers = { - cloudfoundry = cloudfoundry.official - } - cf_space_id = data.cloudfoundry_space.space.id - name = "${local.app_name}-logo-upload-bucket-${local.env}" -} - -module "api_network_route" { - source = "../shared/container_networking" - - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - source_app_name = "${local.app_name}-${local.env}" - destination_app_name = "notify-api-${local.env}" -} - -# Looks like we have to upgrade 'shared' before we can do this -# module "api_network_route_new" { -# source = "../shared/container_networking" -# providers = { -# cloudfoundry = cloudfoundry.official -# } -# cf_space_id = data.cloudfoundry_space.space.id -# source_app_name = "${local.app_name}-${local.env}" -# destination_app_name = "notify-api-${local.env}" -# } diff --git a/notifications-admin/terraform/staging/providers.tf b/notifications-admin/terraform/staging/providers.tf deleted file mode 100644 index e14251f..0000000 --- a/notifications-admin/terraform/staging/providers.tf +++ /dev/null @@ -1,37 +0,0 @@ -terraform { - required_version = "~> 1.7" - required_providers { - cloudfoundry = { - source = "cloudfoundry/cloudfoundry" - version = "1.9.0" - } - cfcommunity = { - source = "cloudfoundry-community/cloudfoundry" - version = "0.53.1" - } - } - - backend "s3" { - bucket = "cg-6b759c13-6253-4a64-9bda-dd1f620185b0" - key = "admin.tfstate.stage" - encrypt = "true" - region = "us-gov-west-1" - use_lockfile = "true" - } -} - -# Official provider (should be default but aliased for now) -provider "cloudfoundry" { - alias = "official" - api_url = "https://api.fr.cloud.gov" - user = var.cf_user - password = var.cf_password -} - -# Community provider (should be aliased but default for now) -provider "cfcommunity" { - api_url = "https://api.fr.cloud.gov" - user = var.cf_user - password = var.cf_password - app_logs_max = 30 -} diff --git a/notifications-admin/terraform/staging/variables.tf b/notifications-admin/terraform/staging/variables.tf deleted file mode 100644 index bd8f741..0000000 --- a/notifications-admin/terraform/staging/variables.tf +++ /dev/null @@ -1,5 +0,0 @@ -variable "cf_password" { - type = string - sensitive = true -} -variable "cf_user" {} diff --git a/notifications-admin/tests/app/test_cloudfoundry_config.py b/notifications-admin/tests/app/test_cloudfoundry_config.py deleted file mode 100644 index 10ee8ea..0000000 --- a/notifications-admin/tests/app/test_cloudfoundry_config.py +++ /dev/null @@ -1,70 +0,0 @@ -import json -import os - -import pytest - -from app.cloudfoundry_config import CloudfoundryConfig - -bucket_credentials = { - "access_key_id": "contact-list-access", - "bucket": "contact-list-bucket", - "region": "us-gov-west-1", - "secret_access_key": "contact-list-secret", -} - - -@pytest.fixture -def vcap_services(): - return { - "aws-elasticache-redis": [{"credentials": {"uri": "rediss://xxx:6379"}}], - "s3": [ - { - "name": "notify-api-csv-upload-bucket-test", - "credentials": { - "access_key_id": "csv-access", - "bucket": "csv-upload-bucket", - "region": "us-gov-west-1", - "secret_access_key": "csv-secret", - }, - }, - { - "name": "notify-api-contact-list-bucket-test", - "credentials": bucket_credentials, - }, - ], - } - - -def test_redis_url(vcap_services): - os.environ["VCAP_SERVICES"] = json.dumps(vcap_services) - - assert CloudfoundryConfig().redis_url == "rediss://xxx:6379" - - -def test_redis_url_falls_back_to_REDIS_URL(): - expected = "rediss://yyy:6379" - os.environ["REDIS_URL"] = expected - os.environ["VCAP_SERVICES"] = "" - - assert CloudfoundryConfig().redis_url == expected - - -def test_s3_bucket_credentials(vcap_services): - os.environ["VCAP_SERVICES"] = json.dumps(vcap_services) - - assert ( - CloudfoundryConfig().s3_credentials("notify-api-contact-list-bucket-test") - == bucket_credentials - ) - - -def test_s3_bucket_credentials_falls_back_to_empty_creds(): - os.environ["VCAP_SERVICES"] = "" - expected = { - "bucket": "", - "access_key_id": "", - "secret_access_key": "", - "region": "", - } - - assert CloudfoundryConfig().s3_credentials("bucket") == expected diff --git a/notifications-api/.profile b/notifications-api/.profile deleted file mode 100644 index c6fb0b0..0000000 --- a/notifications-api/.profile +++ /dev/null @@ -1,9 +0,0 @@ -## -# Cloud Foundry app initialization script -# https://docs.cloudfoundry.org/devguide/deploy-apps/deploy-app.html#profile -## - -export http_proxy=$egress_proxy -export https_proxy=$egress_proxy -export NEW_RELIC_PROXY_HOST=$egress_proxy -export no_proxy="apps.internal,s3-fips.us-gov-west-1.amazonaws.com" diff --git a/notifications-api/app/cloudfoundry_config.py b/notifications-api/app/cloudfoundry_config.py deleted file mode 100644 index 602ee98..0000000 --- a/notifications-api/app/cloudfoundry_config.py +++ /dev/null @@ -1,110 +0,0 @@ -import json -from os import getenv - - -class CloudfoundryConfig: - def __init__(self): - self.parsed_services = json.loads(getenv("VCAP_SERVICES") or "{}") - buckets = self.parsed_services.get("s3") or [] - self.s3_buckets = {bucket["name"]: bucket["credentials"] for bucket in buckets} - self._empty_bucket_credentials = { - "bucket": "", - "access_key_id": "", # nosec B105 - "secret_access_key": "", # nosec B105 - "region": "", - } - - @property - def database_url(self): - return getenv("DATABASE_URL", "").replace("postgres://", "postgresql://") - - @property - def redis_url(self): - try: - return self.parsed_services["aws-elasticache-redis"][0]["credentials"][ - "uri" - ].replace("redis://", "rediss://") - except KeyError: - return getenv("REDIS_URL") - - def s3_credentials(self, service_name): - return self.s3_buckets.get(service_name) or self._empty_bucket_credentials - - @property - def ses_email_domain(self): - try: - domain_arn = self._ses_credentials("domain_arn") - except KeyError: - domain_arn = getenv("SES_DOMAIN_ARN", "dev.notify.gov") - return domain_arn.split("/")[-1] - - # TODO remove this after notifications-api #258 - @property - def ses_domain_arn(self): - try: - domain_arn = self._ses_credentials("domain_arn") - except KeyError: - domain_arn = getenv("SES_DOMAIN_ARN", "dev.notify.gov") - return domain_arn - - @property - def ses_region(self): - try: - return self._ses_credentials("region") - except KeyError: - return getenv("SES_AWS_REGION", "us-west-1") - - @property - def ses_access_key(self): - try: - return self._ses_credentials("smtp_user") - except KeyError: - return getenv("SES_AWS_ACCESS_KEY_ID") - - @property - def ses_secret_key(self): - try: - return self._ses_credentials("secret_access_key") - except KeyError: - return getenv("SES_AWS_SECRET_ACCESS_KEY") - - @property - def sns_access_key(self): - try: - return self._sns_credentials("aws_access_key_id") - except KeyError: - return getenv("SNS_AWS_ACCESS_KEY_ID") - - @property - def sns_secret_key(self): - try: - return self._sns_credentials("aws_secret_access_key") - except KeyError: - return getenv("SNS_AWS_SECRET_ACCESS_KEY") - - @property - def sns_region(self): - try: - return self._sns_credentials("region") - except KeyError: - return getenv("SNS_AWS_REGION", "us-west-1") - - @property - def sns_topic_arns(self): - try: - return [ - self._ses_credentials("bounce_topic_arn"), - self._ses_credentials("complaint_topic_arn"), - self._ses_credentials("delivery_topic_arn"), - ] - except KeyError: - return [] - - def _ses_credentials(self, key): - return self.parsed_services["datagov-smtp"][0]["credentials"][key] - - def _sns_credentials(self, key): - return self.parsed_services["ttsnotify-sms"][0]["credentials"][key] - - -cloud_config = CloudfoundryConfig() diff --git a/notifications-api/deploy-config/demo.yml b/notifications-api/deploy-config/demo.yml deleted file mode 100644 index 600ea55..0000000 --- a/notifications-api/deploy-config/demo.yml +++ /dev/null @@ -1,10 +0,0 @@ -env: demo -web_instances: 2 -web_memory: 1G -worker_instances: 2 -worker_memory: 512M -scheduler_memory: 256M -public_api_route: notify-api-demo.app.cloud.gov -admin_base_url: https://notify-demo.app.cloud.gov -redis_enabled: 1 -default_toll_free_number: "+18337581259" diff --git a/notifications-api/deploy-config/egress_proxy/notify-api-demo.allow.acl b/notifications-api/deploy-config/egress_proxy/notify-api-demo.allow.acl deleted file mode 100644 index 6aa6aa2..0000000 --- a/notifications-api/deploy-config/egress_proxy/notify-api-demo.allow.acl +++ /dev/null @@ -1,12 +0,0 @@ -logs-fips.us-east-1.amazonaws.com -monitoring-fips.us-west-2.amazonaws.com -email-fips.us-west-2.amazonaws.com -s3-fips.us-east-1.amazonaws.com -s3-fips.us-east-2.amazonaws.com -s3-fips.us-west-1.amazonaws.com -s3-fips.us-west-2.amazonaws.com -sns-fips.us-east-1.amazonaws.com -gov-collector.newrelic.com -egress-proxy-notify-api-demo.apps.internal -idp.int.identitysandbox.gov -secure.login.gov diff --git a/notifications-api/deploy-config/egress_proxy/notify-api-demo.deny.acl b/notifications-api/deploy-config/egress_proxy/notify-api-demo.deny.acl deleted file mode 100644 index e69de29..0000000 diff --git a/notifications-api/deploy-config/egress_proxy/notify-api-demo.deploy.acl b/notifications-api/deploy-config/egress_proxy/notify-api-demo.deploy.acl deleted file mode 100644 index 914deba..0000000 --- a/notifications-api/deploy-config/egress_proxy/notify-api-demo.deploy.acl +++ /dev/null @@ -1,3 +0,0 @@ -Update this file to force a re-deploy of the egress proxy even when notify-api-staging..acl haven't changed - -20230412: Redeploy to re-calculate the list of allowed s3 buckets diff --git a/notifications-api/deploy-config/egress_proxy/notify-api-production.allow.acl b/notifications-api/deploy-config/egress_proxy/notify-api-production.allow.acl deleted file mode 100644 index a7a7063..0000000 --- a/notifications-api/deploy-config/egress_proxy/notify-api-production.allow.acl +++ /dev/null @@ -1,11 +0,0 @@ -logs.us-gov-west-1.amazonaws.com -monitoring-fips.us-west-2.amazonaws.com -monitoring.us-gov-west-1.amazonaws.com -email-fips.us-gov-west-1.amazonaws.com -s3-fips.us-gov-east-1.amazonaws.com -s3-fips.us-gov-west-1.amazonaws.com -sns.us-gov-west-1.amazonaws.com -gov-collector.newrelic.com -egress-proxy-notify-api-production.apps.internal -idp.int.identitysandbox.gov -secure.login.gov diff --git a/notifications-api/deploy-config/egress_proxy/notify-api-production.deny.acl b/notifications-api/deploy-config/egress_proxy/notify-api-production.deny.acl deleted file mode 100644 index e69de29..0000000 diff --git a/notifications-api/deploy-config/egress_proxy/notify-api-production.deploy.acl b/notifications-api/deploy-config/egress_proxy/notify-api-production.deploy.acl deleted file mode 100644 index e5a3a54..0000000 --- a/notifications-api/deploy-config/egress_proxy/notify-api-production.deploy.acl +++ /dev/null @@ -1 +0,0 @@ -Update this file to force a re-deploy of the egress proxy even when notify-api-production..acl haven't changed diff --git a/notifications-api/deploy-config/egress_proxy/notify-api-staging.allow.acl b/notifications-api/deploy-config/egress_proxy/notify-api-staging.allow.acl deleted file mode 100644 index eb46ce9..0000000 --- a/notifications-api/deploy-config/egress_proxy/notify-api-staging.allow.acl +++ /dev/null @@ -1,12 +0,0 @@ -logs-fips.us-west-2.amazonaws.com -monitoring-fips.us-west-2.amazonaws.com -email-fips.us-west-2.amazonaws.com -s3-fips.us-east-1.amazonaws.com -s3-fips.us-east-2.amazonaws.com -s3-fips.us-west-1.amazonaws.com -s3-fips.us-west-2.amazonaws.com -sns-fips.us-west-2.amazonaws.com -gov-collector.newrelic.com -egress-proxy-notify-api-staging.apps.internal -idp.int.identitysandbox.gov -secure.login.gov diff --git a/notifications-api/deploy-config/egress_proxy/notify-api-staging.deny.acl b/notifications-api/deploy-config/egress_proxy/notify-api-staging.deny.acl deleted file mode 100644 index e69de29..0000000 diff --git a/notifications-api/deploy-config/egress_proxy/notify-api-staging.deploy.acl b/notifications-api/deploy-config/egress_proxy/notify-api-staging.deploy.acl deleted file mode 100644 index eb2abe8..0000000 --- a/notifications-api/deploy-config/egress_proxy/notify-api-staging.deploy.acl +++ /dev/null @@ -1,4 +0,0 @@ -Update this file to force a re-deploy of the egress proxy even when notify-api-staging..acl haven't changed - -20230412: Redeploy to re-calculate the list of allowed s3 buckets -20231107: Redeploy to update cloud.gov users in Terraform config diff --git a/notifications-api/deploy-config/production.yml b/notifications-api/deploy-config/production.yml deleted file mode 100644 index d09084a..0000000 --- a/notifications-api/deploy-config/production.yml +++ /dev/null @@ -1,10 +0,0 @@ -env: production -web_instances: 2 -web_memory: 3G -worker_instances: 4 -worker_memory: 2G -scheduler_memory: 256M -public_api_route: notify-api.app.cloud.gov -admin_base_url: https://beta.notify.gov -redis_enabled: 1 -default_toll_free_number: "+18447952263" diff --git a/notifications-api/deploy-config/sandbox.yml b/notifications-api/deploy-config/sandbox.yml deleted file mode 100644 index afaf40c..0000000 --- a/notifications-api/deploy-config/sandbox.yml +++ /dev/null @@ -1,18 +0,0 @@ -env: sandbox -web_instances: 1 -web_memory: 1G -worker_instances: 1 -worker_memory: 512M -scheduler_memory: 256M -public_api_route: notify-api-sandbox.app.cloud.gov -admin_base_url: https://notify-sandbox.app.cloud.gov -redis_enabled: 1 -default_toll_free_number: "+18885989205" -ADMIN_CLIENT_SECRET: sandbox-notify-secret-key -API_HOST_NAME: https://notify-api-sandbox.app.cloud.gov -DANGEROUS_SALT: sandbox-notify-salt -LOGIN_DOT_GOV_REGISTRATION_URL: https://idp.int.identitysandbox.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:test_notify_gov&nonce=NONCE&prompt=select_account&redirect_uri=https://notify-sandbox.app.cloud.gov/set-up-your-profile&response_type=code&scope=openid+email&state=STATE -NEW_RELIC_LICENSE_KEY: "" -NOTIFY_E2E_TEST_EMAIL: fake.user@example.com -NOTIFY_E2E_TEST_PASSWORD: "don't write secrets to the sample file" -SECRET_KEY: sandbox-notify-secret-key diff --git a/notifications-api/deploy-config/staging.yml b/notifications-api/deploy-config/staging.yml deleted file mode 100644 index 5bb9481..0000000 --- a/notifications-api/deploy-config/staging.yml +++ /dev/null @@ -1,10 +0,0 @@ -env: staging -web_instances: 2 -web_memory: 1.5G -worker_instances: 2 -worker_memory: 1.5G -scheduler_memory: 256M -public_api_route: notify-api-staging.app.cloud.gov -admin_base_url: https://notify-staging.app.cloud.gov -redis_enabled: 1 -default_toll_free_number: "+18556438890" diff --git a/notifications-api/manifest.yml b/notifications-api/manifest.yml deleted file mode 100644 index 76b8e0e..0000000 --- a/notifications-api/manifest.yml +++ /dev/null @@ -1,59 +0,0 @@ ---- -applications: - - name: notify-api-((env)) - buildpack: python_buildpack - stack: cflinuxfs4 - instances: 1 - disk_quota: 2G - routes: - - route: ((public_api_route)) - - route: notify-api-((env)).apps.internal - - services: - - notify-api-rds-((env)) - - notify-api-redis-v70-((env)) - - notify-api-csv-upload-bucket-((env)) - - name: notify-api-ses-((env)) - parameters: - notification_webhook: "https://((public_api_route))/notifications/email/ses" - - notify-api-sns-((env)) - - processes: - - type: web - instances: ((web_instances)) - memory: ((web_memory)) - command: ./scripts/migrate_and_run_web.sh - - type: worker - instances: ((worker_instances)) - memory: ((worker_memory)) - command: newrelic-admin run-program celery -A run_celery.notify_celery worker --loglevel=INFO --pool=gevent --concurrency=20 --prefetch-multiplier=2 - - type: scheduler - instances: 1 - memory: ((scheduler_memory)) - command: celery -A run_celery.notify_celery beat --loglevel=INFO - - env: - NOTIFY_APP_NAME: api - FLASK_APP: application.py - FLASK_DEBUG: "false" - NEW_RELIC_CONFIG_FILE: newrelic.ini - NEW_RELIC_ENVIRONMENT: ((env)) - NEW_RELIC_LICENSE_KEY: ((NEW_RELIC_LICENSE_KEY)) - - REDIS_ENABLED: ((redis_enabled)) - NOTIFY_ENVIRONMENT: ((env)) - API_HOST_NAME: https://((public_api_route)) - ADMIN_BASE_URL: ((admin_base_url)) - NOTIFY_E2E_TEST_EMAIL: ((NOTIFY_E2E_TEST_EMAIL)) - NOTIFY_E2E_TEST_PASSWORD: ((NOTIFY_E2E_TEST_PASSWORD)) - LOGIN_DOT_GOV_REGISTRATION_URL: ((LOGIN_DOT_GOV_REGISTRATION_URL)) - - # Credentials variables - INTERNAL_CLIENT_API_KEYS: '{"notify-admin":["((ADMIN_CLIENT_SECRET))"]}' - DANGEROUS_SALT: ((DANGEROUS_SALT)) - SECRET_KEY: ((SECRET_KEY)) - AWS_US_TOLL_FREE_NUMBER: ((default_toll_free_number)) - - SSL_CERT_FILE: "/etc/ssl/certs/ca-certificates.crt" - REQUESTS_CA_BUNDLE: "/etc/ssl/certs/ca-certificates.crt" - NEW_RELIC_CA_BUNDLE_PATH: "/etc/ssl/certs/ca-certificates.crt" diff --git a/notifications-api/paas-failwhale/README.md b/notifications-api/paas-failwhale/README.md deleted file mode 100644 index f1e3a79..0000000 --- a/notifications-api/paas-failwhale/README.md +++ /dev/null @@ -1,27 +0,0 @@ -### What is it? - -This is a simple static error page to present to API users in case of a (planned) downtime. -It is deployed as an individual app and remains dormant until a route is assigned to it. -It returns a 503 error code and a standard json response for all routes. - - -### How do I use it? - -It should already be deployed, but if not (or if you need to make changes to the nginx config) you can deploy it by running - - cf push notify-api-failwhale - -To enable it you need to run - - make enable-failwhale - -and to disable it - - make disable-failwhale - - -Where `` is any of - -- preview -- staging -- production diff --git a/notifications-api/paas-failwhale/manifest.yml b/notifications-api/paas-failwhale/manifest.yml deleted file mode 100644 index bc8feb9..0000000 --- a/notifications-api/paas-failwhale/manifest.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- - -applications: - - name: notify-api-failwhale - buildpacks: - - nginx_buildpack - memory: 256M - no-route: true diff --git a/notifications-api/paas-failwhale/nginx.conf b/notifications-api/paas-failwhale/nginx.conf deleted file mode 100644 index 458615c..0000000 --- a/notifications-api/paas-failwhale/nginx.conf +++ /dev/null @@ -1,31 +0,0 @@ -worker_processes 1; -daemon off; - -error_log ./error.log; -events { worker_connections 8192; } - -http { - charset utf-8; - log_format cloudfoundry '$http_x_forwarded_for - $http_referer - [$time_local] "$request" $status $body_bytes_sent'; - access_log ./access.log cloudfoundry; - - keepalive_timeout 30; - server_tokens off; - - server { - listen {{port}}; - server_name localhost; - - location / { - set $RESP '{'; - set $RESP '${RESP} "status_code": 503,'; - set $RESP '${RESP} "errors": [ {'; - set $RESP '${RESP} "error": "PlannedMaintenanceError",'; - set $RESP '${RESP} "message": "We’re performing some essential updates. Notify will be back shortly. Please check https://status.notifications.service.gov.uk/ for more details"'; - set $RESP '${RESP} } ] }'; - - add_header Content-Type application/json; - return 503 '$RESP'; - } - } -} diff --git a/notifications-api/scripts/deploy_to_sandbox.sh b/notifications-api/scripts/deploy_to_sandbox.sh deleted file mode 100755 index 683e875..0000000 --- a/notifications-api/scripts/deploy_to_sandbox.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -# Create a requirements.txt file so dependencies are properly managed with the -# deploy. This will overwrite any existing requirements.txt file to make sure -# it is always up-to-date. -poetry export --without-hashes --format=requirements.txt > requirements.txt - -# Target the notify-sandbox space and deploy to cloud.gov with a cf push. -# All environment variables are accounted for in the deploy-config/sandbox.yml -# file, no need to add any of your own or source a .env* file. - -# If this errors out because you need to be logged in, login first with this: -# cf login -a api.fr.cloud.gov --sso -cf target -o gsa-tts-benefits-studio -s notify-sandbox -cf push -f manifest.yml --vars-file deploy-config/sandbox.yml --strategy rolling diff --git a/notifications-api/terraform/README.md b/notifications-api/terraform/README.md deleted file mode 100644 index 24e9a55..0000000 --- a/notifications-api/terraform/README.md +++ /dev/null @@ -1,245 +0,0 @@ -# Terraform - -illustration of space exploration - -This directory holds the Terraform modules for maintaining Notify.gov's API infrastructure. You might want to: -* [Set up](#retrieving-existing-bucket-credentials) the Sandbox and develop Terraform, -* [Maintain](#maintenance) software versions or CI/CD, -* [Learn](#structure) about the directory structure, or -* [Troubleshoot](#troubleshooting) error messages - -The Admin app repo [has its own terraform directory](https://github.com/GSA/notifications-admin/tree/main/terraform) but a lot of the below instructions apply to both apps. - -:tv: [Video introduction](https://drive.google.com/file/d/13SR3M8IowYBa4Wp_YEcuAURZ74EcCYoc/) to Notify infrastructure - -## Retrieving existing bucket credentials - -:green_book: New developers start here! - -Assuming [initial setup](#initial-setup) is complete — which it should be if Notify.gov is online — Terraform state is stored in a shared remote backend. If you are going to be writing Terraform for any of our deployment environments you'll need to hook up to this backend. (You don't need to do this if you are just writing code for the `development` module, because it stores state locally on your laptop.) - -1. Enter the bootstrap module with `cd bootstrap` -1. Run `./import.sh` to import the bucket containing remote terraform state into your local state -1. Follow instructions under [Use bootstrap credentials](#use-bootstrap-credentials) - -### Use bootstrap credentials - -1. Run `./run.sh show -json`. -1. In the output, locate `access_key_id` and `secret_access_key` within the `bucket_creds` resource. These values are secret, so don't share them with anyone or copy them to anywhere online. -1. Add the following to `~/.aws/credentials`: - ``` - [notify-terraform-backend] - aws_access_key_id = - aws_secret_access_key = - ``` -1. Check which AWS profile you are using with `aws configure list`. If needed, use `export AWS_PROFILE=notify-terraform-backend` to change to the profile and credentials you just added. - -These credentials will allow Terraform to access the AWS/Cloud.gov bucket in which developers share Terraform state files. Now you are ready to develop Terraform using the [Workflow for deployed environments](#workflow-for-deployed-environments). - -## Workflow for deployed environments - -These are the steps for developing Terraform code for our deployed environment modules (`sandbox`, `demo`, `staging` and `production`) locally on your laptop. Or for setting up a new deployment environment, or otherwise for running Terraform manually in any module that uses remote state. You don't need to do all this to run code in the `development` module, because it is not a deployed environment and it does not use remote state. - -> [!CAUTION] -> There is one risky step below (`apply`) which is safe only in the `sandbox` environment and **should not** be run in any other deployed environment. - -These steps assume shared [Terraform state credentials](#terraform-state-credentials) exist in s3, and that you are [Using those credentials](#use-bootstrap-credentials). - -1. `cd` to the environment you plan to work in. When developing new features/resources, try out your code in `sandbox`. Only once the code is proven should you copy-and-paste it to each higher environment. - -1. Run `cf spaces` and, from the output, copy the space name for the environment you are working in, such as `notify-sandbox`. - -1. Next you will set up a SpaceDeployer service account instance. This is something like a stub user account, just for deployment. Note these two values which you will use both to create and destroy the account: - 1. `` will be the string you copied from the prior step - 1. `` can be anything, although we recommend something that communicates the purpose of the deployer. For example: "circleci-deployer" for the credentials CircleCI uses to deploy the application, or "sandbox-" for credentials to run terraform manually. - - Put those two values into this command: - ```bash - ../create_service_account.sh -s -u > secrets.auto.tfvars - ``` - - The script will output the `username` (as `cf_user`) and `password` (as `cf_password`) for your ``. The [cloud.gov service account documentation](https://cloud.gov/docs/services/cloud-gov-service-account/) has more information. - - Some resources you might work on require a SpaceDeployer account with higher permissions. Add the `-m` flag to the command to get this. - - The command uses the redirection operator (`>`) to write that output to the `secrets.auto.tfvars` file. Terraform will find the username and password there, and use them as input variables. - -1. While still in an environment directory, initialize Terraform: - ```bash - terraform init - ``` - - If this command fails, you may need to run `terraform init -upgrade` to make sure new module versions are picked up. Or, `terraform init -migrate-state` to bump the remote backend. - -1. Then, run Terraform in a non-destructive way: - ```bash - terraform plan - ``` - - This will show you any pending changes that Terraform is ready to make. - - :pencil: Now is the time to write any HCL code (aka Terraform code) you are planning to write, re-running `terraform plan` to confirm that the code works as you develop. Keep in mind that any changes to the codebase that you commit will be run by the CI/CD pipeline. - -1. **Only if it is safe to do so**, apply your changes. - - :skull: Applying changes in the wrong directory can mess up a deployed environment that people are relying on - - Double-check what directory you are in, like with the `pwd` command. You should probably only apply while in the `sandbox` directory / environment. - - Once you are sure it is safe, run: - ```bash - terraform apply - ``` - - This command *will deploy your changes* to the cloud. This is a healthy part of testing your code in the sandbox, or if you are creating a new environment (a new directory). **Do not** apply in environments that people are relying upon. - - If you need to go on to deploy application code on top of the resources you just instantiated, you will [use `cf push`](https://github.com/GSA/notifications-api/blob/main/docs/all.md#deploying-to-the-sandbox) - -1. Remove the space deployer service instance when you are done manually running Terraform. - ```bash - # and have the same values as used above. - ./destroy_service_account.sh -s -u - ``` - - List `cf services` if you are unsure which space deployer service instances still exist - - Optionally, you can also `rm secrets.auto.tfvars` - -## Maintenance - -### Version upgrade checklist - -These version numbers are hardcoded in Terraform or shell scripts. We should periodically check them for upgrades. - -* Cloud Foundry Terraform plugin in every module in the API and Admin apps, [here for example](sandbox/providers.tf#L6). -* The [terraform-cloudgov module](https://github.com/GSA-TTS/terraform-cloudgov/), the version of which is referred to serveral times in most modules, [here for example](sandbox/main.tf#L16). -* Cloud Service Broker (CSB) version in [the SMS](https://github.com/GSA/usnotify-ssb/blob/main/app-setup-sms.sh) and [the SMTP](https://github.com/GSA/usnotify-ssb/blob/main/app-setup-smtp.sh) download scripts of the usnotify-ssb repo. -* SMS and SMTP brokerpak versions, also in the download scripts of the usnotify-ssb repo. (And we may have to help maintain the [SMTP brokerpak project](https://github.com/GSA-TTS/datagov-brokerpak-smtp) itself.) -* The version of Redis used in deployed environment modules, [here for example](sandbox/main.tf#L33). To upgrade, the resource must be destroyed and replaced. The versions supported are limited by Cloud.gov. -* A required minimum version of Terraform is noted in every providers.tf file, [here for example](sandbox/providers.tf#L2). It would be best to keep it in sync with the version used by our CI/CD deployment pipeline. But, it does not need to be updated with every new Terraform release. - -:tv: Some of these version upgrades are discussed in our [video introduction](https://drive.google.com/file/d/13SR3M8IowYBa4Wp_YEcuAURZ74EcCYoc/). - -### SpaceDeployers - -A [SpaceDeployer](https://cloud.gov/docs/services/cloud-gov-service-account/) account is required to run terraform or -deploy the application from the CI/CD pipeline. During CI/CD maintenance you might need to create a new account: - -`./create_service_account.sh -s -u ` - -SpaceDeployers are also needed to run Terraform locally — they fill user and password input variables (via `deployers` within `main.tf`) that some of our Terraform modules require when they start running. Using a SpaceDeployer account locally is covered in [Workflow for deployed environments](#workflow-for-deployed-environments). - -## Structure - -The `terraform` directory contains sub-directories (`staging`, `production`, etc.) named for deployment environments. Each of these is a *module*, which is just Terraform's word for a directory with some .tf files in it. Each module governs the infrastructure of the environment for which it is named. This directory structure forms "[bulkheads](https://blog.gruntwork.io/how-to-manage-terraform-state-28f5697e68fa)" which isolate Terraform commands to a single environment, limiting accidental damage. - -The `development` module is rather different from the other environment modules. While the other environments can be used to create (or destroy) cloud resources, the development module mostly just sets up access to pre-existing resources needed for local software development. - -The `bootstrap` directory is not an environment module. Instead, it sets up infrastructure needed to deploy Terraform in any of the environments. If you are new to the project, [this is where you should start](#retrieving-existing-bucket-credentials). - -Similarly, `shared` is not an environment. It is a module that lends code to all the environments. Please note that changes to `shared` codebase will be applied to all envrionments the next time CI/CD (or a user) runs Terraform in that environment. - -> [!WARNING] -> Editing `shared` code is risky because it will be applied to production - -Files within these directories look like this: - -``` -- bootstrap/ - |- main.tf - |- providers.tf - |- variables.tf - |- run.sh - |- teardown_creds.sh - |- import.sh -- / - |- main.tf - |- providers.tf - |- secrets.auto.tfvars - |- variables.tf -``` - -In the environment-specific modules: -- `providers.tf` lists the required providers -- `main.tf` calls the shared Terraform code, but this is also a place where you can add any other services, resources, etc, which you would like to set up for that environment -- `variables.tf` lists the variables that will be needed, either to pass through to the child module or for use in this module -- `secrets.auto.tfvars` is a file which contains the information about the service-key and other secrets that should not be shared - -In the bootstrap module: -- `providers.tf` lists the required providers -- `main.tf` sets up s3 bucket to be shared across all environments. It lives in `prod` to communicate that it should not be deleted -- `variables.tf` lists the variables that will be needed. Most values are hard-coded in this module -- `run.sh` Helper script to set up a space deployer and run terraform. The terraform action (`show`/`plan`/`apply`/`destroy`) is passed as an argument -- `teardown_creds.sh` Helper script to remove the space deployer setup as part of `run.sh` -- `import.sh` Helper script to create a new local state file in case terraform changes are needed - -## Troubleshooting - -### Expired token - -``` -The token expired, was revoked, or the token ID is incorrect. Please log back in to re-authenticate. -``` -You need to re-authenticate with the Cloud Foundry CLI -``` -cf login -a api.fr.cloud.gov --sso -``` -You may also need to log in again to the Cloud.gov website. - -### CF account not authorized - -``` -Error: You are not authorized to perform the requested action -``` -This error indicates that the Cloud Foundry user account (or service account) needs OrgManager permissions to take the action. -* When you create a SpaceDeployer service account, use the `-m` flag when running the `./create_service_account.sh` script -* Your own CF user may may also require OrgManager permissions to run the script - -### Services limit -``` -You have exceeded your organization's services limit. -``` -Too many Cloud Foundry services have been created without being destroyed. Perhaps Terraform developers have forgotten to delete their SpaceDeployers after they finish with them. List `cf services` to see. - -### Unknown error -``` -Error: Service Instance xx-name-xx failed xx-UUID-xx, reason: [Job (xx-UUID-xx) failed: An unknown error occurred.] -``` -This unhelpful message may be clarified by looking in the Cloud.gov web UI. Among the list of service instances (Cloud Foundry → Organizations → gsa-tts-benefits-studio → Spaces → your-space-name → Service instances) check for pending or erroring items. Refer below if you discover a [domain identity verification](#Domain_identity_verification) error. - -The audit event logs may also provide insight. They are visible in web UI or [in the terminal](https://v3-apidocs.cloudfoundry.org/version/3.159.0/#audit-events). - -### Domain identity verification -``` -Error: Error creating SES domain identity verification: Expected domain verification Success, but was in state Pending -``` -This error comes via the [Supplementary Service Broker](https://github.com/GSA/usnotify-ssb/) and originates from the [SMTP Brokerpak](https://github.com/GSA-TTS/datagov-brokerpak-smtp) it uses. You can run the [broker provisioning locally](https://github.com/GSA-TTS/datagov-brokerpak-smtp/tree/main/terraform/provision) to tinker with the error. - -### Validating provider credentials -``` -Error: validating provider credentials: retrieving caller identity from STS: operation error STS: GetCallerIdentity, https response error StatusCode: 403 -``` -The steps in [Use bootstrap credentials](#use-bootstrap-credentials) may not be complete. Or the AWS CLI may have reverted to the default profile, in which case, re-run: -```bash -export AWS_PROFILE=notify-terraform-backend -``` - -### No valid credential sources -``` -Error: No valid credential sources found -Please see https://www.terraform.io/docs/language/settings/backends/s3.html for more information about providing credentials. - -Error: failed to refresh cached credentials, no EC2 IMDS role found, operation error ec2imds: GetMetadata, request canceled, context deadline exceeded -``` -You are not hooked up to the remote backend that stores Terraform state -Run steps in [Retrieving existing bucket credentials](#retrieving-existing-bucket-credentials). - -### Space Deployers will be updated in-place -``` -# module.egress-space.cloudfoundry_space_users.deployers will be updated in-place - ~ resource "cloudfoundry_space_users" "deployers" { - ~ developers = [ - - "xxx-GUID-xxx", - + "yyy-GUID-yyy", -``` -The environment was last deployed by someone other than you, using a different Space Deployer account. If you are working in the Sandbox environment, this is fine; go ahead and apply the changes. After you do, the other person evidently also working in the Sandbox env will then see the same message. The two of you might play tug-of-war with different GUIDs, but this is inconsequential. diff --git a/notifications-api/terraform/bootstrap/import.sh b/notifications-api/terraform/bootstrap/import.sh deleted file mode 100755 index 9140711..0000000 --- a/notifications-api/terraform/bootstrap/import.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -read -p "Are you sure you want to import terraform state (y/n)? " verify - -if [[ $verify == "y" ]]; then - echo "Importing bootstrap state" - ./run.sh import module.s3.cloudfoundry_service_instance.bucket 6b759c13-6253-4a64-9bda-dd1f620185b0 - ./run.sh import cloudfoundry_service_key.bucket_creds a8e40295-68b7-42ba-8955-d82ba262e948 - ./run.sh plan -else - echo "Not importing bootstrap state" -fi diff --git a/notifications-api/terraform/bootstrap/main.tf b/notifications-api/terraform/bootstrap/main.tf deleted file mode 100644 index a518530..0000000 --- a/notifications-api/terraform/bootstrap/main.tf +++ /dev/null @@ -1,20 +0,0 @@ -locals { - s3_service_name = "notify-terraform-state" -} - -module "s3" { - source = "github.com/GSA-TTS/terraform-cloudgov//s3?ref=v1.0.0" - - cf_org_name = "gsa-tts-benefits-studio" - cf_space_name = "notify-management" - name = local.s3_service_name -} - -resource "cloudfoundry_service_key" "bucket_creds" { - name = "${local.s3_service_name}-access" - service_instance = module.s3.bucket_id - - lifecycle { - prevent_destroy = true - } -} diff --git a/notifications-api/terraform/bootstrap/providers.tf b/notifications-api/terraform/bootstrap/providers.tf deleted file mode 100644 index 7b9ce5c..0000000 --- a/notifications-api/terraform/bootstrap/providers.tf +++ /dev/null @@ -1,16 +0,0 @@ -terraform { - required_version = "~> 1.7" - required_providers { - cloudfoundry = { - source = "cloudfoundry-community/cloudfoundry" - version = "0.53.1" - } - } -} - -provider "cloudfoundry" { - api_url = "https://api.fr.cloud.gov" - user = var.cf_user - password = var.cf_password - app_logs_max = 30 -} diff --git a/notifications-api/terraform/bootstrap/run.sh b/notifications-api/terraform/bootstrap/run.sh deleted file mode 100755 index 1ac3954..0000000 --- a/notifications-api/terraform/bootstrap/run.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -if [[ ! -f "secrets.auto.tfvars" ]]; then - ../create_service_account.sh -s notify-management -u config-bootstrap-deployer > secrets.auto.tfvars -fi - -if [[ $# -gt 0 ]]; then - echo "Running terraform $@" - terraform $@ -else - echo "Not running terraform" -fi diff --git a/notifications-api/terraform/bootstrap/teardown_creds.sh b/notifications-api/terraform/bootstrap/teardown_creds.sh deleted file mode 100755 index 77207a6..0000000 --- a/notifications-api/terraform/bootstrap/teardown_creds.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -../destroy_service_account.sh -s notify-management -u config-bootstrap-deployer - -rm secrets.auto.tfvars diff --git a/notifications-api/terraform/bootstrap/variables.tf b/notifications-api/terraform/bootstrap/variables.tf deleted file mode 100644 index a24f2f3..0000000 --- a/notifications-api/terraform/bootstrap/variables.tf +++ /dev/null @@ -1,4 +0,0 @@ -variable "cf_password" { - sensitive = true -} -variable "cf_user" {} diff --git a/notifications-api/terraform/create_service_account.sh b/notifications-api/terraform/create_service_account.sh deleted file mode 100755 index 44c5e2a..0000000 --- a/notifications-api/terraform/create_service_account.sh +++ /dev/null @@ -1,92 +0,0 @@ -#!/usr/bin/env bash - -org="gsa-tts-benefits-studio" - -usage=" -$0: Create a Service User Account for a given space - -Usage: - $0 -h - $0 -s -u [-r ] [-o ] [-m] - -Options: --h: show help and exit --s : configure the space to act on. Required --u : set the service user name. Required --r : set the service user's role to either space-deployer or space-auditor. Default: space-deployer --m: If provided, make the service user an OrgManager --o : configure the organization to act on. Default: $org - -Notes: -* OrgManager is required for terraform to create -egress spaces -* Requires cf-cli@8 & jq -" - -cf_version=`cf --version | cut -d " " -f 3` -if [[ $cf_version != 8.* ]]; then - echo "$usage" >&2 - exit 1 -fi -command -v jq >/dev/null || { echo "$usage" >&2; exit 1; } - -set -e -set -o pipefail - -space="" -service="" -role="space-deployer" -org_manager="false" - -while getopts ":hms:u:r:o:" opt; do - case "$opt" in - s) - space=${OPTARG} - ;; - u) - service=${OPTARG} - ;; - r) - role=${OPTARG} - ;; - o) - org=${OPTARG} - ;; - m) - org_manager="true" - ;; - h) - echo "$usage" - exit 0 - ;; - esac -done - -if [[ $space = "" || $service = "" ]]; then - echo "$usage" >&2 - exit 1 -fi - -cf target -o $org -s $space >&2 - -# create user account service -cf create-service cloud-gov-service-account $role $service >&2 - -# create service key -cf create-service-key $service service-account-key >&2 - -# output service key to stdout in secrets.auto.tfvars format -creds=`cf service-key $service service-account-key | tail -n +2 | jq '.credentials'` -username=`echo $creds | jq -r '.username'` -password=`echo $creds | jq -r '.password'` - -if [[ $org_manager = "true" ]]; then - cf set-org-role $username $org OrgManager >&2 -fi - -cat << EOF -# generated with $0 -s $space -u $service -r $role -o $org -# revoke with $(dirname $0)/destroy_service_account.sh -s $space -u $service -o $org - -cf_user = "$username" -cf_password = "$password" -EOF diff --git a/notifications-api/terraform/demo/main.tf b/notifications-api/terraform/demo/main.tf deleted file mode 100644 index 3f24985..0000000 --- a/notifications-api/terraform/demo/main.tf +++ /dev/null @@ -1,76 +0,0 @@ -locals { - cf_org_name = "gsa-tts-benefits-studio" - cf_space_name = "notify-demo" - env = "demo" - app_name = "notify-api" -} - -resource "null_resource" "prevent_destroy" { - - lifecycle { - prevent_destroy = true - } -} - -module "database" { - source = "github.com/GSA-TTS/terraform-cloudgov//database?ref=v1.0.0" - - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-rds-${local.env}" - rds_plan_name = "small-psql" -} - -module "redis-v70" { - source = "github.com/GSA-TTS/terraform-cloudgov//redis?ref=v1.0.0" - - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-redis-v70-${local.env}" - redis_plan_name = "redis-dev" - json_params = jsonencode( - { - "engineVersion" : "7.0", - } - ) -} - -module "csv_upload_bucket" { - source = "github.com/GSA-TTS/terraform-cloudgov//s3?ref=v1.0.0" - - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-csv-upload-bucket-${local.env}" -} - -module "egress-space" { - source = "../shared/egress_space" - - cf_org_name = local.cf_org_name - cf_restricted_space_name = local.cf_space_name - deployers = [ - var.cf_user, - "alex.janousek@gsa.gov" - ] -} - -module "ses_email" { - source = "../shared/ses" - - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-ses-${local.env}" - aws_region = "us-west-2" - mail_from_subdomain = "mail" - email_receipt_error = "notify-support@gsa.gov" -} - -module "sns_sms" { - source = "../shared/sns" - - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-sns-${local.env}" - aws_region = "us-west-2" - monthly_spend_limit = 25 -} diff --git a/notifications-api/terraform/demo/providers.tf b/notifications-api/terraform/demo/providers.tf deleted file mode 100644 index 6345972..0000000 --- a/notifications-api/terraform/demo/providers.tf +++ /dev/null @@ -1,37 +0,0 @@ -terraform { - required_version = "~> 1.7" - required_providers { - cloudfoundry = { - source = "cloudfoundry/cloudfoundry" - version = "1.9.0" - } - cfcommunity = { - source = "cloudfoundry-community/cloudfoundry" - version = "0.53.1" - } - } - - backend "s3" { - bucket = "cg-6b759c13-6253-4a64-9bda-dd1f620185b0" - key = "api.tfstate.demo" - encrypt = "true" - region = "us-gov-west-1" - use_lockfile = "true" - } -} - -# Official provider (should be default but aliased for now) -provider "cloudfoundry" { - alias = "official" - api_url = "https://api.fr.cloud.gov" - user = var.cf_user - password = var.cf_password -} - -# Community provider (should be aliased but default for now) -provider "cfcommunity" { - api_url = "https://api.fr.cloud.gov" - user = var.cf_user - password = var.cf_password - app_logs_max = 30 -} diff --git a/notifications-api/terraform/demo/variables.tf b/notifications-api/terraform/demo/variables.tf deleted file mode 100644 index a24f2f3..0000000 --- a/notifications-api/terraform/demo/variables.tf +++ /dev/null @@ -1,4 +0,0 @@ -variable "cf_password" { - sensitive = true -} -variable "cf_user" {} diff --git a/notifications-api/terraform/destroy_service_account.sh b/notifications-api/terraform/destroy_service_account.sh deleted file mode 100755 index 9a4c250..0000000 --- a/notifications-api/terraform/destroy_service_account.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env bash - -org="gsa-tts-benefits-studio" - -usage=" -$0: Destroy a Service User Account in a given space - -Usage: - $0 -h - $0 -s -u [-o ] - -Options: --h: show help and exit --s : configure the space to act on. Required --u : configure the service user name to destroy. Required --o : configure the organization to act on. Default: $org -" - -set -e - -space="" -service="" - -while getopts ":hs:u:o:" opt; do - case "$opt" in - s) - space=${OPTARG} - ;; - u) - service=${OPTARG} - ;; - o) - org=${OPTARG} - ;; - h) - echo "$usage" - exit 0 - ;; - esac -done - -if [[ $space = "" || $service = "" ]]; then - echo "$usage" - exit 1 -fi - -cf target -o $org -s $space - -# destroy service key -cf delete-service-key $service service-account-key -f - -# destroy service -cf delete-service $service -f diff --git a/notifications-api/terraform/development/main.tf b/notifications-api/terraform/development/main.tf deleted file mode 100644 index 6296750..0000000 --- a/notifications-api/terraform/development/main.tf +++ /dev/null @@ -1,77 +0,0 @@ -locals { - cf_org_name = "gsa-tts-benefits-studio" - cf_space_name = "notify-local-dev" - key_name = "${var.username}-api-dev-key" -} - -module "csv_upload_bucket" { - source = "github.com/GSA-TTS/terraform-cloudgov//s3?ref=v1.0.0" - - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${var.username}-csv-upload-bucket" -} -resource "cloudfoundry_service_key" "csv_key" { - name = local.key_name - service_instance = module.csv_upload_bucket.bucket_id -} - -data "cloudfoundry_space" "staging" { - org_name = local.cf_org_name - name = "notify-staging" -} - -data "cloudfoundry_service_instance" "ses_email" { - name_or_id = "notify-api-ses-staging" - space = data.cloudfoundry_space.staging.id -} -resource "cloudfoundry_service_key" "ses_key" { - name = local.key_name - service_instance = data.cloudfoundry_service_instance.ses_email.id - params_json = jsonencode({ - source_ips = [var.source_ip] - }) -} - -data "cloudfoundry_service_instance" "sns_sms" { - name_or_id = "notify-api-sns-staging" - space = data.cloudfoundry_space.staging.id -} -resource "cloudfoundry_service_key" "sns_key" { - name = local.key_name - service_instance = data.cloudfoundry_service_instance.sns_sms.id - params_json = jsonencode({ - source_ips = [var.source_ip] - }) -} - -locals { - credentials = <> .env" - } -} diff --git a/notifications-api/terraform/development/providers.tf b/notifications-api/terraform/development/providers.tf deleted file mode 100644 index 7b9ce5c..0000000 --- a/notifications-api/terraform/development/providers.tf +++ /dev/null @@ -1,16 +0,0 @@ -terraform { - required_version = "~> 1.7" - required_providers { - cloudfoundry = { - source = "cloudfoundry-community/cloudfoundry" - version = "0.53.1" - } - } -} - -provider "cloudfoundry" { - api_url = "https://api.fr.cloud.gov" - user = var.cf_user - password = var.cf_password - app_logs_max = 30 -} diff --git a/notifications-api/terraform/development/reset.sh b/notifications-api/terraform/development/reset.sh deleted file mode 100755 index ec5cf25..0000000 --- a/notifications-api/terraform/development/reset.sh +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env bash - -username=`whoami` -org="gsa-tts-benefits-studio" - -usage=" -$0: Reset terraform state so run.sh can be run again or for a new username - -Usage: - $0 -h - $0 [-u ] - -Options: --h: show help and exit --u : your username. Default: $username - -Notes: -* Requires cf-cli@8 -" - -while getopts ":hu:" opt; do - case "$opt" in - u) - username=${OPTARG} - ;; - h) - echo "$usage" - exit 0 - ;; - esac -done - -read -p "Are you sure you want to import terraform state and remove existing service keys for $username (y/n)? " verify - -if [[ $verify != "y" ]]; then - exit 0 -fi - -# ensure we're in the correct directory -cd $(dirname $0) - -service_account="$username-terraform" - -if [[ ! -s "secrets.auto.tfvars" ]]; then - # create user in notify-local-dev space to create s3 buckets - ../create_service_account.sh -s notify-local-dev -u $service_account > secrets.auto.tfvars - - # grant user access to notify-staging to create a service key for SES and SNS - cg_username=`cf service-key $service_account service-account-key | tail -n +2 | jq -r '.credentials.username'` - cf set-space-role $cg_username $org notify-staging SpaceDeveloper -fi - -echo "Importing terraform state for $username" -terraform init -upgrade - -key_name=$username-api-dev-key - -cf t -s notify-local-dev -terraform import -var "username=$username" module.csv_upload_bucket.cloudfoundry_service_instance.bucket $(cf service --guid $username-csv-upload-bucket) -cf delete-service-key -f $username-csv-upload-bucket $key_name -cf t -s notify-staging -cf delete-service-key -f notify-api-ses-staging $key_name -cf delete-service-key -f notify-api-sns-staging $key_name - -./run.sh -u $username diff --git a/notifications-api/terraform/development/run.sh b/notifications-api/terraform/development/run.sh deleted file mode 100755 index 4fe1450..0000000 --- a/notifications-api/terraform/development/run.sh +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env bash - -username=`whoami` -org="gsa-tts-benefits-studio" - -usage=" -$0: Create development infrastructure - -Usage: - $0 -h - $0 [-u ] [-k] [-d] - -Options: --h: show help and exit --u : your username. Default: $username --k: keep service user. Default is to remove them after run --d: Destroy development resources. Default is to create them - -Notes: -* Requires cf-cli@8 -" - -action="apply" -creds="remove" - -while getopts ":hkdu:" opt; do - case "$opt" in - u) - username=${OPTARG} - ;; - k) - creds="keep" - ;; - d) - action="destroy" - ;; - h) - echo "$usage" - exit 0 - ;; - esac -done - -set -e - -# ensure we're in the correct directory -cd $(dirname $0) - -service_account="$username-terraform" - -if [[ ! -s "secrets.auto.tfvars" ]]; then - # create user in notify-local-dev space to create s3 buckets - ../create_service_account.sh -s notify-local-dev -u $service_account > secrets.auto.tfvars - - # grant user access to notify-staging to create a service key for SES and SNS - cg_username=`cf service-key $service_account service-account-key | tail -n +2 | jq -r '.credentials.username'` - cf set-space-role $cg_username $org notify-staging SpaceDeveloper -fi - -if [[ ! -f "../../.env" ]]; then - cp ../../sample.env ../../.env -fi - -set +e - -terraform init -terraform $action -var="username=$username" - -set -e - -if [[ $creds = "remove" ]]; then - ../destroy_service_account.sh -s notify-local-dev -u $service_account - rm secrets.auto.tfvars -fi - -exit 0 diff --git a/notifications-api/terraform/development/variables.tf b/notifications-api/terraform/development/variables.tf deleted file mode 100644 index d6f2df3..0000000 --- a/notifications-api/terraform/development/variables.tf +++ /dev/null @@ -1,9 +0,0 @@ -variable "cf_password" { - sensitive = true -} -variable "cf_user" {} -variable "username" {} -variable "source_ip" { - type = string - default = "0.0.0.0/0" -} diff --git a/notifications-api/terraform/ops/cloudgov_user_report.py b/notifications-api/terraform/ops/cloudgov_user_report.py deleted file mode 100644 index 02a9d84..0000000 --- a/notifications-api/terraform/ops/cloudgov_user_report.py +++ /dev/null @@ -1,85 +0,0 @@ -from subprocess import check_output - -from cloudfoundry_client.client import CloudFoundryClient - -ORG_NAME = "gsa-tts-benefits-studio" - - -client = CloudFoundryClient.build_from_cf_config() -org_guid = check_output(f"cf org {ORG_NAME} --guid", shell=True).decode().strip() -space_guids = list( - map(lambda item: item["guid"], client.v3.spaces.list(organization_guids=org_guid)) -) - - -class RoleCollector: - def __init__(self): - self._map = {} - - def add(self, role): - user = role.user - if self._map.get(user.guid) is None: - self._map[user.guid] = {"user": user, "roles": [role]} - else: - self._map[user.guid]["roles"].append(role) - - def print(self): - for user_roles in self._map.values(): - user = user_roles["user"] - print(f"{user.type}: {user.username} has roles:") - for role in user_roles["roles"]: - if role.space: - print(f" {role.type} in {role.space.name}") - else: - print(f" {role.type}") - - -role_collector = RoleCollector() - - -class User: - def __init__(self, entity): - self.guid = entity["guid"] - self._username = entity["username"] - self._is_service_account = entity["origin"] != "gsa.gov" - self.type = "Bot" if self._is_service_account else "User" - - @property - def username(self): - if self._is_service_account: - return client.v3.service_credential_bindings.get( - self._username, include="service_instance" - ).service_instance()["name"] - else: - return self._username - - -class Space: - def __init__(self, entity): - self.name = entity["name"] - - -class Role: - def __init__(self, entity): - self._fields = entity - self.type = entity["type"] - self.user = User(entity.user()) - - @property - def space(self): - try: - return Space(self._fields.space()) - except AttributeError: - return None - - -for role in map( - Role, client.v3.roles.list(organization_guids=org_guid, include="user") -): - role_collector.add(role) -for role in map(Role, client.v3.roles.list(space_guids=space_guids, include="user")): - role_collector.add(role) - - -if __name__ == "__main__": - role_collector.print() diff --git a/notifications-api/terraform/production/main.tf b/notifications-api/terraform/production/main.tf deleted file mode 100644 index 9543fdd..0000000 --- a/notifications-api/terraform/production/main.tf +++ /dev/null @@ -1,76 +0,0 @@ -locals { - cf_org_name = "gsa-tts-benefits-studio" - cf_space_name = "notify-production" - env = "production" - app_name = "notify-api" -} - -resource "null_resource" "prevent_destroy" { - - lifecycle { - prevent_destroy = true - } -} - -module "database" { - source = "github.com/GSA-TTS/terraform-cloudgov//database?ref=v1.0.0" - - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-rds-${local.env}" - rds_plan_name = "medium-gp-psql-redundant" -} - -module "redis-v70" { - source = "github.com/GSA-TTS/terraform-cloudgov//redis?ref=v1.0.0" - - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-redis-v70-${local.env}" - redis_plan_name = "redis-3node-large" - json_params = jsonencode( - { - "engineVersion" : "7.0", - } - ) -} - -module "csv_upload_bucket" { - source = "github.com/GSA-TTS/terraform-cloudgov//s3?ref=v1.0.0" - - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-csv-upload-bucket-${local.env}" -} - -module "egress-space" { - source = "../shared/egress_space" - - cf_org_name = local.cf_org_name - cf_restricted_space_name = local.cf_space_name - deployers = [ - var.cf_user - ] -} - -module "ses_email" { - source = "../shared/ses" - - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-ses-${local.env}" - aws_region = "us-gov-west-1" - email_domain = "notify.gov" - mail_from_subdomain = "mail" - email_receipt_error = "notify-support@gsa.gov" -} - -module "sns_sms" { - source = "../shared/sns" - - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-sns-${local.env}" - aws_region = "us-gov-west-1" - monthly_spend_limit = 1000 -} diff --git a/notifications-api/terraform/production/providers.tf b/notifications-api/terraform/production/providers.tf deleted file mode 100644 index 634e5dc..0000000 --- a/notifications-api/terraform/production/providers.tf +++ /dev/null @@ -1,37 +0,0 @@ -terraform { - required_version = "~> 1.7" - required_providers { - cloudfoundry = { - source = "cloudfoundry/cloudfoundry" - version = "1.9.0" - } - cfcommunity = { - source = "cloudfoundry-community/cloudfoundry" - version = "0.53.1" - } - } - - backend "s3" { - bucket = "cg-6b759c13-6253-4a64-9bda-dd1f620185b0" - key = "api.tfstate.prod" - encrypt = "true" - region = "us-gov-west-1" - use_lockfile = "true" - } -} - -# Official provider (should be default but aliased for now) -provider "cloudfoundry" { - alias = "official" - api_url = "https://api.fr.cloud.gov" - user = var.cf_user - password = var.cf_password -} - -# Community provider (should be aliased but default for now) -provider "cfcommunity" { - api_url = "https://api.fr.cloud.gov" - user = var.cf_user - password = var.cf_password - app_logs_max = 30 -} diff --git a/notifications-api/terraform/production/variables.tf b/notifications-api/terraform/production/variables.tf deleted file mode 100644 index a24f2f3..0000000 --- a/notifications-api/terraform/production/variables.tf +++ /dev/null @@ -1,4 +0,0 @@ -variable "cf_password" { - sensitive = true -} -variable "cf_user" {} diff --git a/notifications-api/terraform/sandbox/main.tf b/notifications-api/terraform/sandbox/main.tf deleted file mode 100644 index 148f351..0000000 --- a/notifications-api/terraform/sandbox/main.tf +++ /dev/null @@ -1,75 +0,0 @@ -locals { - cf_org_name = "gsa-tts-benefits-studio" - cf_space_name = "notify-sandbox" - env = "sandbox" - app_name = "notify-api" -} - -resource "null_resource" "prevent_destroy" { - - lifecycle { - prevent_destroy = false - } -} - -module "database" { - source = "github.com/GSA-TTS/terraform-cloudgov//database?ref=v1.0.0" - - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-rds-${local.env}" - rds_plan_name = "micro-psql" -} - -module "redis-v70" { - source = "github.com/GSA-TTS/terraform-cloudgov//redis?ref=v1.0.0" - - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-redis-v70-${local.env}" - redis_plan_name = "redis-dev" - json_params = jsonencode( - { - "engineVersion" : "7.0", - } - ) -} - -module "csv_upload_bucket" { - source = "github.com/GSA-TTS/terraform-cloudgov//s3?ref=v1.0.0" - - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-csv-upload-bucket-${local.env}" -} - -module "egress-space" { - source = "../shared/egress_space" - - cf_org_name = local.cf_org_name - cf_restricted_space_name = local.cf_space_name - deployers = [ - var.cf_user, - "alex.janousek@gsa.gov" - ] -} - -module "ses_email" { - source = "../shared/ses" - - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-ses-${local.env}" - aws_region = "us-west-2" - email_receipt_error = "notify-support@gsa.gov" -} - -module "sns_sms" { - source = "../shared/sns" - - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-sns-${local.env}" - aws_region = "us-east-2" - monthly_spend_limit = 1 -} diff --git a/notifications-api/terraform/sandbox/providers.tf b/notifications-api/terraform/sandbox/providers.tf deleted file mode 100644 index debd967..0000000 --- a/notifications-api/terraform/sandbox/providers.tf +++ /dev/null @@ -1,24 +0,0 @@ -terraform { - required_version = "~> 1.7" - required_providers { - cloudfoundry = { - source = "cloudfoundry-community/cloudfoundry" - version = "0.53.1" - } - } - - backend "s3" { - bucket = "cg-6b759c13-6253-4a64-9bda-dd1f620185b0" - key = "api.tfstate.sandbox" - encrypt = "true" - region = "us-gov-west-1" - use_lockfile = "true" - } -} - -provider "cloudfoundry" { - api_url = "https://api.fr.cloud.gov" - user = var.cf_user - password = var.cf_password - app_logs_max = 30 -} diff --git a/notifications-api/terraform/sandbox/variables.tf b/notifications-api/terraform/sandbox/variables.tf deleted file mode 100644 index a24f2f3..0000000 --- a/notifications-api/terraform/sandbox/variables.tf +++ /dev/null @@ -1,4 +0,0 @@ -variable "cf_password" { - sensitive = true -} -variable "cf_user" {} diff --git a/notifications-api/terraform/set_space_egress.sh b/notifications-api/terraform/set_space_egress.sh deleted file mode 100755 index ee2b6b3..0000000 --- a/notifications-api/terraform/set_space_egress.sh +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env bash - -org="gsa-tts-benefits-studio" - -usage=" -$0: Set egress rules for given space - -Usage: - $0 -h - $0 -s [-o ] [-p] [-t] - -Options: --h: show help and exit --s : configure the space to act on. Required --o : configure the organization to act on. Default: $org --p: Add the public egress rules --t: Add the trusted egress rules - -Notes: -* If -p or -t are not passed, the related security groups will be removed, if they were present -" - -set -e - -space="" -public=false -trusted=false - -while getopts ":hs:o:pt" opt; do - case "$opt" in - s) - space=${OPTARG} - ;; - o) - org=${OPTARG} - ;; - p) - public=true - ;; - t) - trusted=true - ;; - h) - echo "$usage" - exit 0 - ;; - esac -done - -if [[ $space = "" ]]; then - echo "$usage" - exit 1 -fi - -if [[ $public = true ]]; then - cf bind-security-group public_networks_egress $org --space $space -else - cf unbind-security-group public_networks_egress $org $space -fi - -if [[ $trusted = true ]]; then - cf bind-security-group trusted_local_networks_egress $org --space $space -else - cf unbind-security-group trusted_local_networks_egress $org $space -fi - -echo "Done" diff --git a/notifications-api/terraform/shared/egress_space/main.tf b/notifications-api/terraform/shared/egress_space/main.tf deleted file mode 100644 index 4b841ad..0000000 --- a/notifications-api/terraform/shared/egress_space/main.tf +++ /dev/null @@ -1,36 +0,0 @@ -### -# Target space/org -### - -data "cloudfoundry_org" "org" { - name = var.cf_org_name -} - -### -# Egress Space -### - -resource "cloudfoundry_space" "public_egress" { - name = "${var.cf_restricted_space_name}-egress" - org = data.cloudfoundry_org.org.id -} - -### -# User roles -### - -data "cloudfoundry_user" "users" { - for_each = var.deployers - name = each.key - org_id = data.cloudfoundry_org.org.id -} - -locals { - user_ids = [for user in data.cloudfoundry_user.users : user.id] -} - -resource "cloudfoundry_space_users" "deployers" { - space = cloudfoundry_space.public_egress.id - managers = local.user_ids - developers = local.user_ids -} diff --git a/notifications-api/terraform/shared/egress_space/providers.tf b/notifications-api/terraform/shared/egress_space/providers.tf deleted file mode 100644 index dec8379..0000000 --- a/notifications-api/terraform/shared/egress_space/providers.tf +++ /dev/null @@ -1,9 +0,0 @@ -terraform { - required_version = "~> 1.7" - required_providers { - cloudfoundry = { - source = "cloudfoundry-community/cloudfoundry" - version = "0.53.1" - } - } -} diff --git a/notifications-api/terraform/shared/egress_space/variables.tf b/notifications-api/terraform/shared/egress_space/variables.tf deleted file mode 100644 index 45bcc71..0000000 --- a/notifications-api/terraform/shared/egress_space/variables.tf +++ /dev/null @@ -1,5 +0,0 @@ -variable "cf_org_name" {} -variable "cf_restricted_space_name" {} -variable "deployers" { - type = set(string) -} diff --git a/notifications-api/terraform/shared/ses/main.tf b/notifications-api/terraform/shared/ses/main.tf deleted file mode 100644 index 4c1bb54..0000000 --- a/notifications-api/terraform/shared/ses/main.tf +++ /dev/null @@ -1,29 +0,0 @@ -### -# Target space/org -### - -data "cloudfoundry_space" "space" { - org_name = var.cf_org_name - name = var.cf_space_name -} - -### -# SES instance -### - -data "cloudfoundry_service" "ses" { - name = "datagov-smtp" -} - -resource "cloudfoundry_service_instance" "ses" { - name = var.name - space = data.cloudfoundry_space.space.id - service_plan = data.cloudfoundry_service.ses.service_plans["base"] - json_params = jsonencode({ - region = var.aws_region - domain = var.email_domain - mail_from_subdomain = var.mail_from_subdomain - email_receipt_error = var.email_receipt_error - enable_feedback_notifications = true - }) -} diff --git a/notifications-api/terraform/shared/ses/providers.tf b/notifications-api/terraform/shared/ses/providers.tf deleted file mode 100644 index dec8379..0000000 --- a/notifications-api/terraform/shared/ses/providers.tf +++ /dev/null @@ -1,9 +0,0 @@ -terraform { - required_version = "~> 1.7" - required_providers { - cloudfoundry = { - source = "cloudfoundry-community/cloudfoundry" - version = "0.53.1" - } - } -} diff --git a/notifications-api/terraform/shared/ses/variables.tf b/notifications-api/terraform/shared/ses/variables.tf deleted file mode 100644 index a922616..0000000 --- a/notifications-api/terraform/shared/ses/variables.tf +++ /dev/null @@ -1,36 +0,0 @@ -variable "cf_org_name" { - type = string - description = "cloud.gov organization name" -} - -variable "cf_space_name" { - type = string - description = "cloud.gov space name (staging or prod)" -} - -variable "name" { - type = string - description = "name of the service instance" -} - -variable "aws_region" { - type = string - description = "AWS region the SES instance is in" -} - -variable "email_domain" { - type = string - default = "" - description = "domain name that emails will be coming from" -} - -variable "email_receipt_error" { - type = string - description = "email address to list in SPF records for errors to be sent to" -} - -variable "mail_from_subdomain" { - type = string - description = "Subdomain of email_domain to set as the mail-from header" - default = "" -} diff --git a/notifications-api/terraform/shared/sns/main.tf b/notifications-api/terraform/shared/sns/main.tf deleted file mode 100644 index aa0079f..0000000 --- a/notifications-api/terraform/shared/sns/main.tf +++ /dev/null @@ -1,26 +0,0 @@ -### -# Target space/org -### - -data "cloudfoundry_space" "space" { - org_name = var.cf_org_name - name = var.cf_space_name -} - -### -# SES instance -### - -data "cloudfoundry_service" "sns" { - name = "ttsnotify-sms" -} - -resource "cloudfoundry_service_instance" "sns" { - name = var.name - space = data.cloudfoundry_space.space.id - service_plan = data.cloudfoundry_service.sns.service_plans["base"] - json_params = jsonencode({ - region = var.aws_region - monthly_spend_limit = var.monthly_spend_limit - }) -} diff --git a/notifications-api/terraform/shared/sns/providers.tf b/notifications-api/terraform/shared/sns/providers.tf deleted file mode 100644 index dec8379..0000000 --- a/notifications-api/terraform/shared/sns/providers.tf +++ /dev/null @@ -1,9 +0,0 @@ -terraform { - required_version = "~> 1.7" - required_providers { - cloudfoundry = { - source = "cloudfoundry-community/cloudfoundry" - version = "0.53.1" - } - } -} diff --git a/notifications-api/terraform/shared/sns/variables.tf b/notifications-api/terraform/shared/sns/variables.tf deleted file mode 100644 index acf7c50..0000000 --- a/notifications-api/terraform/shared/sns/variables.tf +++ /dev/null @@ -1,24 +0,0 @@ -variable "cf_org_name" { - type = string - description = "cloud.gov organization name" -} - -variable "cf_space_name" { - type = string - description = "cloud.gov space name (staging or prod)" -} - -variable "name" { - type = string - description = "name of the service instance" -} - -variable "aws_region" { - type = string - description = "AWS region the SNS settings are set in" -} - -variable "monthly_spend_limit" { - type = number - description = "SMS budget limit in USD. Support request must be made before raising above 1" -} diff --git a/notifications-api/terraform/staging/main.tf b/notifications-api/terraform/staging/main.tf deleted file mode 100644 index 194c556..0000000 --- a/notifications-api/terraform/staging/main.tf +++ /dev/null @@ -1,85 +0,0 @@ -locals { - cf_org_name = "gsa-tts-benefits-studio" - cf_space_name = "notify-staging" - env = "staging" - app_name = "notify-api" -} - -resource "null_resource" "prevent_destroy" { - - lifecycle { - prevent_destroy = false # destroying staging is allowed - } -} - - -data "cloudfoundry_space" "space" { - provider = cloudfoundry.official - org = "9e428562-a2d9-41b4-9c23-1ef5237fb44e" - name = local.cf_space_name -} - -module "database" { - source = "github.com/GSA-TTS/terraform-cloudgov//database?ref=v1.0.0" - - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-rds-${local.env}" - rds_plan_name = "small-psql" -} - - -module "redis-v70" { - source = "github.com/GSA-TTS/terraform-cloudgov//redis?ref=v2.4.0" - # Right now the default is cfcommunity, remove this when default is cloudfoundry - providers = { - cloudfoundry = cloudfoundry.official - } - cf_space_id = data.cloudfoundry_space.space.id - name = "${local.app_name}-redis-v70-${local.env}" - redis_plan_name = "redis-dev" - json_params = jsonencode( - { - "engineVersion" : "7.0", - } - ) -} - -module "csv_upload_bucket" { - source = "github.com/GSA-TTS/terraform-cloudgov//s3?ref=v1.0.0" - - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-csv-upload-bucket-${local.env}" -} - -module "egress-space" { - source = "../shared/egress_space" - - cf_org_name = local.cf_org_name - cf_restricted_space_name = local.cf_space_name - deployers = [ - var.cf_user, - "alex.janousek@gsa.gov" - ] -} - -module "ses_email" { - source = "../shared/ses" - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-ses-${local.env}" - aws_region = "us-west-2" - mail_from_subdomain = "mail" - email_receipt_error = "notify-support@gsa.gov" -} - -module "sns_sms" { - source = "../shared/sns" - - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-sns-${local.env}" - aws_region = "us-west-2" - monthly_spend_limit = 25 -} diff --git a/notifications-api/terraform/staging/providers.tf b/notifications-api/terraform/staging/providers.tf deleted file mode 100644 index 49d7234..0000000 --- a/notifications-api/terraform/staging/providers.tf +++ /dev/null @@ -1,37 +0,0 @@ -terraform { - required_version = "~> 1.7" - required_providers { - cloudfoundry = { - source = "cloudfoundry/cloudfoundry" - version = "1.9.0" - } - cfcommunity = { - source = "cloudfoundry-community/cloudfoundry" - version = "0.53.1" - } - } - - backend "s3" { - bucket = "cg-6b759c13-6253-4a64-9bda-dd1f620185b0" - key = "api.tfstate.stage" - encrypt = "true" - region = "us-gov-west-1" - use_lockfile = "true" - } -} - -# Official provider (should be default but aliased for now) -provider "cloudfoundry" { - alias = "official" - api_url = "https://api.fr.cloud.gov" - user = var.cf_user - password = var.cf_password -} - -# Community provider (should be aliased but default for now) -provider "cfcommunity" { - api_url = "https://api.fr.cloud.gov" - user = var.cf_user - password = var.cf_password - app_logs_max = 30 -} diff --git a/notifications-api/terraform/staging/variables.tf b/notifications-api/terraform/staging/variables.tf deleted file mode 100644 index a24f2f3..0000000 --- a/notifications-api/terraform/staging/variables.tf +++ /dev/null @@ -1,4 +0,0 @@ -variable "cf_password" { - sensitive = true -} -variable "cf_user" {} diff --git a/notifications-api/tests/app/test_cloudfoundry_config.py b/notifications-api/tests/app/test_cloudfoundry_config.py deleted file mode 100644 index d683a3c..0000000 --- a/notifications-api/tests/app/test_cloudfoundry_config.py +++ /dev/null @@ -1,82 +0,0 @@ -import json -import os - -import pytest - -from app.cloudfoundry_config import CloudfoundryConfig - -_bucket_credentials = { - "access_key_id": "csv-access", - "bucket": "csv-upload-bucket", - "region": "us-gov-west-1", - "secret_access_key": "csv-secret", -} -_postgres_url = "postgres://postgres:password@localhost:5432/db_name" - - -@pytest.fixture -def vcap_services(): - return { - "aws-rds": [{"credentials": {"uri": _postgres_url}}], - "aws-elasticache-redis": [{"credentials": {"uri": "redis://xxx:6379"}}], - "s3": [ - { - "name": "notify-api-csv-upload-bucket-test", - "credentials": _bucket_credentials, - }, - { - "name": "notify-api-contact-list-bucket-test", - "credentials": { - "access_key_id": "contact-access", - "bucket": "contact-list-bucket", - "region": "us-gov-west-1", - "secret_access_key": "contact-secret", - }, - }, - ], - "user-provided": [], - } - - -def test_database_url(vcap_services): - os.environ["DATABASE_URL"] = _postgres_url - - assert ( - CloudfoundryConfig().database_url - == "postgresql://postgres:password@localhost:5432/db_name" - ) - - -def test_redis_url(vcap_services): - os.environ["VCAP_SERVICES"] = json.dumps(vcap_services) - - assert CloudfoundryConfig().redis_url == "rediss://xxx:6379" - - -def test_redis_url_falls_back_to_REDIS_URL(): - expected = "redis://yyy:6379" - os.environ["REDIS_URL"] = expected - os.environ["VCAP_SERVICES"] = "" - - assert CloudfoundryConfig().redis_url == expected - - -def test_s3_bucket_credentials(vcap_services): - os.environ["VCAP_SERVICES"] = json.dumps(vcap_services) - - assert ( - CloudfoundryConfig().s3_credentials("notify-api-csv-upload-bucket-test") - == _bucket_credentials - ) - - -def test_s3_bucket_credentials_falls_back_to_empty_creds(): - os.environ["VCAP_SERVICES"] = "" - expected = { - "bucket": "", - "access_key_id": "", - "secret_access_key": "", - "region": "", - } - - assert CloudfoundryConfig().s3_credentials("bucket") == expected From 9b7ded014f283c6f12fa2a5fee1f315799846a49 Mon Sep 17 00:00:00 2001 From: Rachel Rogers Date: Tue, 24 Mar 2026 08:21:01 -0700 Subject: [PATCH 2/8] Replace Cloud.gov config class with Aws config class. Use IAM role for authentication instead of env variables. --- .ds.baseline | 4 +- notifications-admin/app/aws_config.py | 16 +++++ notifications-admin/app/config.py | 21 ++---- .../app/main/views/activity.py | 6 -- notifications-admin/app/s3_client/__init__.py | 8 --- .../app/s3_client/s3_csv_client.py | 14 +--- .../app/s3_client/s3_logo_client.py | 10 +-- notifications-admin/notifications_utils/s3.py | 18 +---- .../main/views/test_download_availability.py | 4 -- .../app/s3_client/test_s3_client_coverage.py | 14 +--- .../app/s3_client/test_s3_logo_client.py | 4 -- .../tests/app/test_aws_config.py | 23 ++++++ notifications-api/app/aws/s3.py | 51 +++----------- notifications-api/app/aws_config.py | 42 +++++++++++ notifications-api/app/clients/__init__.py | 4 -- .../app/clients/cloudwatch/aws_cloudwatch.py | 6 +- .../app/clients/email/aws_ses.py | 4 +- .../app/clients/pinpoint/aws_pinpoint.py | 4 +- notifications-api/app/clients/sms/aws_sns.py | 6 +- notifications-api/app/commands.py | 19 +---- notifications-api/app/config.py | 10 +-- notifications-api/pyproject.toml | 1 - notifications-api/sample.env | 4 +- notifications-api/tests/app/aws/test_s3.py | 53 ++------------ .../tests/app/clients/test_aws_cloudwatch.py | 2 +- .../app/delivery/test_send_to_providers.py | 7 +- .../tests/app/test_aws_config.py | 70 +++++++++++++++++++ 27 files changed, 197 insertions(+), 228 deletions(-) create mode 100644 notifications-admin/app/aws_config.py create mode 100644 notifications-admin/tests/app/test_aws_config.py create mode 100644 notifications-api/app/aws_config.py create mode 100644 notifications-api/tests/app/test_aws_config.py diff --git a/.ds.baseline b/.ds.baseline index c14ff7f..fc926a9 100644 --- a/.ds.baseline +++ b/.ds.baseline @@ -227,7 +227,7 @@ "filename": "notifications-admin/app/config.py", "hashed_secret": "577a4c667e4af8682ca431857214b3a920883efc", "is_verified": false, - "line_number": 127, + "line_number": 125, "is_secret": false } ], @@ -931,5 +931,5 @@ } ] }, - "generated_at": "2026-03-11T19:25:54Z" + "generated_at": "2026-03-24T15:18:53Z" } diff --git a/notifications-admin/app/aws_config.py b/notifications-admin/app/aws_config.py new file mode 100644 index 0000000..401355e --- /dev/null +++ b/notifications-admin/app/aws_config.py @@ -0,0 +1,16 @@ +from os import getenv + + +class AwsConfig: + @property + def redis_url(self): + return getenv("REDIS_URL") + + def s3_credentials(self, bucket_name): + return { + "bucket": getenv(f"{bucket_name}_BUCKET_NAME", bucket_name), + "region": getenv(f"{bucket_name}_AWS_REGION", "us-east-1"), + } + + +cloud_config = AwsConfig() diff --git a/notifications-admin/app/config.py b/notifications-admin/app/config.py index d715e4a..944943e 100644 --- a/notifications-admin/app/config.py +++ b/notifications-admin/app/config.py @@ -3,7 +3,7 @@ import newrelic.agent -from app.cloudfoundry_config import cloud_config +from app.aws_config import cloud_config from notifications_utils import DAILY_MESSAGE_LIMIT @@ -98,8 +98,6 @@ def _s3_credentials_from_env(bucket_prefix): "bucket": getenv( f"{bucket_prefix}_BUCKET_NAME", f"{bucket_prefix}-test-bucket-name" ), - "access_key_id": getenv(f"{bucket_prefix}_AWS_ACCESS_KEY_ID"), - "secret_access_key": getenv(f"{bucket_prefix}_AWS_SECRET_ACCESS_KEY"), "region": getenv(f"{bucket_prefix}_AWS_REGION"), } @@ -151,12 +149,8 @@ class Production(Config): DEBUG = False # buckets - CSV_UPLOAD_BUCKET = cloud_config.s3_credentials( - f"notify-api-csv-upload-bucket-{getenv('NOTIFY_ENVIRONMENT')}" - ) - LOGO_UPLOAD_BUCKET = cloud_config.s3_credentials( - f"notify-admin-logo-upload-bucket-{getenv('NOTIFY_ENVIRONMENT')}" - ) + CSV_UPLOAD_BUCKET = _s3_credentials_from_env("CSV") + LOGO_UPLOAD_BUCKET = _s3_credentials_from_env("LOGO") class Staging(Production): @@ -183,12 +177,8 @@ class E2ETest(Staging): WTF_CSRF_ENABLED = False # buckets - mirror staging - CSV_UPLOAD_BUCKET = cloud_config.s3_credentials( - "notify-api-csv-upload-bucket-staging" - ) - LOGO_UPLOAD_BUCKET = cloud_config.s3_credentials( - "notify-admin-logo-upload-bucket-staging" - ) + CSV_UPLOAD_BUCKET = _s3_credentials_from_env("CSV") + LOGO_UPLOAD_BUCKET = _s3_credentials_from_env("LOGO") class Demo(Staging): @@ -201,7 +191,6 @@ class Sandbox(Staging): class Scanning(Production): HTTP_PROTOCOL = "http" - API_HOST_NAME = "https://notify-api-staging.app.cloud.gov/" SECRET_KEY = "dev-notify-secret-key" # nosec B105 - only used in development ADMIN_CLIENT_USER_NAME = "notify-admin" ADMIN_CLIENT_SECRET = ( diff --git a/notifications-admin/app/main/views/activity.py b/notifications-admin/app/main/views/activity.py index d664c47..0e59b5f 100644 --- a/notifications-admin/app/main/views/activity.py +++ b/notifications-admin/app/main/views/activity.py @@ -23,8 +23,6 @@ def get_report_info(service_id, report_name, s3_config): obj = get_s3_object( s3_config["bucket"], key, - s3_config["access_key_id"], - s3_config["secret_access_key"], s3_config["region"], ) @@ -56,10 +54,6 @@ def get_download_availability(service_id): # Get S3 config before spawning greenlets s3_config = { "bucket": current_app.config["CSV_UPLOAD_BUCKET"]["bucket"], - "access_key_id": current_app.config["CSV_UPLOAD_BUCKET"]["access_key_id"], - "secret_access_key": current_app.config["CSV_UPLOAD_BUCKET"][ - "secret_access_key" - ], "region": current_app.config["CSV_UPLOAD_BUCKET"]["region"], } diff --git a/notifications-admin/app/s3_client/__init__.py b/notifications-admin/app/s3_client/__init__.py index dac9a75..256f817 100644 --- a/notifications-admin/app/s3_client/__init__.py +++ b/notifications-admin/app/s3_client/__init__.py @@ -8,27 +8,19 @@ from notifications_utils.s3 import S3ObjectNotFound AWS_CLIENT_CONFIG = Config( - # This config is required to enable S3 to connect to FIPS-enabled - # endpoints. See https://aws.amazon.com/compliance/fips/ for more - # information. s3={ "addressing_style": "virtual", }, - use_fips_endpoint=True, ) def get_s3_object( bucket_name, filename, - access_key, - secret_key, region, ): # To inspect contents: obj.get()['Body'].read().decode('utf-8') session = Session( - aws_access_key_id=access_key, - aws_secret_access_key=secret_key, region_name=region, ) s3 = session.resource( diff --git a/notifications-admin/app/s3_client/s3_csv_client.py b/notifications-admin/app/s3_client/s3_csv_client.py index 518e183..a8b61c6 100644 --- a/notifications-admin/app/s3_client/s3_csv_client.py +++ b/notifications-admin/app/s3_client/s3_csv_client.py @@ -20,13 +20,7 @@ def get_csv_location(service_id, upload_id): key = NEW_FILE_LOCATION_STRUCTURE.format(service_id, upload_id) region = current_app.config["CSV_UPLOAD_BUCKET"]["region"] - return ( - bucket, - key, - current_app.config["CSV_UPLOAD_BUCKET"]["access_key_id"], - current_app.config["CSV_UPLOAD_BUCKET"]["secret_access_key"], - region, - ) + return (bucket, key, region) def get_csv_upload(service_id, upload_id): @@ -45,9 +39,7 @@ def s3upload(service_id, filedata): filedata = remove_blank_lines(filedata) upload_id = str(uuid.uuid4()) - bucket_name, file_location, access_key, secret_key, region = get_csv_location( - service_id, upload_id - ) + bucket_name, file_location, region = get_csv_location(service_id, upload_id) if bucket_name == "": exp_bucket = current_app.config["CSV_UPLOAD_BUCKET"]["bucket"] exp_region = current_app.config["CSV_UPLOAD_BUCKET"]["region"] @@ -61,8 +53,6 @@ def s3upload(service_id, filedata): region=region, bucket_name=bucket_name, file_location=file_location, - access_key=access_key, - secret_key=secret_key, ) return upload_id diff --git a/notifications-admin/app/s3_client/s3_logo_client.py b/notifications-admin/app/s3_client/s3_logo_client.py index 5a8453a..406c3fe 100644 --- a/notifications-admin/app/s3_client/s3_logo_client.py +++ b/notifications-admin/app/s3_client/s3_logo_client.py @@ -14,8 +14,6 @@ def get_logo_location(filename=None): return ( bucket_creds("bucket"), filename, - bucket_creds("access_key_id"), - bucket_creds("secret_access_key"), bucket_creds("region"), ) @@ -31,8 +29,8 @@ def delete_s3_object(filename): def persist_logo(old_name, new_name): if old_name == new_name: return - bucket_name, filename, access_key, secret_key, region = get_logo_location(new_name) - get_s3_object(bucket_name, filename, access_key, secret_key, region).copy_from( + bucket_name, filename, region = get_logo_location(new_name) + get_s3_object(bucket_name, filename, region).copy_from( CopySource="{}/{}".format(bucket_name, old_name) ) delete_s3_object(old_name) @@ -41,8 +39,6 @@ def persist_logo(old_name, new_name): def get_s3_objects_filter_by_prefix(prefix): bucket_name = bucket_creds("bucket") session = Session( - aws_access_key_id=bucket_creds("access_key_id"), - aws_secret_access_key=bucket_creds("secret_access_key"), region_name=bucket_creds("region"), ) s3 = session.resource("s3") @@ -67,8 +63,6 @@ def upload_email_logo(filename, filedata, user_id): bucket_name=bucket_name, file_location=upload_file_name, content_type="image/png", - access_key=bucket_creds("access_key_id"), - secret_key=bucket_creds("secret_access_key"), ) return upload_file_name diff --git a/notifications-admin/notifications_utils/s3.py b/notifications-admin/notifications_utils/s3.py index f7b3422..f6e7028 100644 --- a/notifications-admin/notifications_utils/s3.py +++ b/notifications-admin/notifications_utils/s3.py @@ -7,19 +7,11 @@ from flask import current_app AWS_CLIENT_CONFIG = Config( - # This config is required to enable S3 to connect to FIPS-enabled - # endpoints. See https://aws.amazon.com/compliance/fips/ for more - # information. s3={ "addressing_style": "virtual", }, - use_fips_endpoint=True, ) -default_access_key_id = os.environ.get("AWS_ACCESS_KEY_ID") -default_secret_access_key = os.environ.get("AWS_SECRET_ACCESS_KEY") -default_region = os.environ.get("AWS_REGION") - def s3upload( filedata, @@ -29,12 +21,8 @@ def s3upload( content_type="binary/octet-stream", tags=None, metadata=None, - access_key=default_access_key_id, - secret_key=default_secret_access_key, ): session = Session( - aws_access_key_id=access_key, - aws_secret_access_key=secret_key, region_name=region, ) _s3 = session.resource( @@ -84,14 +72,10 @@ class S3ObjectNotFound(botocore.exceptions.ClientError): def s3download( bucket_name, filename, - region=default_region, - access_key=default_access_key_id, - secret_key=default_secret_access_key, + region=None, ): try: session = Session( - aws_access_key_id=access_key, - aws_secret_access_key=secret_key, region_name=region, ) s3 = session.resource("s3", config=AWS_CLIENT_CONFIG) diff --git a/notifications-admin/tests/app/main/views/test_download_availability.py b/notifications-admin/tests/app/main/views/test_download_availability.py index aeb3488..9791855 100644 --- a/notifications-admin/tests/app/main/views/test_download_availability.py +++ b/notifications-admin/tests/app/main/views/test_download_availability.py @@ -13,8 +13,6 @@ def test_report_metadata_loading_fix(mocker): with patch("app.s3_client.check_s3_file_exists", return_value=True): s3_config = { "bucket": "test-bucket", - "access_key_id": "test-key", - "secret_access_key": "test-secret", # pragma: allowlist secret "region": "us-east-1", } @@ -31,8 +29,6 @@ def test_report_not_found(mocker): with patch("app.s3_client.check_s3_file_exists", return_value=False): s3_config = { "bucket": "test-bucket", - "access_key_id": "test-key", - "secret_access_key": "test-secret", # pragma: allowlist secret "region": "us-east-1", } diff --git a/notifications-admin/tests/app/s3_client/test_s3_client_coverage.py b/notifications-admin/tests/app/s3_client/test_s3_client_coverage.py index 2cc055f..a5eb4c7 100644 --- a/notifications-admin/tests/app/s3_client/test_s3_client_coverage.py +++ b/notifications-admin/tests/app/s3_client/test_s3_client_coverage.py @@ -30,13 +30,9 @@ def test_get_s3_object_production(self, mock_session): mock_session.return_value.resource.return_value = mock_resource mock_resource.Object.return_value = mock_obj - result = get_s3_object( - "test-bucket", "test-file.txt", "access-key", "secret-key", "us-east-1" - ) + result = get_s3_object("test-bucket", "test-file.txt", "us-east-1") mock_session.assert_called_once_with( - aws_access_key_id="access-key", - aws_secret_access_key="secret-key", region_name="us-east-1", ) mock_session.return_value.resource.assert_called_once_with( @@ -58,9 +54,7 @@ def test_get_s3_object_test_env_with_mock(self, mock_session): mock_resource.Object.return_value = mock_obj mock_resource.Bucket.return_value = mock_bucket - result = get_s3_object( - "test-bucket", "test-file.txt", "access-key", "secret-key", "us-east-1" - ) + result = get_s3_object("test-bucket", "test-file.txt", "us-east-1") assert result == mock_obj @@ -78,9 +72,7 @@ def test_get_s3_object_test_env_without_mock(self, mock_session): mock_resource.Bucket.return_value = mock_bucket with pytest.raises(Exception, match="Test is not mocked") as exc_info: - get_s3_object( - "test-bucket", "test-file.txt", "access-key", "secret-key", "us-east-1" - ) + get_s3_object("test-bucket", "test-file.txt", "us-east-1") assert "Test is not mocked" in str(exc_info.value) diff --git a/notifications-admin/tests/app/s3_client/test_s3_logo_client.py b/notifications-admin/tests/app/s3_client/test_s3_logo_client.py index dc6e419..11216de 100644 --- a/notifications-admin/tests/app/s3_client/test_s3_logo_client.py +++ b/notifications-admin/tests/app/s3_client/test_s3_logo_client.py @@ -45,8 +45,6 @@ def test_upload_email_logo_calls_correct_args( file_location=upload_filename, bucket_name=bucket_credentials["bucket"], content_type="image/png", - access_key=bucket_credentials["access_key_id"], - secret_key=bucket_credentials["secret_access_key"], ) @@ -65,8 +63,6 @@ def test_persist_logo( mocked_get_s3_object.assert_called_once_with( bucket_credentials["bucket"], new_filename, - bucket_credentials["access_key_id"], - bucket_credentials["secret_access_key"], bucket_credentials["region"], ) mocked_delete_s3_object.assert_called_once_with(upload_filename) diff --git a/notifications-admin/tests/app/test_aws_config.py b/notifications-admin/tests/app/test_aws_config.py new file mode 100644 index 0000000..72499eb --- /dev/null +++ b/notifications-admin/tests/app/test_aws_config.py @@ -0,0 +1,23 @@ +import os + +from app.aws_config import AwsConfig + + +def test_redis_url(): + os.environ["REDIS_URL"] = "redis://localhost:6379" + assert AwsConfig().redis_url == "redis://localhost:6379" + + +def test_redis_url_returns_none_when_not_set(): + os.environ.pop("REDIS_URL", None) + assert AwsConfig().redis_url is None + + +def test_s3_credentials(): + os.environ["CSV_BUCKET_NAME"] = "my-csv-bucket" + os.environ["CSV_AWS_REGION"] = "us-east-1" + creds = AwsConfig().s3_credentials("CSV") + assert creds == { + "bucket": "my-csv-bucket", + "region": "us-east-1", + } diff --git a/notifications-api/app/aws/s3.py b/notifications-api/app/aws/s3.py index ce4e5f6..15565e9 100644 --- a/notifications-api/app/aws/s3.py +++ b/notifications-api/app/aws/s3.py @@ -67,28 +67,15 @@ def clean_cache(): def get_s3_client(): - - access_key = current_app.config["CSV_UPLOAD_BUCKET"]["access_key_id"] - secret_key = current_app.config["CSV_UPLOAD_BUCKET"]["secret_access_key"] region = current_app.config["CSV_UPLOAD_BUCKET"]["region"] - session = Session( - aws_access_key_id=access_key, - aws_secret_access_key=secret_key, - region_name=region, - ) + session = Session(region_name=region) s3_client = session.client("s3", config=AWS_CLIENT_CONFIG) return s3_client def get_s3_resource(): - access_key = current_app.config["CSV_UPLOAD_BUCKET"]["access_key_id"] - secret_key = current_app.config["CSV_UPLOAD_BUCKET"]["secret_access_key"] region = current_app.config["CSV_UPLOAD_BUCKET"]["region"] - session = Session( - aws_access_key_id=access_key, - aws_secret_access_key=secret_key, - region_name=region, - ) + session = Session(region_name=region) s3_resource = session.resource("s3", config=AWS_CLIENT_CONFIG) return s3_resource @@ -307,14 +294,12 @@ def get_s3_files(): ) -def get_s3_file(bucket_name, file_location, access_key, secret_key, region): - s3_file = get_s3_object(bucket_name, file_location, access_key, secret_key, region) +def get_s3_file(bucket_name, file_location): + s3_file = get_s3_object(bucket_name, file_location) return s3_file.get()["Body"].read().decode("utf-8") -def download_from_s3( - bucket_name, s3_key, local_filename, access_key, secret_key, region -): +def download_from_s3(bucket_name, s3_key, local_filename): s3 = get_s3_client() result = None @@ -334,7 +319,7 @@ def download_from_s3( return result -def get_s3_object(bucket_name, file_location, access_key, secret_key, region): +def get_s3_object(bucket_name, file_location): s3 = get_s3_resource() try: @@ -345,7 +330,7 @@ def get_s3_object(bucket_name, file_location, access_key, secret_key, region): ) -def purge_bucket(bucket_name, access_key, secret_key, region): +def purge_bucket(bucket_name): s3 = get_s3_resource() bucket = s3.Bucket(bucket_name) bucket.objects.all().delete() @@ -353,15 +338,10 @@ def purge_bucket(bucket_name, access_key, secret_key, region): def file_exists(file_location): bucket_name = current_app.config["CSV_UPLOAD_BUCKET"]["bucket"] - access_key = current_app.config["CSV_UPLOAD_BUCKET"]["access_key_id"] - secret_key = current_app.config["CSV_UPLOAD_BUCKET"]["secret_access_key"] - region = current_app.config["CSV_UPLOAD_BUCKET"]["region"] try: # try and access metadata of object - get_s3_object( - bucket_name, file_location, access_key, secret_key, region - ).metadata + get_s3_object(bucket_name, file_location).metadata return True except botocore.exceptions.ClientError as e: if e.response["ResponseMetadata"]["HTTPStatusCode"] == 404: @@ -370,13 +350,9 @@ def file_exists(file_location): def get_job_location(service_id, job_id): - return ( current_app.config["CSV_UPLOAD_BUCKET"]["bucket"], NEW_FILE_LOCATION_STRUCTURE.format(service_id, job_id), - current_app.config["CSV_UPLOAD_BUCKET"]["access_key_id"], - current_app.config["CSV_UPLOAD_BUCKET"]["secret_access_key"], - current_app.config["CSV_UPLOAD_BUCKET"]["region"], ) @@ -386,13 +362,9 @@ def get_old_job_location(service_id, job_id): but it will take a few days where we have to support both formats. Remove this when everything works with the NEW_FILE_LOCATION_STRUCTURE. """ - return ( current_app.config["CSV_UPLOAD_BUCKET"]["bucket"], FILE_LOCATION_STRUCTURE.format(service_id, job_id), - current_app.config["CSV_UPLOAD_BUCKET"]["access_key_id"], - current_app.config["CSV_UPLOAD_BUCKET"]["secret_access_key"], - current_app.config["CSV_UPLOAD_BUCKET"]["region"], ) @@ -623,8 +595,8 @@ def remove_job_from_s3(service_id, job_id): return remove_s3_object(*get_job_location(service_id, job_id)) -def remove_s3_object(bucket_name, object_key, access_key, secret_key, region): - obj = get_s3_object(bucket_name, object_key, access_key, secret_key, region) +def remove_s3_object(bucket_name, object_key): + obj = get_s3_object(bucket_name, object_key) return obj.delete() @@ -632,9 +604,6 @@ def remove_csv_object(object_key): obj = get_s3_object( current_app.config["CSV_UPLOAD_BUCKET"]["bucket"], object_key, - current_app.config["CSV_UPLOAD_BUCKET"]["access_key_id"], - current_app.config["CSV_UPLOAD_BUCKET"]["secret_access_key"], - current_app.config["CSV_UPLOAD_BUCKET"]["region"], ) return obj.delete() diff --git a/notifications-api/app/aws_config.py b/notifications-api/app/aws_config.py new file mode 100644 index 0000000..2c30a0f --- /dev/null +++ b/notifications-api/app/aws_config.py @@ -0,0 +1,42 @@ +from os import getenv + + +class AwsConfig: + @property + def database_url(self): + return getenv("DATABASE_URL", "").replace("postgres://", "postgresql://") + + @property + def redis_url(self): + return getenv("REDIS_URL") + + def s3_credentials(self, bucket_name): + return { + "bucket": getenv(f"{bucket_name}_BUCKET_NAME", bucket_name), + "region": getenv(f"{bucket_name}_AWS_REGION", "us-east-1"), + } + + @property + def ses_email_domain(self): + domain_arn = getenv("SES_DOMAIN_ARN", "") + return domain_arn.split("/")[-1] if "/" in domain_arn else domain_arn + + @property + def ses_domain_arn(self): + return getenv("SES_DOMAIN_ARN", "") + + @property + def ses_region(self): + return getenv("SES_AWS_REGION", "us-east-1") + + @property + def sns_region(self): + return getenv("SNS_AWS_REGION", "us-east-1") + + @property + def sns_topic_arns(self): + arns = getenv("VALID_SNS_TOPICS", "") + return [a.strip() for a in arns.split(",") if a.strip()] + + +cloud_config = AwsConfig() diff --git a/notifications-api/app/clients/__init__.py b/notifications-api/app/clients/__init__.py index f185e45..c25b816 100644 --- a/notifications-api/app/clients/__init__.py +++ b/notifications-api/app/clients/__init__.py @@ -6,13 +6,9 @@ from app.enums import NotificationType AWS_CLIENT_CONFIG = Config( - # This config is required to enable S3 to connect to FIPS-enabled - # endpoints. See https://aws.amazon.com/compliance/fips/ for more - # information. s3={ "addressing_style": "virtual", }, - use_fips_endpoint=True, max_pool_connections=50, # This should be equal or greater than our celery concurrency ) diff --git a/notifications-api/app/clients/cloudwatch/aws_cloudwatch.py b/notifications-api/app/clients/cloudwatch/aws_cloudwatch.py index 3ad5151..08d6c59 100644 --- a/notifications-api/app/clients/cloudwatch/aws_cloudwatch.py +++ b/notifications-api/app/clients/cloudwatch/aws_cloudwatch.py @@ -5,8 +5,8 @@ from boto3 import client from flask import current_app +from app.aws_config import cloud_config from app.clients import AWS_CLIENT_CONFIG, Client -from app.cloudfoundry_config import cloud_config from app.utils import hilite @@ -20,8 +20,6 @@ def init_app(self, current_app, *args, **kwargs): self._client = client( "logs", region_name=cloud_config.sns_region, - aws_access_key_id=cloud_config.sns_access_key, - aws_secret_access_key=cloud_config.sns_secret_key, config=AWS_CLIENT_CONFIG, endpoint_url=os.getenv("LOCALSTACK_ENDPOINT_URL"), ) @@ -30,8 +28,6 @@ def init_app(self, current_app, *args, **kwargs): self._client = client( "logs", region_name=cloud_config.sns_region, - aws_access_key_id=cloud_config.sns_access_key, - aws_secret_access_key=cloud_config.sns_secret_key, config=AWS_CLIENT_CONFIG, ) self._is_localstack = False diff --git a/notifications-api/app/clients/email/aws_ses.py b/notifications-api/app/clients/email/aws_ses.py index a5c404c..3412e45 100644 --- a/notifications-api/app/clients/email/aws_ses.py +++ b/notifications-api/app/clients/email/aws_ses.py @@ -4,13 +4,13 @@ from boto3 import client from flask import current_app +from app.aws_config import cloud_config from app.clients import AWS_CLIENT_CONFIG from app.clients.email import ( EmailClient, EmailClientException, EmailClientNonRetryableException, ) -from app.cloudfoundry_config import cloud_config from app.enums import NotificationStatus, StatisticsType ses_response_map = { @@ -62,8 +62,6 @@ def init_app(self, *args, **kwargs): self._client = client( "ses", region_name=cloud_config.ses_region, - aws_access_key_id=cloud_config.ses_access_key, - aws_secret_access_key=cloud_config.ses_secret_key, config=AWS_CLIENT_CONFIG, ) super(AwsSesClient, self).__init__(*args, **kwargs) diff --git a/notifications-api/app/clients/pinpoint/aws_pinpoint.py b/notifications-api/app/clients/pinpoint/aws_pinpoint.py index 0c4fc05..f3c6cd0 100644 --- a/notifications-api/app/clients/pinpoint/aws_pinpoint.py +++ b/notifications-api/app/clients/pinpoint/aws_pinpoint.py @@ -2,8 +2,8 @@ from botocore.exceptions import ClientError from flask import current_app +from app.aws_config import cloud_config from app.clients import AWS_CLIENT_CONFIG, Client -from app.cloudfoundry_config import cloud_config from app.utils import hilite @@ -13,8 +13,6 @@ def init_app(self, current_app, *args, **kwargs): self._client = client( "pinpoint", region_name=cloud_config.sns_region, - aws_access_key_id=cloud_config.sns_access_key, - aws_secret_access_key=cloud_config.sns_secret_key, config=AWS_CLIENT_CONFIG, ) diff --git a/notifications-api/app/clients/sms/aws_sns.py b/notifications-api/app/clients/sms/aws_sns.py index c05bc94..b638536 100644 --- a/notifications-api/app/clients/sms/aws_sns.py +++ b/notifications-api/app/clients/sms/aws_sns.py @@ -7,9 +7,9 @@ from boto3 import client from flask import current_app +from app.aws_config import cloud_config from app.clients import AWS_CLIENT_CONFIG from app.clients.sms import SmsClient -from app.cloudfoundry_config import cloud_config from app.utils import hilite @@ -23,8 +23,6 @@ def init_app(self, current_app, *args, **kwargs): self._client = client( "sns", region_name=cloud_config.sns_region, - aws_access_key_id=cloud_config.sns_access_key, - aws_secret_access_key=cloud_config.sns_secret_key, config=AWS_CLIENT_CONFIG, endpoint_url=os.getenv("LOCALSTACK_ENDPOINT_URL"), ) @@ -32,8 +30,6 @@ def init_app(self, current_app, *args, **kwargs): self._client = client( "sns", region_name=cloud_config.sns_region, - aws_access_key_id=cloud_config.sns_access_key, - aws_secret_access_key=cloud_config.sns_secret_key, config=AWS_CLIENT_CONFIG, ) diff --git a/notifications-api/app/commands.py b/notifications-api/app/commands.py index 53f3260..4bf7170 100644 --- a/notifications-api/app/commands.py +++ b/notifications-api/app/commands.py @@ -19,10 +19,7 @@ from app import db, redis_store from app.aws import s3 from app.celery.nightly_tasks import cleanup_unfinished_jobs -from app.celery.tasks import ( - generate_notification_reports_task, - process_row, -) +from app.celery.tasks import generate_notification_reports_task, process_row from app.dao.annual_billing_dao import ( dao_create_or_update_annual_billing_for_year, set_default_free_allowance_for_service, @@ -543,13 +540,7 @@ def download_csv_file_by_name(csv_filename): # poetry run flask command download-csv-file-by-name -f # cf run-task notify-api-production --command "flask command download-csv-file-by-name -f " bucket_name = current_app.config["CSV_UPLOAD_BUCKET"]["bucket"] - access_key = current_app.config["CSV_UPLOAD_BUCKET"]["access_key_id"] - secret = current_app.config["CSV_UPLOAD_BUCKET"]["secret_access_key"] - region = current_app.config["CSV_UPLOAD_BUCKET"]["region"] - - s3.download_from_s3( - bucket_name, csv_filename, "download.csv", access_key, secret, region - ) + s3.download_from_s3(bucket_name, csv_filename, "download.csv") @notify_command(name="dump-sms-senders") @@ -883,11 +874,7 @@ def promote_user_to_platform_admin(user_email_address): @notify_command(name="purge-csv-bucket") def purge_csv_bucket(): bucket_name = current_app.config["CSV_UPLOAD_BUCKET"]["bucket"] - access_key = current_app.config["CSV_UPLOAD_BUCKET"]["access_key_id"] - secret = current_app.config["CSV_UPLOAD_BUCKET"]["secret_access_key"] - region = current_app.config["CSV_UPLOAD_BUCKET"]["region"] - - s3.purge_bucket(bucket_name, access_key, secret, region) + s3.purge_bucket(bucket_name) """ diff --git a/notifications-api/app/config.py b/notifications-api/app/config.py index d9ae367..92a7bbf 100644 --- a/notifications-api/app/config.py +++ b/notifications-api/app/config.py @@ -7,8 +7,8 @@ from kombu import Exchange, Queue import notifications_utils +from app.aws_config import cloud_config from app.clients import AWS_CLIENT_CONFIG -from app.cloudfoundry_config import cloud_config class QueueNames(object): @@ -54,8 +54,6 @@ class TaskNames(object): session = Session( - aws_access_key_id=getenv("CSV_AWS_ACCESS_KEY_ID"), - aws_secret_access_key=getenv("CSV_AWS_SECRET_ACCESS_KEY"), region_name=getenv("CSV_AWS_REGION"), ) @@ -356,8 +354,6 @@ class Config(object): def _s3_credentials_from_env(bucket_prefix): return { "bucket": getenv(f"{bucket_prefix}_BUCKET_NAME"), - "access_key_id": getenv(f"{bucket_prefix}_AWS_ACCESS_KEY_ID"), - "secret_access_key": getenv(f"{bucket_prefix}_AWS_SECRET_ACCESS_KEY"), "region": getenv(f"{bucket_prefix}_AWS_REGION"), } @@ -407,9 +403,7 @@ class Test(Development): class Production(Config): # buckets - CSV_UPLOAD_BUCKET = cloud_config.s3_credentials( - f"notify-api-csv-upload-bucket-{Config.NOTIFY_ENVIRONMENT}" - ) + CSV_UPLOAD_BUCKET = _s3_credentials_from_env("CSV") FROM_NUMBER = "Flexion Messaging" diff --git a/notifications-api/pyproject.toml b/notifications-api/pyproject.toml index 1927f7d..2097ad0 100644 --- a/notifications-api/pyproject.toml +++ b/notifications-api/pyproject.toml @@ -89,7 +89,6 @@ wheel = ">=0.46.2" bandit = "*" black = "^26.1.0" cyclonedx-python-lib = "^11.6.0" -cloudfoundry-client = "*" exceptiongroup = "==1.3.1" flake8 = "^7.3.0" flake8-bugbear = "^25.11.29" diff --git a/notifications-api/sample.env b/notifications-api/sample.env index 4b97012..8defdf9 100644 --- a/notifications-api/sample.env +++ b/notifications-api/sample.env @@ -9,7 +9,7 @@ AWS_US_TOLL_FREE_NUMBER=+18556438890 ############################################################# -# Local Docker setup, all overwritten in cloud.gov +# Local Docker setup # ADMIN_BASE_URL=http://admin:6012 # API_HOST_NAME=http://dev:6011 # REDIS_URL=redis://redis:6380 @@ -20,7 +20,7 @@ AWS_US_TOLL_FREE_NUMBER=+18556438890 # If you want to do local development with localstack copy this to your .env file and uncomment it # LOCALSTACK_ENDPOINT_URL=http://localhost:4566 -# Local direct setup, all overwritten in cloud.gov +# Local direct setup ADMIN_BASE_URL=http://localhost:6012 API_HOST_NAME=http://localhost:6011 REDIS_URL=redis://localhost:6379 diff --git a/notifications-api/tests/app/aws/test_s3.py b/notifications-api/tests/app/aws/test_s3.py index 6a24599..f79b98a 100644 --- a/notifications-api/tests/app/aws/test_s3.py +++ b/notifications-api/tests/app/aws/test_s3.py @@ -1,7 +1,6 @@ import os import time from datetime import timedelta -from os import getenv from unittest.mock import MagicMock, Mock, call, patch import botocore @@ -36,10 +35,6 @@ from app.utils import utc_now from notifications_utils import aware_utcnow -default_access_key = getenv("CSV_AWS_ACCESS_KEY_ID") -default_secret_key = getenv("CSV_AWS_SECRET_ACCESS_KEY") -default_region = getenv("CSV_AWS_REGION") - def single_s3_object_stub(key="foo", last_modified=None): return { @@ -115,11 +110,7 @@ def test_download_from_s3_success(mocker): bucket_name = "test_bucket" s3_key = "test_key" local_filename = "test_file" - access_key = "access_key" - region = "test_region" - download_from_s3( - bucket_name, s3_key, local_filename, access_key, "secret_key", region - ) + download_from_s3(bucket_name, s3_key, local_filename) mock_s3.download_file.assert_called_once_with(bucket_name, s3_key, local_filename) mock_logger.info.assert_called_once_with( f"File downloaded successfully to {local_filename}" @@ -134,9 +125,7 @@ def test_download_from_s3_no_credentials_error(mocker): mock_s3.download_file.side_effect = botocore.exceptions.NoCredentialsError mock_get_s3_client.return_value = mock_s3 try: - download_from_s3( - "test_bucket", "test_key", "test_file", "access_key", "secret_key", "region" - ) + download_from_s3("test_bucket", "test_key", "test_file") except Exception: pass mock_logger.exception.assert_called_once_with("Credentials not found") @@ -150,9 +139,7 @@ def test_download_from_s3_general_exception(mocker): mock_s3.download_file.side_effect = Exception() mock_get_s3_client.return_value = mock_s3 try: - download_from_s3( - "test_bucket", "test_key", "test_file", "access_key", "secret_key", "region" - ) + download_from_s3("test_bucket", "test_key", "test_file") except Exception: pass mock_logger.exception.assert_called_once() @@ -179,21 +166,9 @@ def test_list_s3_objects(mocker): def test_get_s3_file_makes_correct_call(notify_api, mocker): get_s3_mock = mocker.patch("app.aws.s3.get_s3_object") - get_s3_file( - "foo-bucket", - "bar-file.txt", - default_access_key, - default_secret_key, - default_region, - ) + get_s3_file("foo-bucket", "bar-file.txt") - get_s3_mock.assert_called_with( - "foo-bucket", - "bar-file.txt", - default_access_key, - default_secret_key, - default_region, - ) + get_s3_mock.assert_called_with("foo-bucket", "bar-file.txt") @pytest.mark.parametrize( @@ -353,9 +328,6 @@ def test_remove_csv_object(notify_api, mocker): get_s3_mock.assert_called_once_with( os.getenv("CSV_BUCKET_NAME"), "mykey", - default_access_key, - default_secret_key, - default_region, ) @@ -364,17 +336,11 @@ def test_remove_csv_object_alternate(notify_api, mocker): remove_s3_object( os.getenv("CSV_BUCKET_NAME"), "mykey", - default_access_key, - default_secret_key, - default_region, ) get_s3_mock.assert_called_once_with( os.getenv("CSV_BUCKET_NAME"), "mykey", - default_access_key, - default_secret_key, - default_region, ) @@ -442,7 +408,6 @@ def test_get_s3_client(mocker): sa_key = f"{sa_key}ret_access_key" mock_current_app.config = { "CSV_UPLOAD_BUCKET": { - "access_key_id": "test_access_key", sa_key: "test_s_key", "region": "us-west-100", } @@ -464,7 +429,6 @@ def test_get_s3_resource(mocker): mock_current_app.config = { "CSV_UPLOAD_BUCKET": { - "access_key_id": "test_access_key", sa_key: "test_s_key", "region": "us-west-100", } @@ -534,10 +498,7 @@ def test_get_s3_object_client_error(mocker): bucket_name = "test-bucket" file_location = "nonexistent-file.txt" - access_key = "test-access-key" - skey = "skey" - region = "us-west-200" - result = get_s3_object(bucket_name, file_location, access_key, skey, region) + result = get_s3_object(bucket_name, file_location) assert result is None mock_s3.Object.assert_called_once_with(bucket_name, file_location) mock_logger.exception.assert_called_once_with( @@ -551,7 +512,7 @@ def test_purge_bucket(mocker): mock_s3_resource.Bucket.return_value = mock_bucket mocker.patch("app.aws.s3.get_s3_resource", return_value=mock_s3_resource) - purge_bucket("my-bucket", "access-key", "secret-key", "region") + purge_bucket("my-bucket") # Assert that the bucket's objects.all().delete() method was called mock_bucket.objects.all.return_value.delete.assert_called_once() diff --git a/notifications-api/tests/app/clients/test_aws_cloudwatch.py b/notifications-api/tests/app/clients/test_aws_cloudwatch.py index 4671dd5..019c13f 100644 --- a/notifications-api/tests/app/clients/test_aws_cloudwatch.py +++ b/notifications-api/tests/app/clients/test_aws_cloudwatch.py @@ -239,7 +239,7 @@ def test_get_receipts(): @patch("app.clients.cloudwatch.aws_cloudwatch.cloud_config") def test_check_delivery_receipts(mock_cloud_config): client = AwsCloudwatchClient() - mock_cloud_config.sns_regions = "us-north-1" + mock_cloud_config.sns_region = "us-north-1" mock_cloud_config.ses_domain_arn = ( "arn:aws:ses:us-north-1:123456789012:identity/example.com" ) diff --git a/notifications-api/tests/app/delivery/test_send_to_providers.py b/notifications-api/tests/app/delivery/test_send_to_providers.py index b0d5255..501b156 100644 --- a/notifications-api/tests/app/delivery/test_send_to_providers.py +++ b/notifications-api/tests/app/delivery/test_send_to_providers.py @@ -9,14 +9,11 @@ import app from app import db, notification_provider_clients -from app.cloudfoundry_config import cloud_config +from app.aws_config import cloud_config from app.dao import notifications_dao from app.dao.provider_details_dao import get_provider_details_by_identifier from app.delivery import send_to_providers -from app.delivery.send_to_providers import ( - get_html_email_options, - get_logo_url, -) +from app.delivery.send_to_providers import get_html_email_options, get_logo_url from app.enums import BrandType, KeyType, NotificationStatus, NotificationType from app.exceptions import NotificationTechnicalFailureException from app.models import EmailBranding, Notification diff --git a/notifications-api/tests/app/test_aws_config.py b/notifications-api/tests/app/test_aws_config.py new file mode 100644 index 0000000..036f283 --- /dev/null +++ b/notifications-api/tests/app/test_aws_config.py @@ -0,0 +1,70 @@ +import os + +from app.aws_config import AwsConfig + + +def test_database_url(): + os.environ["DATABASE_URL"] = "postgres://postgres:password@localhost:5432/db_name" + assert ( + AwsConfig().database_url + == "postgresql://postgres:password@localhost:5432/db_name" + ) + + +def test_database_url_already_postgresql(): + os.environ["DATABASE_URL"] = "postgresql://postgres:password@localhost:5432/db_name" + assert ( + AwsConfig().database_url + == "postgresql://postgres:password@localhost:5432/db_name" + ) + + +def test_redis_url(): + os.environ["REDIS_URL"] = "redis://localhost:6379" + assert AwsConfig().redis_url == "redis://localhost:6379" + + +def test_redis_url_returns_none_when_not_set(): + os.environ.pop("REDIS_URL", None) + assert AwsConfig().redis_url is None + + +def test_sns_region(): + os.environ["SNS_AWS_REGION"] = "us-west-2" + assert AwsConfig().sns_region == "us-west-2" + + +def test_sns_region_defaults_to_us_east_1(): + os.environ.pop("SNS_AWS_REGION", None) + assert AwsConfig().sns_region == "us-east-1" + + +def test_ses_region(): + os.environ["SES_AWS_REGION"] = "us-west-2" + assert AwsConfig().ses_region == "us-west-2" + + +def test_ses_email_domain(): + os.environ["SES_DOMAIN_ARN"] = "arn:aws:ses:us-east-1:123456789:identity/example.gov" + assert AwsConfig().ses_email_domain == "example.gov" + + +def test_ses_domain_arn(): + arn = "arn:aws:ses:us-east-1:123456789:identity/example.gov" + os.environ["SES_DOMAIN_ARN"] = arn + assert AwsConfig().ses_domain_arn == arn + + +def test_sns_topic_arns(): + os.environ["VALID_SNS_TOPICS"] = ( + "arn:aws:sns:us-east-1:123:bounce, arn:aws:sns:us-east-1:123:complaint" + ) + assert AwsConfig().sns_topic_arns == [ + "arn:aws:sns:us-east-1:123:bounce", + "arn:aws:sns:us-east-1:123:complaint", + ] + + +def test_sns_topic_arns_empty_when_not_set(): + os.environ.pop("VALID_SNS_TOPICS", None) + assert AwsConfig().sns_topic_arns == [] From 1d5a98dc5f7491b092ba0100eeea8797855ac92f Mon Sep 17 00:00:00 2001 From: Rachel Rogers Date: Tue, 24 Mar 2026 08:26:25 -0700 Subject: [PATCH 3/8] Remove references to gov-bam.nr-data.net from CSP configuration to stop 403 errors. Still need to remove newrelic.html and other new relic environment variables --- notifications-admin/app/__init__.py | 3 --- notifications-admin/app/templates/components/head.html | 3 --- notifications-admin/tests/app/main/views/test_headers.py | 1 - 3 files changed, 7 deletions(-) diff --git a/notifications-admin/app/__init__.py b/notifications-admin/app/__init__.py index ebd403a..c028dcf 100644 --- a/notifications-admin/app/__init__.py +++ b/notifications-admin/app/__init__.py @@ -157,16 +157,13 @@ def _csp(config): "'self'", asset_domain, "https://js-agent.newrelic.com", - "https://gov-bam.nr-data.net", "https://www.googletagmanager.com", "https://www.google-analytics.com", - "https://dap.digitalgov.gov", ], "connect-src": list( dict.fromkeys( [ "'self'", - "https://gov-bam.nr-data.net", "https://www.google-analytics.com", f"{api_public_url}", ] diff --git a/notifications-admin/app/templates/components/head.html b/notifications-admin/app/templates/components/head.html index 1e31ab8..7b6b37a 100644 --- a/notifications-admin/app/templates/components/head.html +++ b/notifications-admin/app/templates/components/head.html @@ -3,9 +3,6 @@ - {% if config['NR_MONITOR_ON'] %} - {% include "partials/newrelic.html" -%} - {% endif %} {# Ensure that older IE versions always render with the correct rendering engine #} diff --git a/notifications-admin/tests/app/main/views/test_headers.py b/notifications-admin/tests/app/main/views/test_headers.py index 3ce8bec..55a6684 100644 --- a/notifications-admin/tests/app/main/views/test_headers.py +++ b/notifications-admin/tests/app/main/views/test_headers.py @@ -38,7 +38,6 @@ def test_owasp_useful_headers_set( config = current_app.config expected_sources = { "'self'", - "https://gov-bam.nr-data.net", "https://www.google-analytics.com", config["API_PUBLIC_URL"], } From 7dcbffa58694ccda1195acdd65f229d8dbb695c6 Mon Sep 17 00:00:00 2001 From: Rachel Rogers Date: Tue, 24 Mar 2026 08:33:22 -0700 Subject: [PATCH 4/8] Add infrastructure to support SNS texts. --- build-and-deploy-to-ecr.sh | 20 +++- terraform/modules/app-module/ecs.tf | 133 +++++++++++++++++++++- terraform/modules/app-module/iam.tf | 78 ++++++++++++- terraform/modules/app-module/sns.tf | 16 +++ terraform/modules/app-module/variables.tf | 6 + 5 files changed, 248 insertions(+), 5 deletions(-) create mode 100644 terraform/modules/app-module/sns.tf diff --git a/build-and-deploy-to-ecr.sh b/build-and-deploy-to-ecr.sh index 8c8ceca..9280df2 100755 --- a/build-and-deploy-to-ecr.sh +++ b/build-and-deploy-to-ecr.sh @@ -6,6 +6,8 @@ APP_NAME="flexion-notify" ENV="dev" CLUSTER="${APP_NAME}-${ENV}-cluster" SERVICE="${APP_NAME}-${ENV}-notify-service" +CELERY_SERVICE="${APP_NAME}-${ENV}-celery-worker-service" +CELERY_BEAT_SERVICE="${APP_NAME}-${ENV}-celery-beat-service" if command -v docker &>/dev/null; then CONTAINER_CLI="docker" @@ -31,7 +33,7 @@ echo "Building and pushing frontend image..." ${CONTAINER_CLI} build --platform linux/arm64 -t "${ECR_BASE}/frontend:latest" ./notifications-admin ${CONTAINER_CLI} push "${ECR_BASE}/frontend:latest" -echo "Forcing new ECS deployment..." +echo "Forcing new ECS deployment (notify service)..." aws ecs update-service \ --cluster "${CLUSTER}" \ --service "${SERVICE}" \ @@ -39,4 +41,20 @@ aws ecs update-service \ --region "${REGION}" \ --output text --query 'service.serviceName' +echo "Forcing new ECS deployment (celery worker)..." +aws ecs update-service \ + --cluster "${CLUSTER}" \ + --service "${CELERY_SERVICE}" \ + --force-new-deployment \ + --region "${REGION}" \ + --output text --query 'service.serviceName' + +echo "Forcing new ECS deployment (celery beat)..." +aws ecs update-service \ + --cluster "${CLUSTER}" \ + --service "${CELERY_BEAT_SERVICE}" \ + --force-new-deployment \ + --region "${REGION}" \ + --output text --query 'service.serviceName' + echo "Done!" diff --git a/terraform/modules/app-module/ecs.tf b/terraform/modules/app-module/ecs.tf index ac13f87..74ae87d 100644 --- a/terraform/modules/app-module/ecs.tf +++ b/terraform/modules/app-module/ecs.tf @@ -132,7 +132,7 @@ resource "aws_ecs_task_definition" "frontend_task" { { "name" : "FLASK_DEBUG", "value" : "true" }, { "name" : "WERKZEUG_DEBUG_PIN", "value" : "off" }, { "name" : "DEBUG", "value" : "True" }, - { "name" : "AWS_US_TOLL_FREE_NUMBER", "value" : "+18556438890" }, + { "name" : "AWS_US_TOLL_FREE_NUMBER", "value" : "+16562312984" }, { "name" : "SNS_AWS_REGION", "value" : "${var.aws_region}" }, { "name" : "SMS_ENABLED", "value" : "True" }, { "name" : "CSV_BUCKET_NAME", "value" : "${aws_s3_bucket.csv_upload_bucket.bucket}" }, @@ -183,4 +183,135 @@ resource "aws_ecs_service" "notify_service" { desired_count = 1 } +resource "aws_ecs_task_definition" "celery_worker_task" { + family = "${var.app_name}-${var.env}-celery-worker-td" + network_mode = "awsvpc" + requires_compatibilities = ["FARGATE"] + cpu = "1024" + memory = "2048" + task_role_arn = aws_iam_role.ecs_task_execution_role.arn + execution_role_arn = aws_iam_role.ecs_task_execution_role.arn + + container_definitions = jsonencode([ + { + name = "celery-worker" + image = aws_ecr_repository.flexion_notify_backend_ecr_repo.repository_url + command = ["poetry", "run", "celery", "-A", "run_celery.notify_celery", "worker", "--pidfile=/tmp/celery.pid", "--loglevel=INFO", "--pool=gevent", "--concurrency=20"] + essential = true + environment = [ + { "name" : "DATABASE_URL", "value" : "postgresql://${var.db_username}:${var.db_password}@${aws_rds_cluster_instance.instances.endpoint}:5432/notification_api" }, + { "name" : "PGPASSWORD", "value" : var.db_password }, + { "name" : "PGUSER", "value" : var.db_username }, + { "name" : "PGHOST", "value" : aws_rds_cluster_instance.instances.endpoint }, + { "name" : "REDIS_URL", "value" : "rediss://:${var.redis_auth_token}@${aws_elasticache_replication_group.redis.primary_endpoint_address}:${aws_elasticache_replication_group.redis.port}" }, + { "name" : "NOTIFY_ENVIRONMENT", "value" : "development" }, + { "name" : "NOTIFY_LOG_PATH", "value" : "/dev/null" }, + { "name" : "API_HOST_NAME", "value" : "http://localhost:6011" }, + { "name" : "SECRET_KEY", "value" : "dev-notify-secret-key" }, + { "name" : "DANGEROUS_SALT", "value" : "dev-notify-salt" }, + { "name" : "REDIS_ENABLED", "value" : "1" }, + { "name" : "AWS_US_TOLL_FREE_NUMBER", "value" : "+16562312984" }, + { "name" : "SNS_AWS_REGION", "value" : "${var.aws_region}" }, + { "name" : "SMS_ENABLED", "value" : "True" }, + { "name" : "CSV_BUCKET_NAME", "value" : "${aws_s3_bucket.csv_upload_bucket.bucket}" }, + { "name" : "LOGO_UPLOAD_BUCKET_NAME", "value" : "${aws_s3_bucket.admin_logo_upload_bucket.bucket}" }, + { "name" : "SERVICE_LOGS_BUCKET_NAME", "value" : "${aws_s3_bucket.flexion_notify_service_logs.bucket}" }, + ] + logConfiguration = { + logDriver = "awslogs" + options = { + awslogs-group = aws_cloudwatch_log_group.notify_log_group.name + awslogs-region = var.aws_region + awslogs-stream-prefix = "celery-worker" + } + } + } + ]) + runtime_platform { + cpu_architecture = "ARM64" + operating_system_family = "LINUX" + } +} + +resource "aws_ecs_service" "celery_worker_service" { + name = "${var.app_name}-${var.env}-celery-worker-service" + cluster = aws_ecs_cluster.notify_cluster.id + task_definition = aws_ecs_task_definition.celery_worker_task.arn + launch_type = "FARGATE" + enable_execute_command = true + + network_configuration { + subnets = data.aws_subnets.public_subnets.ids + security_groups = [aws_security_group.notify_svc_sg.id] + assign_public_ip = true + } + + desired_count = 1 +} +resource "aws_ecs_task_definition" "celery_beat_task" { + family = "${var.app_name}-${var.env}-celery-beat-td" + network_mode = "awsvpc" + requires_compatibilities = ["FARGATE"] + cpu = "256" + memory = "512" + task_role_arn = aws_iam_role.ecs_task_execution_role.arn + execution_role_arn = aws_iam_role.ecs_task_execution_role.arn + + container_definitions = jsonencode([ + { + name = "celery-beat" + image = aws_ecr_repository.flexion_notify_backend_ecr_repo.repository_url + command = ["poetry", "run", "celery", "-A", "run_celery.notify_celery", "beat", "--loglevel=INFO"] + essential = true + environment = [ + { "name" : "DATABASE_URL", "value" : "postgresql://${var.db_username}:${var.db_password}@${aws_rds_cluster_instance.instances.endpoint}:5432/notification_api" }, + { "name" : "PGPASSWORD", "value" : var.db_password }, + { "name" : "PGUSER", "value" : var.db_username }, + { "name" : "PGHOST", "value" : aws_rds_cluster_instance.instances.endpoint }, + { "name" : "REDIS_URL", "value" : "rediss://:${var.redis_auth_token}@${aws_elasticache_replication_group.redis.primary_endpoint_address}:${aws_elasticache_replication_group.redis.port}" }, + { "name" : "NOTIFY_ENVIRONMENT", "value" : "development" }, + { "name" : "NOTIFY_LOG_PATH", "value" : "/dev/null" }, + { "name" : "API_HOST_NAME", "value" : "http://localhost:6011" }, + { "name" : "SECRET_KEY", "value" : "dev-notify-secret-key" }, + { "name" : "DANGEROUS_SALT", "value" : "dev-notify-salt" }, + { "name" : "REDIS_ENABLED", "value" : "1" }, + { "name" : "AWS_US_TOLL_FREE_NUMBER", "value" : "+16562312984" }, + { "name" : "SNS_AWS_REGION", "value" : "${var.aws_region}" }, + { "name" : "SMS_ENABLED", "value" : "True" }, + { "name" : "CSV_BUCKET_NAME", "value" : "${aws_s3_bucket.csv_upload_bucket.bucket}" }, + { "name" : "LOGO_UPLOAD_BUCKET_NAME", "value" : "${aws_s3_bucket.admin_logo_upload_bucket.bucket}" }, + { "name" : "SERVICE_LOGS_BUCKET_NAME", "value" : "${aws_s3_bucket.flexion_notify_service_logs.bucket}" }, + ] + logConfiguration = { + logDriver = "awslogs" + options = { + awslogs-group = aws_cloudwatch_log_group.notify_log_group.name + awslogs-region = var.aws_region + awslogs-stream-prefix = "celery-beat" + } + } + } + ]) + runtime_platform { + cpu_architecture = "ARM64" + operating_system_family = "LINUX" + } +} + +resource "aws_ecs_service" "celery_beat_service" { + name = "${var.app_name}-${var.env}-celery-beat-service" + cluster = aws_ecs_cluster.notify_cluster.id + task_definition = aws_ecs_task_definition.celery_beat_task.arn + launch_type = "FARGATE" + enable_execute_command = true + + network_configuration { + subnets = data.aws_subnets.public_subnets.ids + security_groups = [aws_security_group.notify_svc_sg.id] + assign_public_ip = true + } + + # Only one beat instance should run at a time to avoid duplicate task scheduling + desired_count = 1 +} diff --git a/terraform/modules/app-module/iam.tf b/terraform/modules/app-module/iam.tf index 87fdbcb..fc60922 100644 --- a/terraform/modules/app-module/iam.tf +++ b/terraform/modules/app-module/iam.tf @@ -54,7 +54,7 @@ resource "aws_iam_role_policy_attachment" "ecs_task_execution_ssm_policy_attachm resource "aws_iam_policy" "ecs_task_execution_logging_policy" { name = "${var.app_name}_${var.env}_ecs_task_execution_logging_policy" - description = "Policy to allow ECS tasks to send logs to CloudWatch" + description = "Policy to allow ECS tasks to send and read logs from CloudWatch" policy = jsonencode({ Version = "2012-10-17", @@ -63,9 +63,13 @@ resource "aws_iam_policy" "ecs_task_execution_logging_policy" { Effect = "Allow", Action = [ "logs:CreateLogStream", - "logs:PutLogEvents" + "logs:PutLogEvents", + "logs:GetLogEvents", + "logs:FilterLogEvents", + "logs:DescribeLogStreams", + "logs:DescribeLogGroups" ], - Resource = "${aws_cloudwatch_log_group.notify_log_group.arn}:*" + Resource = "*" } ] }) @@ -178,6 +182,29 @@ resource "aws_iam_role_policy_attachment" "ecs_task_sns_policy_attachment" { policy_arn = aws_iam_policy.ecs_task_sns_policy.arn } +resource "aws_iam_policy" "ecs_task_pinpoint_policy" { + name = "${var.app_name}_${var.env}_ecs_task_pinpoint_policy" + description = "Policy to allow ECS tasks to validate phone numbers with Pinpoint" + + policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Effect = "Allow", + Action = [ + "mobiletargeting:PhoneNumberValidate" + ], + Resource = "*" + } + ] + }) +} + +resource "aws_iam_role_policy_attachment" "ecs_task_pinpoint_policy_attachment" { + role = aws_iam_role.ecs_task_execution_role.name + policy_arn = aws_iam_policy.ecs_task_pinpoint_policy.arn +} + resource "aws_iam_policy" "ecs_task_s3_buckets_policy" { name = "${var.app_name}_${var.env}_ecs_task_s3_buckets_policy" description = "Policy to allow ECS tasks to read and write to application S3 buckets" @@ -220,3 +247,48 @@ resource "aws_iam_role_policy_attachment" "ecs_task_s3_buckets_policy_attachment role = aws_iam_role.ecs_task_execution_role.name policy_arn = aws_iam_policy.ecs_task_s3_buckets_policy.arn } + +resource "aws_iam_role" "sns_cloudwatch_logs_role" { + name = "${var.app_name}-${var.env}-ecsTaskSNSLogsRole" + + assume_role_policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Effect = "Allow", + Principal = { + Service = "sns.amazonaws.com" + }, + Action = "sts:AssumeRole" + } + ] + }) +} + +resource "aws_iam_policy" "sns_cloudwatch_logs_policy" { + name = "${var.app_name}-${var.env}-sns-cloudwatch-logs-policy" + description = "Allows SNS to write SMS delivery logs to CloudWatch" + + policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Effect = "Allow", + Action = [ + "logs:CreateLogStream", + "logs:PutLogEvents", + "logs:DescribeLogStreams" + ], + Resource = [ + "${aws_cloudwatch_log_group.sns_delivery_logs.arn}:*", + "${aws_cloudwatch_log_group.sns_delivery_failure_logs.arn}:*" + ] + } + ] + }) +} + +resource "aws_iam_role_policy_attachment" "sns_cloudwatch_logs_policy_attachment" { + role = aws_iam_role.sns_cloudwatch_logs_role.name + policy_arn = aws_iam_policy.sns_cloudwatch_logs_policy.arn +} diff --git a/terraform/modules/app-module/sns.tf b/terraform/modules/app-module/sns.tf new file mode 100644 index 0000000..65bb540 --- /dev/null +++ b/terraform/modules/app-module/sns.tf @@ -0,0 +1,16 @@ +resource "aws_sns_sms_preferences" "sms_preferences" { + monthly_spend_limit = var.sns_monthly_spend_limit + default_sms_type = "Transactional" + delivery_status_iam_role_arn = aws_iam_role.sns_cloudwatch_logs_role.arn + delivery_status_success_sampling_rate = "100" +} + +resource "aws_cloudwatch_log_group" "sns_delivery_logs" { + name = "sns/${var.aws_region}/${data.aws_caller_identity.current.account_id}/DirectPublishToPhoneNumber" + retention_in_days = 7 +} + +resource "aws_cloudwatch_log_group" "sns_delivery_failure_logs" { + name = "sns/${var.aws_region}/${data.aws_caller_identity.current.account_id}/DirectPublishToPhoneNumber/Failure" + retention_in_days = 7 +} diff --git a/terraform/modules/app-module/variables.tf b/terraform/modules/app-module/variables.tf index a091403..9339366 100644 --- a/terraform/modules/app-module/variables.tf +++ b/terraform/modules/app-module/variables.tf @@ -122,3 +122,9 @@ variable "okta_registration_url" { type = string description = "Auth authorize endpoint URL with query parameters for registration" } + +variable "sns_monthly_spend_limit" { + type = number + description = "Monthly spend limit for SNS SMS in USD" + default = 1 +} From 8d7b5fc9f425b260d5012b36343b16ff02ec7ee5 Mon Sep 17 00:00:00 2001 From: Rachel Rogers Date: Tue, 24 Mar 2026 08:44:48 -0700 Subject: [PATCH 5/8] Update Poetry version to 2.3.2 in Dockerfiles and lock files for notifications-admin and notifications-api. --- notifications-admin/Dockerfile | 2 +- notifications-admin/poetry.lock | 181 ++++++++++++++--------------- notifications-admin/pyproject.toml | 1 + notifications-api/Dockerfile | 2 +- notifications-api/poetry.lock | 127 +++++--------------- 5 files changed, 117 insertions(+), 196 deletions(-) diff --git a/notifications-admin/Dockerfile b/notifications-admin/Dockerfile index cb7c144..33963c8 100644 --- a/notifications-admin/Dockerfile +++ b/notifications-admin/Dockerfile @@ -8,7 +8,7 @@ RUN curl -fsSL https://deb.nodesource.com/setup_24.x | bash - && \ apt-get install -y nodejs && \ rm -rf /var/lib/apt/lists/* -ENV POETRY_VERSION=1.8.5 +ENV POETRY_VERSION=2.3.2 RUN curl -sSL https://install.python-poetry.org | python3 - && \ ln -s /root/.local/bin/poetry /usr/local/bin/poetry diff --git a/notifications-admin/poetry.lock b/notifications-admin/poetry.lock index 182cbbb..1e8c2b2 100644 --- a/notifications-admin/poetry.lock +++ b/notifications-admin/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. [[package]] name = "ago" @@ -3400,104 +3400,95 @@ toml = ["tomli (>=2.0.1)"] [[package]] name = "rapidfuzz" -version = "3.14.0" +version = "3.14.3" description = "rapid fuzzy string matching" optional = false python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "rapidfuzz-3.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:91d8c7d9d38835d5fcf9bc87593add864eaea41eb33654d93ded3006b198a326"}, - {file = "rapidfuzz-3.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5a1e574230262956d28e40191dd44ad3d81d2d29b5e716c6c7c0ba17c4d1524e"}, - {file = "rapidfuzz-3.14.0-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f1eda6546831f15e6d8d27593873129ae5e4d2f05cf13bacc2d5222e117f3038"}, - {file = "rapidfuzz-3.14.0-cp310-cp310-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d29686b524b35f93fc14961026a8cfb37283af76ab6f4ed49aebf4df01b44a4a"}, - {file = "rapidfuzz-3.14.0-cp310-cp310-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0fb99bc445014e893c152e36e98b3e9418cc2c0fa7b83d01f3d1b89e73618ed2"}, - {file = "rapidfuzz-3.14.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d9cd4212ca2ea18d026b3f3dfc1ec25919e75ddfd2c7dd20bf7797f262e2460"}, - {file = "rapidfuzz-3.14.0-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:e6a41c6be1394b17b03bc3af3051f54ba0b4018324a0d4cb34c7d2344ec82e79"}, - {file = "rapidfuzz-3.14.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:19bee793c4a84b0f5153fcff2e7cfeaeeb976497a5892baaadb6eadef7e6f398"}, - {file = "rapidfuzz-3.14.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:977144b50b2f1864c825796ad2d41f47a3fd5b7632a2e9905c4d2c8883a8234d"}, - {file = "rapidfuzz-3.14.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ca7c7274bec8085f7a2b68b0490d270a260385d45280d8a2a8ae5884cfb217ba"}, - {file = "rapidfuzz-3.14.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:efa7eca15825c78dc2b9e9e5824fa095cef8954de98e5a6d2f4ad2416a3d5ddf"}, - {file = "rapidfuzz-3.14.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a780c08c41e7ec4336d7a8fcdcd7920df74de6c57be87b72adad4e1b40a31632"}, - {file = "rapidfuzz-3.14.0-cp310-cp310-win32.whl", hash = "sha256:cf540e48175c0620639aa4f4e2b56d61291935c0f684469e8e125e7fa4daef65"}, - {file = "rapidfuzz-3.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:e7769fbc78aba051f514d8a08374e3989124b2d1eee6888c72706a174d0e8a6d"}, - {file = "rapidfuzz-3.14.0-cp310-cp310-win_arm64.whl", hash = "sha256:71442f5e9fad60a4942df3be340acd5315e59aefc5a83534b6a9aa62db67809d"}, - {file = "rapidfuzz-3.14.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6501e49395ad5cecf1623cb4801639faa1c833dbacc07c26fa7b8f7fa19fd1c0"}, - {file = "rapidfuzz-3.14.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c3cd9b8d5e159c67d242f80cae1b9d9b1502779fc69fcd268a1eb7053f58048"}, - {file = "rapidfuzz-3.14.0-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a578cadbe61f738685ffa20e56e8346847e40ecb033bdc885373a070cfe4a351"}, - {file = "rapidfuzz-3.14.0-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b5b46340872a1736544b23f3c355f292935311623a0e63a271f284ffdbab05e4"}, - {file = "rapidfuzz-3.14.0-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:238422749da213c3dfe36397b746aeda8579682e93b723a1e77655182198e693"}, - {file = "rapidfuzz-3.14.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:83f3ad0e7ad3cf1138e36be26f4cacb7580ac0132b26528a89e8168a0875afd8"}, - {file = "rapidfuzz-3.14.0-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:7c34e34fb7e01aeea1e84192cf01daf1d56ccc8a0b34c0833f9799b341c6d539"}, - {file = "rapidfuzz-3.14.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a58bbbbdd2a150c76c6b3af5ac2bbe9afcff26e6b17e1f60b6bd766cc7094fcf"}, - {file = "rapidfuzz-3.14.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:d0e50b4bea57bfcda4afee993eef390fd8f0a64981c971ac4decd9452143892d"}, - {file = "rapidfuzz-3.14.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:357eb9d394bfc742d3528e8bb13afa9baebc7fbe863071975426b47fc21db220"}, - {file = "rapidfuzz-3.14.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fb960ec526030077658764a309b60e907d86d898f8efbe959845ec2873e514eb"}, - {file = "rapidfuzz-3.14.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6bedb19db81d8d723cc4d914cb079d89ff359364184cc3c3db7cef1fc7819444"}, - {file = "rapidfuzz-3.14.0-cp311-cp311-win32.whl", hash = "sha256:8dba3d6e10a34aa255a6f6922cf249f8d0b9829e6b00854e371d803040044f7f"}, - {file = "rapidfuzz-3.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:ce79e37b23c1cbf1dc557159c8f20f6d71e9d28aef63afcf87bcb58c8add096a"}, - {file = "rapidfuzz-3.14.0-cp311-cp311-win_arm64.whl", hash = "sha256:e140ff4b5d0ea386b998137ddd1335a7bd4201ef987d4cb5a48c3e8c174f8aec"}, - {file = "rapidfuzz-3.14.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:93c8739f7bf7931d690aeb527c27e2a61fd578f076d542ddd37e29fa535546b6"}, - {file = "rapidfuzz-3.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7596e95ab03da6cff70f4ec9a5298b2802e8bdd443159d18180b186c80df1416"}, - {file = "rapidfuzz-3.14.0-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cdd49e097ced3746eadb5fb87379f377c0b093f9aba1133ae4f311b574e2ed8"}, - {file = "rapidfuzz-3.14.0-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f4cd4898f21686bb141e151ba920bcd1744cab339277f484c0f97fe7de2c45c8"}, - {file = "rapidfuzz-3.14.0-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:83427518ad72050add47e2cf581080bde81df7f69882e508da3e08faad166b1f"}, - {file = "rapidfuzz-3.14.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05435b4f2472cbf7aac8b837e2e84a165e595c60d79da851da7cfa85ed15895d"}, - {file = "rapidfuzz-3.14.0-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:2dae744c1cdb8b1411ed511a719b505a0348da1970a652bfc735598e68779287"}, - {file = "rapidfuzz-3.14.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9ca05daaca07232037014fc6ce2c2ef0a05c69712f6a5e77da6da5209fb04d7c"}, - {file = "rapidfuzz-3.14.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:2227f4b3742295f380adefef7b6338c30434f8a8e18a11895a1a7c9308b6635d"}, - {file = "rapidfuzz-3.14.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:847ea42b5a6077bc796e1b99cd357a641207b20e3573917b0469b28b5a22238a"}, - {file = "rapidfuzz-3.14.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:539506f13cf0dd6ef2f846571f8e116dba32a468e52d05a91161785ab7de2ed1"}, - {file = "rapidfuzz-3.14.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:03c4b4d4f45f846e4eae052ee18d39d6afe659d74f6d99df5a0d2c5d53930505"}, - {file = "rapidfuzz-3.14.0-cp312-cp312-win32.whl", hash = "sha256:aff0baa3980a8aeb2ce5e15930140146b5fe3fb2d63c8dc4cb08dfbd2051ceb2"}, - {file = "rapidfuzz-3.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:d1eef7f0694fe4cf991f61adaa040955da1e0072c8c41d7db5eb60e83da9e61b"}, - {file = "rapidfuzz-3.14.0-cp312-cp312-win_arm64.whl", hash = "sha256:269d8d1fe5830eef46a165a5c6dd240a05ad44c281a77957461b79cede1ece0f"}, - {file = "rapidfuzz-3.14.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5cf3828b8cbac02686e1d5c499c58e43c5f613ad936fe19a2d092e53f3308ccd"}, - {file = "rapidfuzz-3.14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:68c3931c19c51c11654cf75f663f34c0c7ea04c456c84ccebfd52b2047121dba"}, - {file = "rapidfuzz-3.14.0-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9b4232168959af46f2c0770769e7986ff6084d97bc4b6b2b16b2bfa34164421b"}, - {file = "rapidfuzz-3.14.0-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:174c784cecfafe22d783b5124ebffa2e02cc01e49ffe60a28ad86d217977f478"}, - {file = "rapidfuzz-3.14.0-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0b2dedf216f43a50f227eee841ef0480e29e26b2ce2d7ee680b28354ede18627"}, - {file = "rapidfuzz-3.14.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5698239eecf5b759630450ef59521ad3637e5bd4afc2b124ae8af2ff73309c41"}, - {file = "rapidfuzz-3.14.0-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:0acc9553fc26f1c291c381a6aa8d3c5625be23b5721f139528af40cc4119ae1d"}, - {file = "rapidfuzz-3.14.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00141dfd3b8c9ae15fbb5fbd191a08bde63cdfb1f63095d8f5faf1698e30da93"}, - {file = "rapidfuzz-3.14.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:67f725c3f5713da6e0750dc23f65f0f822c6937c25e3fc9ee797aa6783bef8c1"}, - {file = "rapidfuzz-3.14.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:ba351cf2678d40a23fb4cbfe82cc45ea338a57518dca62a823c5b6381aa20c68"}, - {file = "rapidfuzz-3.14.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:558323dcd5fb38737226be84c78cafbe427706e47379f02c57c3e35ac3745061"}, - {file = "rapidfuzz-3.14.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cb4e4ea174add5183c707d890a816a85e9330f93e5ded139dab182adc727930c"}, - {file = "rapidfuzz-3.14.0-cp313-cp313-win32.whl", hash = "sha256:ec379e1b407935d729c08da9641cfc5dfb2a7796f74cdd82158ce5986bb8ff88"}, - {file = "rapidfuzz-3.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:4b59ba48a909bdf7ec5dad6e3a5a0004aeec141ae5ddb205d0c5bd4389894cf9"}, - {file = "rapidfuzz-3.14.0-cp313-cp313-win_arm64.whl", hash = "sha256:e688b0a98edea42da450fa6ba41736203ead652a78b558839916c10df855f545"}, - {file = "rapidfuzz-3.14.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:cb6c5a46444a2787e466acd77e162049f061304025ab24da02b59caedea66064"}, - {file = "rapidfuzz-3.14.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:99ed7a9e9ff798157caf3c3d96ca7da6560878902d8f70fa7731acc94e0d293c"}, - {file = "rapidfuzz-3.14.0-cp313-cp313t-win32.whl", hash = "sha256:c8e954dd59291ff0cd51b9c0f425e5dc84731bb006dbd5b7846746fe873a0452"}, - {file = "rapidfuzz-3.14.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5754e3ca259667c46a2b58ca7d7568251d6e23d2f0e354ac1cc5564557f4a32d"}, - {file = "rapidfuzz-3.14.0-cp313-cp313t-win_arm64.whl", hash = "sha256:558865f6825d27006e6ae2e1635cfe236d736c8f2c5c82db6db4b1b6df4478bc"}, - {file = "rapidfuzz-3.14.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:3cc4bd8de6643258c5899f21414f9d45d7589d158eee8d438ea069ead624823b"}, - {file = "rapidfuzz-3.14.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:081aac1acb4ab449f8ea7d4e5ea268227295503e1287f56f0b56c7fc3452da1e"}, - {file = "rapidfuzz-3.14.0-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3e0209c6ef7f2c732e10ce4fccafcf7d9e79eb8660a81179aa307c7bd09fafcd"}, - {file = "rapidfuzz-3.14.0-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6e4610997e9de08395e8632b605488a9efc859fe0516b6993b3925f3057f9da7"}, - {file = "rapidfuzz-3.14.0-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efd0095cde6d0179c92c997ede4b85158bf3c7386043e2fadbee291018b29300"}, - {file = "rapidfuzz-3.14.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0a141c07f9e97c45e67aeed677bac92c08f228c556a80750ea3e191e82d54034"}, - {file = "rapidfuzz-3.14.0-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:5a9de40fa6be7809fd2579c8020b9edaf6f50ffc43082b14e95ad3928a254f22"}, - {file = "rapidfuzz-3.14.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20f510dae17bad8f4909ab32b40617f964af55131e630de7ebc0ffa7f00fe634"}, - {file = "rapidfuzz-3.14.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:79c3fd17a432c3f74de94782d7139f9a22e948cec31659a1a05d67b5c0f4290e"}, - {file = "rapidfuzz-3.14.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:8cde9ffb86ea33d67cce9b26b513a177038be48ee2eb4d856cc60a75cb698db7"}, - {file = "rapidfuzz-3.14.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:cafb657c8f2959761bca40c0da66f29d111e2c40d91f8ed4a75cc486c99b33ae"}, - {file = "rapidfuzz-3.14.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4d80a9f673c534800d73f164ed59620e2ba820ed3840abb67c56022ad043564b"}, - {file = "rapidfuzz-3.14.0-cp314-cp314-win32.whl", hash = "sha256:da9878a01357c7906fb16359b3622ce256933a3286058ee503358859e1442f68"}, - {file = "rapidfuzz-3.14.0-cp314-cp314-win_amd64.whl", hash = "sha256:09af941076ef18f6c2b35acfd5004c60d03414414058e98ece6ca9096f454870"}, - {file = "rapidfuzz-3.14.0-cp314-cp314-win_arm64.whl", hash = "sha256:1a878eb065ce6061038dd1c0b9e8eb7477f7d05d5c5161a1d2a5fa630818f938"}, - {file = "rapidfuzz-3.14.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:33ce0326e6feb0d2207a7ca866a5aa6a2ac2361f1ca43ca32aca505268c18ec9"}, - {file = "rapidfuzz-3.14.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e8056d10e99dedf110e929fdff4de6272057115b28eeef4fb6f0d99fd73c026f"}, - {file = "rapidfuzz-3.14.0-cp314-cp314t-win32.whl", hash = "sha256:ddde238b7076e49c2c21a477ee4b67143e1beaf7a3185388fe0b852e64c6ef52"}, - {file = "rapidfuzz-3.14.0-cp314-cp314t-win_amd64.whl", hash = "sha256:ef24464be04a7da1adea741376ddd2b092e0de53c9b500fd3c2e38e071295c9e"}, - {file = "rapidfuzz-3.14.0-cp314-cp314t-win_arm64.whl", hash = "sha256:fd4a27654f51bed3518bc5bbf166627caf3ddd858b12485380685777421f8933"}, - {file = "rapidfuzz-3.14.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4c9a00ef2f684b1132aeb3c0737483dc8f85a725dbe792aee1d1c3cbcf329b34"}, - {file = "rapidfuzz-3.14.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:2e203d76b3dcd1b466ee196f7adb71009860906303db274ae20c7c5af62bc1a8"}, - {file = "rapidfuzz-3.14.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2b317a71fd938348d8dbbe2f559cda58a67fdcafdd3107afca7ab0fb654efa86"}, - {file = "rapidfuzz-3.14.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e5d610a2c5efdb2a3f9eaecac4ecd6d849efb2522efa36000e006179062056dc"}, - {file = "rapidfuzz-3.14.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:c053cad08ab872df4e201daacb66d7fd04b5b4c395baebb193b9910c63ed22ec"}, - {file = "rapidfuzz-3.14.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:7e52ac8a458b2f09291fa968b23192d6664c7568a43607de2a51a088d016152d"}, - {file = "rapidfuzz-3.14.0.tar.gz", hash = "sha256:672b6ba06150e53d7baf4e3d5f12ffe8c213d5088239a15b5ae586ab245ac8b2"}, + {file = "rapidfuzz-3.14.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b9fcd4d751a4fffa17aed1dde41647923c72c74af02459ad1222e3b0022da3a1"}, + {file = "rapidfuzz-3.14.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ad73afb688b36864a8d9b7344a9cf6da186c471e5790cbf541a635ee0f457f2"}, + {file = "rapidfuzz-3.14.3-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c5fb2d978a601820d2cfd111e2c221a9a7bfdf84b41a3ccbb96ceef29f2f1ac7"}, + {file = "rapidfuzz-3.14.3-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1d83b8b712fa37e06d59f29a4b49e2e9e8635e908fbc21552fe4d1163db9d2a1"}, + {file = "rapidfuzz-3.14.3-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:dc8c07801df5206b81ed6bd6c35cb520cf9b6c64b9b0d19d699f8633dc942897"}, + {file = "rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c71ce6d4231e5ef2e33caa952bfe671cb9fd42e2afb11952df9fad41d5c821f9"}, + {file = "rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:0e38828d1381a0cceb8a4831212b2f673d46f5129a1897b0451c883eaf4a1747"}, + {file = "rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da2a007434323904719158e50f3076a4dadb176ce43df28ed14610c773cc9825"}, + {file = "rapidfuzz-3.14.3-cp310-cp310-win32.whl", hash = "sha256:fce3152f94afcfd12f3dd8cf51e48fa606e3cb56719bccebe3b401f43d0714f9"}, + {file = "rapidfuzz-3.14.3-cp310-cp310-win_amd64.whl", hash = "sha256:37d3c653af15cd88592633e942f5407cb4c64184efab163c40fcebad05f25141"}, + {file = "rapidfuzz-3.14.3-cp310-cp310-win_arm64.whl", hash = "sha256:cc594bbcd3c62f647dfac66800f307beaee56b22aaba1c005e9c4c40ed733923"}, + {file = "rapidfuzz-3.14.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dea2d113e260a5da0c4003e0a5e9fdf24a9dc2bb9eaa43abd030a1e46ce7837d"}, + {file = "rapidfuzz-3.14.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e6c31a4aa68cfa75d7eede8b0ed24b9e458447db604c2db53f358be9843d81d3"}, + {file = "rapidfuzz-3.14.3-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:02821366d928e68ddcb567fed8723dad7ea3a979fada6283e6914d5858674850"}, + {file = "rapidfuzz-3.14.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cfe8df315ab4e6db4e1be72c5170f8e66021acde22cd2f9d04d2058a9fd8162e"}, + {file = "rapidfuzz-3.14.3-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:769f31c60cd79420188fcdb3c823227fc4a6deb35cafec9d14045c7f6743acae"}, + {file = "rapidfuzz-3.14.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:54fa03062124e73086dae66a3451c553c1e20a39c077fd704dc7154092c34c63"}, + {file = "rapidfuzz-3.14.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:834d1e818005ed0d4ae38f6b87b86fad9b0a74085467ece0727d20e15077c094"}, + {file = "rapidfuzz-3.14.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:948b00e8476a91f510dd1ec07272efc7d78c275d83b630455559671d4e33b678"}, + {file = "rapidfuzz-3.14.3-cp311-cp311-win32.whl", hash = "sha256:43d0305c36f504232f18ea04e55f2059bb89f169d3119c4ea96a0e15b59e2a91"}, + {file = "rapidfuzz-3.14.3-cp311-cp311-win_amd64.whl", hash = "sha256:ef6bf930b947bd0735c550683939a032090f1d688dfd8861d6b45307b96fd5c5"}, + {file = "rapidfuzz-3.14.3-cp311-cp311-win_arm64.whl", hash = "sha256:f3eb0ff3b75d6fdccd40b55e7414bb859a1cda77c52762c9c82b85569f5088e7"}, + {file = "rapidfuzz-3.14.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:685c93ea961d135893b5984a5a9851637d23767feabe414ec974f43babbd8226"}, + {file = "rapidfuzz-3.14.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fa7c8f26f009f8c673fbfb443792f0cf8cf50c4e18121ff1e285b5e08a94fbdb"}, + {file = "rapidfuzz-3.14.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57f878330c8d361b2ce76cebb8e3e1dc827293b6abf404e67d53260d27b5d941"}, + {file = "rapidfuzz-3.14.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c5f545f454871e6af05753a0172849c82feaf0f521c5ca62ba09e1b382d6382"}, + {file = "rapidfuzz-3.14.3-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:07aa0b5d8863e3151e05026a28e0d924accf0a7a3b605da978f0359bb804df43"}, + {file = "rapidfuzz-3.14.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73b07566bc7e010e7b5bd490fb04bb312e820970180df6b5655e9e6224c137db"}, + {file = "rapidfuzz-3.14.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6de00eb84c71476af7d3110cf25d8fe7c792d7f5fa86764ef0b4ca97e78ca3ed"}, + {file = "rapidfuzz-3.14.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d7843a1abf0091773a530636fdd2a49a41bcae22f9910b86b4f903e76ddc82dc"}, + {file = "rapidfuzz-3.14.3-cp312-cp312-win32.whl", hash = "sha256:dea97ac3ca18cd3ba8f3d04b5c1fe4aa60e58e8d9b7793d3bd595fdb04128d7a"}, + {file = "rapidfuzz-3.14.3-cp312-cp312-win_amd64.whl", hash = "sha256:b5100fd6bcee4d27f28f4e0a1c6b5127bc8ba7c2a9959cad9eab0bf4a7ab3329"}, + {file = "rapidfuzz-3.14.3-cp312-cp312-win_arm64.whl", hash = "sha256:4e49c9e992bc5fc873bd0fff7ef16a4405130ec42f2ce3d2b735ba5d3d4eb70f"}, + {file = "rapidfuzz-3.14.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dbcb726064b12f356bf10fffdb6db4b6dce5390b23627c08652b3f6e49aa56ae"}, + {file = "rapidfuzz-3.14.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1704fc70d214294e554a2421b473779bcdeef715881c5e927dc0f11e1692a0ff"}, + {file = "rapidfuzz-3.14.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc65e72790ddfd310c2c8912b45106e3800fefe160b0c2ef4d6b6fec4e826457"}, + {file = "rapidfuzz-3.14.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43e38c1305cffae8472572a0584d4ffc2f130865586a81038ca3965301f7c97c"}, + {file = "rapidfuzz-3.14.3-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:e195a77d06c03c98b3fc06b8a28576ba824392ce40de8c708f96ce04849a052e"}, + {file = "rapidfuzz-3.14.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1b7ef2f4b8583a744338a18f12c69693c194fb6777c0e9ada98cd4d9e8f09d10"}, + {file = "rapidfuzz-3.14.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a2135b138bcdcb4c3742d417f215ac2d8c2b87bde15b0feede231ae95f09ec41"}, + {file = "rapidfuzz-3.14.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:33a325ed0e8e1aa20c3e75f8ab057a7b248fdea7843c2a19ade0008906c14af0"}, + {file = "rapidfuzz-3.14.3-cp313-cp313-win32.whl", hash = "sha256:8383b6d0d92f6cd008f3c9216535be215a064b2cc890398a678b56e6d280cb63"}, + {file = "rapidfuzz-3.14.3-cp313-cp313-win_amd64.whl", hash = "sha256:e6b5e3036976f0fde888687d91be86d81f9ac5f7b02e218913c38285b756be6c"}, + {file = "rapidfuzz-3.14.3-cp313-cp313-win_arm64.whl", hash = "sha256:7ba009977601d8b0828bfac9a110b195b3e4e79b350dcfa48c11269a9f1918a0"}, + {file = "rapidfuzz-3.14.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a0a28add871425c2fe94358c6300bbeb0bc2ed828ca003420ac6825408f5a424"}, + {file = "rapidfuzz-3.14.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:010e12e2411a4854b0434f920e72b717c43f8ec48d57e7affe5c42ecfa05dd0e"}, + {file = "rapidfuzz-3.14.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cfc3d57abd83c734d1714ec39c88a34dd69c85474918ebc21296f1e61eb5ca8"}, + {file = "rapidfuzz-3.14.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89acb8cbb52904f763e5ac238083b9fc193bed8d1f03c80568b20e4cef43a519"}, + {file = "rapidfuzz-3.14.3-cp313-cp313t-manylinux_2_31_armv7l.whl", hash = "sha256:7d9af908c2f371bfb9c985bd134e295038e3031e666e4b2ade1e7cb7f5af2f1a"}, + {file = "rapidfuzz-3.14.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:1f1925619627f8798f8c3a391d81071336942e5fe8467bc3c567f982e7ce2897"}, + {file = "rapidfuzz-3.14.3-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:152555187360978119e98ce3e8263d70dd0c40c7541193fc302e9b7125cf8f58"}, + {file = "rapidfuzz-3.14.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:52619d25a09546b8db078981ca88939d72caa6b8701edd8b22e16482a38e799f"}, + {file = "rapidfuzz-3.14.3-cp313-cp313t-win32.whl", hash = "sha256:489ce98a895c98cad284f0a47960c3e264c724cb4cfd47a1430fa091c0c25204"}, + {file = "rapidfuzz-3.14.3-cp313-cp313t-win_amd64.whl", hash = "sha256:656e52b054d5b5c2524169240e50cfa080b04b1c613c5f90a2465e84888d6f15"}, + {file = "rapidfuzz-3.14.3-cp313-cp313t-win_arm64.whl", hash = "sha256:c7e40c0a0af02ad6e57e89f62bef8604f55a04ecae90b0ceeda591bbf5923317"}, + {file = "rapidfuzz-3.14.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:442125473b247227d3f2de807a11da6c08ccf536572d1be943f8e262bae7e4ea"}, + {file = "rapidfuzz-3.14.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1ec0c8c0c3d4f97ced46b2e191e883f8c82dbbf6d5ebc1842366d7eff13cd5a6"}, + {file = "rapidfuzz-3.14.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2dc37bc20272f388b8c3a4eba4febc6e77e50a8f450c472def4751e7678f55e4"}, + {file = "rapidfuzz-3.14.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dee362e7e79bae940a5e2b3f6d09c6554db6a4e301cc68343886c08be99844f1"}, + {file = "rapidfuzz-3.14.3-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:4b39921df948388a863f0e267edf2c36302983459b021ab928d4b801cbe6a421"}, + {file = "rapidfuzz-3.14.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:beda6aa9bc44d1d81242e7b291b446be352d3451f8217fcb068fc2933927d53b"}, + {file = "rapidfuzz-3.14.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:6a014ba09657abfcfeed64b7d09407acb29af436d7fc075b23a298a7e4a6b41c"}, + {file = "rapidfuzz-3.14.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:32eeafa3abce138bb725550c0e228fc7eaeec7059aa8093d9cbbec2b58c2371a"}, + {file = "rapidfuzz-3.14.3-cp314-cp314-win32.whl", hash = "sha256:adb44d996fc610c7da8c5048775b21db60dd63b1548f078e95858c05c86876a3"}, + {file = "rapidfuzz-3.14.3-cp314-cp314-win_amd64.whl", hash = "sha256:f3d15d8527e2b293e38ce6e437631af0708df29eafd7c9fc48210854c94472f9"}, + {file = "rapidfuzz-3.14.3-cp314-cp314-win_arm64.whl", hash = "sha256:576e4b9012a67e0bf54fccb69a7b6c94d4e86a9540a62f1a5144977359133583"}, + {file = "rapidfuzz-3.14.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:cec3c0da88562727dd5a5a364bd9efeb535400ff0bfb1443156dd139a1dd7b50"}, + {file = "rapidfuzz-3.14.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d1fa009f8b1100e4880868137e7bf0501422898f7674f2adcd85d5a67f041296"}, + {file = "rapidfuzz-3.14.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b86daa7419b5e8b180690efd1fdbac43ff19230803282521c5b5a9c83977655"}, + {file = "rapidfuzz-3.14.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7bd1816db05d6c5ffb3a4df0a2b7b56fb8c81ef584d08e37058afa217da91b1"}, + {file = "rapidfuzz-3.14.3-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:33da4bbaf44e9755b0ce192597f3bde7372fe2e381ab305f41b707a95ac57aa7"}, + {file = "rapidfuzz-3.14.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3fecce764cf5a991ee2195a844196da840aba72029b2612f95ac68a8b74946bf"}, + {file = "rapidfuzz-3.14.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:ecd7453e02cf072258c3a6b8e930230d789d5d46cc849503729f9ce475d0e785"}, + {file = "rapidfuzz-3.14.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ea188aa00e9bcae8c8411f006a5f2f06c4607a02f24eab0d8dc58566aa911f35"}, + {file = "rapidfuzz-3.14.3-cp314-cp314t-win32.whl", hash = "sha256:7ccbf68100c170e9a0581accbe9291850936711548c6688ce3bfb897b8c589ad"}, + {file = "rapidfuzz-3.14.3-cp314-cp314t-win_amd64.whl", hash = "sha256:9ec02e62ae765a318d6de38df609c57fc6dacc65c0ed1fd489036834fd8a620c"}, + {file = "rapidfuzz-3.14.3-cp314-cp314t-win_arm64.whl", hash = "sha256:e805e52322ae29aa945baf7168b6c898120fbc16d2b8f940b658a5e9e3999253"}, + {file = "rapidfuzz-3.14.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7cf174b52cb3ef5d49e45d0a1133b7e7d0ecf770ed01f97ae9962c5c91d97d23"}, + {file = "rapidfuzz-3.14.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:442cba39957a008dfc5bdef21a9c3f4379e30ffb4e41b8555dbaf4887eca9300"}, + {file = "rapidfuzz-3.14.3-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1faa0f8f76ba75fd7b142c984947c280ef6558b5067af2ae9b8729b0a0f99ede"}, + {file = "rapidfuzz-3.14.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1e6eefec45625c634926a9fd46c9e4f31118ac8f3156fff9494422cee45207e6"}, + {file = "rapidfuzz-3.14.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56fefb4382bb12250f164250240b9dd7772e41c5c8ae976fd598a32292449cc5"}, + {file = "rapidfuzz-3.14.3.tar.gz", hash = "sha256:2491937177868bc4b1e469087601d53f925e8d270ccc21e07404b4b5814b7b5f"}, ] [package.extras] @@ -4522,4 +4513,4 @@ cffi = ["cffi (>=1.17) ; python_version >= \"3.13\" and platform_python_implemen [metadata] lock-version = "2.1" python-versions = "^3.13.2" -content-hash = "16684eb24d967aae882ef9cee5d354cc0cca04f9c0bd2705ecaa4213bbc8425c" +content-hash = "8abc714421da48d8e4fdf56bf9ce0f70e0bdd41cfbde09d0b0b18a186c4b694b" diff --git a/notifications-admin/pyproject.toml b/notifications-admin/pyproject.toml index 51f6d3b..445bd96 100644 --- a/notifications-admin/pyproject.toml +++ b/notifications-admin/pyproject.toml @@ -98,6 +98,7 @@ radon = "^6.0.1" requests-mock = "^1.11.0" vulture = "^2.14" poetry-dotenv-plugin = "^0.2.0" +rapidfuzz = ">=3.14.1" [build-system] diff --git a/notifications-api/Dockerfile b/notifications-api/Dockerfile index ea607a5..b2c5f7a 100644 --- a/notifications-api/Dockerfile +++ b/notifications-api/Dockerfile @@ -3,7 +3,7 @@ FROM python:3.13.12-slim # Install system dependencies (postgresql-client for psql to run init-databases.sql) RUN apt-get update && apt-get install -y curl git postgresql-client && rm -rf /var/lib/apt/lists/* -ENV POETRY_VERSION=1.8.5 +ENV POETRY_VERSION=2.3.2 RUN curl -sSL https://install.python-poetry.org | python3 - && \ ln -s /root/.local/bin/poetry /usr/local/bin/poetry diff --git a/notifications-api/poetry.lock b/notifications-api/poetry.lock index 70e521d..b1e6001 100644 --- a/notifications-api/poetry.lock +++ b/notifications-api/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.3.2 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -6,7 +6,7 @@ version = "2.6.1" description = "Happy Eyeballs for asyncio" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] +groups = ["main"] files = [ {file = "aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8"}, {file = "aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558"}, @@ -18,7 +18,7 @@ version = "3.13.3" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] +groups = ["main"] files = [ {file = "aiohttp-3.13.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5a372fd5afd301b3a89582817fdcdb6c34124787c70dbcc616f259013e7eef7"}, {file = "aiohttp-3.13.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:147e422fd1223005c22b4fe080f5d93ced44460f5f9c105406b753612b587821"}, @@ -160,7 +160,7 @@ version = "1.4.0" description = "aiosignal: a list of registered asynchronous callbacks" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] +groups = ["main"] files = [ {file = "aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e"}, {file = "aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7"}, @@ -783,6 +783,7 @@ files = [ {file = "cffi-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9"}, {file = "cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529"}, ] +markers = {dev = "platform_python_implementation != \"PyPy\" or sys_platform == \"darwin\""} [package.dependencies] pycparser = {version = "*", markers = "implementation_name != \"PyPy\""} @@ -1005,27 +1006,6 @@ prompt-toolkit = ">=3.0.36" [package.extras] testing = ["pytest (>=7.2.1)", "pytest-cov (>=4.0.0)", "tox (>=4.4.3)"] -[[package]] -name = "cloudfoundry-client" -version = "1.40.3" -description = "A client library for CloudFoundry" -optional = false -python-versions = ">=3.10" -groups = ["dev"] -files = [ - {file = "cloudfoundry_client-1.40.3-py3-none-any.whl", hash = "sha256:3b12faf9a1f7e415f463f0f62fe211d9d3c17925cbc21b29ef75ea1946fdffc0"}, - {file = "cloudfoundry_client-1.40.3.tar.gz", hash = "sha256:ae2e1cba8d3e9b6fd68363963af90ca7f486755cce14c4650628f4c268825778"}, -] - -[package.dependencies] -aiohttp = ">=3.8.0" -oauth2-client = "1.4.2" -polling2 = "0.5.0" -protobuf = "6.33.5" -PyYAML = ">=6.0" -requests = ">=2.5.0" -websocket-client = ">=1.9.0,<1.10.0" - [[package]] name = "colorama" version = "0.4.6" @@ -1647,7 +1627,7 @@ version = "1.8.0" description = "A list-like structure which implements collections.abc.MutableSequence" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] +groups = ["main"] files = [ {file = "frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011"}, {file = "frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565"}, @@ -2328,7 +2308,7 @@ fqdn = {version = "*", optional = true, markers = "extra == \"format\""} idna = {version = "*", optional = true, markers = "extra == \"format\""} isoduration = {version = "*", optional = true, markers = "extra == \"format\""} jsonpointer = {version = ">1.13", optional = true, markers = "extra == \"format\""} -jsonschema-specifications = ">=2023.03.6" +jsonschema-specifications = ">=2023.3.6" referencing = ">=0.28.4" rfc3339-validator = {version = "*", optional = true, markers = "extra == \"format\""} rfc3987 = {version = "*", optional = true, markers = "extra == \"format\""} @@ -2413,7 +2393,7 @@ librabbitmq = ["librabbitmq (>=2.0.0) ; python_version < \"3.11\""] mongodb = ["pymongo (==4.15.3)"] msgpack = ["msgpack (==1.1.2)"] pyro = ["pyro4 (==4.82)"] -qpid = ["qpid-python (==1.36.0-1)", "qpid-tools (==1.36.0-1)"] +qpid = ["qpid-python (==1.36.0.post1)", "qpid-tools (==1.36.0.post1)"] redis = ["redis (>=4.5.2,!=4.5.5,!=5.0.2,<6.5)"] slmq = ["softlayer_messaging (>=1.0.3)"] sqlalchemy = ["sqlalchemy (>=1.4.48,<2.1)"] @@ -2981,7 +2961,7 @@ version = "6.7.1" description = "multidict implementation" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] +groups = ["main"] files = [ {file = "multidict-6.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c93c3db7ea657dd4637d57e74ab73de31bccefe144d3d4ce370052035bc85fb5"}, {file = "multidict-6.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:974e72a2474600827abaeda71af0c53d9ebbc3c2eb7da37b37d7829ae31232d8"}, @@ -3279,21 +3259,6 @@ files = [ {file = "numpy-2.4.2.tar.gz", hash = "sha256:659a6107e31a83c4e33f763942275fd278b21d095094044eb35569e86a21ddae"}, ] -[[package]] -name = "oauth2-client" -version = "1.4.2" -description = "A client library for OAuth2" -optional = false -python-versions = "*" -groups = ["dev"] -files = [ - {file = "oauth2-client-1.4.2.tar.gz", hash = "sha256:5381900448ff1ae762eb7c65c501002eac46bb5ca2f49477fdfeaf9e9969f284"}, - {file = "oauth2_client-1.4.2-py3-none-any.whl", hash = "sha256:7b938ba8166128a3c4c15ad23ca0c95a2468f8e8b6069d019ebc73360c15c7ca"}, -] - -[package.dependencies] -requests = ">=2.5.0" - [[package]] name = "ordered-set" version = "4.1.0" @@ -3579,18 +3544,6 @@ files = [ poetry = ">=1.2.0a1" python-dotenv = ">=0.10.0" -[[package]] -name = "polling2" -version = "0.5.0" -description = "Updated polling utility with many configurable options" -optional = false -python-versions = "*" -groups = ["dev"] -files = [ - {file = "polling2-0.5.0-py2.py3-none-any.whl", hash = "sha256:ad86d56fbd7502f0856cac2d0109d595c18fa6c7fb12c88cee5e5d16c17286c1"}, - {file = "polling2-0.5.0.tar.gz", hash = "sha256:90b7da82cf7adbb48029724d3546af93f21ab6e592ec37c8c4619aedd010e342"}, -] - [[package]] name = "pre-commit" version = "4.5.1" @@ -3631,7 +3584,7 @@ version = "0.4.1" description = "Accelerated property cache" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] +groups = ["main"] files = [ {file = "propcache-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db"}, {file = "propcache-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8"}, @@ -3757,26 +3710,6 @@ files = [ {file = "propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d"}, ] -[[package]] -name = "protobuf" -version = "6.33.5" -description = "" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "protobuf-6.33.5-cp310-abi3-win32.whl", hash = "sha256:d71b040839446bac0f4d162e758bea99c8251161dae9d0983a3b88dee345153b"}, - {file = "protobuf-6.33.5-cp310-abi3-win_amd64.whl", hash = "sha256:3093804752167bcab3998bec9f1048baae6e29505adaf1afd14a37bddede533c"}, - {file = "protobuf-6.33.5-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:a5cb85982d95d906df1e2210e58f8e4f1e3cdc088e52c921a041f9c9a0386de5"}, - {file = "protobuf-6.33.5-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:9b71e0281f36f179d00cbcb119cb19dec4d14a81393e5ea220f64b286173e190"}, - {file = "protobuf-6.33.5-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:8afa18e1d6d20af15b417e728e9f60f3aa108ee76f23c3b2c07a2c3b546d3afd"}, - {file = "protobuf-6.33.5-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:cbf16ba3350fb7b889fca858fb215967792dc125b35c7976ca4818bee3521cf0"}, - {file = "protobuf-6.33.5-cp39-cp39-win32.whl", hash = "sha256:a3157e62729aafb8df6da2c03aa5c0937c7266c626ce11a278b6eb7963c4e37c"}, - {file = "protobuf-6.33.5-cp39-cp39-win_amd64.whl", hash = "sha256:8f04fa32763dcdb4973d537d6b54e615cc61108c7cb38fe59310c3192d29510a"}, - {file = "protobuf-6.33.5-py3-none-any.whl", hash = "sha256:69915a973dd0f60f31a08b8318b73eab2bd6a392c79184b3612226b0a3f8ec02"}, - {file = "protobuf-6.33.5.tar.gz", hash = "sha256:6ddcac2a081f8b7b9642c09406bc6a4290128fce5f471cddd165960bb9119e5c"}, -] - [[package]] name = "psycopg2-binary" version = "2.9.11" @@ -3791,8 +3724,10 @@ files = [ {file = "psycopg2_binary-2.9.11-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c47676e5b485393f069b4d7a811267d3168ce46f988fa602658b8bb901e9e64d"}, {file = "psycopg2_binary-2.9.11-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:a28d8c01a7b27a1e3265b11250ba7557e5f72b5ee9e5f3a2fa8d2949c29bf5d2"}, {file = "psycopg2_binary-2.9.11-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5f3f2732cf504a1aa9e9609d02f79bea1067d99edf844ab92c247bbca143303b"}, + {file = "psycopg2_binary-2.9.11-cp310-cp310-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:865f9945ed1b3950d968ec4690ce68c55019d79e4497366d36e090327ce7db14"}, {file = "psycopg2_binary-2.9.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:91537a8df2bde69b1c1db01d6d944c831ca793952e4f57892600e96cee95f2cd"}, {file = "psycopg2_binary-2.9.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4dca1f356a67ecb68c81a7bc7809f1569ad9e152ce7fd02c2f2036862ca9f66b"}, + {file = "psycopg2_binary-2.9.11-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:0da4de5c1ac69d94ed4364b6cbe7190c1a70d325f112ba783d83f8440285f152"}, {file = "psycopg2_binary-2.9.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37d8412565a7267f7d79e29ab66876e55cb5e8e7b3bbf94f8206f6795f8f7e7e"}, {file = "psycopg2_binary-2.9.11-cp310-cp310-win_amd64.whl", hash = "sha256:c665f01ec8ab273a61c62beeb8cce3014c214429ced8a308ca1fc410ecac3a39"}, {file = "psycopg2_binary-2.9.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0e8480afd62362d0a6a27dd09e4ca2def6fa50ed3a4e7c09165266106b2ffa10"}, @@ -3800,8 +3735,10 @@ files = [ {file = "psycopg2_binary-2.9.11-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2e164359396576a3cc701ba8af4751ae68a07235d7a380c631184a611220d9a4"}, {file = "psycopg2_binary-2.9.11-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:d57c9c387660b8893093459738b6abddbb30a7eab058b77b0d0d1c7d521ddfd7"}, {file = "psycopg2_binary-2.9.11-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2c226ef95eb2250974bf6fa7a842082b31f68385c4f3268370e3f3870e7859ee"}, + {file = "psycopg2_binary-2.9.11-cp311-cp311-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a311f1edc9967723d3511ea7d2708e2c3592e3405677bf53d5c7246753591fbb"}, {file = "psycopg2_binary-2.9.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ebb415404821b6d1c47353ebe9c8645967a5235e6d88f914147e7fd411419e6f"}, {file = "psycopg2_binary-2.9.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f07c9c4a5093258a03b28fab9b4f151aa376989e7f35f855088234e656ee6a94"}, + {file = "psycopg2_binary-2.9.11-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:00ce1830d971f43b667abe4a56e42c1e2d594b32da4802e44a73bacacb25535f"}, {file = "psycopg2_binary-2.9.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cffe9d7697ae7456649617e8bb8d7a45afb71cd13f7ab22af3e5c61f04840908"}, {file = "psycopg2_binary-2.9.11-cp311-cp311-win_amd64.whl", hash = "sha256:304fd7b7f97eef30e91b8f7e720b3db75fee010b520e434ea35ed1ff22501d03"}, {file = "psycopg2_binary-2.9.11-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:be9b840ac0525a283a96b556616f5b4820e0526addb8dcf6525a0fa162730be4"}, @@ -3809,8 +3746,10 @@ files = [ {file = "psycopg2_binary-2.9.11-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ab8905b5dcb05bf3fb22e0cf90e10f469563486ffb6a96569e51f897c750a76a"}, {file = "psycopg2_binary-2.9.11-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:bf940cd7e7fec19181fdbc29d76911741153d51cab52e5c21165f3262125685e"}, {file = "psycopg2_binary-2.9.11-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fa0f693d3c68ae925966f0b14b8edda71696608039f4ed61b1fe9ffa468d16db"}, + {file = "psycopg2_binary-2.9.11-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a1cf393f1cdaf6a9b57c0a719a1068ba1069f022a59b8b1fe44b006745b59757"}, {file = "psycopg2_binary-2.9.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ef7a6beb4beaa62f88592ccc65df20328029d721db309cb3250b0aae0fa146c3"}, {file = "psycopg2_binary-2.9.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:31b32c457a6025e74d233957cc9736742ac5a6cb196c6b68499f6bb51390bd6a"}, + {file = "psycopg2_binary-2.9.11-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:edcb3aeb11cb4bf13a2af3c53a15b3d612edeb6409047ea0b5d6a21a9d744b34"}, {file = "psycopg2_binary-2.9.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:62b6d93d7c0b61a1dd6197d208ab613eb7dcfdcca0a49c42ceb082257991de9d"}, {file = "psycopg2_binary-2.9.11-cp312-cp312-win_amd64.whl", hash = "sha256:b33fabeb1fde21180479b2d4667e994de7bbf0eec22832ba5d9b5e4cf65b6c6d"}, {file = "psycopg2_binary-2.9.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b8fb3db325435d34235b044b199e56cdf9ff41223a4b9752e8576465170bb38c"}, @@ -3818,8 +3757,10 @@ files = [ {file = "psycopg2_binary-2.9.11-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8c55b385daa2f92cb64b12ec4536c66954ac53654c7f15a203578da4e78105c0"}, {file = "psycopg2_binary-2.9.11-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c0377174bf1dd416993d16edc15357f6eb17ac998244cca19bc67cdc0e2e5766"}, {file = "psycopg2_binary-2.9.11-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5c6ff3335ce08c75afaed19e08699e8aacf95d4a260b495a4a8545244fe2ceb3"}, + {file = "psycopg2_binary-2.9.11-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:84011ba3109e06ac412f95399b704d3d6950e386b7994475b231cf61eec2fc1f"}, {file = "psycopg2_binary-2.9.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ba34475ceb08cccbdd98f6b46916917ae6eeb92b5ae111df10b544c3a4621dc4"}, {file = "psycopg2_binary-2.9.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b31e90fdd0f968c2de3b26ab014314fe814225b6c324f770952f7d38abf17e3c"}, + {file = "psycopg2_binary-2.9.11-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:d526864e0f67f74937a8fce859bd56c979f5e2ec57ca7c627f5f1071ef7fee60"}, {file = "psycopg2_binary-2.9.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:04195548662fa544626c8ea0f06561eb6203f1984ba5b4562764fbeb4c3d14b1"}, {file = "psycopg2_binary-2.9.11-cp313-cp313-win_amd64.whl", hash = "sha256:efff12b432179443f54e230fdf60de1f6cc726b6c832db8701227d089310e8aa"}, {file = "psycopg2_binary-2.9.11-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:92e3b669236327083a2e33ccfa0d320dd01b9803b3e14dd986a4fc54aa00f4e1"}, @@ -3827,8 +3768,10 @@ files = [ {file = "psycopg2_binary-2.9.11-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9b52a3f9bb540a3e4ec0f6ba6d31339727b2950c9772850d6545b7eae0b9d7c5"}, {file = "psycopg2_binary-2.9.11-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:db4fd476874ccfdbb630a54426964959e58da4c61c9feba73e6094d51303d7d8"}, {file = "psycopg2_binary-2.9.11-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:47f212c1d3be608a12937cc131bd85502954398aaa1320cb4c14421a0ffccf4c"}, + {file = "psycopg2_binary-2.9.11-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e35b7abae2b0adab776add56111df1735ccc71406e56203515e228a8dc07089f"}, {file = "psycopg2_binary-2.9.11-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fcf21be3ce5f5659daefd2b3b3b6e4727b028221ddc94e6c1523425579664747"}, {file = "psycopg2_binary-2.9.11-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:9bd81e64e8de111237737b29d68039b9c813bdf520156af36d26819c9a979e5f"}, + {file = "psycopg2_binary-2.9.11-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:32770a4d666fbdafab017086655bcddab791d7cb260a16679cc5a7338b64343b"}, {file = "psycopg2_binary-2.9.11-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c3cb3a676873d7506825221045bd70e0427c905b9c8ee8d6acd70cfcbd6e576d"}, {file = "psycopg2_binary-2.9.11-cp314-cp314-win_amd64.whl", hash = "sha256:4012c9c954dfaccd28f94e84ab9f94e12df76b4afb22331b1f0d3154893a6316"}, {file = "psycopg2_binary-2.9.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:20e7fb94e20b03dcc783f76c0865f9da39559dcc0c28dd1a3fce0d01902a6b9c"}, @@ -3836,8 +3779,10 @@ files = [ {file = "psycopg2_binary-2.9.11-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9d3a9edcfbe77a3ed4bc72836d466dfce4174beb79eda79ea155cc77237ed9e8"}, {file = "psycopg2_binary-2.9.11-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:44fc5c2b8fa871ce7f0023f619f1349a0aa03a0857f2c96fbc01c657dcbbdb49"}, {file = "psycopg2_binary-2.9.11-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9c55460033867b4622cda1b6872edf445809535144152e5d14941ef591980edf"}, + {file = "psycopg2_binary-2.9.11-cp39-cp39-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2d11098a83cca92deaeaed3d58cfd150d49b3b06ee0d0852be466bf87596899e"}, {file = "psycopg2_binary-2.9.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:691c807d94aecfbc76a14e1408847d59ff5b5906a04a23e12a89007672b9e819"}, {file = "psycopg2_binary-2.9.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:8b81627b691f29c4c30a8f322546ad039c40c328373b11dff7490a3e1b517855"}, + {file = "psycopg2_binary-2.9.11-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:b637d6d941209e8d96a072d7977238eea128046effbf37d1d8b2c0764750017d"}, {file = "psycopg2_binary-2.9.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:41360b01c140c2a03d346cec3280cf8a71aa07d94f3b1509fa0161c366af66b4"}, {file = "psycopg2_binary-2.9.11-cp39-cp39-win_amd64.whl", hash = "sha256:875039274f8a2361e5207857899706da840768e2a775bf8c65e82f60b197df02"}, ] @@ -3892,6 +3837,7 @@ files = [ {file = "pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992"}, {file = "pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29"}, ] +markers = {dev = "(platform_python_implementation != \"PyPy\" or sys_platform == \"darwin\") and implementation_name != \"PyPy\""} [[package]] name = "pyflakes" @@ -4827,10 +4773,10 @@ files = [ ] [package.dependencies] -botocore = ">=1.37.4,<2.0a.0" +botocore = ">=1.37.4,<2.0a0" [package.extras] -crt = ["botocore[crt] (>=1.37.4,<2.0a.0)"] +crt = ["botocore[crt] (>=1.37.4,<2.0a0)"] [[package]] name = "secretstorage" @@ -5393,23 +5339,6 @@ files = [ {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, ] -[[package]] -name = "websocket-client" -version = "1.9.0" -description = "WebSocket client for Python with low level API options" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "websocket_client-1.9.0-py3-none-any.whl", hash = "sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef"}, - {file = "websocket_client-1.9.0.tar.gz", hash = "sha256:9e813624b6eb619999a97dc7958469217c3176312b3a16a4bd1bc7e08a46ec98"}, -] - -[package.extras] -docs = ["Sphinx (>=6.0)", "myst-parser (>=2.0.0)", "sphinx_rtd_theme (>=1.1.0)"] -optional = ["python-socks", "wsaccel"] -test = ["pytest", "websockets"] - [[package]] name = "werkzeug" version = "3.1.6" @@ -5654,7 +5583,7 @@ version = "1.22.0" description = "Yet another URL library" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] +groups = ["main"] files = [ {file = "yarl-1.22.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c7bd6683587567e5a49ee6e336e0612bec8329be1b7d4c8af5687dcdeb67ee1e"}, {file = "yarl-1.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5cdac20da754f3a723cceea5b3448e1a2074866406adeb4ef35b469d089adb8f"}, @@ -5965,9 +5894,9 @@ files = [ ] [package.extras] -cffi = ["cffi (>=1.17,<2.0) ; platform_python_implementation != \"PyPy\" and python_version < \"3.14\"", "cffi (>=2.0.0b) ; platform_python_implementation != \"PyPy\" and python_version >= \"3.14\""] +cffi = ["cffi (>=1.17,<2.0) ; platform_python_implementation != \"PyPy\" and python_version < \"3.14\"", "cffi (>=2.0.0b0) ; platform_python_implementation != \"PyPy\" and python_version >= \"3.14\""] [metadata] lock-version = "2.1" python-versions = "^3.13.2" -content-hash = "c02d9e9b4bd6f47d14c2d9b9b10269505db0373e59ad8ec92dd99f7551b46867" +content-hash = "78bf7523e9efc334fc2b3c7a892de14c2962ee68779a9f4144823fa349069593" From eb993d5429e5f69ed0e7e5a8256cbf01ad6c26e8 Mon Sep 17 00:00:00 2001 From: Rachel Rogers Date: Tue, 24 Mar 2026 10:06:11 -0700 Subject: [PATCH 6/8] Undo changes related to removing new relic, that will be done in a different PR --- notifications-admin/app/__init__.py | 3 +++ notifications-admin/app/templates/components/head.html | 3 +++ notifications-admin/tests/app/main/views/test_headers.py | 1 + 3 files changed, 7 insertions(+) diff --git a/notifications-admin/app/__init__.py b/notifications-admin/app/__init__.py index c028dcf..ebd403a 100644 --- a/notifications-admin/app/__init__.py +++ b/notifications-admin/app/__init__.py @@ -157,13 +157,16 @@ def _csp(config): "'self'", asset_domain, "https://js-agent.newrelic.com", + "https://gov-bam.nr-data.net", "https://www.googletagmanager.com", "https://www.google-analytics.com", + "https://dap.digitalgov.gov", ], "connect-src": list( dict.fromkeys( [ "'self'", + "https://gov-bam.nr-data.net", "https://www.google-analytics.com", f"{api_public_url}", ] diff --git a/notifications-admin/app/templates/components/head.html b/notifications-admin/app/templates/components/head.html index 7b6b37a..1e31ab8 100644 --- a/notifications-admin/app/templates/components/head.html +++ b/notifications-admin/app/templates/components/head.html @@ -3,6 +3,9 @@ + {% if config['NR_MONITOR_ON'] %} + {% include "partials/newrelic.html" -%} + {% endif %} {# Ensure that older IE versions always render with the correct rendering engine #} diff --git a/notifications-admin/tests/app/main/views/test_headers.py b/notifications-admin/tests/app/main/views/test_headers.py index 55a6684..3ce8bec 100644 --- a/notifications-admin/tests/app/main/views/test_headers.py +++ b/notifications-admin/tests/app/main/views/test_headers.py @@ -38,6 +38,7 @@ def test_owasp_useful_headers_set( config = current_app.config expected_sources = { "'self'", + "https://gov-bam.nr-data.net", "https://www.google-analytics.com", config["API_PUBLIC_URL"], } From 349faf8501f26afc9d4dc6261837bf0b42185010 Mon Sep 17 00:00:00 2001 From: Rachel Rogers Date: Tue, 24 Mar 2026 10:08:11 -0700 Subject: [PATCH 7/8] Remove unused package from ui project --- notifications-admin/poetry.lock | 2 +- notifications-admin/pyproject.toml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/notifications-admin/poetry.lock b/notifications-admin/poetry.lock index 1e8c2b2..350ab47 100644 --- a/notifications-admin/poetry.lock +++ b/notifications-admin/poetry.lock @@ -4513,4 +4513,4 @@ cffi = ["cffi (>=1.17) ; python_version >= \"3.13\" and platform_python_implemen [metadata] lock-version = "2.1" python-versions = "^3.13.2" -content-hash = "8abc714421da48d8e4fdf56bf9ce0f70e0bdd41cfbde09d0b0b18a186c4b694b" +content-hash = "16684eb24d967aae882ef9cee5d354cc0cca04f9c0bd2705ecaa4213bbc8425c" diff --git a/notifications-admin/pyproject.toml b/notifications-admin/pyproject.toml index 445bd96..51f6d3b 100644 --- a/notifications-admin/pyproject.toml +++ b/notifications-admin/pyproject.toml @@ -98,7 +98,6 @@ radon = "^6.0.1" requests-mock = "^1.11.0" vulture = "^2.14" poetry-dotenv-plugin = "^0.2.0" -rapidfuzz = ">=3.14.1" [build-system] From fc5bde41a660a26cded08d63a24e3fd3a25e4f6f Mon Sep 17 00:00:00 2001 From: Rachel Rogers Date: Tue, 24 Mar 2026 10:25:04 -0700 Subject: [PATCH 8/8] Update GitHub Actions workflows to use latest action versions. Ensure all ECS services are redeployed once images are rebuilt --- .github/workflows/deploy-dev.yml | 34 ++++++++++++++++---------------- .github/workflows/pr-checks.yml | 26 ++++++++++++------------ 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/.github/workflows/deploy-dev.yml b/.github/workflows/deploy-dev.yml index 0d1c125..057ce15 100644 --- a/.github/workflows/deploy-dev.yml +++ b/.github/workflows/deploy-dev.yml @@ -28,8 +28,6 @@ jobs: - name: Setup Terraform uses: hashicorp/setup-terraform@v4 - # with: - # terraform_wrapper: false - name: Terraform init working-directory: terraform/environments/dev @@ -51,19 +49,19 @@ jobs: runs-on: ubuntu-latest needs: terraform steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v4 + uses: aws-actions/configure-aws-credentials@v6 with: role-to-assume: ${{ secrets.AWS_ROLE_ARN }} aws-region: us-east-1 - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Login to ECR run: | @@ -84,19 +82,19 @@ jobs: runs-on: ubuntu-latest needs: terraform steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v4 + uses: aws-actions/configure-aws-credentials@v6 with: role-to-assume: ${{ secrets.AWS_ROLE_ARN }} aws-region: us-east-1 - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Login to ECR run: | @@ -118,16 +116,18 @@ jobs: needs: [build-api, build-admin] steps: - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v4 + uses: aws-actions/configure-aws-credentials@v6 with: role-to-assume: ${{ secrets.AWS_ROLE_ARN }} aws-region: us-east-1 - name: Force new ECS deployment run: | - aws ecs update-service \ - --cluster flexion-notify-dev-cluster \ - --service flexion-notify-dev-notify-service \ - --force-new-deployment \ - --region us-east-1 \ - --output text --query 'service.serviceName' + for service in flexion-notify-dev-notify-service flexion-notify-dev-celery-worker-service flexion-notify-dev-celery-beat-service; do + aws ecs update-service \ + --cluster flexion-notify-dev-cluster \ + --service "$service" \ + --force-new-deployment \ + --region us-east-1 \ + --output text --query 'service.serviceName' + done diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index efa133e..6e4aff1 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -11,10 +11,10 @@ jobs: working-directory: notifications-api steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: "3.13.12" @@ -34,10 +34,10 @@ jobs: working-directory: notifications-admin steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: "24.10.0" # TODO: Setup caching? @@ -46,7 +46,7 @@ jobs: run: npm ci --no-audit - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: "3.13.12" @@ -85,10 +85,10 @@ jobs: - 5432:5432 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: "3.13.12" @@ -122,17 +122,17 @@ jobs: working-directory: notifications-admin steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: recursive - name: Set up Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: "24.10.0" - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: "3.13.12" @@ -178,17 +178,17 @@ jobs: - 6379:6379 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: recursive - name: Set up Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: "24.10.0" - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: "3.13.12"