-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathssmdocs.tf
271 lines (225 loc) · 9.82 KB
/
ssmdocs.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
# SSM Document For go, this handles continous updates of new container images
resource "aws_ssm_document" "setup_golang_scripts" {
name = "setup-golang-scripts"
document_type = "Command"
document_format = "YAML"
content = <<DOC
schemaVersion: '2.2'
description: 'Advanced Blue-Green Deployment Setup for golang Application'
parameters: {}
mainSteps:
- action: aws:runShellScript
name: setup_advanced_golang_deployment_scripts
inputs:
runCommand:
- |
sudo tee /usr/local/bin/update-golang-docker-app.sh > /dev/null << 'EOF'
#!/bin/bash
# Configuration Variables
REGISTRY="YOURAWSACCOUNTID.dkr.ecr.YOURAWSREGION.amazonaws.com"
IMAGE_NAME="myappimage"
LEGACY_CONTAINER="myapp-stg-golang"
BLUE_CONTAINER_NAME="myapp-stg-golang-blue"
GREEN_CONTAINER_NAME="myapp-stg-golang-green"
STG_GOLANG_ADMINMAIL=$(aws ssm get-parameter --name "/infra/prod/adminmail" --with-decryption --query "Parameter.Value" --output text --region YOURAWSREGION)
DEPLOY_LOG="/var/log/myappstg_deployments.log"
APP_LOG_DIR="/var/log/myappstglogs"
S3_BUCKET="YOURS3BUCKETFORLOGS"
HEALTH_CHECK_URL="/health"
START_PORT=8080
MAX_RETRIES=30
RETRY_INTERVAL=15
# Logging function
log_message() {
local message="$1"
echo "$(date '+%Y-%m-%d %H:%M:%S'): $message" | tee -a "$DEPLOY_LOG"
}
# Verify image signature
verify_image_signature() {
local full_image_path="$1"
local image_digest=$(sudo docker inspect "$full_image_path" | jq -r '.[0].RepoDigests[0]' | cut -d'@' -f2)
if [ -z "$image_digest" ]; then
echo "Failed to get image digest"
return 1
fi
echo "Verifying image signature for digest: $image_digest"
export AWS_DEFAULT_REGION=REPLACEYOURAWSREGION
export HOME=$(pwd)
if ! export HOME=/home/ec2-user && notation verify "$REGISTRY/$IMAGE_NAME@$image_digest"; then
echo "Image signature verification failed"
return 1
fi
echo "Image signature verified successfully"
return 0
}
# Error handling function
handle_error() {
local error_message="$1"
log_message "ERROR: $error_message"
# Attempt to clean up green container if it exists
if docker ps -a | grep -q "$GREEN_CONTAINER_NAME"; then
docker logs "$GREEN_CONTAINER_NAME" >> "$DEPLOY_LOG" 2>&1 || true
docker stop "$GREEN_CONTAINER_NAME" 2>&1 || true
docker rm "$GREEN_CONTAINER_NAME" 2>&1 || true
fi
exit 1
}
# Find an available port
find_available_port() {
local current_port=$START_PORT
local max_attempts=10
for ((attempt=1; attempt<=max_attempts; attempt++)); do
if ! docker ps --format '{{.Ports}}' | grep -i ":$current_port->" >/dev/null 2>&1; then
if command -v telnet >/dev/null 2>&1; then
if ! timeout 1 telnet localhost "$current_port" 2>/dev/null | grep -q "Connected to"; then
echo "$current_port"
return 0
fi
else
local pid
pid=$(lsof -i :"$current_port" -t 2>/dev/null)
if [ -z "$pid" ]; then
echo "$current_port"
return 0
fi
fi
fi
((current_port++))
done
handle_error "Could not find an available port starting from $START_PORT"
}
# Cleanup port forwarding
cleanup_port_forwarding() {
local port=8080
local pid=$(sudo lsof -t -i TCP:$port -s TCP:LISTEN 2>/dev/null | xargs -r ps -o pid,comm | awk '/socat$/ {print $1}')
if [ -n "$pid" ]; then
echo "Stopping socat forwarding on port $port (PID: $pid)"
sudo kill "$pid"
else
echo "No socat process found specifically for port $port"
fi
}
perform_health_check() {
local port="$1"
local max_retries="$2"
local retry_interval="$3"
for ((i=1; i<=max_retries; i++)); do
if curl -k -f --max-time 30 "https://localhost:$port$HEALTH_CHECK_URL" >/dev/null 2>&1; then
return 0
fi
log_message "Health check attempt $i failed. Waiting $retry_interval seconds..."
sleep "$retry_interval"
done
return 1
}
# Log archiving function
archive_old_logs() {
find "$APP_LOG_DIR" -type f -name "*.log" -mtime +7 2>/dev/null | while read -r log_file; do
local s3_path="s3://$S3_BUCKET/logsssss/$(basename "$log_file")"
aws s3 cp "$log_file" "$s3_path" && rm "$log_file" || true
done
}
# Image cleanup function
cleanup_old_images() {
docker image prune -a --filter "until=120h" --force || true
}
# Deploy function
deploy_golang_app() {
local new_tag="$1"
local FULL_IMAGE_PATH="$REGISTRY/$IMAGE_NAME:$new_tag"
if docker ps -a | grep -q "$LEGACY_CONTAINER"; then
docker rename "$LEGACY_CONTAINER" "$BLUE_CONTAINER_NAME" 2>/dev/null || true
fi
if docker ps -a | grep -q "$GREEN_CONTAINER_NAME"; then
docker rm -f "$GREEN_CONTAINER_NAME" 2>/dev/null || true
fi
aws ecr get-login-password --region YOURAWSREGION | docker login --username AWS --password-stdin "$REGISTRY" ||
handle_error "ECR login failed"
docker pull "$REGISTRY/$IMAGE_NAME:$new_tag" ||
handle_error "Failed to pull image $REGISTRY/$IMAGE_NAME:$new_tag"
if ! verify_image_signature "$FULL_IMAGE_PATH"; then
handle_error "Image signature verification failed"
fi
cleanup_port_forwarding
local local_port
local_port=$(find_available_port) ||
handle_error "Could not find an available port"
docker run --restart always -d --name "$GREEN_CONTAINER_NAME" \
-p "$local_port:8000" \
-e APP_ENV=stg \
-e JUST_A_CHILL_GUY=justaplaceholder \
"$FULL_IMAGE_PATH"
if ! perform_health_check "$local_port" "$MAX_RETRIES" "$RETRY_INTERVAL"; then
handle_error "Health check failed for $new_tag"
fi
mkdir -p "$APP_LOG_DIR"
if [ ! -d "$APP_LOG_DIR" ]; then
mkdir -p "$APP_LOG_DIR" || handle_error "Failed to create log directory $APP_LOG_DIR"
fi
if docker ps -a | grep -q "$BLUE_CONTAINER_NAME"; then
docker logs "$BLUE_CONTAINER_NAME" > "$APP_LOG_DIR/$BLUE_CONTAINER_NAME_$new_tag.log" 2>&1 || true
docker rm -f "$BLUE_CONTAINER_NAME" || true
fi
docker rename "$GREEN_CONTAINER_NAME" "$BLUE_CONTAINER_NAME"
docker update --restart=always "$BLUE_CONTAINER_NAME"
sudo nohup socat TCP-LISTEN:8080,reuseaddr,fork TCP:localhost:"$local_port" > /tmp/socat.log 2>&1 &
log_message "Successfully deployed $new_tag on port $local_port"
}
main() {
command -v docker >/dev/null 2>&1 || handle_error "Docker is not installed"
command -v aws >/dev/null 2>&1 || handle_error "AWS CLI is not installed"
if [ $# -ne 1 ]; then
handle_error "Usage: $0 <new_tag|push_logs>"
fi
local action="$1"
if [ "$action" = "push_logs" ]; then
archive_old_logs
cleanup_old_images
log_message "Log archiving and cleanup completed successfully"
else
deploy_golang_app "$action"
archive_old_logs
cleanup_old_images
log_message "Deployment and cleanup completed successfully for tag $action"
fi
}
main "$@"
EOF
- sudo chmod +x /usr/local/bin/update-golang-docker-app.sh
- echo "50 23 * * * /usr/local/bin/update-golang-docker-app.sh push_logs" | sudo tee /etc/cron.d/end-of-day-cron
- sudo chmod 644 /etc/cron.d/end-of-day-cron
DOC
}
resource "aws_ssm_document" "update_go_app" {
name = "UpdateGolangDockerApp"
document_type = "Command"
document_format = "JSON"
content = jsonencode({
schemaVersion = "2.2"
description = "Update Golang Docker application for staging"
parameters = {
NEWTAG = {
type = "String"
description = "The new Docker image tag to deploy"
}
}
mainSteps = [
{
action = "aws:runShellScript"
name = "updateGolangDockerApp"
inputs = {
runCommand = [
"/usr/local/bin/update-golang-docker-app.sh \"{{NEWTAG}}\""
]
}
}
]
})
}
resource "aws_ssm_association" "run_golang_script_stg" {
name = aws_ssm_document.setup_golang_scripts.name
targets {
key = "InstanceIds"
values = [module.examplesite_instance.id]
}
}