Skip to content

Custom path & possibility to publish layer and/or lambda #18

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

Open
wants to merge 47 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
b6b8c78
Add possibility to customize code path
feraudet Oct 27, 2021
5d53a67
Add possibility to publish layer and/or lambda
feraudet Oct 27, 2021
9729c7b
Test
feraudet Oct 27, 2021
157fac3
Test
feraudet Oct 27, 2021
9573f36
Test
feraudet Oct 27, 2021
a43611f
Remove debug cmd
feraudet Oct 27, 2021
ccfd358
Use s3 bucket to store lambda code
feraudet Nov 22, 2021
dc56017
add architectures and runtimes parameters
Nov 22, 2021
593b313
Fully backward uncompatible upgrade. Now deploy.json contains a list …
Nov 23, 2021
2f64dd6
upgrade python version from 3.6 to 3.9
Nov 23, 2021
ebf372e
fix zip -x arguments management
Nov 23, 2021
516bc23
fix rm arguments in entrypoint.sh
Nov 23, 2021
cbaa6b3
add missing layers input to action.yml
Nov 23, 2021
f8114c6
fix INPUT_NAME#TAG splitting pattern
Nov 23, 2021
bc30139
fix layer version retrieval
Nov 23, 2021
574bf89
fix again
Nov 23, 2021
a46ee9a
fix last layer version retrival
Nov 23, 2021
ee63d57
fix get_last_layer_version_arn again
Nov 23, 2021
ea5f852
fix mistyped variable name in entrypoint.sh
Nov 23, 2021
5d3445a
prevent deploy_lambda_layer() from failing silently
Nov 23, 2021
7e8a5ab
fix entrypoint.sh again
Nov 23, 2021
fbb2298
Fully backward incompatible upgrade. One block in deploy is a functio…
Nov 23, 2021
e3254a2
fix zip -x parameters
Nov 23, 2021
19de057
add debug message on $INPUT_EXCLUDES in make_archive()
Nov 23, 2021
07e4c2c
more debug
Nov 23, 2021
0ebdda7
fix zip -x opts processing
Nov 23, 2021
95311f6
try again
Nov 23, 2021
27173b2
try again
Nov 24, 2021
42ed5ed
try again
Nov 24, 2021
cc0e046
add set -x to entrypoint.sh
Nov 24, 2021
8a80886
fix zip -x parameters using set -f
Nov 24, 2021
62d0db4
make archive for layers to have all the contents in python/ directory
Nov 24, 2021
8a01b8f
fix misnamed variable
Nov 24, 2021
de341e1
fix ln arguments
Nov 24, 2021
dcab608
add -v opt to ln
Nov 24, 2021
44adf91
add ls -l
Nov 24, 2021
ba4994f
fix lambda function archive build
Nov 24, 2021
bf94039
remove bash opt set -x
Nov 24, 2021
25204cd
First working commit of the newly designed py-lambda-action
Nov 24, 2021
4605aa9
Disable update-function-code if the lambda function does not exist. A…
Dec 28, 2021
b548059
Add log messages in deploy_lambda_function()
Dec 28, 2021
2c1be5b
update debug message
Dec 28, 2021
d5ded75
enable stderr on get-function operation
Dec 28, 2021
61cb04f
enable stdout on get-function operation
Dec 28, 2021
5464894
add lambda_function_exists() tool
Dec 28, 2021
d210194
fix publish-version
Dec 28, 2021
9f357bc
add aws wait command to wait previous operation before sending the ne…
Dec 28, 2021
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.sw*
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.6
FROM python:3.9

