diff --git a/ansible/ecr-lifecycle/ecr_lifecycle.json b/ansible/ecr-lifecycle/ecr_lifecycle.json new file mode 100644 index 000000000..cfdea1bdf --- /dev/null +++ b/ansible/ecr-lifecycle/ecr_lifecycle.json @@ -0,0 +1,36 @@ +{ + "rules": [ + { + "rulePriority": 1, + "description": "Keep the 10 most recent ECS deployment images - AMEND NUMBER AFTER TEST", + "selection": { + "tagStatus": "tagged", + "tagPrefixList": ["ecs-"], + "countType": "imageCountMoreThan", + "countNumber": 800 + }, + "action": { "type": "expire" } + }, + { + "rulePriority": 2, + "description": "Never expire the 'latest' tag", + "selection": { + "tagStatus": "tagged", + "tagPrefixList": ["latest"], + "countType": "imageCountMoreThan", + "countNumber": 9999 + }, + "action": { "type": "expire" } + }, + { + "rulePriority": 3, + "description": "Keep the 5 most recent build images (all tags) - AMEND NUMBER AFTER TEST", + "selection": { + "tagStatus": "any", + "countType": "imageCountMoreThan", + "countNumber": 800 + }, + "action": { "type": "expire" } + } + ] +} diff --git a/ansible/roles/build-ecs-proxies/tasks/build-container.yml b/ansible/roles/build-ecs-proxies/tasks/build-container.yml index 56be84bb0..7e90e04f7 100644 --- a/ansible/roles/build-ecs-proxies/tasks/build-container.yml +++ b/ansible/roles/build-ecs-proxies/tasks/build-container.yml @@ -31,3 +31,53 @@ ansible.builtin.command: cmd: "docker push {{ image_name }}" when: build_result.rc == 0 + +- name: Get existing lifecycle policy JSON for {{ service_id }}_{{ item }} + ansible.builtin.command: > + {{ aws_cmd }} ecr get-lifecycle-policy + --repository-name {{ service_id }}_{{ item }} + --query 'lifecyclePolicyText' + --output text + register: existing_policy_raw + failed_when: false + changed_when: false + +- name: Parse existing lifecycle policy JSON if present + set_fact: + existing_policy_json: "{{ existing_policy_raw.stdout | from_json }}" + when: + - existing_policy_raw.stdout is defined + - existing_policy_raw.stdout != "" + - existing_policy_raw.stdout != "None" + - existing_policy_raw.stdout != "null" + +- name: Ensure existing_policy_json always exists + set_fact: + existing_policy_json: {} + when: existing_policy_json is not defined + +- name: Read lifecycle policy from the shared file + ansible.builtin.slurp: + src: "{{ playbook_dir }}/ecr-lifecycle/ecr_lifecycle.json" + register: desired_policy_raw + +- name: Debug raw slurp output + debug: + var: desired_policy_raw + +- name: Show decoded lifecycle policy content + debug: + msg: "{{ desired_policy_raw.content | b64decode }}" + +- name: Decode lifecycle policy file + set_fact: + desired_policy_json: "{{ desired_policy_raw.content | b64decode | from_json }}" + +- name: Apply lifecycle policy to ecr {{ service_id }}_{{ item }} if different + ansible.builtin.command: > + {{ aws_cmd }} ecr put-lifecycle-policy + --repository-name {{ service_id }}_{{ item }} + --lifecycle-policy-text '{{ desired_policy_json | to_json }}' + when: + - existing_policy_json != desired_policy_json + diff --git a/ansible/roles/create-api-deployment-pre-reqs/templates/terraform/iam.tf b/ansible/roles/create-api-deployment-pre-reqs/templates/terraform/iam.tf index 29eb55a3e..775b01a11 100644 --- a/ansible/roles/create-api-deployment-pre-reqs/templates/terraform/iam.tf +++ b/ansible/roles/create-api-deployment-pre-reqs/templates/terraform/iam.tf @@ -69,6 +69,8 @@ data "aws_iam_policy_document" "ecs-execution-role" { "ecr:DescribeRepositories", "ecr:ListImages", "ecr:DescribeImages", + "ecr:GetLifecyclePolicy", + "ecr:PutLifecyclePolicy", "s3:GetObject" ] @@ -173,6 +175,18 @@ data "aws_iam_policy_document" "deploy-user" { } + statement { + actions = [ + "ecr:GetLifecyclePolicy", + "ecr:PutLifecyclePolicy" + ] + + resources = [ + "arn:aws:ecr:${local.region}:${local.account_id}:repository/${var.service_id}", + "arn:aws:ecr:${local.region}:${local.account_id}:repository/${var.service_id}_*" + ] + } + statement { actions = [ "s3:ListBucket", diff --git a/ansible/roles/create-ecr-build-role/vars/main.yml b/ansible/roles/create-ecr-build-role/vars/main.yml index c40db5b1a..817fd7bb0 100644 --- a/ansible/roles/create-ecr-build-role/vars/main.yml +++ b/ansible/roles/create-ecr-build-role/vars/main.yml @@ -44,6 +44,7 @@ aws_ecs_policy: - "ecr:StartImageScan" - "ecr:StartLifecyclePolicyPreview" - "ecr:UploadLayerPart" + - "ecr:PutLifecyclePolicy" Resource: [ "arn:aws:ecr:{{ aws_region }}:{{ aws_account_id }}:repository/{{ service_id }}_*" ] diff --git a/ansible/roles/deploy-ecs-proxies/tasks/main.yml b/ansible/roles/deploy-ecs-proxies/tasks/main.yml index 668c8cb0e..23baa9d3a 100644 --- a/ansible/roles/deploy-ecs-proxies/tasks/main.yml +++ b/ansible/roles/deploy-ecs-proxies/tasks/main.yml @@ -82,6 +82,44 @@ register: tfapply when: not do_not_terraform + - name: Retag ECS image using ECR API (release pipelines only) + when: lookup('env', 'RELEASE_RELEASEID') | length > 0 + vars: + # Choose PTL for lower envs, PROD for prod + TARGET_ACCOUNT: >- + {{ + (apigee_environment == "prod") + | ternary(PROD_ACCOUNT_ID, PTL_ACCOUNT_ID) + }} + REPO: "{{ service_id }}_{{ ecs_service[0].name }}" + OLD: "{{ build_label }}" + NEW: "ecs-{{ build_label }}" + shell: | + # 1. Fetch manifest from the SAME account (PTL for lower envs, PROD for prod) + MANIFEST=$(aws ecr batch-get-image \ + --region eu-west-2 \ + --registry-id {{ TARGET_ACCOUNT }} \ + --repository-name {{ REPO }} \ + --image-ids imageTag={{ OLD }} \ + --query 'images[0].imageManifest' \ + --output text) + + # Safety check: ensure the base tag exists + if [ -z "$MANIFEST" ]; then + echo "ERROR: Tag {{ OLD }} does not exist in account {{ TARGET_ACCOUNT }}" + exit 1 + fi + + # 2. Write the new tag into the SAME account + aws ecr put-image \ + --region eu-west-2 \ + --registry-id {{ TARGET_ACCOUNT }} \ + --repository-name {{ REPO }} \ + --image-tag {{ NEW }} \ + --image-manifest "$MANIFEST" + args: + executable: /bin/bash + rescue: - name: output plan debug: diff --git a/ansible/roles/deploy-ecs-proxies/templates/terraform/locals.tf b/ansible/roles/deploy-ecs-proxies/templates/terraform/locals.tf index c01c869d5..9556883d4 100644 --- a/ansible/roles/deploy-ecs-proxies/templates/terraform/locals.tf +++ b/ansible/roles/deploy-ecs-proxies/templates/terraform/locals.tf @@ -49,7 +49,7 @@ locals { ( container | combine( - {'image': '${local.account_id}.dkr.ecr.eu-west-2.amazonaws.com/' + service_id + '_' + container.name + ':' + build_label } + {'image': '${local.account_id}.dkr.ecr.eu-west-2.amazonaws.com/' + service_id + '_' + container.name + ':ecs-' + build_label } ) ) | to_json }},