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

CI/CD Examples #189

Merged
merged 14 commits into from
Mar 10, 2024
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ crash.log
# control as they are data points which are potentially sensitive and subject
# to change depending on the environment.
#
*.tfvars
#*.tfvars

# Ignore override files as they are usually used to override resources locally and so
# are not checked in
Expand Down
29 changes: 29 additions & 0 deletions application-code/ecsdemo-cicd/appspec.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"version": 0.0,
"Resources": [
{
"TargetService": {
"Type": "AWS::ECS::Service",
"Properties": {
"TaskDefinition": "<PLACEHOLDER>",
"LoadBalancerInfo": {
"ContainerName": "fargate_task_container",
"ContainerPort": 80
},
"PlatformVersion": "LATEST",
"NetworkConfiguration": {
"awsvpcConfiguration": {
"subnets": [
"<PLACEHOLDER>"
],
"securityGroups": [
"<PLACEHOLDER>"
],
"assignPublicIp": "DISABLED"
}
}
}
}
}
]
}
30 changes: 30 additions & 0 deletions application-code/ecsdemo-cicd/buildspec-cicd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
version: 0.2

phases:
pre_build:
commands:
- echo $REPO_URL
- REPOSITORY=${REPO_URL%/*}
- echo Logging in to Amazon ECR...
- aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $REPOSITORY
- COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
- IMAGE_TAG=${COMMIT_HASH:=latest}
build:
commands:
- echo Build started on `date`
- echo Building the Docker image...
- docker build -t $REPO_URL .
post_build:
commands:
- echo Building Task Definition
- python3 create-configs.py $REPO_URL:$IMAGE_TAG core-infra-external-state ecsdemo-frontend development us-west-2
- echo Pushing the Docker image...
- docker tag $REPO_URL $REPO_URL:$IMAGE_TAG
- docker push $REPO_URL:$IMAGE_TAG
- echo Preparing spec files in new folder
- mkdir artifacts
- printf '[{"name":"%s","imageUri":"%s"}]' "$CONTAINER_NAME" "$REPO_URL:$IMAGE_TAG" > artifacts/imagedefinitions.json
- cat artifacts/imagedefinitions.json
artifacts:
files:
- '**/*'
114 changes: 114 additions & 0 deletions application-code/ecsdemo-cicd/create-configs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. SPDX-License-Identifier: MIT-0

#Example Command
# python3 create-configs.py public.ecr.aws/docker/library/httpd:latest core-infra ecsdemo-frontend development us-west-2

import boto3
import json
import sys
import re

#input new ecs image
image = sys.argv[1]

#input ecs cluster name
cluster_name = sys.argv[2]

#input ecs service name
service_name = sys.argv[3]

#input app environment
app_environment = sys.argv[4]

#input region
region = sys.argv[5]

################################################################################
# AppSpec
################################################################################

# AWS ECS client
ecs_client = boto3.client('ecs')

service_information = ecs_client.describe_services(
cluster=cluster_name,
services=[
service_name,
],
)

subnets=service_information['services'][0]['taskSets'][0]['networkConfiguration']['awsvpcConfiguration']['subnets']
security_groups=service_information['services'][0]['taskSets'][0]['networkConfiguration']['awsvpcConfiguration']['securityGroups']
container_name=service_information['services'][0]['taskSets'][0]['loadBalancers'][0]['containerName']
task_definition=service_information['services'][0]['taskDefinition']

# Removing the last colon and the following numbers
updated_arn = re.sub(r':\d+$', '', task_definition)

# Extracting task revision numbers
rev_number = re.findall(r'\d+$', task_definition)

if rev_number:
# Incrementing task revision numbers
updated_numbers = int(rev_number[0]) + 1

# Constructing the updated ARN with the incremented numbers
new_task_definition_arn = f'{updated_arn}:{updated_numbers}'

with open('appspec.json', 'r') as file:
app_spec_original_json = json.load(file)

app_spec_original_json['Resources'][0]['TargetService']['Properties']['TaskDefinition'] = new_task_definition_arn
app_spec_original_json['Resources'][0]['TargetService']['Properties']['LoadBalancerInfo']['ContainerName'] = container_name
app_spec_original_json['Resources'][0]['TargetService']['Properties']['NetworkConfiguration']['awsvpcConfiguration']['subnets'] = subnets
app_spec_original_json['Resources'][0]['TargetService']['Properties']['NetworkConfiguration']['awsvpcConfiguration']['securityGroups'] = security_groups

# Save the modified JSON to a new file
app_spec_file_name = f'{app_environment}-appspec.json.json'

# Save the modified JSON to a new file
with open(app_spec_file_name, 'w') as file:
json.dump(app_spec_original_json, file, indent=2)

################################################################################
# Task Def
################################################################################

# Get task definition details using AWS SDK
response = ecs_client.describe_task_definition(taskDefinition=task_definition)

task_definition_details = response['taskDefinition']

# Extracting required values
execution_role_arn = task_definition_details['executionRoleArn']
task_role_arn = task_definition_details['taskRoleArn']
cpu = task_definition_details['cpu']
memory = task_definition_details['memory']
family = task_definition_details['family']
name = task_definition_details['containerDefinitions'][0]['name']
log_group = task_definition_details['containerDefinitions'][0]['logConfiguration']['options']['awslogs-group']
log_region = task_definition_details['containerDefinitions'][0]['logConfiguration']['options']['awslogs-region']
log_prefix = task_definition_details['containerDefinitions'][0]['logConfiguration']['options']['awslogs-stream-prefix']

# Load the original JSON file
with open('task-definition.json', 'r') as file:
task_def_original_json = json.load(file)

# Replace placeholders with extracted values
task_def_original_json['executionRoleArn'] = execution_role_arn
task_def_original_json['taskRoleArn'] = task_role_arn
task_def_original_json['cpu'] = cpu
task_def_original_json['memory'] = memory
task_def_original_json['family'] = family
task_def_original_json['containerDefinitions'][0]['name'] = name
task_def_original_json['containerDefinitions'][0]['image'] = image
task_def_original_json['containerDefinitions'][0]['logConfiguration']['options']['awslogs-group'] = log_group
task_def_original_json['containerDefinitions'][0]['logConfiguration']['options']['awslogs-region'] = log_region
task_def_original_json['containerDefinitions'][0]['logConfiguration']['options']['awslogs-stream-prefix'] = log_prefix

# Save the modified JSON to a new file
task_def_file_name = f'{app_environment}-task-definition.json'

# Save the modified JSON to a new file
with open(task_def_file_name, 'w') as file:
json.dump(task_def_original_json, file, indent=2)
28 changes: 28 additions & 0 deletions application-code/ecsdemo-cicd/service-definition.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"taskDefinition": "<PLACEHOLDER>",
"cluster": "default",
"loadBalancers": [
{
"targetGroupArn": "<PLACEHOLDER>",
"containerName": "<PLACEHOLDER>",
"containerPort": 80
}
],
"desiredCount": 3,
"launchType": "FARGATE",
"schedulingStrategy": "REPLICA",
"deploymentController": {
"type": "CODE_DEPLOY"
},
"networkConfiguration": {
"awsvpcConfiguration": {
"subnets": [
"<PLACEHOLDER>"
],
"securityGroups": [
"<PLACEHOLDER>"
],
"assignPublicIp": "DISABLED"
}
}
}
42 changes: 42 additions & 0 deletions application-code/ecsdemo-cicd/task-definition.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"family": "<PLACEHOLDER>",
"containerDefinitions": [
{
"portMappings": [
{
"hostPort": 80,
"protocol": "tcp",
"containerPort": 80
}
],
"image": "<PLACEHOLDER>",
"essential": true,
"name": "<PLACEHOLDER>",
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "<PLACEHOLDER>",
"awslogs-region": "<PLACEHOLDER>",
"awslogs-stream-prefix": "<PLACEHOLDER>"
}
},
"privileged": false,
"linuxParameters": {
"capabilities": {
"drop": [
"SYS_ADMIN",
"NET_ADMIN"
]
}
}
}
],
"cpu": "<PLACEHOLDER>",
"memory": "<PLACEHOLDER>",
"taskRoleArn": "<PLACEHOLDER>",
"executionRoleArn": "<PLACEHOLDER>",
"requiresCompatibilities": [
"FARGATE"
],
"networkMode": "awsvpc"
}
79 changes: 79 additions & 0 deletions terraform/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
build/
plan.out
plan.out.json

# Local .terraform directories
.terraform/

# .tfstate files
*.tfstate
*.tfstate.*

# Crash log files
crash.log

# Exclude all .tfvars files, which are likely to contain sentitive data, such as
# password, private keys, and other secrets. These should not be part of version
# control as they are data points which are potentially sensitive and subject
# to change depending on the environment.
#
#*.tfvars

# Ignore override files as they are usually used to override resources locally and so
# are not checked in
override.tf
override.tf.json
*_override.tf
*_override.tf.json

# Include override files you do wish to add to version control using negated pattern
#
# !example_override.tf

# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
# example: *tfplan*

# Ignore CLI configuration files
.terraformrc
terraform.rc
.terraform.lock.hcl

go.mod
go.sum

# Build files
.DS_Store
node_modules
/dist

# local env files
.env.local
.env.*.local

# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*

# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
.envrc

# CDK directories
cdk.out/
cdk.context.json

# Python
__pycache__/

# Customized ENV files
.env*
.venv/
output.json
Loading
Loading