RUN apt-get update
RUN apt-get install -y jq zip
Expand Down
57 changes: 49 additions & 8 deletions action.yml
Original file line number Diff line number Diff line change
@@ -1,24 +1,65 @@
name: Py Lambda Deploy
author: Mariam Maarouf
description: Deploy python code to AWS Lambda with dependencies in a separate layer.
description: |
Deploy python code to AWS Lambda with dependencies in a separate layer.
inputs:
requirements_txt:
description: the name/path to the requirements.txt file
target:
description: lambda or layer
required: true
default: 'requirements.txt'
lambda_layer_arn:
description: The ARN for the Lambda layer the dependencies should be pushed to without the version (every push is a new version).
name:
description: target name
required: true
architectures:
description: Target architectures
required: false
default: 'x86_64'
runtimes:
description: Compatible runtimes in space-separated string
required: false
default: 'python3.9 python3.8 python3.7 python3.6'
path:
description: Path to the code.
required: false
pip:
description: The name/path to the requirements.pip file
required: false
patterns:
description: Regex patterns to gather files for the archive (not used yet)
required: false
excludes:
description: zip -x patterns to exclude files from the archive
required: false
layers:
description: Lambda Layers to add to Lambda Function
required: false
lambda_layer_arn:
description: |
The ARN for the Lambda layer the dependencies should be pushed to
without the version (every push is a new version).
required: false
lambda_function_name:
description: The Lambda function name. Check the AWS docs/readme for examples.
description: |
The Lambda function name. Check the AWS docs/readme for examples.
required: false
s3_bucket:
description: S3 bucket where upload lambda zip code.
required: true
runs:
using: 'docker'
image: 'Dockerfile'
args:
- ${{ inputs.requirements_txt }}
- ${{ inputs.target }}
- ${{ inputs.name }}
- ${{ inputs.architectures }}
- ${{ inputs.runtimes }}
- ${{ inputs.path }}
- ${{ inputs.pip }}
- ${{ inputs.patterns }}
- ${{ inputs.excludes }}
- ${{ inputs.layers }}
- ${{ inputs.lambda_layer_arn }}
- ${{ inputs.lambda_function_name }}
- ${{ inputs.s3_bucket }}
branding:
icon: 'layers'
color: 'yellow'
191 changes: 166 additions & 25 deletions entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,38 +1,179 @@
#!/bin/bash
set -e

