Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: DBTP-1507 Setup codepipeline notifications #314

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
2fbcbc1
add buildspec-deploy.yml file for codebase module
tony-griffin Jan 15, 2025
d74a458
add install platform -helper
tony-griffin Jan 15, 2025
31f6ee1
Merge branch 'DBTP-1503-manual-pipeline' into DBTP-1507-setup-codepip…
tony-griffin Jan 15, 2025
065f434
Add codebuild env vars for deploy buildspec
tony-griffin Jan 15, 2025
7c43eb5
Add local env vars for use in deploy buildspec
tony-griffin Jan 15, 2025
8f0e3a4
Add platform-helper notify slack messages to codebase deploy buildspec
tony-griffin Jan 15, 2025
ad16c0f
Add slack channel module variable
tony-griffin Jan 15, 2025
1a1f80c
Add slack channel variable parameter store type
tony-griffin Jan 15, 2025
0b84d7c
Update tests to reflect new codebase codebuild env vars
tony-griffin Jan 16, 2025
04667f7
Add platform-helper notify command with emojis
tony-griffin Jan 16, 2025
6479d1f
Add IAM permissions for ECR & SSM access for deploy buildspec
tony-griffin Jan 16, 2025
5f1a862
Build env vars for pipeline execution ID URL
tony-griffin Jan 17, 2025
cd25d02
Add default codepipeline execution ID env var at pipeline level
tony-griffin Jan 17, 2025
5c7cddf
Update terraform tests to reflect adding env vars to codebuild actions
tony-griffin Jan 17, 2025
069b091
Add extra codebuild env vars to manual release codebase pipeline
tony-griffin Jan 18, 2025
3e61b5d
FIX: environment variable for manual release pipeline
tony-griffin Jan 18, 2025
26d8ebf
Update tests for manual release pipeline env vars
tony-griffin Jan 18, 2025
2631bef
Remove unnecessary local vars for region & account
tony-griffin Jan 19, 2025
9e0bec7
Update message output
tony-griffin Jan 20, 2025
58767b9
Merge branch 'DBTP-1503-manual-pipeline' into DBTP-1507-setup-codepip…
JohnStainsby Jan 21, 2025
6aad173
Merge branch 'DBTP-1507-setup-codepipeline-notifications' of github.c…
JohnStainsby Jan 21, 2025
8e6a57b
Update codebase-pipelines/buildspec-deploy.yml
tony-griffin Jan 21, 2025
3503dec
Update codebase-pipelines/buildspec-deploy.yml
tony-griffin Jan 21, 2025
28ce523
Update bash syntax in buildspec file
tony-griffin Jan 21, 2025
e877faa
Update name of ssm deploy access policy and data policy
tony-griffin Jan 21, 2025
740442a
Update name of ssm deploy data policy test override
tony-griffin Jan 21, 2025
af54f02
Update role policy name
tony-griffin Jan 21, 2025
2f40fb0
Add missing tests for data policy and policy for deploy ssm access
tony-griffin Jan 21, 2025
6323922
Update codebase-pipelines/tests/unit.tftest.hcl
tony-griffin Jan 21, 2025
5b28002
Fix test indentation
tony-griffin Jan 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 79 additions & 5 deletions codebase-pipelines/buildspec-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,63 @@ version: 0.2
env:
variables:
DEPLOY_TIMEOUT: 1800
parameter-store:
SLACK_TOKEN: /codebuild/slack_oauth_token

phases:
install:
commands:
- echo "Starting deployment script for ${SERVICE} service"
- pip install yq --quiet
- echo "Installing platform-helper"
- pip install dbt-platform-helper --quiet
- platform-helper --version
- echo "Installing regclient"
- curl -s -L https://github.com/regclient/regclient/releases/latest/download/regctl-linux-amd64 > /usr/local/bin/regctl
- chmod +x /usr/local/bin/regctl

build:
commands:
- set -e

# Extract timestamp from image config and check if it exists
- aws ecr get-login-password --region ${AWS_REGION} | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com

# construct Slack message env vars
- SLACK_REF=$(regctl image config "${REPOSITORY_URL}:${IMAGE_TAG}" | jq -r '.config.Labels."uk.gov.trade.digital.build.timestamp"')
- |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in general, these multiline commands should be reserved for blocks, such as if/then/else clauses, function definitions while loops etc. So I'd put line 30 in it's own single line item.

if [ "${SLACK_REF}" = "null" ] || [ -z "${SLACK_REF}" ]; then
echo "Image contains no timestamp label"
exit 1
fi
echo "Found image timestamp ${SLACK_REF}"

- UPPERCASE_SERVICE=$(echo "${SERVICE}" | tr '[:lower:]' '[:upper:]')
- UPPERCASE_TAG=$(echo "${IMAGE_TAG}" | tr '[:lower:]' '[:upper:]')

