Skip to content

Commit

Permalink
CI/CD Examples (#189)
Browse files Browse the repository at this point in the history
* added cicd terraform examples

* updated cicd readme with cleanup

* added copywrite to create-configs.

* updated permissions

* updated cicd examples

* updated readme

* updated tf to pass pre-commit checks

* change code for linting

* change tf version for compatibility

* fix execution error issue

* change lint

---------

Co-authored-by: Jooyoung Kim <[email protected]>
Co-authored-by: jooyoung <[email protected]>
  • Loading branch information
3 people authored Mar 10, 2024
1 parent 58734da commit e969d45
Show file tree
Hide file tree
Showing 38 changed files with 1,892 additions and 1 deletion.
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

0 comments on commit e969d45

Please sign in to comment.