This repository has been archived by the owner on Jul 8, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathme
executable file
·225 lines (210 loc) · 7.02 KB
/
me
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
#!/bin/bash
#------------------------------------------------------------------------------
# Copyright (C) 2019-2020 British Crown (Met Office) & Contributors
#------------------------------------------------------------------------------
# Usage:
#
# # Display Help.
# me help
# me usage
#
# # Make sure you are using the correct profile for the role.
# export AWS_PROFILE=<profile-name-for-role>
#
# # Deploy an application,
# # where <variant> is a name under "variants/".
# me deploy <variant>
# me deploy stf-jules-dev # E.g. deploy stf-jules-dev
#
# # Destroy an application deployment when no longer used,
# # where <variant> is a name under "variants/".
# me destroy <variant>
# me destroy stf-jules-dev # E.g. destroy the deployed stf-jules-dev
#
# Notes:
#
# 1. Commands may prompt for multi-factor authentication.
# 2. JULES build requires:
# * Docker
# * JULES source tree based on vn5.6 or vn5.7 under "jules-sources/".
#
# Environment variables:
#
# JULES_SOURCE - Location of JULES source tree (default="jules-source/")
#------------------------------------------------------------------------------
set -euo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
me::deploy() {
local VARIANT="$1"
local VARIANTD="${HERE}/variants/${VARIANT}"
local STACK0="${VARIANT}-artifacts"
local STACK1="${VARIANT}-main"
# Check uncommitted changes
pushd "${HERE}"
if git diff-index --quiet HEAD --; then
SOURCEREV="$(git describe --always)"
else
echo "${HERE}: abort on uncommitted changes in source directory." >&2
exit 1
fi
popd
# Check required software
local NAME
for NAME in aws cfn-lint docker; do
if ! "${NAME}" --version 1>'/dev/null' 2>&1; then
echo "Sorry, this script requires ${NAME}" >&2
exit 1
fi
done
# Lint cloudformation templates
cfn-lint -I "${HERE}/functions/**/cfn.yaml"
# Display current caller identity
aws sts get-caller-identity
# S3 bucket for storing deployment artifacts, e.g. Lambda packages
# shellcheck disable=SC2046
cfn::deploy-stack "${STACK0}" \
--template-file "${HERE}/functions/${STACK0#${VARIANT}-}/cfn.yaml" \
--tags $(< "${VARIANTD}/tags.txt") \
--no-fail-on-empty-changeset
local ARTIFACTS_BUCKET
ARTIFACTS_BUCKET="$(cfn::get-resource-id "${STACK0}" 'Bucket')"
# Artifact - Lambda package
me::artifact-create \
"s3://${ARTIFACTS_BUCKET}/${SOURCEREV}/lambdas/main.zip" \
me::build-main-package "${HERE}"
# shellcheck disable=SC2046
cfn::deploy-stack "${STACK1}" \
--capabilities='CAPABILITY_IAM' \
--template-file "${HERE}/functions/${STACK1#${VARIANT}-}/cfn.yaml" \
--parameter-overrides \
"ArtifactsBucket=${ARTIFACTS_BUCKET}" \
"SourceRev=${SOURCEREV}" \
--tags $(< "${VARIANTD}/tags.txt") \
--no-fail-on-empty-changeset
# Add Bucket notification configuration to trigger Lambda
# Note: This cannot be done in Cloudformation due to known bug.
me::add-bucket-notification-configuration "${STACK1}"
}
me::destroy() {
local VARIANT="$1"
local VARIANTD="${HERE}/variants/${VARIANT}"
local STACK0="${VARIANT}-artifacts"
local STACK1="${VARIANT}-main"
# Destroy the MO Service Hub data service stack, e.g. subscribe and ingest
cfn::s3-purge "${STACK1}" 'ConfigBucket' 'OutputBucket'
cfn::delete-stack "${STACK1}"
# Remove contents in artifacts bucket, then the stack itself
cfn::s3-purge "${STACK0}" 'Bucket'
cfn::delete-stack "${STACK0}"
}
me::help() {
sed -n '/^# Usage:$/,/^#-\+$/ {s/^#$//p; s/^# //p}' "$0"
}
me::usage() {
me::help "$@"
}
me::artifact-create() {
# Create ./package/python/ and cd to ./package/. Run "$2 ...".
# Zip content, then upload resulting artifact to "$1".
local DEST="$1"
shift 1
local WORKD
WORKD="$(mktemp -d)"
pushd "${WORKD}"
mkdir -p './package/'
"$@"
pushd './package/'
zip -r9 "../package.zip" .
popd # 'package'
aws s3 cp 'package.zip' "${DEST}"
popd # "${WORKD}"
rm -fr "${WORKD}"
}
me::build-main-package() {
# Build JULES and copy Lambda handler model in package directory
local HERE="$1"
docker run --rm \
-u "$(id -u):$(id -g)" \
-v "${JULES_SOURCE:-${HERE}/jules-source}:/opt/jules" \
-v "${HERE}/etc/fcm-make/platform/lambda.cfg:/opt/jules/etc/fcm-make/platform/lambda.cfg" \
-v "${PWD}/package:/tmp/package" \
-e JULES_PLATFORM='lambda' \
'matthewrmshin/lambda-gfortran-fcm-make-netcdf' \
-f '/opt/jules/etc/fcm-make/make.cfg' 'build.target=jules.exe'
cp -p "${HERE}/functions/main/function.py" './package/'
}
me::add-bucket-notification-configuration() {
# Add Bucket notification configuration to trigger Lambda
local STACK1="$1"
local CONFIG_BUCKET
CONFIG_BUCKET="$(cfn::get-resource-id "${STACK1}" 'ConfigBucket')"
local LAMBDA
LAMBDA="$(cfn::get-resource-id "${STACK1}" 'Lambda')"
local LAMBDA_ARN
LAMBDA_ARN="$( \
aws lambda get-function --function-name "${LAMBDA}" \
--query Configuration.FunctionArn --output text)"
aws s3api put-bucket-notification-configuration \
--bucket "${CONFIG_BUCKET}" \
--notification-configuration '
{
"LambdaFunctionConfigurations": [
{
"LambdaFunctionArn": "'"${LAMBDA_ARN}"'",
"Events": ["s3:ObjectCreated:*"]
}
]
}
'
}
cfn::get-resource-id() {
# Get physical resource ID for resource by created by Cloudformation.
local STACK="$1"
local RESOURCE="$2"
aws cloudformation describe-stack-resource \
--stack-name "${STACK}" \
--logical-resource-id "${RESOURCE}" \
--output 'text' \
--query 'StackResourceDetail.PhysicalResourceId'
}
cfn::delete-stack() {
# Wrap `aws cloudformation delete-stack ...`
local STACK="$1"
set -x
aws cloudformation delete-stack --stack-name "${STACK}"
aws cloudformation 'wait' 'stack-delete-complete' --stack-name "${STACK}"
{ set +x; } 2>'/dev/null'
}
cfn::deploy-stack() {
# Wrap `aws cloudformation deploy-stack ...`
local STACK="$1"
shift 1
set -x
aws cloudformation deploy --stack-name "${STACK}" "$@"
{ set +x; } 2>'/dev/null'
aws cloudformation describe-stacks --stack-name "${STACK}" \
--query 'Stacks[0].Outputs'
}
cfn::s3-purge() {
# Remove buckets created by Cloudformation.
local STACK="$1"
shift 1
for BUCKET_RESOURCE in "$@"; do
local BUCKET
BUCKET="$(cfn::get-resource-id "${STACK}" "${BUCKET_RESOURCE}")"
aws s3 rm --recursive "s3://${BUCKET}/"
aws s3 rb "s3://${BUCKET}/"
done
}
main() {
local ACTION="$1"
shift 1
if declare -F "me::${ACTION}" 1>'/dev/null' 2>&1; then
"me::${ACTION}" "$@"
exit
else
echo "${ACTION}: action not recognised." >&2
exit 1
fi
}
main "$@"