# Extract the pipeline name from CODEBUILD_INITIATOR default env var
- |
if [[ "${CODEBUILD_INITIATOR}" == codepipeline/* ]]; then
PIPELINE_NAME=$(echo "${CODEBUILD_INITIATOR}" | cut -d'/' -f2)
echo "Pipeline name is ${PIPELINE_NAME}"
else
echo "ERROR: Build not triggered by CodePipeline."
exit 1
fi

# Construct the pipeline execution URL
- PIPELINE_EXECUTION_URL="https://${AWS_REGION}.console.aws.amazon.com/codesuite/codepipeline/pipelines/${PIPELINE_NAME}/executions/${PIPELINE_EXECUTION_ID}"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just remove some of these random empty lines in this section.

- echo "Pipeline execution URL ${PIPELINE_EXECUTION_URL}"

- BUILD_ID_PREFIX=$(echo $CODEBUILD_BUILD_ID | cut -d':' -f1)
- echo "BUILD_ID_PREFIX - ${BUILD_ID_PREFIX}"

- |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be just a single line item. - MESSAGE=...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried adding as a single line comment but there was a syntax error because of the use of the ":" which only resolved when using multiline

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI - you should be able to just surround the line in single quotes. It's because it thinks it's a map rather than a string. Surrounding in single quotes will make the YAML parser treat it as a string.

But happy for you to leave it as a multi-line quote in this case as it's probably neater.

MESSAGE=":rocket: STARTED - Deployment of ${UPPERCASE_SERVICE} service - Tag: ${UPPERCASE_TAG} - <https://${AWS_REGION}.console.aws.amazon.com/codesuite/codebuild/${AWS_ACCOUNT_ID}/projects/${BUILD_ID_PREFIX}/build/${CODEBUILD_BUILD_ID}/?region=${AWS_REGION}|Build log> | <${PIPELINE_EXECUTION_URL}|Pipeline execution url>"

- platform-helper notify add-comment "${SLACK_CHANNEL_ID}" "${SLACK_TOKEN}" "${SLACK_REF}" "${MESSAGE}"

# Check if the specified image tag exists
- |
if ! aws ecr describe-images --repository-name "${REPOSITORY_NAME}" --image-ids "imageTag=${IMAGE_TAG}" > /dev/null 2>&1; then
Expand Down Expand Up @@ -52,7 +99,7 @@ phases:
- cd "${CODEBUILD_SRC_DIR}"

# If count is a range, get the first value otherwise get count
- |
- |
if [ $(yq '.count | type == "object"' copilot/web/manifest.yml) == "true" ]; then
count=$(yq '.count.range' copilot/web/manifest.yml | tr -d '"' | cut -d '-' -f1)
else
Expand Down Expand Up @@ -85,10 +132,10 @@ phases:
sleep 10
now=$( date +%s )
elapsed=$(( now-start ))

deploy_status=$(aws ecs list-service-deployments --cluster "${cluster}" --service "${service_name}" --created-at "after=${start}" | jq -r '.serviceDeployments[0].status')
echo "Deployment status after ${elapsed} seconds: ${deploy_status}"

if [[ ${elapsed} -gt ${DEPLOY_TIMEOUT} ]]; then
echo "Error: deployment not completed in ${DEPLOY_TIMEOUT} seconds"
exit 1
Expand All @@ -97,7 +144,34 @@ phases:

# Check deployment success
- |
if [ "${deploy_status}" != "SUCCESSFUL" ]; then
echo "Error: deployment did not succeed"
case "${deploy_status}" in
SUCCESSFUL)
STATUS_EMOJI=":large_green_circle:"
STATUS_TEXT="COMPLETE"
;;
ROLLBACK_SUCCESSFUL)
STATUS_EMOJI=":red_circle:"
STATUS_TEXT="FAILED"
;;
ROLLBACK_FAILED)
STATUS_EMOJI=":red_circle::warning:"
STATUS_TEXT="FAILED"
;;
*)
STATUS_EMOJI=":large_blue_circle:"
STATUS_TEXT="STOP_REQUESTED/STOPPED/PENDING/IN_PROGRESS"
;;
esac

- echo "Status emoji set to ${STATUS_EMOJI}"

- MESSAGE="${STATUS_EMOJI} ${STATUS_TEXT} - Deployment of ${UPPERCASE_TAG} to ${UPPERCASE_SERVICE} service - ${deploy_status} - <${PIPELINE_EXECUTION_URL}|Pipeline execution url>"

- platform-helper notify add-comment "${SLACK_CHANNEL_ID}" "${SLACK_TOKEN}" "${SLACK_REF}" "${MESSAGE}"

# Exit with an error code if deployment did not succeed
- |
if [ "${deploy_status}" != "SUCCESSFUL" ]; then
echo "Error: deployment status is ${deploy_status}"
exit 1
fi
18 changes: 14 additions & 4 deletions codebase-pipelines/codepipeline.tf
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,16 @@ resource "aws_codepipeline" "codebase_pipeline" {
ProjectName = aws_codebuild_project.codebase_deploy.name
EnvironmentVariables : jsonencode([
{ name : "APPLICATION", value : var.application },
{ name : "AWS_REGION", value : data.aws_region.current.name },
{ name : "AWS_ACCOUNT_ID", value : data.aws_caller_identity.current.account_id },
{ name : "ENVIRONMENT", value : stage.value.name },
{ name : "SERVICE", value : action.value.name },
{ name : "IMAGE_TAG", value : "#{variables.IMAGE_TAG}" },
{ name : "PIPELINE_EXECUTION_ID", value : "#{codepipeline.PipelineExecutionId}" },
{ name : "PREFIXED_REPOSITORY_NAME", value : local.prefixed_repository_name },
{ name : "REPOSITORY_URL", value : local.repository_url },
{ name : "REPOSITORY_NAME", value : local.ecr_name },
{ name : "IMAGE_TAG", value : "#{variables.IMAGE_TAG}" }
{ name : "SERVICE", value : action.value.name },
{ name : "SLACK_CHANNEL_ID", value : var.slack_channel, type : "PARAMETER_STORE" },
])
}
}
Expand Down Expand Up @@ -158,11 +163,16 @@ resource "aws_codepipeline" "manual_release_pipeline" {
ProjectName = aws_codebuild_project.codebase_deploy.name
EnvironmentVariables : jsonencode([
{ name : "APPLICATION", value : var.application },
{ name : "AWS_REGION", value : data.aws_region.current.name },
{ name : "AWS_ACCOUNT_ID", value : data.aws_caller_identity.current.account_id },
{ name : "ENVIRONMENT", value : "#{variables.ENVIRONMENT}" },
{ name : "SERVICE", value : action.value.name },
{ name : "IMAGE_TAG", value : "#{variables.IMAGE_TAG}" },
{ name : "PIPELINE_EXECUTION_ID", value : "#{codepipeline.PipelineExecutionId}" },
{ name : "PREFIXED_REPOSITORY_NAME", value : local.prefixed_repository_name },
{ name : "REPOSITORY_URL", value : local.repository_url },
{ name : "REPOSITORY_NAME", value : local.ecr_name },
{ name : "IMAGE_TAG", value : "#{variables.IMAGE_TAG}" }
{ name : "SERVICE", value : action.value.name },
{ name : "SLACK_CHANNEL_ID", value : var.slack_channel, type : "PARAMETER_STORE" },
])
}
}
Expand Down
32 changes: 31 additions & 1 deletion codebase-pipelines/iam.tf
Original file line number Diff line number Diff line change
Expand Up @@ -180,12 +180,23 @@ data "aws_iam_policy_document" "ecr_access_for_codebase_pipeline" {
statement {
effect = "Allow"
actions = [
"ecr:DescribeImages"
"ecr:DescribeImages",
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer"
]
resources = [
aws_ecr_repository.this.arn
]
}
statement {
effect = "Allow"
actions = [
"ecr:GetAuthorizationToken"
]
resources = [
"*"
]
}
}

resource "aws_iam_role_policy" "artifact_store_access_for_codebase_pipeline" {
Expand Down Expand Up @@ -279,3 +290,22 @@ data "aws_iam_policy_document" "environment_deploy_role_access" {
]
}
}

resource "aws_iam_role_policy" "deploy_ssm_access" {
name = "deploy-ssm-access"
role = aws_iam_role.codebase_deploy.name
policy = data.aws_iam_policy_document.deploy_ssm_access.json
}

data "aws_iam_policy_document" "deploy_ssm_access" {
statement {
effect = "Allow"
actions = [
"ssm:GetParameter",
"ssm:GetParameters"
]
resources = [
"arn:aws:ssm:${local.account_region}:parameter/codebuild/slack_*"
]
}
}
5 changes: 3 additions & 2 deletions codebase-pipelines/locals.tf
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ locals {

account_region = "${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}"

ecr_name = "${var.application}/${var.codebase}"
repository_url = "${data.aws_caller_identity.current.account_id}.dkr.ecr.${data.aws_region.current.name}.amazonaws.com/${local.ecr_name}"
ecr_name = "${var.application}/${var.codebase}"
prefixed_repository_name = "uktrade/${var.application}"
repository_url = "${data.aws_caller_identity.current.account_id}.dkr.ecr.${data.aws_region.current.name}.amazonaws.com/${local.ecr_name}"

pipeline_branches = distinct([
for pipeline in var.pipelines : pipeline.branch if lookup(pipeline, "branch", null) != null
Expand Down
Loading
Loading