From 882062a25111d905197bd3d83f5ab2f0d7164c33 Mon Sep 17 00:00:00 2001 From: kreanimator Date: Wed, 1 May 2024 10:54:46 +0300 Subject: [PATCH 01/18] initial commit --- layers/sosw_layer/layer.rst | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 layers/sosw_layer/layer.rst diff --git a/layers/sosw_layer/layer.rst b/layers/sosw_layer/layer.rst new file mode 100644 index 0000000..e69de29 From 33bfc39b455d1d55f1d4fd52b57352bdb32b8571 Mon Sep 17 00:00:00 2001 From: kreanimator Date: Wed, 1 May 2024 11:04:28 +0300 Subject: [PATCH 02/18] Added templates --- layers/deploy-layer.sh | 66 +++++++++++++++++++++++ layers/deploy.sh | 89 ++++++++++++++++++++++++++++++++ layers/sosw_layer/samconfig.toml | 11 ++++ layers/sosw_layer/sosw.yaml | 23 +++++++++ layers/sosw_layer/template.yaml | 24 +++++++++ 5 files changed, 213 insertions(+) create mode 100755 layers/deploy-layer.sh create mode 100755 layers/deploy.sh create mode 100644 layers/sosw_layer/samconfig.toml create mode 100644 layers/sosw_layer/sosw.yaml create mode 100644 layers/sosw_layer/template.yaml diff --git a/layers/deploy-layer.sh b/layers/deploy-layer.sh new file mode 100755 index 0000000..9c6ddd3 --- /dev/null +++ b/layers/deploy-layer.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +set -e + +# Change ACCOUNT_ID and S3 bucket name appropriately. +ACCOUNT_ID=926629045956 +BUCKET_NAME=app-control-$ACCOUNT_ID +NAME=ddb_models +REPOSITORY=fs-lambdas +REPOSITORY_PATH=ssh://git-codecommit.us-west-2.amazonaws.com/v1/repos/fs-lambdas +PROFILE=ngr-926629045956 + +HELPMSG="USAGE: ./deploy.sh [-n name] [-r full url of the repository] [-p profile] +Deploys 'ddb-models' layer. Use -p in case you have specific profile (not the default one) +in you .aws/config with appropriate permissions. +NAME parameter should not contain _ characters" + +while getopts ":n:p:r:h" option +do + case "$option" + in + n) NAME=$OPTARG;; + p) PROFILE=$OPTARG;; + r) REPOSITORY_PATH=$OPTARG;REPOSITORY=`echo $OPTARG | awk '$!_=$-_=$NF' FS='[/]'`;; + h|*) echo -e "$HELPMSG";exit;; + esac +done + +STACK_NAME="layer-`echo $NAME | sed s/_/-/g`" + +echo "REPOSITORY_PATH=$REPOSITORY_PATH" +echo "REPOSITORY=$REPOSITORY" +echo "PROFILE=$PROFILE" +echo "NAME=$NAME" + +rm -rf /tmp/$REPOSITORY +rm -rf /tmp/$NAME.zip +rm -rf /tmp/$NAME-stable.zip +rm -rf /tmp/layer-$NAME +echo "Cleaned old artifacts from /tmp" + +git clone $REPOSITORY_PATH /tmp/$REPOSITORY +cd /tmp/$REPOSITORY +mkdir -p /tmp/layer-$NAME/python +cp -r $NAME /tmp/layer-$NAME/python/ +cd /tmp/layer-$NAME +zip -r /tmp/$NAME-stable.zip * +cd /tmp/ +unzip -l /tmp/$NAME-stable.zip +MD5=`cat /tmp/layer-$NAME/python/ddb_models/*.py | md5` +echo "Created a new package in /tmp/$NAME-stable.zip with MD5 of Python files: $MD5" + +aws s3 cp /tmp/$NAME-stable.zip s3://$BUCKET_NAME/lambda_layers/ --profile $PROFILE +echo "Uploaded $NAME-stable.zip to S3 bucket to $BUCKET_NAME." + +echo "Deploying stack $NAME with CloudFormation" +pwd +aws cloudformation package --template-file /Users/ngr/Programming/fs-cf-infrastructure/layers/$NAME/template.yaml \ + --output-template-file deployment-output.yaml --s3-bucket $BUCKET_NAME --profile $PROFILE +echo "Created package from CloudFormation template" + +echo "Calling for CloudFormation to deploy" +aws cloudformation deploy --template-file ./deployment-output.yaml --stack-name $STACK_NAME \ + --parameter-overrides FileName=$NAME-stable.zip PackageMD5=$MD5 \ + --capabilities CAPABILITY_NAMED_IAM --profile $PROFILE + +echo "Finished with stack $stack_name. If there were changes in the YAML, they should be applied." diff --git a/layers/deploy.sh b/layers/deploy.sh new file mode 100755 index 0000000..2e72668 --- /dev/null +++ b/layers/deploy.sh @@ -0,0 +1,89 @@ +#!/usr/bin/env bash +set -e + +# Change ACCOUNT_ID and S3 bucket name appropriately. +ACCOUNT_ID=[YOUR_ACCOUNT_ID] #You need to change YOUR_ACCOUNT_ID with your actual account id. +BUCKET_NAME="app-control-$ACCOUNT_ID" + +NAME=sosw +ORGANIZATION=$NAME +PROFILE=ngr-926629045956 + +HELPMSG="USAGE: ./deploy.sh [-v branch] [-p profile] +Deploys sosw layer. Installs sosw from latest pip version, or from a specific branch if you use -v.\n +Use -p in case you have specific profile (not the default one) in you .aws/config with appropriate permissions." + +while getopts ":v:p:fh" option +do + case "$option" + in + v) VERSION=$OPTARG;; + p) PROFILE=$OPTARG;; + f) FORCE_RANDOM_NAME=true;; + h|*) echo -e "$HELPMSG";exit;; + esac +done + +#VERSION=0_7_43 + +# Install package with respect to the rule of Lambda Layers: +# https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html + +if [[ ${VERSION} ]]; then + echo "Deploying a specific version from branch: $VERSION" + pip3 install git+https://github.com/$ORGANIZATION/$NAME.git@$VERSION --no-dependencies -t $NAME/python/ +else + VERSION="stable" + pip3 install $NAME --no-dependencies -t $NAME/python/ +fi + +# Package other (non-sosw) reqired libraries +pip3 install aws_lambda_powertools -t $NAME/python/ +pip3 install aws_xray_sdk -t $NAME/python/ +pip3 install bson --no-dependencies -t $NAME/python/ +pip3 install requests -t $NAME/python/ + +if [[ $FORCE_RANDOM_NAME ]]; then + echo "Generated a random suffix for file name." + FILE_NAME=$NAME-$VERSION-$RANDOM +else + FILE_NAME=$NAME-$VERSION +fi + +zip_path="/tmp/$FILE_NAME.zip" +stack_name="layer-$NAME" + +echo "Packaging..." +if [ -f "$zip_path" ] +then + rm $zip_path + echo "Removed the old package." +fi + + +cd $NAME +zip -qr $zip_path * +cd .. +echo "Created a new package in $zip_path." + +aws s3 cp $zip_path s3://$BUCKET_NAME/lambda_layers/ --profile $PROFILE +echo "Uploaded $zip_path to S3 bucket to $BUCKET_NAME." + +if test -z "$ENVIRONMENT" +then + env_name="prod" +else + env_name=$ENVIRONMENT +fi + +echo "Deploying stack $stack_name with CloudFormation for environment ${env_name}" +aws cloudformation package --template-file $NAME/$NAME.yaml --output-template-file deployment-output.yaml \ + --s3-bucket $BUCKET_NAME --profile $PROFILE +echo "Created package from CloudFormation template" + +echo "Calling for CloudFormation to deploy" +aws cloudformation deploy --template-file ./deployment-output.yaml --stack-name $stack_name \ + --parameter-overrides SoswVersion=$VERSION EnvironmentName=$env_name FileName=$FILE_NAME.zip \ + --capabilities CAPABILITY_NAMED_IAM --profile $PROFILE + +echo "Finished with stack $stack_name. If there were changes in the YAML, they should be applied." diff --git a/layers/sosw_layer/samconfig.toml b/layers/sosw_layer/samconfig.toml new file mode 100644 index 0000000..5f00992 --- /dev/null +++ b/layers/sosw_layer/samconfig.toml @@ -0,0 +1,11 @@ +version = 0.1 + +[default.deploy.parameters] +stack_name = "sosw-layer" +s3_prefix = "sosw-layer" +region = "us-west-2" +resolve_s3 = true +confirm_changeset = true +force-upload = true +capabilities = "CAPABILITY_IAM" + diff --git a/layers/sosw_layer/sosw.yaml b/layers/sosw_layer/sosw.yaml new file mode 100644 index 0000000..9edebe5 --- /dev/null +++ b/layers/sosw_layer/sosw.yaml @@ -0,0 +1,23 @@ +AWSTemplateFormatVersion: "2010-09-09" +Description: "sosw - Latest version" + +Parameters: + FileName: + Description: Custom name of file to use. + Type: String + Default: sosw-stable.zip + +Resources: + SoswLayer: + Type: "AWS::Lambda::LayerVersion" + Description: "sosw, bson, aws_lambda_powertools, aws_xray_sdk, requests" + Properties: + CompatibleRuntimes: + - python3.10 + - python3.11 + Content: + S3Bucket: !Sub "app-control-${AWS::AccountId}" + S3Key: !Sub "lambda_layers/${FileName}" + Description: "sosw" + LayerName: "sosw" + LicenseInfo: "MIT" diff --git a/layers/sosw_layer/template.yaml b/layers/sosw_layer/template.yaml new file mode 100644 index 0000000..e801c17 --- /dev/null +++ b/layers/sosw_layer/template.yaml @@ -0,0 +1,24 @@ +AWSTemplateFormatVersion: 2010-09-09 +Transform: AWS::Serverless-2016-10-31 +Description: SOSW layer + +Parameters: + FileName: + Description: Custom name of file to use. + Type: String + Default: sosw-stable.zip + +Resources: + SoswLayer: + Type: "AWS::Lambda::LayerVersion" + Description: "sosw, bson, aws_lambda_powertools, aws_xray_sdk, requests" + Properties: + CompatibleRuntimes: + - python3.10 + - python3.11 + Content: + S3Bucket: !Sub "app-control-${AWS::AccountId}" + S3Key: !Sub "lambda_layers/${FileName}" + Description: "sosw" + LayerName: "sosw" + LicenseInfo: "MIT" From 1e647bfadcf42c31d5993a8d4d74fb43565ae850 Mon Sep 17 00:00:00 2001 From: kreanimator Date: Wed, 1 May 2024 12:39:28 +0300 Subject: [PATCH 03/18] Temporarily puted rst here --- layers/deploy-layer.sh | 66 ------------- layers/deploy.sh | 8 +- .../sosw_layer/img/lambda-layers-diagram.png | Bin 0 -> 71382 bytes layers/sosw_layer/layer.rst | 87 ++++++++++++++++++ 4 files changed, 91 insertions(+), 70 deletions(-) delete mode 100755 layers/deploy-layer.sh create mode 100644 layers/sosw_layer/img/lambda-layers-diagram.png diff --git a/layers/deploy-layer.sh b/layers/deploy-layer.sh deleted file mode 100755 index 9c6ddd3..0000000 --- a/layers/deploy-layer.sh +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Change ACCOUNT_ID and S3 bucket name appropriately. -ACCOUNT_ID=926629045956 -BUCKET_NAME=app-control-$ACCOUNT_ID -NAME=ddb_models -REPOSITORY=fs-lambdas -REPOSITORY_PATH=ssh://git-codecommit.us-west-2.amazonaws.com/v1/repos/fs-lambdas -PROFILE=ngr-926629045956 - -HELPMSG="USAGE: ./deploy.sh [-n name] [-r full url of the repository] [-p profile] -Deploys 'ddb-models' layer. Use -p in case you have specific profile (not the default one) -in you .aws/config with appropriate permissions. -NAME parameter should not contain _ characters" - -while getopts ":n:p:r:h" option -do - case "$option" - in - n) NAME=$OPTARG;; - p) PROFILE=$OPTARG;; - r) REPOSITORY_PATH=$OPTARG;REPOSITORY=`echo $OPTARG | awk '$!_=$-_=$NF' FS='[/]'`;; - h|*) echo -e "$HELPMSG";exit;; - esac -done - -STACK_NAME="layer-`echo $NAME | sed s/_/-/g`" - -echo "REPOSITORY_PATH=$REPOSITORY_PATH" -echo "REPOSITORY=$REPOSITORY" -echo "PROFILE=$PROFILE" -echo "NAME=$NAME" - -rm -rf /tmp/$REPOSITORY -rm -rf /tmp/$NAME.zip -rm -rf /tmp/$NAME-stable.zip -rm -rf /tmp/layer-$NAME -echo "Cleaned old artifacts from /tmp" - -git clone $REPOSITORY_PATH /tmp/$REPOSITORY -cd /tmp/$REPOSITORY -mkdir -p /tmp/layer-$NAME/python -cp -r $NAME /tmp/layer-$NAME/python/ -cd /tmp/layer-$NAME -zip -r /tmp/$NAME-stable.zip * -cd /tmp/ -unzip -l /tmp/$NAME-stable.zip -MD5=`cat /tmp/layer-$NAME/python/ddb_models/*.py | md5` -echo "Created a new package in /tmp/$NAME-stable.zip with MD5 of Python files: $MD5" - -aws s3 cp /tmp/$NAME-stable.zip s3://$BUCKET_NAME/lambda_layers/ --profile $PROFILE -echo "Uploaded $NAME-stable.zip to S3 bucket to $BUCKET_NAME." - -echo "Deploying stack $NAME with CloudFormation" -pwd -aws cloudformation package --template-file /Users/ngr/Programming/fs-cf-infrastructure/layers/$NAME/template.yaml \ - --output-template-file deployment-output.yaml --s3-bucket $BUCKET_NAME --profile $PROFILE -echo "Created package from CloudFormation template" - -echo "Calling for CloudFormation to deploy" -aws cloudformation deploy --template-file ./deployment-output.yaml --stack-name $STACK_NAME \ - --parameter-overrides FileName=$NAME-stable.zip PackageMD5=$MD5 \ - --capabilities CAPABILITY_NAMED_IAM --profile $PROFILE - -echo "Finished with stack $stack_name. If there were changes in the YAML, they should be applied." diff --git a/layers/deploy.sh b/layers/deploy.sh index 2e72668..d221825 100755 --- a/layers/deploy.sh +++ b/layers/deploy.sh @@ -2,12 +2,12 @@ set -e # Change ACCOUNT_ID and S3 bucket name appropriately. -ACCOUNT_ID=[YOUR_ACCOUNT_ID] #You need to change YOUR_ACCOUNT_ID with your actual account id. -BUCKET_NAME="app-control-$ACCOUNT_ID" +ACCOUNT_ID=YOUR_ACCOUNT_ID #You need to change YOUR_ACCOUNT_ID with your actual account id. +BUCKET_NAME=app-control-$ACCOUNT_ID NAME=sosw ORGANIZATION=$NAME -PROFILE=ngr-926629045956 +PROFILE=default HELPMSG="USAGE: ./deploy.sh [-v branch] [-p profile] Deploys sosw layer. Installs sosw from latest pip version, or from a specific branch if you use -v.\n @@ -37,7 +37,7 @@ else pip3 install $NAME --no-dependencies -t $NAME/python/ fi -# Package other (non-sosw) reqired libraries +# Package other (non-sosw) required libraries pip3 install aws_lambda_powertools -t $NAME/python/ pip3 install aws_xray_sdk -t $NAME/python/ pip3 install bson --no-dependencies -t $NAME/python/ diff --git a/layers/sosw_layer/img/lambda-layers-diagram.png b/layers/sosw_layer/img/lambda-layers-diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..dbbba2ec951ead3c5e37f53dd6802b5fa2787454 GIT binary patch literal 71382 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 + +------------------ +How to run +------------------ + +While in the same folder as your ``deploy.sh`` file just run: + +.. code-block:: bash + + ./deploy.sh [-v branch] [-p profile] + +It will create a folder with all dependencies. You only need to pack it into zip-archive. + +------------------ +What's next? +------------------ + +You simply need to load Layer zip file to your bucket and deploy your +Layer with AWS SAM or CloudFormation by using one of the following templates. + +------------------ +CloudFormation +------------------ + +To deploy a layer by CloudFormation you will need to create a ``.yaml`` file with the following code + +`sosw-layer.yaml +`_ + +And after to create a stack in AWS CloudFormation. +To create a stack you run the ``aws cloudformation create-stack`` command. +You must provide the stack name, the location of a valid template, and any input parameters. + +.. code-block:: bash + + aws cloudformation create-stack \ + --stack-name myteststack \ + --template-body file:///home/testuser/mytemplate.json \ + --parameters ParameterKey=Parm1,ParameterValue=test1 ParameterKey=Parm2,ParameterValue=test2 + +Or upload it directly via GUI. + +------------------ +AWS SAM +------------------ + +To deploy Layer with AWS SAM you will simply need to create two files ``samconfig.toml`` and ``template.yaml`` +which are represented below. + +`samconfig.toml +`_ + +`template.yaml +`_ + +After you create these files you can run them just by entering ``sam build && sam deploy`` in your console. + +------------------ +Versions +------------------ + +Lambda creates a new version of your function each time that you publish the function. +The new version is a copy of the unpublished version of the function. +The unpublished version is named ``$LATEST``. \ No newline at end of file From af576e3162cf5a4cca1c3be968027c96b9f7b667 Mon Sep 17 00:00:00 2001 From: kreanimator Date: Wed, 1 May 2024 13:29:13 +0300 Subject: [PATCH 04/18] Deleted redundunt files --- .../sosw_layer/img/lambda-layers-diagram.png | Bin 71382 -> 0 bytes layers/sosw_layer/layer.rst | 87 ------------------ 2 files changed, 87 deletions(-) delete mode 100644 layers/sosw_layer/img/lambda-layers-diagram.png delete mode 100644 layers/sosw_layer/layer.rst diff --git a/layers/sosw_layer/img/lambda-layers-diagram.png b/layers/sosw_layer/img/lambda-layers-diagram.png deleted file mode 100644 index dbbba2ec951ead3c5e37f53dd6802b5fa2787454..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 71382 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 - ------------------- -How to run ------------------- - -While in the same folder as your ``deploy.sh`` file just run: - -.. code-block:: bash - - ./deploy.sh [-v branch] [-p profile] - -It will create a folder with all dependencies. You only need to pack it into zip-archive. - ------------------- -What's next? ------------------- - -You simply need to load Layer zip file to your bucket and deploy your -Layer with AWS SAM or CloudFormation by using one of the following templates. - ------------------- -CloudFormation ------------------- - -To deploy a layer by CloudFormation you will need to create a ``.yaml`` file with the following code - -`sosw-layer.yaml -`_ - -And after to create a stack in AWS CloudFormation. -To create a stack you run the ``aws cloudformation create-stack`` command. -You must provide the stack name, the location of a valid template, and any input parameters. - -.. code-block:: bash - - aws cloudformation create-stack \ - --stack-name myteststack \ - --template-body file:///home/testuser/mytemplate.json \ - --parameters ParameterKey=Parm1,ParameterValue=test1 ParameterKey=Parm2,ParameterValue=test2 - -Or upload it directly via GUI. - ------------------- -AWS SAM ------------------- - -To deploy Layer with AWS SAM you will simply need to create two files ``samconfig.toml`` and ``template.yaml`` -which are represented below. - -`samconfig.toml -`_ - -`template.yaml -`_ - -After you create these files you can run them just by entering ``sam build && sam deploy`` in your console. - ------------------- -Versions ------------------- - -Lambda creates a new version of your function each time that you publish the function. -The new version is a copy of the unpublished version of the function. -The unpublished version is named ``$LATEST``. \ No newline at end of file From 7394af7eb03eba738b846472cf5119d641c63f68 Mon Sep 17 00:00:00 2001 From: kreanimator Date: Wed, 1 May 2024 14:30:12 +0300 Subject: [PATCH 05/18] Added code to change layer versions --- layers/layers_versions_changer.py | 53 +++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 layers/layers_versions_changer.py diff --git a/layers/layers_versions_changer.py b/layers/layers_versions_changer.py new file mode 100644 index 0000000..bedda55 --- /dev/null +++ b/layers/layers_versions_changer.py @@ -0,0 +1,53 @@ +import os +import boto3 +import yaml +from yaml import Loader, SafeLoader + + +def sub_constructor(loader, node): + value = loader.construct_scalar(node) + return {"Fn::Sub": value} + + +Loader.add_constructor('!Sub', sub_constructor) +SafeLoader.add_constructor('!Sub', sub_constructor) + + +client = boto3.client('lambda') +layer_change_key = 'SOSW_LAYER' +curr_runtime = 'python3.11' +test_layer_name = "sosw" + + +def main(): + try: + response = get_lambda_layer_version(curr_runtime, test_layer_name) + print(response) + if response: + change_lambda_layer_version(response, layer_change_key) + except RuntimeError as r: + raise RuntimeError(f"Something went wrong: {r}") + + +def get_lambda_layer_version(runtime: str, layer_name: str) -> dict: + response = client.list_layer_versions(CompatibleRuntime=runtime, LayerName=layer_name) + return response + + +def change_lambda_layer_version(response: dict, change_key: str): + layer_version = response['LayerVersions'][0]['Version'] + print(f"Layer version: {layer_version}") + for root, dirs, files in os.walk('.'): + for file in files: + if file.endswith('.yaml'): + file_path = os.path.join(root, file) + with open(file_path, 'r') as f: + yaml_data = yaml.safe_load(f) + if change_key in yaml_data: + yaml_data[change_key] = layer_version + with open(file_path, 'w') as fw: + yaml.dump(yaml_data, fw) + + +if __name__ == "__main__": + main() From 4da44e6d77e749dc37aa479f52bc5be5e7f61d1d Mon Sep 17 00:00:00 2001 From: kreanimator Date: Wed, 1 May 2024 15:29:53 +0300 Subject: [PATCH 06/18] Optimized code to change layer version only in Lambda templates for cloudformation and SAM --- layers/lambdas_for_test/test1.yaml | 62 ++++++++++++++++++++++++++++++ layers/lambdas_for_test/test2.yaml | 50 ++++++++++++++++++++++++ layers/lambdas_for_test/test3.yaml | 24 ++++++++++++ layers/layers_versions_changer.py | 37 ++++++++---------- 4 files changed, 153 insertions(+), 20 deletions(-) create mode 100644 layers/lambdas_for_test/test1.yaml create mode 100644 layers/lambdas_for_test/test2.yaml create mode 100644 layers/lambdas_for_test/test3.yaml diff --git a/layers/lambdas_for_test/test1.yaml b/layers/lambdas_for_test/test1.yaml new file mode 100644 index 0000000..1fae7d9 --- /dev/null +++ b/layers/lambdas_for_test/test1.yaml @@ -0,0 +1,62 @@ +AWSTemplateFormatVersion: 2010-09-09 +Transform: AWS::Serverless-2016-10-31 +Description: Event bus to DDb www-sosw-form-records + +Parameters: + CustomFunctionName: + Type: String + Default: web_form_processor + CustomTableName: + Type: String + Default: www_sosw_form_records + Stage: + Type: String + Default: dev + +Conditions: + isProd: !Equals [ !Ref Stage, prod ] + +Resources: + LambdaWebFormProcessor: + Type: AWS::Serverless::Function + Properties: + FunctionName: !If [ isProd, !Sub "${CustomFunctionName}", !Sub "${Stage}_${CustomFunctionName}" ] + CodeUri: src/ + Description: Event bus to DDb www_sosw_form_records + Handler: app.lambda_handler + MemorySize: 256 + PackageType: Zip + Runtime: python3.11 + Timeout: 3 + Tracing: Active + + Events: + Trigger: + Type: EventBridgeRule + Properties: + EventBusName: 'default' + Pattern: + source: + - 'ApiGw' + detail-type: + - 'www-form-apply' + - 'www-form-contact-us' + - 'www-form-event-registration' + - 'www-form-event-testcloud' + Policies: + - SNSPublishMessagePolicy: + TopicName: !If [ isProd, "web-forms-sosw", !Sub "${Stage}-web-forms-sosw" ] + - DynamoDBCrudPolicy: + TableName: !If [ isProd, !Sub "${CustomTableName}", !Sub "${Stage}_${CustomTableName}" ] + Layers: + - !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:layer:sosw:SOSW_LAYER_PLACEHOLDER" + Tags: + Env: !Ref Stage + + FunctionResourcePermission: + Type: 'AWS::Lambda::Permission' + Properties: + Action: 'lambda:InvokeFunction' + Principal: events.amazonaws.com + FunctionName: !Ref LambdaWebFormProcessor + SourceArn: !Sub 'arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:*/*' diff --git a/layers/lambdas_for_test/test2.yaml b/layers/lambdas_for_test/test2.yaml new file mode 100644 index 0000000..db60712 --- /dev/null +++ b/layers/lambdas_for_test/test2.yaml @@ -0,0 +1,50 @@ +AWSTemplateFormatVersion: "2010-09-09" +Description: sys_lambda_env_setter + +Resources: + + SysLambdaEnvSetterRole: + Type: "AWS::IAM::Role" + Properties: + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Principal: + Service: + - "lambda.amazonaws.com" + Action: + - "sts:AssumeRole" + + Path: "/" + ManagedPolicyArns: + - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + + Policies: + - PolicyName: "SysLambdaEnvSetterCustom" + PolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Action: + - lambda:GetFunctionConfiguration + - lambda:UpdateFunctionConfiguration + Resource: '*' + + RoleName: "sys_lambda_env_setter_role" + + SysLambdaEnvSetterFunction: + Type: AWS::Lambda::Function + Properties: + FunctionName: sys_lambda_env_setter + Runtime: python3.11 + Role: !GetAtt SysLambdaEnvSetterRole.Arn + Handler: app.lambda_handler + Code: + + S3Bucket: !Sub "sosw-s3-${AWS::AccountId}" + S3Key: lambda_packages/sys_lambda_env_setter.zip + Layers: + - !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:layer:sosw:SOSW_LAYER_PLACEHOLDER" + + Description: Set environmental variables for Lambda functions diff --git a/layers/lambdas_for_test/test3.yaml b/layers/lambdas_for_test/test3.yaml new file mode 100644 index 0000000..6cf1378 --- /dev/null +++ b/layers/lambdas_for_test/test3.yaml @@ -0,0 +1,24 @@ +AWSTemplateFormatVersion: 2010-09-09 +Transform: AWS::Serverless-2016-10-31 +Description: SOSW layer + +Parameters: + FileName: + Description: Custom name of file to use. + Type: String + Default: sosw-stable.zip + +Resources: + SoswLayer: + Type: "AWS::Lambda::LayerVersion" + Description: "sosw, bson, aws_lambda_powertools, aws_xray_sdk, requests" + Properties: + CompatibleRuntimes: + - python3.10 + - python3.11 + Content: + S3Bucket: !Sub "app-control-${AWS::SOSW_LAYER_PLACEHOLDER}" + S3Key: !Sub "lambda_layers/${SOSW_LAYER_PLACEHOLDER}" + Description: "sosw" + LayerName: "sosw" + LicenseInfo: "MIT" diff --git a/layers/layers_versions_changer.py b/layers/layers_versions_changer.py index bedda55..6439ad9 100644 --- a/layers/layers_versions_changer.py +++ b/layers/layers_versions_changer.py @@ -1,22 +1,11 @@ import os import boto3 -import yaml -from yaml import Loader, SafeLoader - - -def sub_constructor(loader, node): - value = loader.construct_scalar(node) - return {"Fn::Sub": value} - - -Loader.add_constructor('!Sub', sub_constructor) -SafeLoader.add_constructor('!Sub', sub_constructor) - client = boto3.client('lambda') -layer_change_key = 'SOSW_LAYER' +layer_change_key = 'SOSW_LAYER_PLACEHOLDER' curr_runtime = 'python3.11' test_layer_name = "sosw" +resource_types = ['AWS::Serverless::Function', 'AWS::Lambda::Function'] def main(): @@ -24,7 +13,7 @@ def main(): response = get_lambda_layer_version(curr_runtime, test_layer_name) print(response) if response: - change_lambda_layer_version(response, layer_change_key) + change_lambda_layer_version(response, layer_change_key,resource_types) except RuntimeError as r: raise RuntimeError(f"Something went wrong: {r}") @@ -34,19 +23,27 @@ def get_lambda_layer_version(runtime: str, layer_name: str) -> dict: return response -def change_lambda_layer_version(response: dict, change_key: str): +def change_lambda_layer_version(response: dict, change_key: str, resource_types_to_change: list[str]): layer_version = response['LayerVersions'][0]['Version'] print(f"Layer version: {layer_version}") + print(change_key) for root, dirs, files in os.walk('.'): for file in files: if file.endswith('.yaml'): file_path = os.path.join(root, file) with open(file_path, 'r') as f: - yaml_data = yaml.safe_load(f) - if change_key in yaml_data: - yaml_data[change_key] = layer_version - with open(file_path, 'w') as fw: - yaml.dump(yaml_data, fw) + if contains_resource_type(f, resource_types_to_change): + lines = f.readlines() + lines = [line.replace(change_key, str(layer_version)) for line in lines] + with open(file_path, "w") as fw: + fw.writelines(lines) + + +def contains_resource_type(file_handle, resource_types_to_check: list[str]) -> bool: + for line in file_handle: + if 'Type' in line and any(resource_type in line for resource_type in resource_types_to_check): + return True + return False if __name__ == "__main__": From 1fe31c8074d2f727720c2ccc0d49f0778b4e27f6 Mon Sep 17 00:00:00 2001 From: kreanimator Date: Wed, 1 May 2024 16:23:47 +0300 Subject: [PATCH 07/18] Finished function to check and change layer version --- layers/layers_versions_changer.py | 36 ++++++++++++++++++------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/layers/layers_versions_changer.py b/layers/layers_versions_changer.py index 6439ad9..a8cb8a1 100644 --- a/layers/layers_versions_changer.py +++ b/layers/layers_versions_changer.py @@ -1,47 +1,53 @@ import os +from typing import TextIO + import boto3 +import yaml client = boto3.client('lambda') layer_change_key = 'SOSW_LAYER_PLACEHOLDER' -curr_runtime = 'python3.11' test_layer_name = "sosw" resource_types = ['AWS::Serverless::Function', 'AWS::Lambda::Function'] def main(): try: - response = get_lambda_layer_version(curr_runtime, test_layer_name) + response = get_lambda_layer_version(test_layer_name) print(response) if response: - change_lambda_layer_version(response, layer_change_key,resource_types) + change_lambda_layer_version(response, layer_change_key, resource_types) except RuntimeError as r: raise RuntimeError(f"Something went wrong: {r}") -def get_lambda_layer_version(runtime: str, layer_name: str) -> dict: - response = client.list_layer_versions(CompatibleRuntime=runtime, LayerName=layer_name) +def get_lambda_layer_version(layer_name: str) -> dict: + response = client.list_layer_versions(LayerName=layer_name) return response -def change_lambda_layer_version(response: dict, change_key: str, resource_types_to_change: list[str]): +def change_lambda_layer_version(response: dict, change_key: str, resources_to_change: list[str]): layer_version = response['LayerVersions'][0]['Version'] print(f"Layer version: {layer_version}") - print(change_key) + print(f"Change key: {change_key}") for root, dirs, files in os.walk('.'): for file in files: if file.endswith('.yaml'): file_path = os.path.join(root, file) + with open(file_path, 'r') as f: - if contains_resource_type(f, resource_types_to_change): - lines = f.readlines() - lines = [line.replace(change_key, str(layer_version)) for line in lines] - with open(file_path, "w") as fw: - fw.writelines(lines) + data = f.read() + if not contains_resource_type(data, resources_to_change): + print("Resource to change not found") + continue + + with open(file_path, "w") as f: + f.write(data.replace(change_key, str(layer_version))) -def contains_resource_type(file_handle, resource_types_to_check: list[str]) -> bool: - for line in file_handle: - if 'Type' in line and any(resource_type in line for resource_type in resource_types_to_check): +def contains_resource_type(data: str, resource_types_to_check: list[str]) -> bool: + for resource_type in resource_types_to_check: + if resource_type in data: + print(f"Found resource type in line: {resource_type}") return True return False From ce8874e9dda630436391e68d1dbb7479134b524e Mon Sep 17 00:00:00 2001 From: kreanimator Date: Wed, 1 May 2024 16:36:58 +0300 Subject: [PATCH 08/18] Changed functionality to check the folder where it is implemented from --- layers/layers_versions_changer.py | 28 +++++++++++++----------- layers/{lambdas_for_test => }/test1.yaml | 0 layers/{lambdas_for_test => }/test2.yaml | 0 layers/{lambdas_for_test => }/test3.yaml | 0 4 files changed, 15 insertions(+), 13 deletions(-) rename layers/{lambdas_for_test => }/test1.yaml (100%) rename layers/{lambdas_for_test => }/test2.yaml (100%) rename layers/{lambdas_for_test => }/test3.yaml (100%) diff --git a/layers/layers_versions_changer.py b/layers/layers_versions_changer.py index a8cb8a1..da3f692 100644 --- a/layers/layers_versions_changer.py +++ b/layers/layers_versions_changer.py @@ -29,19 +29,21 @@ def change_lambda_layer_version(response: dict, change_key: str, resources_to_ch layer_version = response['LayerVersions'][0]['Version'] print(f"Layer version: {layer_version}") print(f"Change key: {change_key}") - for root, dirs, files in os.walk('.'): - for file in files: - if file.endswith('.yaml'): - file_path = os.path.join(root, file) - - with open(file_path, 'r') as f: - data = f.read() - if not contains_resource_type(data, resources_to_change): - print("Resource to change not found") - continue - - with open(file_path, "w") as f: - f.write(data.replace(change_key, str(layer_version))) + # for root, dirs, files in os.walk('.'): # Use it only if you need scan files in all repository + for file in os.listdir("."): + if file.endswith('.yaml'): + # file_path = os.path.join(root, file) # Use it only if you need scan files in all repository + # for file in files: + file_path = os.path.join('.', file) + + with open(file_path, 'r') as f: + data = f.read() + if not contains_resource_type(data, resources_to_change): + print("Resource to change not found") + continue + + with open(file_path, "w") as f: + f.write(data.replace(change_key, str(layer_version))) def contains_resource_type(data: str, resource_types_to_check: list[str]) -> bool: diff --git a/layers/lambdas_for_test/test1.yaml b/layers/test1.yaml similarity index 100% rename from layers/lambdas_for_test/test1.yaml rename to layers/test1.yaml diff --git a/layers/lambdas_for_test/test2.yaml b/layers/test2.yaml similarity index 100% rename from layers/lambdas_for_test/test2.yaml rename to layers/test2.yaml diff --git a/layers/lambdas_for_test/test3.yaml b/layers/test3.yaml similarity index 100% rename from layers/lambdas_for_test/test3.yaml rename to layers/test3.yaml From ffd21045ffaaba378e9e53777dacd9bedbf64347 Mon Sep 17 00:00:00 2001 From: kreanimator Date: Wed, 1 May 2024 18:11:34 +0300 Subject: [PATCH 09/18] Added makefile for SAM --- .../layers_versions_changer.py | 8 ++-- layers_version_changer/samconfig.toml | 11 +++++ layers_version_changer/src/Makefile | 4 ++ layers_version_changer/src/app.py | 44 +++++++++++++++++++ layers_version_changer/template.yaml | 37 ++++++++++++++++ {layers => layers_version_changer}/test1.yaml | 0 {layers => layers_version_changer}/test2.yaml | 0 {layers => layers_version_changer}/test3.yaml | 0 8 files changed, 99 insertions(+), 5 deletions(-) rename {layers => layers_version_changer}/layers_versions_changer.py (92%) create mode 100644 layers_version_changer/samconfig.toml create mode 100644 layers_version_changer/src/Makefile create mode 100644 layers_version_changer/src/app.py create mode 100644 layers_version_changer/template.yaml rename {layers => layers_version_changer}/test1.yaml (100%) rename {layers => layers_version_changer}/test2.yaml (100%) rename {layers => layers_version_changer}/test3.yaml (100%) diff --git a/layers/layers_versions_changer.py b/layers_version_changer/layers_versions_changer.py similarity index 92% rename from layers/layers_versions_changer.py rename to layers_version_changer/layers_versions_changer.py index da3f692..fce6cf4 100644 --- a/layers/layers_versions_changer.py +++ b/layers_version_changer/layers_versions_changer.py @@ -1,8 +1,5 @@ import os -from typing import TextIO - import boto3 -import yaml client = boto3.client('lambda') layer_change_key = 'SOSW_LAYER_PLACEHOLDER' @@ -30,11 +27,12 @@ def change_lambda_layer_version(response: dict, change_key: str, resources_to_ch print(f"Layer version: {layer_version}") print(f"Change key: {change_key}") # for root, dirs, files in os.walk('.'): # Use it only if you need scan files in all repository - for file in os.listdir("."): + script_dir = os.path.dirname(__file__) + for file in os.listdir(script_dir): if file.endswith('.yaml'): # file_path = os.path.join(root, file) # Use it only if you need scan files in all repository # for file in files: - file_path = os.path.join('.', file) + file_path = os.path.join(script_dir, file) with open(file_path, 'r') as f: data = f.read() diff --git a/layers_version_changer/samconfig.toml b/layers_version_changer/samconfig.toml new file mode 100644 index 0000000..070b660 --- /dev/null +++ b/layers_version_changer/samconfig.toml @@ -0,0 +1,11 @@ +version = 0.1 + +[default.deploy.parameters] +stack_name = "first-lambda" +s3_prefix = "first-lambda" +region = "us-west-2" +resolve_s3 = true +confirm_changeset = true +force-upload = true +capabilities = "CAPABILITY_IAM" + diff --git a/layers_version_changer/src/Makefile b/layers_version_changer/src/Makefile new file mode 100644 index 0000000..d43ff00 --- /dev/null +++ b/layers_version_changer/src/Makefile @@ -0,0 +1,4 @@ +build-YourFirstLambdaWithSOSW: + pwd + ls -la + python -m ..layers_versions_changer ./ diff --git a/layers_version_changer/src/app.py b/layers_version_changer/src/app.py new file mode 100644 index 0000000..cc90b7f --- /dev/null +++ b/layers_version_changer/src/app.py @@ -0,0 +1,44 @@ +import os + +from aws_lambda_powertools import Logger +from aws_xray_sdk.core import patch_all +from sosw.app import Processor as SoswProcessor, get_lambda_handler, LambdaGlobals + + +logger = Logger() +patch_all() + +IS_PROD = os.getenv('env') == 'prod' # TEMPORARY + + +class Processor(SoswProcessor): + DEFAULT_CONFIG = { + } + + + def get_config(self, name): + pass + + + def __call__(self, event): + """ + Call the Processor + """ + logger.info("Hello from Lambda!") + + +global_vars = LambdaGlobals() +lambda_handler = get_lambda_handler(Processor, global_vars) + + + + + + + + + + + +global_vars = LambdaGlobals() +lambda_handler = get_lambda_handler(Processor, global_vars) diff --git a/layers_version_changer/template.yaml b/layers_version_changer/template.yaml new file mode 100644 index 0000000..5c91321 --- /dev/null +++ b/layers_version_changer/template.yaml @@ -0,0 +1,37 @@ +AWSTemplateFormatVersion: 2010-09-09 +Transform: AWS::Serverless-2016-10-31 +Description: Sample template to use SAM with Makefile + +Parameters: + CustomFunctionName: + Type: String + Default: your_happy_lambda + Stage: + Type: String + Default: dev + +Conditions: + isProd: !Equals [ !Ref Stage, prod ] + +Resources: + YourFirstLambdaWithSOSW: + Type: AWS::Serverless::Function + Properties: + FunctionName: !If [ isProd, !Sub "${CustomFunctionName}", !Sub "${Stage}_${CustomFunctionName}" ] + CodeUri: src + Description: Your sample lambda function + Handler: app.lambda_handler + MemorySize: 256 + PackageType: Zip + Runtime: python3.11 + Timeout: 3 + Tracing: Active + Metadata: + BuildMethod: makefile + + Layers: + - !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:layer:sosw:SOSW_LAYER_PLACEHOLDER" + Tags: + Env: !Ref Stage + + diff --git a/layers/test1.yaml b/layers_version_changer/test1.yaml similarity index 100% rename from layers/test1.yaml rename to layers_version_changer/test1.yaml diff --git a/layers/test2.yaml b/layers_version_changer/test2.yaml similarity index 100% rename from layers/test2.yaml rename to layers_version_changer/test2.yaml diff --git a/layers/test3.yaml b/layers_version_changer/test3.yaml similarity index 100% rename from layers/test3.yaml rename to layers_version_changer/test3.yaml From 64632846c5dd465ede341b451649543aabaa27e8 Mon Sep 17 00:00:00 2001 From: ngr Date: Wed, 1 May 2024 19:13:15 +0300 Subject: [PATCH 10/18] .gitignore --- layers/{sosw_layer => sosw}/samconfig.toml | 0 layers/{sosw_layer => sosw}/sosw.yaml | 2 ++ layers/{sosw_layer => sosw}/template.yaml | 0 3 files changed, 2 insertions(+) rename layers/{sosw_layer => sosw}/samconfig.toml (100%) rename layers/{sosw_layer => sosw}/sosw.yaml (91%) rename layers/{sosw_layer => sosw}/template.yaml (100%) diff --git a/layers/sosw_layer/samconfig.toml b/layers/sosw/samconfig.toml similarity index 100% rename from layers/sosw_layer/samconfig.toml rename to layers/sosw/samconfig.toml diff --git a/layers/sosw_layer/sosw.yaml b/layers/sosw/sosw.yaml similarity index 91% rename from layers/sosw_layer/sosw.yaml rename to layers/sosw/sosw.yaml index 9edebe5..374ba14 100644 --- a/layers/sosw_layer/sosw.yaml +++ b/layers/sosw/sosw.yaml @@ -10,6 +10,8 @@ Parameters: Resources: SoswLayer: Type: "AWS::Lambda::LayerVersion" + DeletionPolicy: Retain + UpdateReplacePolicy: Retain Description: "sosw, bson, aws_lambda_powertools, aws_xray_sdk, requests" Properties: CompatibleRuntimes: diff --git a/layers/sosw_layer/template.yaml b/layers/sosw/template.yaml similarity index 100% rename from layers/sosw_layer/template.yaml rename to layers/sosw/template.yaml From cfc2f28ac97a667f46853b9229e1d065025d63d7 Mon Sep 17 00:00:00 2001 From: kreanimator Date: Wed, 8 May 2024 11:48:14 +0300 Subject: [PATCH 11/18] Fixed code styling --- layers/README.md | 0 layers_version_changer/layers_versions_changer.py | 2 ++ layers_version_changer/samconfig.toml | 1 - layers_version_changer/src/app.py | 13 ------------- layers_version_changer/template.yaml | 2 -- 5 files changed, 2 insertions(+), 16 deletions(-) create mode 100644 layers/README.md diff --git a/layers/README.md b/layers/README.md new file mode 100644 index 0000000..e69de29 diff --git a/layers_version_changer/layers_versions_changer.py b/layers_version_changer/layers_versions_changer.py index fce6cf4..c719c33 100644 --- a/layers_version_changer/layers_versions_changer.py +++ b/layers_version_changer/layers_versions_changer.py @@ -1,6 +1,8 @@ import os import boto3 +__author__ = "Valentin Bakin" + client = boto3.client('lambda') layer_change_key = 'SOSW_LAYER_PLACEHOLDER' test_layer_name = "sosw" diff --git a/layers_version_changer/samconfig.toml b/layers_version_changer/samconfig.toml index 070b660..2912436 100644 --- a/layers_version_changer/samconfig.toml +++ b/layers_version_changer/samconfig.toml @@ -8,4 +8,3 @@ resolve_s3 = true confirm_changeset = true force-upload = true capabilities = "CAPABILITY_IAM" - diff --git a/layers_version_changer/src/app.py b/layers_version_changer/src/app.py index cc90b7f..f9ca605 100644 --- a/layers_version_changer/src/app.py +++ b/layers_version_changer/src/app.py @@ -27,18 +27,5 @@ def __call__(self, event): logger.info("Hello from Lambda!") -global_vars = LambdaGlobals() -lambda_handler = get_lambda_handler(Processor, global_vars) - - - - - - - - - - - global_vars = LambdaGlobals() lambda_handler = get_lambda_handler(Processor, global_vars) diff --git a/layers_version_changer/template.yaml b/layers_version_changer/template.yaml index 5c91321..0807310 100644 --- a/layers_version_changer/template.yaml +++ b/layers_version_changer/template.yaml @@ -33,5 +33,3 @@ Resources: - !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:layer:sosw:SOSW_LAYER_PLACEHOLDER" Tags: Env: !Ref Stage - - From 2a8a5a769bcc8353d1bdf20e8635bb3211995b43 Mon Sep 17 00:00:00 2001 From: kreanimator Date: Wed, 8 May 2024 11:55:26 +0300 Subject: [PATCH 12/18] Added readme file --- layers/README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/layers/README.md b/layers/README.md index e69de29..f58e3e6 100644 --- a/layers/README.md +++ b/layers/README.md @@ -0,0 +1,18 @@ +While in the same folder as your ``depoy.sh`` file just run: + ./deploy.sh [-v branch] [-p profile] + +After pack a new layer in zip archive and put it in your S3 bucket. + +To deploy with CloudFormation use [sosw.yaml] to create a stack run : + ``aws cloudformation create-stack \ + --stack-name myteststack \ + --template-body file:///home/testuser/mytemplate.json \ + --parameters ParameterKey=Parm1,ParameterValue=test1 ParameterKey=Parm2,ParameterValue=test2`` + +Or upload it directly via GUI. + +To deploy with AWS SAM use [samconfig.toml] and [template.yaml] + +In the same folder run ``sam build && sam deploy`` + +Don't forget to change placeholders like ``{FileName}`` with your actual data. From 5d29972ee0f65d3b05ccc69129d885f8666e7543 Mon Sep 17 00:00:00 2001 From: kreanimator Date: Wed, 8 May 2024 14:17:33 +0300 Subject: [PATCH 13/18] Initial commit --- helpers/sosw_image_converter/samconfig.toml | 22 +++++ helpers/sosw_image_converter/src/app.py | 84 +++++++++++++++++++ .../sosw_image_converter/src/requirements.txt | 1 + helpers/sosw_image_converter/template.yaml | 43 ++++++++++ 4 files changed, 150 insertions(+) create mode 100644 helpers/sosw_image_converter/samconfig.toml create mode 100644 helpers/sosw_image_converter/src/app.py create mode 100644 helpers/sosw_image_converter/src/requirements.txt create mode 100644 helpers/sosw_image_converter/template.yaml diff --git a/helpers/sosw_image_converter/samconfig.toml b/helpers/sosw_image_converter/samconfig.toml new file mode 100644 index 0000000..60eb0ae --- /dev/null +++ b/helpers/sosw_image_converter/samconfig.toml @@ -0,0 +1,22 @@ +version = 0.1 + +[default.deploy.parameters] +stack_name = "dev-image-converter" +s3_prefix = "dev-image-converter" +parameter_overrides = "Stage=\"dev\", " +confirm_changeset = false +resolve_s3 = true +region = "us-west-2" +force-upload = true +capabilities = "CAPABILITY_IAM" + + +[prod.deploy.parameters] +stack_name = "image-converter" +s3_prefix = "image-converter" +parameter_overrides = "Stage=\"prod\"" +confirm_changeset = true +resolve_s3 = true +region = "us-west-2" +force-upload = true +capabilities = "CAPABILITY_IAM" diff --git a/helpers/sosw_image_converter/src/app.py b/helpers/sosw_image_converter/src/app.py new file mode 100644 index 0000000..dc3a258 --- /dev/null +++ b/helpers/sosw_image_converter/src/app.py @@ -0,0 +1,84 @@ +import io +import os + +import boto3 +from PIL import Image +from aws_lambda_powertools import Logger +from aws_xray_sdk.core import patch_all +from sosw.app import Processor as SoswProcessor, LambdaGlobals, get_lambda_handler + +__author__ = "Valentin Bakin" + +logger = Logger() +patch_all() + +IS_PROD = os.getenv('env') == 'prod' + + +class Processor(SoswProcessor): + DEFAULT_CONFIG = { + 'init_clients': ['s3'], + 's3_config': { + 'bucket_name': 'img-converter-test', # Change it with your actual s3 bucket + 'path': 'img/', # Change it with your actual path + + 'image_types': ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'tiff'], + }, + 'path_prefix': '/prod', + } + + s3_client: boto3.client = None + + + def get_config(self, name): + pass + + + def __call__(self, event): + + bucket_name = self.config['s3_config']['bucket_name'] + path = self.config['s3_config']['path'] + objects = self.list_s3_objects(bucket_name, path) + + for obj in objects: + key = obj['Key'] + logger.info(obj) + file_format = key.split('.')[-1].lower() + if file_format not in self.config['s3_config']['image_types']: + continue + + image_data = self.download_from_s3(bucket_name, key) + if file_format != 'webp': + webp_data = self.convert_to_webp(image_data) + new_key = key.rsplit('.', 1)[0] + '.webp' + self.upload_to_s3(bucket_name, new_key, webp_data) + # You can use it if you want to delete old images from s3 bucket, + # but it is not recommended cause not all browsers read webp format + # self.delete_from_s3(bucket_name,key) + + + def convert_to_webp(self, image_data): + image = Image.open(io.BytesIO(image_data)) + output = io.BytesIO() + image.save(output, format="WEBP") + return output.getvalue() + + def list_s3_objects(self, bucket_name, path): + response = self.s3_client.list_objects_v2(Bucket=bucket_name, Prefix=path) + logger.info(response.get('Contents', [])) + return response.get('Contents', []) + + def download_from_s3(self, bucket_name, key): + response = self.s3_client.get_object(Bucket=bucket_name, Key=key) + return response['Body'].read() + + def upload_to_s3(self, bucket_name, key, data): + self.s3_client.put_object(Bucket=bucket_name, Key=key, Body=data) + + + def delete_from_s3(self, bucket_name, key): + self.s3_client.delete_object(Bucket=bucket_name, Key=key) + + +global_vars = LambdaGlobals() +lambda_handler = get_lambda_handler(Processor, global_vars) diff --git a/helpers/sosw_image_converter/src/requirements.txt b/helpers/sosw_image_converter/src/requirements.txt new file mode 100644 index 0000000..3af8318 --- /dev/null +++ b/helpers/sosw_image_converter/src/requirements.txt @@ -0,0 +1 @@ +Pillow==10.3.0 \ No newline at end of file diff --git a/helpers/sosw_image_converter/template.yaml b/helpers/sosw_image_converter/template.yaml new file mode 100644 index 0000000..0176115 --- /dev/null +++ b/helpers/sosw_image_converter/template.yaml @@ -0,0 +1,43 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: 'AWS::Serverless-2016-10-31' +Description: Export any image to webp + +Parameters: + CustomFunctionName: + Type: String + Default: image_converter_to_webp + Stage: + Type: String + Default: dev + BucketName: + Type: String + Default: img-converter-test + +Conditions: + isProd: !Equals [ !Ref Stage, prod ] + +Resources: + ImageConverterToWEBP: + Type: AWS::Serverless::Function + Properties: + FunctionName: !If [ isProd, !Ref CustomFunctionName, !Sub "${Stage}_${CustomFunctionName}" ] + CodeUri: src/ + Description: Image converter to webp + Handler: app.lambda_handler + MemorySize: 256 + PackageType: Zip + Runtime: python3.11 + Timeout: 60 + Tracing: Active +# FunctionUrlConfig: +## AuthType: AWS_IAM #For future use +# AuthType: NONE +# InvokeMode: RESPONSE_STREAM + + Policies: + - S3CrudPolicy: + BucketName: !Ref BucketName + Layers: + - !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:layers:sosw:1' + Tags: + Env: !Ref Stage From 00b3daeb1735664c104c78f7b7f01355914e4d23 Mon Sep 17 00:00:00 2001 From: kreanimator Date: Wed, 8 May 2024 14:18:11 +0300 Subject: [PATCH 14/18] Refactored code structure --- .../layers_versions_changer.py | 58 +++++++++++++++++ .../samconfig.toml | 10 +++ .../sosw_layers_version_changer/src/Makefile | 4 ++ .../sosw_layers_version_changer/src/app.py | 31 ++++++++++ .../sosw_layers_version_changer/template.yaml | 35 +++++++++++ .../sosw_layers_version_changer/test1.yaml | 62 +++++++++++++++++++ .../sosw_layers_version_changer/test2.yaml | 50 +++++++++++++++ .../sosw_layers_version_changer/test3.yaml | 24 +++++++ 8 files changed, 274 insertions(+) create mode 100644 helpers/sosw_layers_version_changer/layers_versions_changer.py create mode 100644 helpers/sosw_layers_version_changer/samconfig.toml create mode 100644 helpers/sosw_layers_version_changer/src/Makefile create mode 100644 helpers/sosw_layers_version_changer/src/app.py create mode 100644 helpers/sosw_layers_version_changer/template.yaml create mode 100644 helpers/sosw_layers_version_changer/test1.yaml create mode 100644 helpers/sosw_layers_version_changer/test2.yaml create mode 100644 helpers/sosw_layers_version_changer/test3.yaml diff --git a/helpers/sosw_layers_version_changer/layers_versions_changer.py b/helpers/sosw_layers_version_changer/layers_versions_changer.py new file mode 100644 index 0000000..c719c33 --- /dev/null +++ b/helpers/sosw_layers_version_changer/layers_versions_changer.py @@ -0,0 +1,58 @@ +import os +import boto3 + +__author__ = "Valentin Bakin" + +client = boto3.client('lambda') +layer_change_key = 'SOSW_LAYER_PLACEHOLDER' +test_layer_name = "sosw" +resource_types = ['AWS::Serverless::Function', 'AWS::Lambda::Function'] + + +def main(): + try: + response = get_lambda_layer_version(test_layer_name) + print(response) + if response: + change_lambda_layer_version(response, layer_change_key, resource_types) + except RuntimeError as r: + raise RuntimeError(f"Something went wrong: {r}") + + +def get_lambda_layer_version(layer_name: str) -> dict: + response = client.list_layer_versions(LayerName=layer_name) + return response + + +def change_lambda_layer_version(response: dict, change_key: str, resources_to_change: list[str]): + layer_version = response['LayerVersions'][0]['Version'] + print(f"Layer version: {layer_version}") + print(f"Change key: {change_key}") + # for root, dirs, files in os.walk('.'): # Use it only if you need scan files in all repository + script_dir = os.path.dirname(__file__) + for file in os.listdir(script_dir): + if file.endswith('.yaml'): + # file_path = os.path.join(root, file) # Use it only if you need scan files in all repository + # for file in files: + file_path = os.path.join(script_dir, file) + + with open(file_path, 'r') as f: + data = f.read() + if not contains_resource_type(data, resources_to_change): + print("Resource to change not found") + continue + + with open(file_path, "w") as f: + f.write(data.replace(change_key, str(layer_version))) + + +def contains_resource_type(data: str, resource_types_to_check: list[str]) -> bool: + for resource_type in resource_types_to_check: + if resource_type in data: + print(f"Found resource type in line: {resource_type}") + return True + return False + + +if __name__ == "__main__": + main() diff --git a/helpers/sosw_layers_version_changer/samconfig.toml b/helpers/sosw_layers_version_changer/samconfig.toml new file mode 100644 index 0000000..2912436 --- /dev/null +++ b/helpers/sosw_layers_version_changer/samconfig.toml @@ -0,0 +1,10 @@ +version = 0.1 + +[default.deploy.parameters] +stack_name = "first-lambda" +s3_prefix = "first-lambda" +region = "us-west-2" +resolve_s3 = true +confirm_changeset = true +force-upload = true +capabilities = "CAPABILITY_IAM" diff --git a/helpers/sosw_layers_version_changer/src/Makefile b/helpers/sosw_layers_version_changer/src/Makefile new file mode 100644 index 0000000..d43ff00 --- /dev/null +++ b/helpers/sosw_layers_version_changer/src/Makefile @@ -0,0 +1,4 @@ +build-YourFirstLambdaWithSOSW: + pwd + ls -la + python -m ..layers_versions_changer ./ diff --git a/helpers/sosw_layers_version_changer/src/app.py b/helpers/sosw_layers_version_changer/src/app.py new file mode 100644 index 0000000..f9ca605 --- /dev/null +++ b/helpers/sosw_layers_version_changer/src/app.py @@ -0,0 +1,31 @@ +import os + +from aws_lambda_powertools import Logger +from aws_xray_sdk.core import patch_all +from sosw.app import Processor as SoswProcessor, get_lambda_handler, LambdaGlobals + + +logger = Logger() +patch_all() + +IS_PROD = os.getenv('env') == 'prod' # TEMPORARY + + +class Processor(SoswProcessor): + DEFAULT_CONFIG = { + } + + + def get_config(self, name): + pass + + + def __call__(self, event): + """ + Call the Processor + """ + logger.info("Hello from Lambda!") + + +global_vars = LambdaGlobals() +lambda_handler = get_lambda_handler(Processor, global_vars) diff --git a/helpers/sosw_layers_version_changer/template.yaml b/helpers/sosw_layers_version_changer/template.yaml new file mode 100644 index 0000000..0807310 --- /dev/null +++ b/helpers/sosw_layers_version_changer/template.yaml @@ -0,0 +1,35 @@ +AWSTemplateFormatVersion: 2010-09-09 +Transform: AWS::Serverless-2016-10-31 +Description: Sample template to use SAM with Makefile + +Parameters: + CustomFunctionName: + Type: String + Default: your_happy_lambda + Stage: + Type: String + Default: dev + +Conditions: + isProd: !Equals [ !Ref Stage, prod ] + +Resources: + YourFirstLambdaWithSOSW: + Type: AWS::Serverless::Function + Properties: + FunctionName: !If [ isProd, !Sub "${CustomFunctionName}", !Sub "${Stage}_${CustomFunctionName}" ] + CodeUri: src + Description: Your sample lambda function + Handler: app.lambda_handler + MemorySize: 256 + PackageType: Zip + Runtime: python3.11 + Timeout: 3 + Tracing: Active + Metadata: + BuildMethod: makefile + + Layers: + - !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:layer:sosw:SOSW_LAYER_PLACEHOLDER" + Tags: + Env: !Ref Stage diff --git a/helpers/sosw_layers_version_changer/test1.yaml b/helpers/sosw_layers_version_changer/test1.yaml new file mode 100644 index 0000000..1fae7d9 --- /dev/null +++ b/helpers/sosw_layers_version_changer/test1.yaml @@ -0,0 +1,62 @@ +AWSTemplateFormatVersion: 2010-09-09 +Transform: AWS::Serverless-2016-10-31 +Description: Event bus to DDb www-sosw-form-records + +Parameters: + CustomFunctionName: + Type: String + Default: web_form_processor + CustomTableName: + Type: String + Default: www_sosw_form_records + Stage: + Type: String + Default: dev + +Conditions: + isProd: !Equals [ !Ref Stage, prod ] + +Resources: + LambdaWebFormProcessor: + Type: AWS::Serverless::Function + Properties: + FunctionName: !If [ isProd, !Sub "${CustomFunctionName}", !Sub "${Stage}_${CustomFunctionName}" ] + CodeUri: src/ + Description: Event bus to DDb www_sosw_form_records + Handler: app.lambda_handler + MemorySize: 256 + PackageType: Zip + Runtime: python3.11 + Timeout: 3 + Tracing: Active + + Events: + Trigger: + Type: EventBridgeRule + Properties: + EventBusName: 'default' + Pattern: + source: + - 'ApiGw' + detail-type: + - 'www-form-apply' + - 'www-form-contact-us' + - 'www-form-event-registration' + - 'www-form-event-testcloud' + Policies: + - SNSPublishMessagePolicy: + TopicName: !If [ isProd, "web-forms-sosw", !Sub "${Stage}-web-forms-sosw" ] + - DynamoDBCrudPolicy: + TableName: !If [ isProd, !Sub "${CustomTableName}", !Sub "${Stage}_${CustomTableName}" ] + Layers: + - !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:layer:sosw:SOSW_LAYER_PLACEHOLDER" + Tags: + Env: !Ref Stage + + FunctionResourcePermission: + Type: 'AWS::Lambda::Permission' + Properties: + Action: 'lambda:InvokeFunction' + Principal: events.amazonaws.com + FunctionName: !Ref LambdaWebFormProcessor + SourceArn: !Sub 'arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:*/*' diff --git a/helpers/sosw_layers_version_changer/test2.yaml b/helpers/sosw_layers_version_changer/test2.yaml new file mode 100644 index 0000000..db60712 --- /dev/null +++ b/helpers/sosw_layers_version_changer/test2.yaml @@ -0,0 +1,50 @@ +AWSTemplateFormatVersion: "2010-09-09" +Description: sys_lambda_env_setter + +Resources: + + SysLambdaEnvSetterRole: + Type: "AWS::IAM::Role" + Properties: + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Principal: + Service: + - "lambda.amazonaws.com" + Action: + - "sts:AssumeRole" + + Path: "/" + ManagedPolicyArns: + - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + + Policies: + - PolicyName: "SysLambdaEnvSetterCustom" + PolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Action: + - lambda:GetFunctionConfiguration + - lambda:UpdateFunctionConfiguration + Resource: '*' + + RoleName: "sys_lambda_env_setter_role" + + SysLambdaEnvSetterFunction: + Type: AWS::Lambda::Function + Properties: + FunctionName: sys_lambda_env_setter + Runtime: python3.11 + Role: !GetAtt SysLambdaEnvSetterRole.Arn + Handler: app.lambda_handler + Code: + + S3Bucket: !Sub "sosw-s3-${AWS::AccountId}" + S3Key: lambda_packages/sys_lambda_env_setter.zip + Layers: + - !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:layer:sosw:SOSW_LAYER_PLACEHOLDER" + + Description: Set environmental variables for Lambda functions diff --git a/helpers/sosw_layers_version_changer/test3.yaml b/helpers/sosw_layers_version_changer/test3.yaml new file mode 100644 index 0000000..6cf1378 --- /dev/null +++ b/helpers/sosw_layers_version_changer/test3.yaml @@ -0,0 +1,24 @@ +AWSTemplateFormatVersion: 2010-09-09 +Transform: AWS::Serverless-2016-10-31 +Description: SOSW layer + +Parameters: + FileName: + Description: Custom name of file to use. + Type: String + Default: sosw-stable.zip + +Resources: + SoswLayer: + Type: "AWS::Lambda::LayerVersion" + Description: "sosw, bson, aws_lambda_powertools, aws_xray_sdk, requests" + Properties: + CompatibleRuntimes: + - python3.10 + - python3.11 + Content: + S3Bucket: !Sub "app-control-${AWS::SOSW_LAYER_PLACEHOLDER}" + S3Key: !Sub "lambda_layers/${SOSW_LAYER_PLACEHOLDER}" + Description: "sosw" + LayerName: "sosw" + LicenseInfo: "MIT" From c95aca2a00b2a11c04b784eb811ad2fda7900b19 Mon Sep 17 00:00:00 2001 From: kreanimator Date: Thu, 9 May 2024 14:58:54 +0300 Subject: [PATCH 15/18] Edited code styling --- helpers/sosw_image_converter/src/app.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/helpers/sosw_image_converter/src/app.py b/helpers/sosw_image_converter/src/app.py index dc3a258..9a1c084 100644 --- a/helpers/sosw_image_converter/src/app.py +++ b/helpers/sosw_image_converter/src/app.py @@ -63,15 +63,18 @@ def convert_to_webp(self, image_data): image.save(output, format="WEBP") return output.getvalue() + def list_s3_objects(self, bucket_name, path): response = self.s3_client.list_objects_v2(Bucket=bucket_name, Prefix=path) logger.info(response.get('Contents', [])) return response.get('Contents', []) + def download_from_s3(self, bucket_name, key): response = self.s3_client.get_object(Bucket=bucket_name, Key=key) return response['Body'].read() + def upload_to_s3(self, bucket_name, key, data): self.s3_client.put_object(Bucket=bucket_name, Key=key, Body=data) From ac27ce1e0bc9db9f2cab4a0bd6b22d1d731a3698 Mon Sep 17 00:00:00 2001 From: kreanimator Date: Thu, 9 May 2024 15:03:18 +0300 Subject: [PATCH 16/18] Revert "Edited code styling" This reverts commit c95aca2a00b2a11c04b784eb811ad2fda7900b19. --- helpers/sosw_image_converter/src/app.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/helpers/sosw_image_converter/src/app.py b/helpers/sosw_image_converter/src/app.py index 9a1c084..dc3a258 100644 --- a/helpers/sosw_image_converter/src/app.py +++ b/helpers/sosw_image_converter/src/app.py @@ -63,18 +63,15 @@ def convert_to_webp(self, image_data): image.save(output, format="WEBP") return output.getvalue() - def list_s3_objects(self, bucket_name, path): response = self.s3_client.list_objects_v2(Bucket=bucket_name, Prefix=path) logger.info(response.get('Contents', [])) return response.get('Contents', []) - def download_from_s3(self, bucket_name, key): response = self.s3_client.get_object(Bucket=bucket_name, Key=key) return response['Body'].read() - def upload_to_s3(self, bucket_name, key, data): self.s3_client.put_object(Bucket=bucket_name, Key=key, Body=data) From 6a604b90ef84fc241fb65e653d9ca4e0bc74915b Mon Sep 17 00:00:00 2001 From: kreanimator Date: Thu, 9 May 2024 15:05:27 +0300 Subject: [PATCH 17/18] Deleted redundunt files from this branch --- .../layers_versions_changer.py | 58 ------------ .../samconfig.toml | 10 --- .../sosw_layers_version_changer/src/Makefile | 4 - .../sosw_layers_version_changer/src/app.py | 31 ------- .../sosw_layers_version_changer/template.yaml | 35 -------- .../sosw_layers_version_changer/test1.yaml | 62 ------------- .../sosw_layers_version_changer/test2.yaml | 50 ----------- .../sosw_layers_version_changer/test3.yaml | 24 ----- layers/README.md | 18 ---- layers/deploy.sh | 89 ------------------- layers/sosw/samconfig.toml | 11 --- layers/sosw/sosw.yaml | 25 ------ layers/sosw/template.yaml | 24 ----- .../layers_versions_changer.py | 58 ------------ layers_version_changer/samconfig.toml | 10 --- layers_version_changer/src/Makefile | 4 - layers_version_changer/src/app.py | 31 ------- layers_version_changer/template.yaml | 35 -------- layers_version_changer/test1.yaml | 62 ------------- layers_version_changer/test2.yaml | 50 ----------- layers_version_changer/test3.yaml | 24 ----- 21 files changed, 715 deletions(-) delete mode 100644 helpers/sosw_layers_version_changer/layers_versions_changer.py delete mode 100644 helpers/sosw_layers_version_changer/samconfig.toml delete mode 100644 helpers/sosw_layers_version_changer/src/Makefile delete mode 100644 helpers/sosw_layers_version_changer/src/app.py delete mode 100644 helpers/sosw_layers_version_changer/template.yaml delete mode 100644 helpers/sosw_layers_version_changer/test1.yaml delete mode 100644 helpers/sosw_layers_version_changer/test2.yaml delete mode 100644 helpers/sosw_layers_version_changer/test3.yaml delete mode 100644 layers/README.md delete mode 100755 layers/deploy.sh delete mode 100644 layers/sosw/samconfig.toml delete mode 100644 layers/sosw/sosw.yaml delete mode 100644 layers/sosw/template.yaml delete mode 100644 layers_version_changer/layers_versions_changer.py delete mode 100644 layers_version_changer/samconfig.toml delete mode 100644 layers_version_changer/src/Makefile delete mode 100644 layers_version_changer/src/app.py delete mode 100644 layers_version_changer/template.yaml delete mode 100644 layers_version_changer/test1.yaml delete mode 100644 layers_version_changer/test2.yaml delete mode 100644 layers_version_changer/test3.yaml diff --git a/helpers/sosw_layers_version_changer/layers_versions_changer.py b/helpers/sosw_layers_version_changer/layers_versions_changer.py deleted file mode 100644 index c719c33..0000000 --- a/helpers/sosw_layers_version_changer/layers_versions_changer.py +++ /dev/null @@ -1,58 +0,0 @@ -import os -import boto3 - -__author__ = "Valentin Bakin" - -client = boto3.client('lambda') -layer_change_key = 'SOSW_LAYER_PLACEHOLDER' -test_layer_name = "sosw" -resource_types = ['AWS::Serverless::Function', 'AWS::Lambda::Function'] - - -def main(): - try: - response = get_lambda_layer_version(test_layer_name) - print(response) - if response: - change_lambda_layer_version(response, layer_change_key, resource_types) - except RuntimeError as r: - raise RuntimeError(f"Something went wrong: {r}") - - -def get_lambda_layer_version(layer_name: str) -> dict: - response = client.list_layer_versions(LayerName=layer_name) - return response - - -def change_lambda_layer_version(response: dict, change_key: str, resources_to_change: list[str]): - layer_version = response['LayerVersions'][0]['Version'] - print(f"Layer version: {layer_version}") - print(f"Change key: {change_key}") - # for root, dirs, files in os.walk('.'): # Use it only if you need scan files in all repository - script_dir = os.path.dirname(__file__) - for file in os.listdir(script_dir): - if file.endswith('.yaml'): - # file_path = os.path.join(root, file) # Use it only if you need scan files in all repository - # for file in files: - file_path = os.path.join(script_dir, file) - - with open(file_path, 'r') as f: - data = f.read() - if not contains_resource_type(data, resources_to_change): - print("Resource to change not found") - continue - - with open(file_path, "w") as f: - f.write(data.replace(change_key, str(layer_version))) - - -def contains_resource_type(data: str, resource_types_to_check: list[str]) -> bool: - for resource_type in resource_types_to_check: - if resource_type in data: - print(f"Found resource type in line: {resource_type}") - return True - return False - - -if __name__ == "__main__": - main() diff --git a/helpers/sosw_layers_version_changer/samconfig.toml b/helpers/sosw_layers_version_changer/samconfig.toml deleted file mode 100644 index 2912436..0000000 --- a/helpers/sosw_layers_version_changer/samconfig.toml +++ /dev/null @@ -1,10 +0,0 @@ -version = 0.1 - -[default.deploy.parameters] -stack_name = "first-lambda" -s3_prefix = "first-lambda" -region = "us-west-2" -resolve_s3 = true -confirm_changeset = true -force-upload = true -capabilities = "CAPABILITY_IAM" diff --git a/helpers/sosw_layers_version_changer/src/Makefile b/helpers/sosw_layers_version_changer/src/Makefile deleted file mode 100644 index d43ff00..0000000 --- a/helpers/sosw_layers_version_changer/src/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -build-YourFirstLambdaWithSOSW: - pwd - ls -la - python -m ..layers_versions_changer ./ diff --git a/helpers/sosw_layers_version_changer/src/app.py b/helpers/sosw_layers_version_changer/src/app.py deleted file mode 100644 index f9ca605..0000000 --- a/helpers/sosw_layers_version_changer/src/app.py +++ /dev/null @@ -1,31 +0,0 @@ -import os - -from aws_lambda_powertools import Logger -from aws_xray_sdk.core import patch_all -from sosw.app import Processor as SoswProcessor, get_lambda_handler, LambdaGlobals - - -logger = Logger() -patch_all() - -IS_PROD = os.getenv('env') == 'prod' # TEMPORARY - - -class Processor(SoswProcessor): - DEFAULT_CONFIG = { - } - - - def get_config(self, name): - pass - - - def __call__(self, event): - """ - Call the Processor - """ - logger.info("Hello from Lambda!") - - -global_vars = LambdaGlobals() -lambda_handler = get_lambda_handler(Processor, global_vars) diff --git a/helpers/sosw_layers_version_changer/template.yaml b/helpers/sosw_layers_version_changer/template.yaml deleted file mode 100644 index 0807310..0000000 --- a/helpers/sosw_layers_version_changer/template.yaml +++ /dev/null @@ -1,35 +0,0 @@ -AWSTemplateFormatVersion: 2010-09-09 -Transform: AWS::Serverless-2016-10-31 -Description: Sample template to use SAM with Makefile - -Parameters: - CustomFunctionName: - Type: String - Default: your_happy_lambda - Stage: - Type: String - Default: dev - -Conditions: - isProd: !Equals [ !Ref Stage, prod ] - -Resources: - YourFirstLambdaWithSOSW: - Type: AWS::Serverless::Function - Properties: - FunctionName: !If [ isProd, !Sub "${CustomFunctionName}", !Sub "${Stage}_${CustomFunctionName}" ] - CodeUri: src - Description: Your sample lambda function - Handler: app.lambda_handler - MemorySize: 256 - PackageType: Zip - Runtime: python3.11 - Timeout: 3 - Tracing: Active - Metadata: - BuildMethod: makefile - - Layers: - - !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:layer:sosw:SOSW_LAYER_PLACEHOLDER" - Tags: - Env: !Ref Stage diff --git a/helpers/sosw_layers_version_changer/test1.yaml b/helpers/sosw_layers_version_changer/test1.yaml deleted file mode 100644 index 1fae7d9..0000000 --- a/helpers/sosw_layers_version_changer/test1.yaml +++ /dev/null @@ -1,62 +0,0 @@ -AWSTemplateFormatVersion: 2010-09-09 -Transform: AWS::Serverless-2016-10-31 -Description: Event bus to DDb www-sosw-form-records - -Parameters: - CustomFunctionName: - Type: String - Default: web_form_processor - CustomTableName: - Type: String - Default: www_sosw_form_records - Stage: - Type: String - Default: dev - -Conditions: - isProd: !Equals [ !Ref Stage, prod ] - -Resources: - LambdaWebFormProcessor: - Type: AWS::Serverless::Function - Properties: - FunctionName: !If [ isProd, !Sub "${CustomFunctionName}", !Sub "${Stage}_${CustomFunctionName}" ] - CodeUri: src/ - Description: Event bus to DDb www_sosw_form_records - Handler: app.lambda_handler - MemorySize: 256 - PackageType: Zip - Runtime: python3.11 - Timeout: 3 - Tracing: Active - - Events: - Trigger: - Type: EventBridgeRule - Properties: - EventBusName: 'default' - Pattern: - source: - - 'ApiGw' - detail-type: - - 'www-form-apply' - - 'www-form-contact-us' - - 'www-form-event-registration' - - 'www-form-event-testcloud' - Policies: - - SNSPublishMessagePolicy: - TopicName: !If [ isProd, "web-forms-sosw", !Sub "${Stage}-web-forms-sosw" ] - - DynamoDBCrudPolicy: - TableName: !If [ isProd, !Sub "${CustomTableName}", !Sub "${Stage}_${CustomTableName}" ] - Layers: - - !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:layer:sosw:SOSW_LAYER_PLACEHOLDER" - Tags: - Env: !Ref Stage - - FunctionResourcePermission: - Type: 'AWS::Lambda::Permission' - Properties: - Action: 'lambda:InvokeFunction' - Principal: events.amazonaws.com - FunctionName: !Ref LambdaWebFormProcessor - SourceArn: !Sub 'arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:*/*' diff --git a/helpers/sosw_layers_version_changer/test2.yaml b/helpers/sosw_layers_version_changer/test2.yaml deleted file mode 100644 index db60712..0000000 --- a/helpers/sosw_layers_version_changer/test2.yaml +++ /dev/null @@ -1,50 +0,0 @@ -AWSTemplateFormatVersion: "2010-09-09" -Description: sys_lambda_env_setter - -Resources: - - SysLambdaEnvSetterRole: - Type: "AWS::IAM::Role" - Properties: - AssumeRolePolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: Allow - Principal: - Service: - - "lambda.amazonaws.com" - Action: - - "sts:AssumeRole" - - Path: "/" - ManagedPolicyArns: - - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - - Policies: - - PolicyName: "SysLambdaEnvSetterCustom" - PolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: Allow - Action: - - lambda:GetFunctionConfiguration - - lambda:UpdateFunctionConfiguration - Resource: '*' - - RoleName: "sys_lambda_env_setter_role" - - SysLambdaEnvSetterFunction: - Type: AWS::Lambda::Function - Properties: - FunctionName: sys_lambda_env_setter - Runtime: python3.11 - Role: !GetAtt SysLambdaEnvSetterRole.Arn - Handler: app.lambda_handler - Code: - - S3Bucket: !Sub "sosw-s3-${AWS::AccountId}" - S3Key: lambda_packages/sys_lambda_env_setter.zip - Layers: - - !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:layer:sosw:SOSW_LAYER_PLACEHOLDER" - - Description: Set environmental variables for Lambda functions diff --git a/helpers/sosw_layers_version_changer/test3.yaml b/helpers/sosw_layers_version_changer/test3.yaml deleted file mode 100644 index 6cf1378..0000000 --- a/helpers/sosw_layers_version_changer/test3.yaml +++ /dev/null @@ -1,24 +0,0 @@ -AWSTemplateFormatVersion: 2010-09-09 -Transform: AWS::Serverless-2016-10-31 -Description: SOSW layer - -Parameters: - FileName: - Description: Custom name of file to use. - Type: String - Default: sosw-stable.zip - -Resources: - SoswLayer: - Type: "AWS::Lambda::LayerVersion" - Description: "sosw, bson, aws_lambda_powertools, aws_xray_sdk, requests" - Properties: - CompatibleRuntimes: - - python3.10 - - python3.11 - Content: - S3Bucket: !Sub "app-control-${AWS::SOSW_LAYER_PLACEHOLDER}" - S3Key: !Sub "lambda_layers/${SOSW_LAYER_PLACEHOLDER}" - Description: "sosw" - LayerName: "sosw" - LicenseInfo: "MIT" diff --git a/layers/README.md b/layers/README.md deleted file mode 100644 index f58e3e6..0000000 --- a/layers/README.md +++ /dev/null @@ -1,18 +0,0 @@ -While in the same folder as your ``depoy.sh`` file just run: - ./deploy.sh [-v branch] [-p profile] - -After pack a new layer in zip archive and put it in your S3 bucket. - -To deploy with CloudFormation use [sosw.yaml] to create a stack run : - ``aws cloudformation create-stack \ - --stack-name myteststack \ - --template-body file:///home/testuser/mytemplate.json \ - --parameters ParameterKey=Parm1,ParameterValue=test1 ParameterKey=Parm2,ParameterValue=test2`` - -Or upload it directly via GUI. - -To deploy with AWS SAM use [samconfig.toml] and [template.yaml] - -In the same folder run ``sam build && sam deploy`` - -Don't forget to change placeholders like ``{FileName}`` with your actual data. diff --git a/layers/deploy.sh b/layers/deploy.sh deleted file mode 100755 index d221825..0000000 --- a/layers/deploy.sh +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Change ACCOUNT_ID and S3 bucket name appropriately. -ACCOUNT_ID=YOUR_ACCOUNT_ID #You need to change YOUR_ACCOUNT_ID with your actual account id. -BUCKET_NAME=app-control-$ACCOUNT_ID - -NAME=sosw -ORGANIZATION=$NAME -PROFILE=default - -HELPMSG="USAGE: ./deploy.sh [-v branch] [-p profile] -Deploys sosw layer. Installs sosw from latest pip version, or from a specific branch if you use -v.\n -Use -p in case you have specific profile (not the default one) in you .aws/config with appropriate permissions." - -while getopts ":v:p:fh" option -do - case "$option" - in - v) VERSION=$OPTARG;; - p) PROFILE=$OPTARG;; - f) FORCE_RANDOM_NAME=true;; - h|*) echo -e "$HELPMSG";exit;; - esac -done - -#VERSION=0_7_43 - -# Install package with respect to the rule of Lambda Layers: -# https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html - -if [[ ${VERSION} ]]; then - echo "Deploying a specific version from branch: $VERSION" - pip3 install git+https://github.com/$ORGANIZATION/$NAME.git@$VERSION --no-dependencies -t $NAME/python/ -else - VERSION="stable" - pip3 install $NAME --no-dependencies -t $NAME/python/ -fi - -# Package other (non-sosw) required libraries -pip3 install aws_lambda_powertools -t $NAME/python/ -pip3 install aws_xray_sdk -t $NAME/python/ -pip3 install bson --no-dependencies -t $NAME/python/ -pip3 install requests -t $NAME/python/ - -if [[ $FORCE_RANDOM_NAME ]]; then - echo "Generated a random suffix for file name." - FILE_NAME=$NAME-$VERSION-$RANDOM -else - FILE_NAME=$NAME-$VERSION -fi - -zip_path="/tmp/$FILE_NAME.zip" -stack_name="layer-$NAME" - -echo "Packaging..." -if [ -f "$zip_path" ] -then - rm $zip_path - echo "Removed the old package." -fi - - -cd $NAME -zip -qr $zip_path * -cd .. -echo "Created a new package in $zip_path." - -aws s3 cp $zip_path s3://$BUCKET_NAME/lambda_layers/ --profile $PROFILE -echo "Uploaded $zip_path to S3 bucket to $BUCKET_NAME." - -if test -z "$ENVIRONMENT" -then - env_name="prod" -else - env_name=$ENVIRONMENT -fi - -echo "Deploying stack $stack_name with CloudFormation for environment ${env_name}" -aws cloudformation package --template-file $NAME/$NAME.yaml --output-template-file deployment-output.yaml \ - --s3-bucket $BUCKET_NAME --profile $PROFILE -echo "Created package from CloudFormation template" - -echo "Calling for CloudFormation to deploy" -aws cloudformation deploy --template-file ./deployment-output.yaml --stack-name $stack_name \ - --parameter-overrides SoswVersion=$VERSION EnvironmentName=$env_name FileName=$FILE_NAME.zip \ - --capabilities CAPABILITY_NAMED_IAM --profile $PROFILE - -echo "Finished with stack $stack_name. If there were changes in the YAML, they should be applied." diff --git a/layers/sosw/samconfig.toml b/layers/sosw/samconfig.toml deleted file mode 100644 index 5f00992..0000000 --- a/layers/sosw/samconfig.toml +++ /dev/null @@ -1,11 +0,0 @@ -version = 0.1 - -[default.deploy.parameters] -stack_name = "sosw-layer" -s3_prefix = "sosw-layer" -region = "us-west-2" -resolve_s3 = true -confirm_changeset = true -force-upload = true -capabilities = "CAPABILITY_IAM" - diff --git a/layers/sosw/sosw.yaml b/layers/sosw/sosw.yaml deleted file mode 100644 index 374ba14..0000000 --- a/layers/sosw/sosw.yaml +++ /dev/null @@ -1,25 +0,0 @@ -AWSTemplateFormatVersion: "2010-09-09" -Description: "sosw - Latest version" - -Parameters: - FileName: - Description: Custom name of file to use. - Type: String - Default: sosw-stable.zip - -Resources: - SoswLayer: - Type: "AWS::Lambda::LayerVersion" - DeletionPolicy: Retain - UpdateReplacePolicy: Retain - Description: "sosw, bson, aws_lambda_powertools, aws_xray_sdk, requests" - Properties: - CompatibleRuntimes: - - python3.10 - - python3.11 - Content: - S3Bucket: !Sub "app-control-${AWS::AccountId}" - S3Key: !Sub "lambda_layers/${FileName}" - Description: "sosw" - LayerName: "sosw" - LicenseInfo: "MIT" diff --git a/layers/sosw/template.yaml b/layers/sosw/template.yaml deleted file mode 100644 index e801c17..0000000 --- a/layers/sosw/template.yaml +++ /dev/null @@ -1,24 +0,0 @@ -AWSTemplateFormatVersion: 2010-09-09 -Transform: AWS::Serverless-2016-10-31 -Description: SOSW layer - -Parameters: - FileName: - Description: Custom name of file to use. - Type: String - Default: sosw-stable.zip - -Resources: - SoswLayer: - Type: "AWS::Lambda::LayerVersion" - Description: "sosw, bson, aws_lambda_powertools, aws_xray_sdk, requests" - Properties: - CompatibleRuntimes: - - python3.10 - - python3.11 - Content: - S3Bucket: !Sub "app-control-${AWS::AccountId}" - S3Key: !Sub "lambda_layers/${FileName}" - Description: "sosw" - LayerName: "sosw" - LicenseInfo: "MIT" diff --git a/layers_version_changer/layers_versions_changer.py b/layers_version_changer/layers_versions_changer.py deleted file mode 100644 index c719c33..0000000 --- a/layers_version_changer/layers_versions_changer.py +++ /dev/null @@ -1,58 +0,0 @@ -import os -import boto3 - -__author__ = "Valentin Bakin" - -client = boto3.client('lambda') -layer_change_key = 'SOSW_LAYER_PLACEHOLDER' -test_layer_name = "sosw" -resource_types = ['AWS::Serverless::Function', 'AWS::Lambda::Function'] - - -def main(): - try: - response = get_lambda_layer_version(test_layer_name) - print(response) - if response: - change_lambda_layer_version(response, layer_change_key, resource_types) - except RuntimeError as r: - raise RuntimeError(f"Something went wrong: {r}") - - -def get_lambda_layer_version(layer_name: str) -> dict: - response = client.list_layer_versions(LayerName=layer_name) - return response - - -def change_lambda_layer_version(response: dict, change_key: str, resources_to_change: list[str]): - layer_version = response['LayerVersions'][0]['Version'] - print(f"Layer version: {layer_version}") - print(f"Change key: {change_key}") - # for root, dirs, files in os.walk('.'): # Use it only if you need scan files in all repository - script_dir = os.path.dirname(__file__) - for file in os.listdir(script_dir): - if file.endswith('.yaml'): - # file_path = os.path.join(root, file) # Use it only if you need scan files in all repository - # for file in files: - file_path = os.path.join(script_dir, file) - - with open(file_path, 'r') as f: - data = f.read() - if not contains_resource_type(data, resources_to_change): - print("Resource to change not found") - continue - - with open(file_path, "w") as f: - f.write(data.replace(change_key, str(layer_version))) - - -def contains_resource_type(data: str, resource_types_to_check: list[str]) -> bool: - for resource_type in resource_types_to_check: - if resource_type in data: - print(f"Found resource type in line: {resource_type}") - return True - return False - - -if __name__ == "__main__": - main() diff --git a/layers_version_changer/samconfig.toml b/layers_version_changer/samconfig.toml deleted file mode 100644 index 2912436..0000000 --- a/layers_version_changer/samconfig.toml +++ /dev/null @@ -1,10 +0,0 @@ -version = 0.1 - -[default.deploy.parameters] -stack_name = "first-lambda" -s3_prefix = "first-lambda" -region = "us-west-2" -resolve_s3 = true -confirm_changeset = true -force-upload = true -capabilities = "CAPABILITY_IAM" diff --git a/layers_version_changer/src/Makefile b/layers_version_changer/src/Makefile deleted file mode 100644 index d43ff00..0000000 --- a/layers_version_changer/src/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -build-YourFirstLambdaWithSOSW: - pwd - ls -la - python -m ..layers_versions_changer ./ diff --git a/layers_version_changer/src/app.py b/layers_version_changer/src/app.py deleted file mode 100644 index f9ca605..0000000 --- a/layers_version_changer/src/app.py +++ /dev/null @@ -1,31 +0,0 @@ -import os - -from aws_lambda_powertools import Logger -from aws_xray_sdk.core import patch_all -from sosw.app import Processor as SoswProcessor, get_lambda_handler, LambdaGlobals - - -logger = Logger() -patch_all() - -IS_PROD = os.getenv('env') == 'prod' # TEMPORARY - - -class Processor(SoswProcessor): - DEFAULT_CONFIG = { - } - - - def get_config(self, name): - pass - - - def __call__(self, event): - """ - Call the Processor - """ - logger.info("Hello from Lambda!") - - -global_vars = LambdaGlobals() -lambda_handler = get_lambda_handler(Processor, global_vars) diff --git a/layers_version_changer/template.yaml b/layers_version_changer/template.yaml deleted file mode 100644 index 0807310..0000000 --- a/layers_version_changer/template.yaml +++ /dev/null @@ -1,35 +0,0 @@ -AWSTemplateFormatVersion: 2010-09-09 -Transform: AWS::Serverless-2016-10-31 -Description: Sample template to use SAM with Makefile - -Parameters: - CustomFunctionName: - Type: String - Default: your_happy_lambda - Stage: - Type: String - Default: dev - -Conditions: - isProd: !Equals [ !Ref Stage, prod ] - -Resources: - YourFirstLambdaWithSOSW: - Type: AWS::Serverless::Function - Properties: - FunctionName: !If [ isProd, !Sub "${CustomFunctionName}", !Sub "${Stage}_${CustomFunctionName}" ] - CodeUri: src - Description: Your sample lambda function - Handler: app.lambda_handler - MemorySize: 256 - PackageType: Zip - Runtime: python3.11 - Timeout: 3 - Tracing: Active - Metadata: - BuildMethod: makefile - - Layers: - - !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:layer:sosw:SOSW_LAYER_PLACEHOLDER" - Tags: - Env: !Ref Stage diff --git a/layers_version_changer/test1.yaml b/layers_version_changer/test1.yaml deleted file mode 100644 index 1fae7d9..0000000 --- a/layers_version_changer/test1.yaml +++ /dev/null @@ -1,62 +0,0 @@ -AWSTemplateFormatVersion: 2010-09-09 -Transform: AWS::Serverless-2016-10-31 -Description: Event bus to DDb www-sosw-form-records - -Parameters: - CustomFunctionName: - Type: String - Default: web_form_processor - CustomTableName: - Type: String - Default: www_sosw_form_records - Stage: - Type: String - Default: dev - -Conditions: - isProd: !Equals [ !Ref Stage, prod ] - -Resources: - LambdaWebFormProcessor: - Type: AWS::Serverless::Function - Properties: - FunctionName: !If [ isProd, !Sub "${CustomFunctionName}", !Sub "${Stage}_${CustomFunctionName}" ] - CodeUri: src/ - Description: Event bus to DDb www_sosw_form_records - Handler: app.lambda_handler - MemorySize: 256 - PackageType: Zip - Runtime: python3.11 - Timeout: 3 - Tracing: Active - - Events: - Trigger: - Type: EventBridgeRule - Properties: - EventBusName: 'default' - Pattern: - source: - - 'ApiGw' - detail-type: - - 'www-form-apply' - - 'www-form-contact-us' - - 'www-form-event-registration' - - 'www-form-event-testcloud' - Policies: - - SNSPublishMessagePolicy: - TopicName: !If [ isProd, "web-forms-sosw", !Sub "${Stage}-web-forms-sosw" ] - - DynamoDBCrudPolicy: - TableName: !If [ isProd, !Sub "${CustomTableName}", !Sub "${Stage}_${CustomTableName}" ] - Layers: - - !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:layer:sosw:SOSW_LAYER_PLACEHOLDER" - Tags: - Env: !Ref Stage - - FunctionResourcePermission: - Type: 'AWS::Lambda::Permission' - Properties: - Action: 'lambda:InvokeFunction' - Principal: events.amazonaws.com - FunctionName: !Ref LambdaWebFormProcessor - SourceArn: !Sub 'arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:*/*' diff --git a/layers_version_changer/test2.yaml b/layers_version_changer/test2.yaml deleted file mode 100644 index db60712..0000000 --- a/layers_version_changer/test2.yaml +++ /dev/null @@ -1,50 +0,0 @@ -AWSTemplateFormatVersion: "2010-09-09" -Description: sys_lambda_env_setter - -Resources: - - SysLambdaEnvSetterRole: - Type: "AWS::IAM::Role" - Properties: - AssumeRolePolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: Allow - Principal: - Service: - - "lambda.amazonaws.com" - Action: - - "sts:AssumeRole" - - Path: "/" - ManagedPolicyArns: - - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - - Policies: - - PolicyName: "SysLambdaEnvSetterCustom" - PolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: Allow - Action: - - lambda:GetFunctionConfiguration - - lambda:UpdateFunctionConfiguration - Resource: '*' - - RoleName: "sys_lambda_env_setter_role" - - SysLambdaEnvSetterFunction: - Type: AWS::Lambda::Function - Properties: - FunctionName: sys_lambda_env_setter - Runtime: python3.11 - Role: !GetAtt SysLambdaEnvSetterRole.Arn - Handler: app.lambda_handler - Code: - - S3Bucket: !Sub "sosw-s3-${AWS::AccountId}" - S3Key: lambda_packages/sys_lambda_env_setter.zip - Layers: - - !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:layer:sosw:SOSW_LAYER_PLACEHOLDER" - - Description: Set environmental variables for Lambda functions diff --git a/layers_version_changer/test3.yaml b/layers_version_changer/test3.yaml deleted file mode 100644 index 6cf1378..0000000 --- a/layers_version_changer/test3.yaml +++ /dev/null @@ -1,24 +0,0 @@ -AWSTemplateFormatVersion: 2010-09-09 -Transform: AWS::Serverless-2016-10-31 -Description: SOSW layer - -Parameters: - FileName: - Description: Custom name of file to use. - Type: String - Default: sosw-stable.zip - -Resources: - SoswLayer: - Type: "AWS::Lambda::LayerVersion" - Description: "sosw, bson, aws_lambda_powertools, aws_xray_sdk, requests" - Properties: - CompatibleRuntimes: - - python3.10 - - python3.11 - Content: - S3Bucket: !Sub "app-control-${AWS::SOSW_LAYER_PLACEHOLDER}" - S3Key: !Sub "lambda_layers/${SOSW_LAYER_PLACEHOLDER}" - Description: "sosw" - LayerName: "sosw" - LicenseInfo: "MIT" From 1f0019b85cc39b1cb5c45daba4281fb081a32dfb Mon Sep 17 00:00:00 2001 From: kreanimator Date: Tue, 11 Jun 2024 15:59:03 +0300 Subject: [PATCH 18/18] Most recent image converter --- .../{sosw_image_converter => webp_image_converter}/samconfig.toml | 0 helpers/{sosw_image_converter => webp_image_converter}/src/app.py | 0 .../src/requirements.txt | 0 .../{sosw_image_converter => webp_image_converter}/template.yaml | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename helpers/{sosw_image_converter => webp_image_converter}/samconfig.toml (100%) rename helpers/{sosw_image_converter => webp_image_converter}/src/app.py (100%) rename helpers/{sosw_image_converter => webp_image_converter}/src/requirements.txt (100%) rename helpers/{sosw_image_converter => webp_image_converter}/template.yaml (100%) diff --git a/helpers/sosw_image_converter/samconfig.toml b/helpers/webp_image_converter/samconfig.toml similarity index 100% rename from helpers/sosw_image_converter/samconfig.toml rename to helpers/webp_image_converter/samconfig.toml diff --git a/helpers/sosw_image_converter/src/app.py b/helpers/webp_image_converter/src/app.py similarity index 100% rename from helpers/sosw_image_converter/src/app.py rename to helpers/webp_image_converter/src/app.py diff --git a/helpers/sosw_image_converter/src/requirements.txt b/helpers/webp_image_converter/src/requirements.txt similarity index 100% rename from helpers/sosw_image_converter/src/requirements.txt rename to helpers/webp_image_converter/src/requirements.txt diff --git a/helpers/sosw_image_converter/template.yaml b/helpers/webp_image_converter/template.yaml similarity index 100% rename from helpers/sosw_image_converter/template.yaml rename to helpers/webp_image_converter/template.yaml