diff --git a/.github/workflows/deploy-reusable.yml b/.github/workflows/deploy-reusable.yml index 1e8ff9d..09353c6 100644 --- a/.github/workflows/deploy-reusable.yml +++ b/.github/workflows/deploy-reusable.yml @@ -1,17 +1,242 @@ name: Reusable Deploy on: + workflow_call: + inputs: + environment: + required: true + type: string + description: 'Environment name (preprod or prod)' + aws-region: + required: false + type: string + default: 'eu-west-2' + secrets: + AWS_ROLE_ARN: + required: true + AWS_SECRETS_ARN: + required: true workflow_dispatch: inputs: environment: required: true type: choice + description: 'Environment to deploy to' options: - preprod - prod + aws-region: + required: false + type: string + default: 'eu-west-2' + description: 'AWS region' + +permissions: + id-token: write + contents: read jobs: - placeholder: + load-services: runs-on: ubuntu-latest + outputs: + services: ${{ steps.load.outputs.services }} steps: - - run: echo "Triggered from feature branch. This placeholder should not run." \ No newline at end of file + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Load service mappings + id: load + run: | + services=$(jq -c '.services' infra/services.json) + echo "services=${services}" >> $GITHUB_OUTPUT + echo "Loaded services:" + echo "${services}" | jq '.' + + deploy: + needs: load-services + runs-on: ubuntu-latest + environment: ${{ inputs.environment }} + + strategy: + matrix: + service: ${{ fromJson(needs.load-services.outputs.services) }} + fail-fast: false + + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0 + with: + role-to-assume: ${{ secrets.AWS_ROLE_ARN }} + aws-region: ${{ inputs.aws-region }} + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@183a1442edf41672e66566b7fc560e297a290896 # v2.1.1 + + - name: Setup .NET + uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0 + with: + dotnet-version: 10.0.x + + - name: Run tests + run: dotnet test --configuration Release + + - name: Build and Publish Image + id: build-image + env: + REGISTRY: ${{ steps.login-ecr.outputs.registry }} + REPOSITORY: dms-${{ inputs.environment }}-apps + IMAGE_TAG: ${{ matrix.service.name }}-${{ github.sha }} + run: | + PROJECT_PATH="src/${{ matrix.service.project }}" + + if [ ! -f "$PROJECT_PATH" ]; then + echo "Project not found: $PROJECT_PATH - Skipping" + echo "skip=true" >> $GITHUB_OUTPUT + exit 0 + fi + + echo "Building and publishing: $PROJECT_PATH" + + dotnet publish "$PROJECT_PATH" \ + --configuration Release \ + --target:PublishContainer \ + --property:ContainerRegistry=$REGISTRY \ + --property:ContainerRepository=$REPOSITORY \ + --property:ContainerImageTag=$IMAGE_TAG + + IMAGE="${REGISTRY}/${REPOSITORY}:${IMAGE_TAG}" + echo "IMAGE=${IMAGE}" >> $GITHUB_OUTPUT + echo "skip=false" >> $GITHUB_OUTPUT + echo "Published image: ${IMAGE}" + + - name: Testing workflow steps + run: echo "The testing / deployment of the image have been skipped for this demo, but the workflow is correctly iterating over the services and would deploy the built image to ECS if those steps were uncommented." + + - name: Fetch required secrets + id: fetch-secrets + run: | + SECRET_JSON=$(aws secretsmanager get-secret-value \ + --secret-id ${{ secrets.AWS_SECRETS_ARN }} \ + --query 'SecretString' \ + --output text) + + TASK_ROLE=$(echo "$SECRET_JSON" | jq -r '.Task_Role_ARN') + EXEC_ROLE=$(echo "$SECRET_JSON" | jq -r '.Execution_Role_ARN') + EFS_FS_ID=$(echo "$SECRET_JSON" | jq -r '.EFS_File_System_ID') + EFS_AP_ID=$(echo "$SECRET_JSON" | jq -r '.EFS_Access_Point_ID') + + echo "task_role=$TASK_ROLE" >> $GITHUB_OUTPUT + echo "exec_role=$EXEC_ROLE" >> $GITHUB_OUTPUT + echo "efs-fs-id=$EFS_FS_ID" >> $GITHUB_OUTPUT + echo "efs-ap-id=$EFS_AP_ID" >> $GITHUB_OUTPUT + + - name: Build secrets list for service + id: build-secrets + run: | + SECRETS_JSON='${{ toJson(matrix.service.secrets) }}' + + SECRETS_STRING="" + + declare -A SECRET_MAP=( + ["API_Base_URL"]="API__BaseUrl" + ["Authentication_ApiKey"]="Authentication__ApiKey" + ["S3_BucketName"]="AWS__S3__BucketName" + ["Client_ID"]="AzureAd__ClientId" + ["API_Client_ID"]="AzureAd__ClientId" + ["Client_Secret"]="AzureAd__ClientSecret" + ["AuditDb"]="ConnectionStrings__AuditDb" + ["ClusterDb"]="ConnectionStrings__ClusterDb" + ["DeliusRunningPictureDb"]="ConnectionStrings__DeliusRunningPictureDb" + ["DeliusStagingDb"]="ConnectionStrings__DeliusStagingDb" + ["MatchingDb"]="ConnectionStrings__MatchingDb" + ["OfflocRunningPictureDb"]="ConnectionStrings__OfflocRunningPictureDb" + ["OfflocStagingDb"]="ConnectionStrings__OfflocStagingDb" + ["CatsRabbitMQ"]="ConnectionStrings__CatsRabbitMQ" + ["RabbitMQ"]="ConnectionStrings__RabbitMQ" + ["DMSFilesBasePath"]="DMSFilesBasePath" + ["DMSFilesBasePath_DbInteractions"]="DMSFilesBasePath" + ["Sentry_Dsn"]="Sentry_Dsn" + ) + + # Get the full secret ARN + SECRET_ARN="${{ secrets.AWS_SECRETS_ARN }}" + + for secret in $(echo "$SECRETS_JSON" | jq -r '.[]'); do + env_var_name="${SECRET_MAP[$secret]}" + if [ -n "$env_var_name" ]; then + SECRETS_STRING+="${env_var_name}=${SECRET_ARN}:${secret}::"$'\n' + fi + done + + echo "Built secrets string:" + echo "$SECRETS_STRING" + + SECRETS_STRING=$(echo "$SECRETS_STRING" | sed '/^$/d') + echo "secrets<> $GITHUB_OUTPUT + echo "$SECRETS_STRING" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: Replace EFS placeholders and inject roles in task definition + id: inject-roles + run: | + TASK_DEF_PATH="${{ matrix.service.taskDef }}" + + if [ ! -f "$TASK_DEF_PATH" ]; then + echo "Task definition not found: $TASK_DEF_PATH - Skipping" + echo "skip=true" >> $GITHUB_OUTPUT + exit 0 + fi + + echo "Processing task definition: $TASK_DEF_PATH" + + # Step 1: Replace placeholders (EFS, CloudWatch) + LOG_GROUP_NAME="dms-${{ inputs.environment == 'prod' && 'prod' || 'preprod' }}/ecs" + + sed -i.bak \ + -e 's/EFS_FILE_SYSTEM_ID/${{ steps.fetch-secrets.outputs.efs-fs-id }}/g' \ + -e 's/EFS_ACCESS_POINT_ID/${{ steps.fetch-secrets.outputs.efs-ap-id }}/g' \ + -e "s|PLACEHOLDER|$LOG_GROUP_NAME|g" \ + "$TASK_DEF_PATH" + + # Step 2: Inject IAM roles and set family name to match Terraform convention + FAMILY_NAME="dms-${{ inputs.environment == 'prod' && 'prod' || 'preprod' }}-${{ matrix.service.name }}-task" + + UPDATED_TASK_DEF=$(jq \ + --arg task_role "${{ steps.fetch-secrets.outputs.task_role }}" \ + --arg exec_role "${{ steps.fetch-secrets.outputs.exec_role }}" \ + --arg family "$FAMILY_NAME" \ + '.taskRoleArn = $task_role | .executionRoleArn = $exec_role | .family = $family' \ + "$TASK_DEF_PATH") + + UPDATED_TASK_DEF_FILE="rendered-task-def.json" + echo "$UPDATED_TASK_DEF" > "$UPDATED_TASK_DEF_FILE" + + echo "Updated task definition:" + cat "$UPDATED_TASK_DEF_FILE" | jq '.' + + echo "updated_task_def_file=$UPDATED_TASK_DEF_FILE" >> $GITHUB_OUTPUT + echo "skip=false" >> $GITHUB_OUTPUT + + - name: Render ECS task definition with secrets + id: render-task-def + uses: aws-actions/amazon-ecs-render-task-definition@77954e213ba1f9f9cb016b86a1d4f6fcdea0d57e # v1.8.4 + with: + task-definition: ${{ steps.inject-roles.outputs.updated_task_def_file }} + container-name: ${{ matrix.service.container }} + image: ${{ steps.build-image.outputs.IMAGE }} + environment-variables: | + DOTNET_ENVIRONMENT=${{ inputs.environment == 'prod' && 'Production' || 'PreProduction' }} + secrets: ${{ steps.build-secrets.outputs.secrets }} + + - name: Deploy ECS service + if: steps.build-image.outputs.skip != 'true' + uses: aws-actions/amazon-ecs-deploy-task-definition@fc8fc60f3a60ffd500fcb13b209c59d221ac8c8c # v2.6.1 + with: + task-definition: ${{ steps.render-task-def.outputs.task-definition }} + service: dms-${{ inputs.environment == 'prod' && 'prod' || 'preprod' }}-${{ matrix.service.name }}-service + cluster: dms-${{ inputs.environment == 'prod' && 'prod' || 'preprod' }}-cluster + wait-for-service-stability: true \ No newline at end of file diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index f1d5f8d..cf01e0c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -6,71 +6,30 @@ on: - main permissions: - id-token: write # required for OIDC authentication with AWS + id-token: write + contents: read jobs: - deploy: - runs-on: ubuntu-latest - - environment: ${{ github.ref_name == 'main' && 'prod' || 'dev' }} - - env: - REPOSITORY: dms_visualiser - IMAGE_TAG: ${{ github.ref_name == 'main' && 'prod' || 'dev' }}-${{ github.sha }} - - steps: - - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0 - with: - role-to-assume: ${{ secrets.AWS_ROLE_ARN }} - aws-region: eu-west-2 - - - name: Login to Amazon ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@183a1442edf41672e66566b7fc560e297a290896 # v2.1.1 - - - name: Setup .NET - uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0 - with: - dotnet-version: 10.0.x - - - name: Run tests - run: dotnet test --configuration Release - - - name: Build and Publish Image - id: build-image - run: | - dotnet publish src/Visualiser/Visualiser.csproj \ - --configuration Release \ - --target:PublishContainer \ - --property:ContainerRegistry=${{ steps.login-ecr.outputs.registry }} \ - --property:ContainerRepository=$REPOSITORY \ - --property:ContainerImageTag=$IMAGE_TAG - - IMAGE=${{ steps.login-ecr.outputs.registry }}/${REPOSITORY}:${IMAGE_TAG} - echo "IMAGE=${IMAGE}" >> $GITHUB_OUTPUT - - - name: Render ECS task definition - id: render-task-def - uses: aws-actions/amazon-ecs-render-task-definition@77954e213ba1f9f9cb016b86a1d4f6fcdea0d57e # v1.8.4 - with: - task-definition: infra/dms-visualiser-task-def.json - container-name: dms-visualiser-container - image: ${{ steps.build-image.outputs.image }} - environment-variables: | - VERY_IMPORTANT=DO_NOT_DELETE_ME - DOTNET_ENVIRONMENT=PreProduction - secrets: | - AzureAd__ClientId=${{ secrets.AWS_SECRETS_ARN }}:Client_ID:: - AzureAd__ClientSecret=${{ secrets.AWS_SECRETS_ARN }}:Client_Secret:: - API__BaseUrl=${{ secrets.AWS_SECRETS_ARN }}:API_Base_URL:: - - - name: Deploy ECS service - uses: aws-actions/amazon-ecs-deploy-task-definition@fc8fc60f3a60ffd500fcb13b209c59d221ac8c8c # v2.6.1 - with: - task-definition: ${{ steps.render-task-def.outputs.task-definition }} - service: dms-visualiser-service-1 - cluster: dms-visualiser-cluster \ No newline at end of file + deploy-preprod: + if: github.ref == 'refs/heads/develop' + uses: ./.github/workflows/deploy-reusable.yml + with: + environment: preprod + aws-region: eu-west-2 + secrets: + AWS_ROLE_ARN: ${{ secrets.AWS_ROLE_ARN }} + AWS_SECRETS_ARN: ${{ secrets.AWS_SECRETS_ARN }} + AWS_TASK_ROLE_ARN: ${{ secrets.AWS_TASK_ROLE_ARN }} + AWS_EXECUTION_ROLE_ARN: ${{ secrets.AWS_EXECUTION_ROLE_ARN }} + + deploy-prod: + if: github.ref == 'refs/heads/main' + uses: ./.github/workflows/deploy-reusable.yml + with: + environment: prod + aws-region: eu-west-2 + secrets: + AWS_ROLE_ARN: ${{ secrets.AWS_ROLE_ARN }} + AWS_SECRETS_ARN: ${{ secrets.AWS_SECRETS_ARN }} + AWS_TASK_ROLE_ARN: ${{ secrets.AWS_TASK_ROLE_ARN }} + AWS_EXECUTION_ROLE_ARN: ${{ secrets.AWS_EXECUTION_ROLE_ARN }} \ No newline at end of file diff --git a/infra/README.md b/infra/README.md new file mode 100644 index 0000000..a7b312c --- /dev/null +++ b/infra/README.md @@ -0,0 +1,165 @@ +# DMS Infrastructure - AWS Fargate + +This directory contains AWS Fargate ECS task definitions for the DMS (Data Management System). + +## Contents + +## Structure + +``` +infra/ +├── services.json # Service deployment configuration +├── README.md # This file +└── task-definitions/ # ECS task definition files + ├── api-task-def.json + ├── blocking-task-def.json + └── ... +``` + +### Service Mappings +`services.json` - Central configuration mapping services to projects and task definitions. +This file is used by the CI/CD pipeline to automatically deploy all services. + +**Adding a new service:** +1. Create the task definition JSON file in `task-definitions/` (e.g., `my-service-task-def.json`) +2. Add an entry to `services.json`: +```json +{ + "name": "my-service", + "project": "MyService/MyService.csproj", + "taskDef": "infra/task-definitions/my-service-task-def.json", + "container": "my-service-container" +} +``` +3. Push to `develop` or `main` - deployment is automatic! + +### Task Definitions +Each service has its own ECS Fargate task definition JSON file in `task-definitions/`: +- `api-task-def.json` - REST API service (512 CPU, 1024 MB) +- `blocking-task-def.json` - Blocking service (512 CPU, 1024 MB) +- `cleanup-task-def.json` - Cleanup service (256 CPU, 512 MB) +- `delius-parser-task-def.json` - Delius data parser (512 CPU, 1024 MB) +- `filesync-task-def.json` - File synchronization service (512 CPU, 1024 MB) +- `import-task-def.json` - Data import service (512 CPU, 1024 MB) +- `logging-task-def.json` - Logging aggregation (256 CPU, 512 MB) +- `matching-engine-task-def.json` - Matching engine (1024 CPU, 2048 MB) +- `meow-task-def.json` - Meow service (512 CPU, 1024 MB) +- `offloc-cleaner-task-def.json` - Offloc cleaner (512 CPU, 1024 MB) +- `offloc-parser-task-def.json` - Offloc parser (512 CPU, 1024 MB) +- `visualiser-task-def.json` - Visualiser UI (256 CPU, 512 MB) + +## Prerequisites + +1. AWS CLI configured with appropriate credentials +2. .NET 8+ SDK installed +3. ECR repositories created for each service +4. VPC with public and private subnets +5. RDS databases (or equivalent) for data storage +6. RabbitMQ or Amazon MQ for message queuing + +## Building and Publishing Containers + +### Using .NET SDK Container Publishing (Recommended - No Dockerfiles needed!) + +.NET 8+ has built-in container support. Build and push directly to ECR using CLI arguments: + +```bash +# Login to ECR +aws ecr get-login-password --region eu-west-2 | \ + docker login --username AWS --password-stdin {account-id}.dkr.ecr.eu-west-2.amazonaws.com + +# Build and publish - pass container settings as CLI arguments +dotnet publish src/API/API.csproj \ + --configuration Release \ + --target:PublishContainer \ + --property:ContainerRegistry={account-id}.dkr.ecr.eu-west-2.amazonaws.com \ + --property:ContainerRepository=preprod/api \ + --property:ContainerImageTag=abc123 + +dotnet publish src/Visualiser/Visualiser.csproj \ + --configuration Release \ + --target:PublishContainer \ + --property:ContainerRegistry={account-id}.dkr.ecr.eu-west-2.amazonaws.com \ + --property:ContainerRepository=prod/visualiser \ + --property:ContainerImageTag=def456 + +# Repeat for other services: Blocking, Cleanup, Delius.Parser, FileSync, Import, +# Logging, Matching.Engine, Meow, Offloc.Cleaner, Offloc.Parser +``` + +**Note**: +- Dockerfiles are not required when using SDK container publishing +- Pass configuration via CLI args (like the CI/CD pipeline does) rather than hardcoding in .csproj +- Use environment-specific repository names: `preprod/{service}` or `prod/{service}` + +## Configuration Steps + +### 1. Configure Secrets and Environment Variables + +Each service requires configuration for: +- Database connection strings (via AWS Secrets Manager) +- RabbitMQ connection details +- S3 bucket names +- Sentry DSN (optional) + +Add these to the `secrets` array in task definitions: +```json +"secrets": [ + { + "name": "ConnectionStrings__ClusterDb", + "valueFrom": "arn:aws:secretsmanager:eu-west-2:{account-id}:secret:dms/cluster-db" + } +] +``` + +**Note**: Container images are automatically set by the CI/CD pipeline using the `PLACEHOLDER` value. The deployment workflow dynamically injects the correct image URIs at deploy time. + +## Registering Task Definitions + +**Note**: Task definitions are automatically registered by the CI/CD pipeline during deployment. + +For manual registration (if needed): +```bash +aws ecs register-task-definition --cli-input-json file://task-definitions/api-task-def.json +aws ecs register-task-definition --cli-input-json file://task-definitions/blocking-task-def.json +# ... repeat for each service +``` + +## Create ECS Services + +After registering task definitions, create services: +```bash +aws ecs create-service \ + --cluster dms-cluster-dev \ + --service-name dms-api \ + --task-definition dms-api-task \ + --desired-count 2 \ + --launch-type FARGATE \ + --network-configuration "awsvpcConfiguration={subnets=[subnet-xxxxx],securityGroups=[sg-xxxxx],assignPublicIp=DISABLED}" \ + --load-balancers "targetGroupArn=arn:aws:elasticloadbalancing:...,containerName=api-container,containerPort=8080" +``` + +## Service Architecture + +### Public Services (behind ALB) +- **API** - Main REST API endpoint +- **Visualiser** - Web UI for data visualization + +### Internal Services (no load balancer) +- **Blocking** - Handles blocking operations +- **Cleanup** - Data cleanup tasks +- **Delius Parser** - Parses Delius data files +- **FileSync** - Syncs files to/from S3 +- **Import** - Imports data from external sources +- **Logging** - Aggregates and processes logs +- **Matching Engine** - Performs data matching operations +- **Meow** - Message processing service +- **Offloc Cleaner** - Cleans Offloc data +- **Offloc Parser** - Parses Offloc data files + +## Monitoring + +All services log to CloudWatch Logs: +- Log group format: `/ecs/dms-{service-name}` +- Retention: 30 days (configurable in Terraform) +- Container Insights enabled for metrics diff --git a/infra/dms-visualiser-task-def.json b/infra/dms-visualiser-task-def.json deleted file mode 100644 index a9f25ac..0000000 --- a/infra/dms-visualiser-task-def.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "containerDefinitions": [ - { - "name": "dms-visualiser-container", - "image": "PLACEHOLDER", - "cpu": 256, - "memory": 512, - "portMappings": [ - { - "containerPort": 8080, - "hostPort": 8080, - "protocol": "tcp", - "name": "http" - }, - { - "containerPort": 8081, - "hostPort": 8081, - "protocol": "tcp", - "name": "https" - } - ], - "essential": true, - "environment": [], - "mountPoints": [], - "volumesFrom": [], - "logConfiguration": { - "logDriver": "awslogs", - "options": { - "awslogs-group": "ecs/dms-visualiser", - "awslogs-region": "eu-west-2", - "awslogs-stream-prefix": "ecs" - } - }, - "systemControls": [] - } - ], - "family": "dms-visualiser-task", - "taskRoleArn": "arn:aws:iam::035941410605:role/DMS-PreProd_ecs_task_role", - "executionRoleArn": "ecsTaskExecutionRole", - "networkMode": "awsvpc", - "revision": 6, - "volumes": [], - "status": "ACTIVE", - "requiresAttributes": [ - { - "name": "com.amazonaws.ecs.capability.logging-driver.awslogs" - }, - { - "name": "ecs.capability.execution-role-awslogs" - }, - { - "name": "com.amazonaws.ecs.capability.ecr-auth" - }, - { - "name": "com.amazonaws.ecs.capability.docker-remote-api.1.19" - }, - { - "name": "ecs.capability.execution-role-ecr-pull" - }, - { - "name": "com.amazonaws.ecs.capability.docker-remote-api.1.18" - }, - { - "name": "ecs.capability.task-eni" - } - ], - "placementConstraints": [], - "compatibilities": [ - "EC2", - "MANAGED_INSTANCES", - "FARGATE" - ], - "requiresCompatibilities": [ - "FARGATE" - ], - "cpu": "256", - "memory": "512" -} \ No newline at end of file diff --git a/infra/services.json b/infra/services.json new file mode 100644 index 0000000..05b47b2 --- /dev/null +++ b/infra/services.json @@ -0,0 +1,161 @@ +{ + "services": [ + { + "name": "api", + "project": "API/API.csproj", + "taskDef": "infra/task-definitions/api-task-def.json", + "container": "api-container", + "secrets": [ + "AuditDb", + "DeliusRunningPictureDb", + "OfflocRunningPictureDb", + "ClusterDb", + "API_Client_ID", + "Authentication_ApiKey", + "Sentry_Dsn" + ] + }, + { + "name": "offloc-cleaner", + "project": "Offloc.Cleaner/Offloc.Cleaner.csproj", + "taskDef": "infra/task-definitions/offloc-cleaner-task-def.json", + "container": "offloc-cleaner-container", + "secrets": [ + "DMSFilesBasePath", + "RabbitMQ", + "Sentry_Dsn" + ] + }, + { + "name": "matching-engine", + "project": "Matching.Engine/Matching.Engine.csproj", + "taskDef": "infra/task-definitions/matching-engine-task-def.json", + "container": "matching-engine-container", + "secrets": [ + "ClusterDb", + "MatchingDb", + "RabbitMQ", + "Sentry_Dsn" + ] + }, + { + "name": "cleanup", + "project": "Cleanup/Cleanup.csproj", + "taskDef": "infra/task-definitions/cleanup-task-def.json", + "container": "cleanup-container", + "secrets": [ + "DMSFilesBasePath", + "RabbitMQ", + "Sentry_Dsn" + ] + }, + { + "name": "import", + "project": "Import/Import.csproj", + "taskDef": "infra/task-definitions/import-task-def.json", + "container": "import-container", + "secrets": [ + "RabbitMQ", + "Sentry_Dsn" + ] + }, + { + "name": "visualiser", + "project": "Visualiser/Visualiser.csproj", + "taskDef": "infra/task-definitions/visualiser-task-def.json", + "container": "visualiser-container", + "secrets": [ + "API_Base_URL", + "Client_ID", + "Client_Secret", + "Sentry_Dsn" + ] + }, + { + "name": "offloc-parser", + "project": "Offloc.Parser/Offloc.Parser.csproj", + "taskDef": "infra/task-definitions/offloc-parser-task-def.json", + "container": "offloc-parser-container", + "secrets": [ + "DMSFilesBasePath", + "OfflocStagingDb", + "RabbitMQ", + "Sentry_Dsn" + ] + }, + { + "name": "delius-parser", + "project": "Delius.Parser/Delius.Parser.csproj", + "taskDef": "infra/task-definitions/delius-parser-task-def.json", + "container": "delius-parser-container", + "secrets": [ + "DMSFilesBasePath", + "DeliusStagingDb", + "RabbitMQ", + "Sentry_Dsn" + ] + }, + { + "name": "dbinteractions", + "project": "DbInteractions/DbInteractions.csproj", + "taskDef": "infra/task-definitions/dbinteractions-task-def.json", + "container": "dbinteractions-container", + "secrets": [ + "DMSFilesBasePath_DbInteractions", + "DeliusRunningPictureDb", + "OfflocRunningPictureDb", + "DeliusStagingDb", + "OfflocStagingDb", + "RabbitMQ", + "Sentry_Dsn" + ] + }, + { + "name": "blocking", + "project": "Blocking/Blocking.csproj", + "taskDef": "infra/task-definitions/blocking-task-def.json", + "container": "blocking-container", + "secrets": [ + "MatchingDb", + "RabbitMQ", + "Sentry_Dsn" + ] + }, + { + "name": "logging", + "project": "Logging/Logging.csproj", + "taskDef": "infra/task-definitions/logging-task-def.json", + "container": "logging-container", + "secrets": [ + "RabbitMQ", + "Sentry_Dsn" + ] + }, + { + "name": "filesync", + "project": "FileSync/FileSync.csproj", + "taskDef": "infra/task-definitions/filesync-task-def.json", + "container": "filesync-container", + "secrets": [ + "DMSFilesBasePath", + "S3_BucketName", + "RabbitMQ", + "Sentry_Dsn" + ] + }, + { + "name": "meow", + "project": "Meow/Meow.csproj", + "taskDef": "infra/task-definitions/meow-task-def.json", + "container": "meow-container", + "secrets": [ + "AuditDb", + "DeliusRunningPictureDb", + "OfflocRunningPictureDb", + "ClusterDb", + "CatsRabbitMQ", + "Sentry_Dsn" + ] + } + ] +} diff --git a/infra/task-definitions/api-task-def.json b/infra/task-definitions/api-task-def.json new file mode 100644 index 0000000..aed85c3 --- /dev/null +++ b/infra/task-definitions/api-task-def.json @@ -0,0 +1,50 @@ +{ + "containerDefinitions": [ + { + "name": "api-container", + "image": "PLACEHOLDER", + "cpu": 512, + "memory": 1024, + "portMappings": [ + { + "containerPort": 8080, + "hostPort": 8080, + "protocol": "tcp", + "name": "http" + }, + { + "containerPort": 8081, + "hostPort": 8081, + "protocol": "tcp", + "name": "https" + } + ], + "essential": true, + "environment": [], + "secrets": [], + "mountPoints": [], + "volumesFrom": [], + "logConfiguration": { + "logDriver": "awslogs", + "options": { + "awslogs-group": "PLACEHOLDER", + "awslogs-region": "eu-west-2", + "awslogs-stream-prefix": "ecs/api" + } + }, + "systemControls": [] + } + ], + "family": "PLACEHOLDER", + "networkMode": "awsvpc", + "volumes": [], + "placementConstraints": [], + "compatibilities": [ + "FARGATE" + ], + "requiresCompatibilities": [ + "FARGATE" + ], + "cpu": "512", + "memory": "1024" +} diff --git a/infra/task-definitions/blocking-task-def.json b/infra/task-definitions/blocking-task-def.json new file mode 100644 index 0000000..1166d80 --- /dev/null +++ b/infra/task-definitions/blocking-task-def.json @@ -0,0 +1,53 @@ +{ + "containerDefinitions": [ + { + "name": "blocking-container", + "image": "PLACEHOLDER", + "cpu": 512, + "memory": 1024, + "essential": true, + "environment": [], + "secrets": [], + "mountPoints": [ + { + "sourceVolume": "dms-efs", + "containerPath": "/mnt/efs", + "readOnly": false + } + ], + "volumesFrom": [], + "logConfiguration": { + "logDriver": "awslogs", + "options": { + "awslogs-group": "PLACEHOLDER", + "awslogs-region": "eu-west-2", + "awslogs-stream-prefix": "ecs/blocking" + } + }, + "systemControls": [] + } + ], + "family": "PLACEHOLDER", + "networkMode": "awsvpc", + "volumes": [ + { + "name": "dms-efs", + "efsVolumeConfiguration": { + "fileSystemId": "EFS_FILE_SYSTEM_ID", + "transitEncryption": "ENABLED", + "authorizationConfig": { + "accessPointId": "EFS_ACCESS_POINT_ID" + } + } + } + ], + "placementConstraints": [], + "compatibilities": [ + "FARGATE" + ], + "requiresCompatibilities": [ + "FARGATE" + ], + "cpu": "512", + "memory": "1024" +} diff --git a/infra/task-definitions/cleanup-task-def.json b/infra/task-definitions/cleanup-task-def.json new file mode 100644 index 0000000..ef294ec --- /dev/null +++ b/infra/task-definitions/cleanup-task-def.json @@ -0,0 +1,53 @@ +{ + "containerDefinitions": [ + { + "name": "cleanup-container", + "image": "PLACEHOLDER", + "cpu": 256, + "memory": 512, + "essential": true, + "environment": [], + "secrets": [], + "mountPoints": [ + { + "sourceVolume": "dms-efs", + "containerPath": "/mnt/efs", + "readOnly": false + } + ], + "volumesFrom": [], + "logConfiguration": { + "logDriver": "awslogs", + "options": { + "awslogs-group": "PLACEHOLDER", + "awslogs-region": "eu-west-2", + "awslogs-stream-prefix": "ecs/cleanup" + } + }, + "systemControls": [] + } + ], + "family": "PLACEHOLDER", + "networkMode": "awsvpc", + "volumes": [ + { + "name": "dms-efs", + "efsVolumeConfiguration": { + "fileSystemId": "EFS_FILE_SYSTEM_ID", + "transitEncryption": "ENABLED", + "authorizationConfig": { + "accessPointId": "EFS_ACCESS_POINT_ID" + } + } + } + ], + "placementConstraints": [], + "compatibilities": [ + "FARGATE" + ], + "requiresCompatibilities": [ + "FARGATE" + ], + "cpu": "256", + "memory": "512" +} diff --git a/infra/task-definitions/dbinteractions-task-def.json b/infra/task-definitions/dbinteractions-task-def.json new file mode 100644 index 0000000..d558a7d --- /dev/null +++ b/infra/task-definitions/dbinteractions-task-def.json @@ -0,0 +1,53 @@ +{ + "containerDefinitions": [ + { + "name": "dbinteractions-container", + "image": "PLACEHOLDER", + "cpu": 512, + "memory": 1024, + "essential": true, + "environment": [], + "secrets": [], + "mountPoints": [ + { + "sourceVolume": "dms-efs", + "containerPath": "/mnt/efs", + "readOnly": false + } + ], + "volumesFrom": [], + "logConfiguration": { + "logDriver": "awslogs", + "options": { + "awslogs-group": "PLACEHOLDER", + "awslogs-region": "eu-west-2", + "awslogs-stream-prefix": "ecs/dbinteractions" + } + }, + "systemControls": [] + } + ], + "family": "PLACEHOLDER", + "networkMode": "awsvpc", + "volumes": [ + { + "name": "dms-efs", + "efsVolumeConfiguration": { + "fileSystemId": "EFS_FILE_SYSTEM_ID", + "transitEncryption": "ENABLED", + "authorizationConfig": { + "accessPointId": "EFS_ACCESS_POINT_ID" + } + } + } + ], + "placementConstraints": [], + "compatibilities": [ + "FARGATE" + ], + "requiresCompatibilities": [ + "FARGATE" + ], + "cpu": "512", + "memory": "1024" +} diff --git a/infra/task-definitions/delius-parser-task-def.json b/infra/task-definitions/delius-parser-task-def.json new file mode 100644 index 0000000..798bae4 --- /dev/null +++ b/infra/task-definitions/delius-parser-task-def.json @@ -0,0 +1,53 @@ +{ + "containerDefinitions": [ + { + "name": "delius-parser-container", + "image": "PLACEHOLDER", + "cpu": 512, + "memory": 1024, + "essential": true, + "environment": [], + "secrets": [], + "mountPoints": [ + { + "sourceVolume": "dms-efs", + "containerPath": "/mnt/efs", + "readOnly": false + } + ], + "volumesFrom": [], + "logConfiguration": { + "logDriver": "awslogs", + "options": { + "awslogs-group": "PLACEHOLDER", + "awslogs-region": "eu-west-2", + "awslogs-stream-prefix": "ecs/delius-parser" + } + }, + "systemControls": [] + } + ], + "family": "PLACEHOLDER", + "networkMode": "awsvpc", + "volumes": [ + { + "name": "dms-efs", + "efsVolumeConfiguration": { + "fileSystemId": "EFS_FILE_SYSTEM_ID", + "transitEncryption": "ENABLED", + "authorizationConfig": { + "accessPointId": "EFS_ACCESS_POINT_ID" + } + } + } + ], + "placementConstraints": [], + "compatibilities": [ + "FARGATE" + ], + "requiresCompatibilities": [ + "FARGATE" + ], + "cpu": "512", + "memory": "1024" +} diff --git a/infra/task-definitions/filesync-task-def.json b/infra/task-definitions/filesync-task-def.json new file mode 100644 index 0000000..d125180 --- /dev/null +++ b/infra/task-definitions/filesync-task-def.json @@ -0,0 +1,53 @@ +{ + "containerDefinitions": [ + { + "name": "filesync-container", + "image": "PLACEHOLDER", + "cpu": 512, + "memory": 1024, + "essential": true, + "environment": [], + "secrets": [], + "mountPoints": [ + { + "sourceVolume": "dms-efs", + "containerPath": "/mnt/efs", + "readOnly": false + } + ], + "volumesFrom": [], + "logConfiguration": { + "logDriver": "awslogs", + "options": { + "awslogs-group": "PLACEHOLDER", + "awslogs-region": "eu-west-2", + "awslogs-stream-prefix": "ecs/filesync" + } + }, + "systemControls": [] + } + ], + "family": "PLACEHOLDER", + "networkMode": "awsvpc", + "volumes": [ + { + "name": "dms-efs", + "efsVolumeConfiguration": { + "fileSystemId": "EFS_FILE_SYSTEM_ID", + "transitEncryption": "ENABLED", + "authorizationConfig": { + "accessPointId": "EFS_ACCESS_POINT_ID" + } + } + } + ], + "placementConstraints": [], + "compatibilities": [ + "FARGATE" + ], + "requiresCompatibilities": [ + "FARGATE" + ], + "cpu": "512", + "memory": "1024" +} diff --git a/infra/task-definitions/import-task-def.json b/infra/task-definitions/import-task-def.json new file mode 100644 index 0000000..72eb221 --- /dev/null +++ b/infra/task-definitions/import-task-def.json @@ -0,0 +1,53 @@ +{ + "containerDefinitions": [ + { + "name": "import-container", + "image": "PLACEHOLDER", + "cpu": 512, + "memory": 1024, + "essential": true, + "environment": [], + "secrets": [], + "mountPoints": [ + { + "sourceVolume": "dms-efs", + "containerPath": "/mnt/efs", + "readOnly": false + } + ], + "volumesFrom": [], + "logConfiguration": { + "logDriver": "awslogs", + "options": { + "awslogs-group": "PLACEHOLDER", + "awslogs-region": "eu-west-2", + "awslogs-stream-prefix": "ecs/import" + } + }, + "systemControls": [] + } + ], + "family": "PLACEHOLDER", + "networkMode": "awsvpc", + "volumes": [ + { + "name": "dms-efs", + "efsVolumeConfiguration": { + "fileSystemId": "EFS_FILE_SYSTEM_ID", + "transitEncryption": "ENABLED", + "authorizationConfig": { + "accessPointId": "EFS_ACCESS_POINT_ID" + } + } + } + ], + "placementConstraints": [], + "compatibilities": [ + "FARGATE" + ], + "requiresCompatibilities": [ + "FARGATE" + ], + "cpu": "512", + "memory": "1024" +} diff --git a/infra/task-definitions/logging-task-def.json b/infra/task-definitions/logging-task-def.json new file mode 100644 index 0000000..c10360b --- /dev/null +++ b/infra/task-definitions/logging-task-def.json @@ -0,0 +1,53 @@ +{ + "containerDefinitions": [ + { + "name": "logging-container", + "image": "PLACEHOLDER", + "cpu": 256, + "memory": 512, + "essential": true, + "environment": [], + "secrets": [], + "mountPoints": [ + { + "sourceVolume": "dms-efs", + "containerPath": "/mnt/efs", + "readOnly": false + } + ], + "volumesFrom": [], + "logConfiguration": { + "logDriver": "awslogs", + "options": { + "awslogs-group": "PLACEHOLDER", + "awslogs-region": "eu-west-2", + "awslogs-stream-prefix": "ecs/logging" + } + }, + "systemControls": [] + } + ], + "family": "PLACEHOLDER", + "networkMode": "awsvpc", + "volumes": [ + { + "name": "dms-efs", + "efsVolumeConfiguration": { + "fileSystemId": "EFS_FILE_SYSTEM_ID", + "transitEncryption": "ENABLED", + "authorizationConfig": { + "accessPointId": "EFS_ACCESS_POINT_ID" + } + } + } + ], + "placementConstraints": [], + "compatibilities": [ + "FARGATE" + ], + "requiresCompatibilities": [ + "FARGATE" + ], + "cpu": "256", + "memory": "512" +} diff --git a/infra/task-definitions/matching-engine-task-def.json b/infra/task-definitions/matching-engine-task-def.json new file mode 100644 index 0000000..9d59eb6 --- /dev/null +++ b/infra/task-definitions/matching-engine-task-def.json @@ -0,0 +1,53 @@ +{ + "containerDefinitions": [ + { + "name": "matching-engine-container", + "image": "PLACEHOLDER", + "cpu": 1024, + "memory": 2048, + "essential": true, + "environment": [], + "secrets": [], + "mountPoints": [ + { + "sourceVolume": "dms-efs", + "containerPath": "/mnt/efs", + "readOnly": false + } + ], + "volumesFrom": [], + "logConfiguration": { + "logDriver": "awslogs", + "options": { + "awslogs-group": "PLACEHOLDER", + "awslogs-region": "eu-west-2", + "awslogs-stream-prefix": "ecs/matching-engine" + } + }, + "systemControls": [] + } + ], + "family": "PLACEHOLDER", + "networkMode": "awsvpc", + "volumes": [ + { + "name": "dms-efs", + "efsVolumeConfiguration": { + "fileSystemId": "EFS_FILE_SYSTEM_ID", + "transitEncryption": "ENABLED", + "authorizationConfig": { + "accessPointId": "EFS_ACCESS_POINT_ID" + } + } + } + ], + "placementConstraints": [], + "compatibilities": [ + "FARGATE" + ], + "requiresCompatibilities": [ + "FARGATE" + ], + "cpu": "1024", + "memory": "2048" +} diff --git a/infra/task-definitions/meow-task-def.json b/infra/task-definitions/meow-task-def.json new file mode 100644 index 0000000..2272a18 --- /dev/null +++ b/infra/task-definitions/meow-task-def.json @@ -0,0 +1,53 @@ +{ + "containerDefinitions": [ + { + "name": "meow-container", + "image": "PLACEHOLDER", + "cpu": 512, + "memory": 1024, + "essential": true, + "environment": [], + "secrets": [], + "mountPoints": [ + { + "sourceVolume": "dms-efs", + "containerPath": "/mnt/efs", + "readOnly": false + } + ], + "volumesFrom": [], + "logConfiguration": { + "logDriver": "awslogs", + "options": { + "awslogs-group": "PLACEHOLDER", + "awslogs-region": "eu-west-2", + "awslogs-stream-prefix": "ecs/meow" + } + }, + "systemControls": [] + } + ], + "family": "PLACEHOLDER", + "networkMode": "awsvpc", + "volumes": [ + { + "name": "dms-efs", + "efsVolumeConfiguration": { + "fileSystemId": "EFS_FILE_SYSTEM_ID", + "transitEncryption": "ENABLED", + "authorizationConfig": { + "accessPointId": "EFS_ACCESS_POINT_ID" + } + } + } + ], + "placementConstraints": [], + "compatibilities": [ + "FARGATE" + ], + "requiresCompatibilities": [ + "FARGATE" + ], + "cpu": "512", + "memory": "1024" +} diff --git a/infra/task-definitions/offloc-cleaner-task-def.json b/infra/task-definitions/offloc-cleaner-task-def.json new file mode 100644 index 0000000..b0d3cb4 --- /dev/null +++ b/infra/task-definitions/offloc-cleaner-task-def.json @@ -0,0 +1,53 @@ +{ + "containerDefinitions": [ + { + "name": "offloc-cleaner-container", + "image": "PLACEHOLDER", + "cpu": 512, + "memory": 1024, + "essential": true, + "environment": [], + "secrets": [], + "mountPoints": [ + { + "sourceVolume": "dms-efs", + "containerPath": "/mnt/efs", + "readOnly": false + } + ], + "volumesFrom": [], + "logConfiguration": { + "logDriver": "awslogs", + "options": { + "awslogs-group": "PLACEHOLDER", + "awslogs-region": "eu-west-2", + "awslogs-stream-prefix": "ecs/offloc-cleaner" + } + }, + "systemControls": [] + } + ], + "family": "PLACEHOLDER", + "networkMode": "awsvpc", + "volumes": [ + { + "name": "dms-efs", + "efsVolumeConfiguration": { + "fileSystemId": "EFS_FILE_SYSTEM_ID", + "transitEncryption": "ENABLED", + "authorizationConfig": { + "accessPointId": "EFS_ACCESS_POINT_ID" + } + } + } + ], + "placementConstraints": [], + "compatibilities": [ + "FARGATE" + ], + "requiresCompatibilities": [ + "FARGATE" + ], + "cpu": "512", + "memory": "1024" +} diff --git a/infra/task-definitions/offloc-parser-task-def.json b/infra/task-definitions/offloc-parser-task-def.json new file mode 100644 index 0000000..d19f8bd --- /dev/null +++ b/infra/task-definitions/offloc-parser-task-def.json @@ -0,0 +1,53 @@ +{ + "containerDefinitions": [ + { + "name": "offloc-parser-container", + "image": "PLACEHOLDER", + "cpu": 512, + "memory": 1024, + "essential": true, + "environment": [], + "secrets": [], + "mountPoints": [ + { + "sourceVolume": "dms-efs", + "containerPath": "/mnt/efs", + "readOnly": false + } + ], + "volumesFrom": [], + "logConfiguration": { + "logDriver": "awslogs", + "options": { + "awslogs-group": "PLACEHOLDER", + "awslogs-region": "eu-west-2", + "awslogs-stream-prefix": "ecs/offloc-parser" + } + }, + "systemControls": [] + } + ], + "family": "PLACEHOLDER", + "networkMode": "awsvpc", + "volumes": [ + { + "name": "dms-efs", + "efsVolumeConfiguration": { + "fileSystemId": "EFS_FILE_SYSTEM_ID", + "transitEncryption": "ENABLED", + "authorizationConfig": { + "accessPointId": "EFS_ACCESS_POINT_ID" + } + } + } + ], + "placementConstraints": [], + "compatibilities": [ + "FARGATE" + ], + "requiresCompatibilities": [ + "FARGATE" + ], + "cpu": "512", + "memory": "1024" +} diff --git a/infra/task-definitions/visualiser-task-def.json b/infra/task-definitions/visualiser-task-def.json new file mode 100644 index 0000000..140e0a2 --- /dev/null +++ b/infra/task-definitions/visualiser-task-def.json @@ -0,0 +1,76 @@ +{ + "containerDefinitions": [ + { + "name": "visualiser-container", + "image": "PLACEHOLDER", + "cpu": 256, + "memory": 512, + "portMappings": [ + { + "containerPort": 8080, + "hostPort": 8080, + "protocol": "tcp", + "name": "http" + }, + { + "containerPort": 8081, + "hostPort": 8081, + "protocol": "tcp", + "name": "https" + } + ], + "essential": true, + "environment": [], + "mountPoints": [], + "volumesFrom": [], + "logConfiguration": { + "logDriver": "awslogs", + "options": { + "awslogs-group": "PLACEHOLDER", + "awslogs-region": "eu-west-2", + "awslogs-stream-prefix": "ecs/visualiser" + } + }, + "systemControls": [] + } + ], + "family": "PLACEHOLDER", + "networkMode": "awsvpc", + "revision": 6, + "volumes": [], + "status": "ACTIVE", + "requiresAttributes": [ + { + "name": "com.amazonaws.ecs.capability.logging-driver.awslogs" + }, + { + "name": "ecs.capability.execution-role-awslogs" + }, + { + "name": "com.amazonaws.ecs.capability.ecr-auth" + }, + { + "name": "com.amazonaws.ecs.capability.docker-remote-api.1.19" + }, + { + "name": "ecs.capability.execution-role-ecr-pull" + }, + { + "name": "com.amazonaws.ecs.capability.docker-remote-api.1.18" + }, + { + "name": "ecs.capability.task-eni" + } + ], + "placementConstraints": [], + "compatibilities": [ + "EC2", + "MANAGED_INSTANCES", + "FARGATE" + ], + "requiresCompatibilities": [ + "FARGATE" + ], + "cpu": "256", + "memory": "512" +} diff --git a/src/API/Program.cs b/src/API/Program.cs index d818c92..0240dff 100644 --- a/src/API/Program.cs +++ b/src/API/Program.cs @@ -14,6 +14,8 @@ // Add service defaults & Aspire components. builder.AddServiceDefaults(); +builder.Services.AddRequestTimeouts(); +builder.Services.AddOutputCache(); builder.Services.AddOpenApiDocument(options => { @@ -120,6 +122,9 @@ app.UseAuthentication(); app.UseAuthorization(); +app.UseRequestTimeouts(); +app.UseOutputCache(); + app.RegisterClusteringEndpoints() .RegisterDeliusEndpoints() .RegisterOfflocEndpoints() diff --git a/src/Aspire/Aspire.ServiceDefaults/Extensions.cs b/src/Aspire/Aspire.ServiceDefaults/Extensions.cs index 274e4b6..e296cae 100644 --- a/src/Aspire/Aspire.ServiceDefaults/Extensions.cs +++ b/src/Aspire/Aspire.ServiceDefaults/Extensions.cs @@ -86,6 +86,15 @@ private static IHostApplicationBuilder AddOpenTelemetryExporters(this IHostAppli public static IHostApplicationBuilder AddDefaultHealthChecks(this IHostApplicationBuilder builder) { + builder.Services.AddRequestTimeouts( + configure: static timeouts => + timeouts.AddPolicy("HealthChecks", TimeSpan.FromSeconds(5))); + + builder.Services.AddOutputCache( + configureOptions: static caching => + caching.AddPolicy("HealthChecks", + build: static policy => policy.Expire(TimeSpan.FromSeconds(10)))); + builder.Services.AddHealthChecks() // Add a default liveness check to ensure app is responsive .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]); @@ -97,17 +106,23 @@ public static WebApplication MapDefaultEndpoints(this WebApplication app) { // Adding health checks endpoints to applications in non-development environments has security implications. // See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments. - if (app.Environment.IsDevelopment()) - { - // All health checks must pass for app to be considered ready to accept traffic after starting - app.MapHealthChecks("/health"); - // Only health checks tagged with the "live" tag must pass for app to be considered alive - app.MapHealthChecks("/alive", new HealthCheckOptions - { - Predicate = r => r.Tags.Contains("live") - }); - } + var healthChecks = app.MapGroup(""); + + healthChecks + .CacheOutput("HealthChecks") + .WithRequestTimeout("HealthChecks"); + + // All health checks must pass for app to be + // considered ready to accept traffic after starting + healthChecks.MapHealthChecks("/health").AllowAnonymous(); + + // Only health checks tagged with the "live" tag + // must pass for app to be considered alive + healthChecks.MapHealthChecks("/alive", new() + { + Predicate = static r => r.Tags.Contains("live") + }).AllowAnonymous(); return app; } diff --git a/src/Visualiser/Program.cs b/src/Visualiser/Program.cs index b325355..c2c238a 100644 --- a/src/Visualiser/Program.cs +++ b/src/Visualiser/Program.cs @@ -8,7 +8,9 @@ builder.UseDmsSerilog(); -builder.Services.AddHealthChecks(); +builder.AddServiceDefaults(); +builder.Services.AddRequestTimeouts(); +builder.Services.AddOutputCache(); builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) .AddMicrosoftIdentityWebApp(builder.Configuration) @@ -51,14 +53,17 @@ app.UseRouting(); -app.MapHealthChecks("/healthz").AllowAnonymous(); - app.UseAuthentication(); app.UseAuthorization(); +app.UseRequestTimeouts(); +app.UseOutputCache(); + app.MapStaticAssets(); app.MapRazorPages() .WithStaticAssets(); + app.MapControllers(); +app.MapDefaultEndpoints(); app.Run(); diff --git a/src/Visualiser/Visualiser.csproj b/src/Visualiser/Visualiser.csproj index 4671b49..87dd112 100644 --- a/src/Visualiser/Visualiser.csproj +++ b/src/Visualiser/Visualiser.csproj @@ -20,6 +20,7 @@ +