install_zip_dependencies(){
echo "Installing and zipping dependencies..."
mkdir python
pip install --target=python -r "${INPUT_REQUIREMENTS_TXT}"
zip -r dependencies.zip ./python



name="$(basename $0)"
DEBUG=1


die()
{
echo ERROR: $name: $@ >&2
exit -1
}

log()
{
echo INFO: $name: $@
}

debug()
{
grep -q yes\\\|1\\\|on\\\|true <<< $DEBUG || return 0
echo DEBUG: $name: $@
}


get_last_layer_version_arn()
{
layer_name="$1"
# TODO: could try all combinations of arch and runtimes
result=$(aws lambda list-layer-versions --layer-name "$layer_name" \
--compatible-architecture "${INPUT_ARCHITECTURES%% *}" \
--compatible-runtime "${INPUT_RUNTIMES%% *}" \
--max-items 1)
arn="$(jq -e .LayerVersions[0].LayerVersionArn <<< "$result"|cut -d\" -f2)"
[ $? != 0 ] || [ "$arn" == "null" ] && return 1
echo -n $arn
}


make_archive()
{
log "Building $INPUT_NAME $INPUT_TARGET archive..."
archive="$(realpath .)/archive.zip"
set -f
trap "rm -f -- '$archive'" EXIT
if [ -z "$INPUT_EXCLUDES" ]; then
zip_opts=
else
zip_opts="-x $INPUT_EXCLUDES"
fi
debug "INPUT_EXCLUDES: $INPUT_EXCLUDES"
debug "zip_opts: $zip_opts"
set +f
tempdir=$(mktemp -d pip.XXXXXXXXXX)
trap "rm -rf -- '$archive' '$tempdir'" EXIT
mkdir "$tempdir/python"
if [ -n "$INPUT_PATH" ]; then
log "Installing codes... : $INPUT_PATH"
for path in $INPUT_PATH; do
ln -vs "$(realpath $path)/"* "$tempdir/python/"
done
fi
if [ -n "$INPUT_PIP" ]; then
log "Installing dependencies... : $INPUT_PIP"
for path in $INPUT_PIP; do
pip install -t "$tempdir/python/" -r "$path"
done
fi
log "Zipping archive..."
set -f
if [ "$INPUT_TARGET" == "layer" ]; then
pushd "$tempdir"
zip -r $archive python $zip_opts
popd
else
pushd "$tempdir/python"
zip -r $archive . $zip_opts
popd
fi
set +f
rm -rf -- "$tempdir"
trap "rm -f -- '$archive'" EXIT
}

publish_dependencies_as_layer(){
echo "Publishing dependencies as a layer..."
local result=$(aws lambda publish-layer-version --layer-name "${INPUT_LAMBDA_LAYER_ARN}" --zip-file fileb://dependencies.zip)
LAYER_VERSION=$(jq '.Version' <<< "$result")
rm -rf python
rm dependencies.zip
list_layer_version_arns()
{
arns=
for layer_name in $@; do
layer_arn="$(get_last_layer_version_arn "$layer_name")"
arns="$arns $layer_arn"
done
echo -n $arns
}

publish_function_code(){
echo "Deploying the code itself..."
zip -r code.zip . -x \*.git\*
aws lambda update-function-code --function-name "${INPUT_LAMBDA_FUNCTION_NAME}" --zip-file fileb://code.zip
lambda_function_exists()
{
aws lambda list-functions | jq '.["Functions"][]["FunctionName"]' | \
grep -q "$INPUT_NAME"
}

update_function_layers(){
echo "Using the layer in the function..."
aws lambda update-function-configuration --function-name "${INPUT_LAMBDA_FUNCTION_NAME}" --layers "${INPUT_LAMBDA_LAYER_ARN}:${LAYER_VERSION}"
deploy_lambda_function()
{
log "Deploying lambda function: $INPUT_NAME..."
s3_url="s3://${INPUT_S3_BUCKET}/${INPUT_NAME}.zip"
aws s3 cp "$archive" "$s3_url"
log "Updating lambda function code: ${INPUT_NAME}"
if lambda_function_exists; then
aws lambda update-function-code \
--architectures "$INPUT_ARCHITECTURES" \
--function-name "$INPUT_NAME" \
--zip-file "fileb://$archive"
opts=
if [ -n "$INPUT_LAYERS" ]; then
layers=$(list_layer_version_arns "$INPUT_LAYERS")
opts="--layers $layers"
fi
aws lambda wait function-updated --function-name "$INPUT_NAME"
log "Lambda function updated: $INPUT_NAME"
retry=4
while ! aws lambda update-function-configuration \
--function-name "${INPUT_NAME}" \
--runtime "${INPUT_RUNTIMES%% *}" $opts; do
retry="$(($retry - 1))"
if [[ $retry -gt 0 ]]; then
die "Cannot update-function-configuration: ${INPUT_NAME}"
fi
sleep 1
done
aws lambda wait function-updated --function-name "$INPUT_NAME"
log "Lambda function configured: $INPUT_NAME"
aws lambda publish-version --function-name "$INPUT_NAME"
log "Lambda function published: $INPUT_NAME"
else
log "No lambda function found: $INPUT_NAME"
fi
rm -f -- "$archive"
trap - EXIT
}

deploy_lambda_function(){
install_zip_dependencies
publish_dependencies_as_layer
publish_function_code
update_function_layers
deploy_lambda_layer()
{
log "Deploying lambda layer: ${INPUT_NAME}..."
local s3_url="s3://${INPUT_S3_BUCKET}/${INPUT_NAME}.zip"
aws s3 cp "$archive" "$s3_url"
local result="$(aws lambda publish-layer-version \
--layer-name "$INPUT_LAMBDA_LAYER_ARN" \
--compatible-architectures $INPUT_ARCHITECTURES \
--compatible-runtimes $INPUT_RUNTIMES \
--zip-file "fileb://$archive" \
)"
arn="$(jq .LayerVersionArn <<< "$result")"
[ $? != 0 ] || [ "$arn" == "null" ] && return 1
echo -n $arn
}

deploy_lambda_function
echo "Done."

TAG="${INPUT_NAME#*#}"
INPUT_NAME="${INPUT_NAME%#*}"


case "$INPUT_TARGET" in
lambda)
make_archive
deploy_lambda_function
;;
layer)
make_archive
deploy_lambda_layer
;;
*)
die Invalid resource target: $INPUT_TARGET
;;
esac


log "Done."