├── README.md ├── lambda ├── .serverless │ ├── cloudformation-template-create-stack.json │ ├── cloudformation-template-update-stack.json │ ├── selenium-lambda.zip │ └── serverless-state.json ├── handler.py └── serverless.yml └── seleniumLayer ├── .gitignore └── serverless.yml /README.md: -------------------------------------------------------------------------------- 1 | # Selenium UI testing with AWS Lambda Layers 2 | 3 | This is an example of set up Selenium testing with AWS lambda layers Python3.6 4 | 5 | ### File Structure 6 | 7 | - seleniumLayer - Selenium 8 | - lambda 9 | 10 | ```bash 11 | ── /seleniumLayer/ # lambda layers 12 | ├── /selenium lambda layer of selenium lib 13 | │ └──/python/ # python libs 14 | │ └── /lib/ 15 | │ └── /python3.6/* 16 | ├── /chromedriver/ # lambda layer of headless Chrome 17 | │ ├── /chromedriver # chrome driver 18 | │ └── /headless-chromium # headless chrome binary 19 | └── /serverless.yaml 20 | ── /lambda/ # lambda function 21 | ├── /handler.py # source code of lambda function 22 | └── /serverless.yaml # serverless config 23 | ``` 24 | ### Stack 25 | 26 | - Python3.6 27 | - Selenium2.37 28 | - [ChromeDriver2.37](https://sites.google.com/a/chromium.org/chromedriver/downloads) 29 | - [Serverless Chrome v1.0.0.41 ](https://github.com/adieuadieu/serverless-chrome/releases?after=v1.0.0-46) 30 | 31 | 32 | ### Install 33 | Go to root directory of project 34 | ```buildoutcfg 35 | # download Selenium 2.37 36 | $ pip3.6 install -t seleniumLayer/selenium/python/lib/python3.6/site-packages selenium==2.37 37 | 38 | # download chrome driver 39 | $ cd seleniumLayer 40 | $ mkdir chromedriver 41 | $ cd chromedriver 42 | $ curl -SL https://chromedriver.storage.googleapis.com/2.37/chromedriver_linux64.zip > chromedriver.zip 43 | $ unzip chromedriver.zip 44 | $ rm chromedriver.zip 45 | 46 | # download chrome binary 47 | $ curl -SL https://github.com/adieuadieu/serverless-chrome/releases/download/v1.0.0-41/stable-headless-chromium-amazonlinux-2017-03.zip > headless-chromium.zip 48 | $ unzip headless-chromium.zip 49 | $ rm headless-chromium.zip 50 | 51 | ``` 52 | 53 | ### Install serverless command 54 | Do this if you don't have serverless in your machine yet 55 | ```Try running, 56 | npm config set prefix /usr/local 57 | and then, 58 | npm i -g serverless 59 | ``` 60 | 61 | ### Deploy Lambda Layers 62 | Go to root directory of project 63 | ```buildoutcfg 64 | $ cd seleniumLayer 65 | $ sls deploy 66 | ``` 67 | 68 | ### Deploy Lambda Function 69 | Go to root directory of project 70 | ```buildoutcfg 71 | $ cd lambda 72 | $ sls deploy 73 | ``` 74 | 75 | ### Start Testing 76 | Go to root directory of project 77 | ```buildoutcfg 78 | $ cd lambda 79 | $ sls invoke --function hello 80 | ``` 81 | -------------------------------------------------------------------------------- /lambda/.serverless/cloudformation-template-create-stack.json: -------------------------------------------------------------------------------- 1 | { 2 | "AWSTemplateFormatVersion": "2010-09-09", 3 | "Description": "The AWS CloudFormation template for this Serverless application", 4 | "Resources": { 5 | "ServerlessDeploymentBucket": { 6 | "Type": "AWS::S3::Bucket" 7 | } 8 | }, 9 | "Outputs": { 10 | "ServerlessDeploymentBucketName": { 11 | "Value": { 12 | "Ref": "ServerlessDeploymentBucket" 13 | } 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /lambda/.serverless/cloudformation-template-update-stack.json: -------------------------------------------------------------------------------- 1 | { 2 | "AWSTemplateFormatVersion": "2010-09-09", 3 | "Description": "The AWS CloudFormation template for this Serverless application", 4 | "Resources": { 5 | "ServerlessDeploymentBucket": { 6 | "Type": "AWS::S3::Bucket" 7 | }, 8 | "HelloLogGroup": { 9 | "Type": "AWS::Logs::LogGroup", 10 | "Properties": { 11 | "LogGroupName": "/aws/lambda/selenium-lambda-dev-hello" 12 | } 13 | }, 14 | "IamRoleLambdaExecution": { 15 | "Type": "AWS::IAM::Role", 16 | "Properties": { 17 | "AssumeRolePolicyDocument": { 18 | "Version": "2012-10-17", 19 | "Statement": [ 20 | { 21 | "Effect": "Allow", 22 | "Principal": { 23 | "Service": [ 24 | "lambda.amazonaws.com" 25 | ] 26 | }, 27 | "Action": [ 28 | "sts:AssumeRole" 29 | ] 30 | } 31 | ] 32 | }, 33 | "Policies": [ 34 | { 35 | "PolicyName": { 36 | "Fn::Join": [ 37 | "-", 38 | [ 39 | "dev", 40 | "selenium-lambda", 41 | "lambda" 42 | ] 43 | ] 44 | }, 45 | "PolicyDocument": { 46 | "Version": "2012-10-17", 47 | "Statement": [ 48 | { 49 | "Effect": "Allow", 50 | "Action": [ 51 | "logs:CreateLogStream" 52 | ], 53 | "Resource": [ 54 | { 55 | "Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/selenium-lambda-dev-hello:*" 56 | } 57 | ] 58 | }, 59 | { 60 | "Effect": "Allow", 61 | "Action": [ 62 | "logs:PutLogEvents" 63 | ], 64 | "Resource": [ 65 | { 66 | "Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/selenium-lambda-dev-hello:*:*" 67 | } 68 | ] 69 | } 70 | ] 71 | } 72 | } 73 | ], 74 | "Path": "/", 75 | "RoleName": { 76 | "Fn::Join": [ 77 | "-", 78 | [ 79 | "selenium-lambda", 80 | "dev", 81 | "ap-southeast-2", 82 | "lambdaRole" 83 | ] 84 | ] 85 | } 86 | } 87 | }, 88 | "HelloLambdaFunction": { 89 | "Type": "AWS::Lambda::Function", 90 | "Properties": { 91 | "Code": { 92 | "S3Bucket": { 93 | "Ref": "ServerlessDeploymentBucket" 94 | }, 95 | "S3Key": "serverless/selenium-lambda/dev/1547610682694-2019-01-16T03:51:22.694Z/selenium-lambda.zip" 96 | }, 97 | "FunctionName": "selenium-lambda-dev-hello", 98 | "Handler": "handler.hello", 99 | "MemorySize": 1024, 100 | "Role": { 101 | "Fn::GetAtt": [ 102 | "IamRoleLambdaExecution", 103 | "Arn" 104 | ] 105 | }, 106 | "Runtime": "python3.6", 107 | "Timeout": 900, 108 | "Layers": [ 109 | "arn:aws:lambda:ap-southeast-2:687416365397:layer:selenium:32", 110 | "arn:aws:lambda:ap-southeast-2:687416365397:layer:chromedriver:13" 111 | ] 112 | }, 113 | "DependsOn": [ 114 | "HelloLogGroup", 115 | "IamRoleLambdaExecution" 116 | ] 117 | }, 118 | "HelloLambdaVersionhPN0gscNhYCtCQfiOZ8ilfVqsB4yHYtcNmjgUL3oQg": { 119 | "Type": "AWS::Lambda::Version", 120 | "DeletionPolicy": "Retain", 121 | "Properties": { 122 | "FunctionName": { 123 | "Ref": "HelloLambdaFunction" 124 | }, 125 | "CodeSha256": "1bMcqsPZ7iDZCYLnYhHvF9ugtYhjAwTLIAsKKpHvcbU=" 126 | } 127 | } 128 | }, 129 | "Outputs": { 130 | "ServerlessDeploymentBucketName": { 131 | "Value": { 132 | "Ref": "ServerlessDeploymentBucket" 133 | } 134 | }, 135 | "HelloLambdaFunctionQualifiedArn": { 136 | "Description": "Current Lambda function version", 137 | "Value": { 138 | "Ref": "HelloLambdaVersionhPN0gscNhYCtCQfiOZ8ilfVqsB4yHYtcNmjgUL3oQg" 139 | } 140 | } 141 | } 142 | } -------------------------------------------------------------------------------- /lambda/.serverless/selenium-lambda.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yai333/Selenium-UI-testing-with-AWS-Lambda-Layers/1b39f78faf9d1b984dc5112cfcec88d3e498ba7c/lambda/.serverless/selenium-lambda.zip -------------------------------------------------------------------------------- /lambda/.serverless/serverless-state.json: -------------------------------------------------------------------------------- 1 | { 2 | "service": { 3 | "service": "selenium-lambda", 4 | "serviceObject": { 5 | "name": "selenium-lambda" 6 | }, 7 | "provider": { 8 | "stage": "dev", 9 | "region": "ap-southeast-2", 10 | "variableSyntax": "\\${([ ~:a-zA-Z0-9._'\",\\-\\/\\(\\)]+?)}", 11 | "name": "aws", 12 | "runtime": "python3.6", 13 | "timeout": 900, 14 | "versionFunctions": true, 15 | "remoteFunctionData": null, 16 | "compiledCloudFormationTemplate": { 17 | "AWSTemplateFormatVersion": "2010-09-09", 18 | "Description": "The AWS CloudFormation template for this Serverless application", 19 | "Resources": { 20 | "ServerlessDeploymentBucket": { 21 | "Type": "AWS::S3::Bucket" 22 | }, 23 | "HelloLogGroup": { 24 | "Type": "AWS::Logs::LogGroup", 25 | "Properties": { 26 | "LogGroupName": "/aws/lambda/selenium-lambda-dev-hello" 27 | } 28 | }, 29 | "IamRoleLambdaExecution": { 30 | "Type": "AWS::IAM::Role", 31 | "Properties": { 32 | "AssumeRolePolicyDocument": { 33 | "Version": "2012-10-17", 34 | "Statement": [ 35 | { 36 | "Effect": "Allow", 37 | "Principal": { 38 | "Service": [ 39 | "lambda.amazonaws.com" 40 | ] 41 | }, 42 | "Action": [ 43 | "sts:AssumeRole" 44 | ] 45 | } 46 | ] 47 | }, 48 | "Policies": [ 49 | { 50 | "PolicyName": { 51 | "Fn::Join": [ 52 | "-", 53 | [ 54 | "dev", 55 | "selenium-lambda", 56 | "lambda" 57 | ] 58 | ] 59 | }, 60 | "PolicyDocument": { 61 | "Version": "2012-10-17", 62 | "Statement": [ 63 | { 64 | "Effect": "Allow", 65 | "Action": [ 66 | "logs:CreateLogStream" 67 | ], 68 | "Resource": [ 69 | { 70 | "Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/selenium-lambda-dev-hello:*" 71 | } 72 | ] 73 | }, 74 | { 75 | "Effect": "Allow", 76 | "Action": [ 77 | "logs:PutLogEvents" 78 | ], 79 | "Resource": [ 80 | { 81 | "Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/selenium-lambda-dev-hello:*:*" 82 | } 83 | ] 84 | } 85 | ] 86 | } 87 | } 88 | ], 89 | "Path": "/", 90 | "RoleName": { 91 | "Fn::Join": [ 92 | "-", 93 | [ 94 | "selenium-lambda", 95 | "dev", 96 | "ap-southeast-2", 97 | "lambdaRole" 98 | ] 99 | ] 100 | } 101 | } 102 | }, 103 | "HelloLambdaFunction": { 104 | "Type": "AWS::Lambda::Function", 105 | "Properties": { 106 | "Code": { 107 | "S3Bucket": { 108 | "Ref": "ServerlessDeploymentBucket" 109 | }, 110 | "S3Key": "serverless/selenium-lambda/dev/1547610682694-2019-01-16T03:51:22.694Z/selenium-lambda.zip" 111 | }, 112 | "FunctionName": "selenium-lambda-dev-hello", 113 | "Handler": "handler.hello", 114 | "MemorySize": 1024, 115 | "Role": { 116 | "Fn::GetAtt": [ 117 | "IamRoleLambdaExecution", 118 | "Arn" 119 | ] 120 | }, 121 | "Runtime": "python3.6", 122 | "Timeout": 900, 123 | "Layers": [ 124 | "arn:aws:lambda:ap-southeast-2:687416365397:layer:selenium:32", 125 | "arn:aws:lambda:ap-southeast-2:687416365397:layer:chromedriver:13" 126 | ] 127 | }, 128 | "DependsOn": [ 129 | "HelloLogGroup", 130 | "IamRoleLambdaExecution" 131 | ] 132 | }, 133 | "HelloLambdaVersionhPN0gscNhYCtCQfiOZ8ilfVqsB4yHYtcNmjgUL3oQg": { 134 | "Type": "AWS::Lambda::Version", 135 | "DeletionPolicy": "Retain", 136 | "Properties": { 137 | "FunctionName": { 138 | "Ref": "HelloLambdaFunction" 139 | }, 140 | "CodeSha256": "1bMcqsPZ7iDZCYLnYhHvF9ugtYhjAwTLIAsKKpHvcbU=" 141 | } 142 | } 143 | }, 144 | "Outputs": { 145 | "ServerlessDeploymentBucketName": { 146 | "Value": { 147 | "Ref": "ServerlessDeploymentBucket" 148 | } 149 | }, 150 | "HelloLambdaFunctionQualifiedArn": { 151 | "Description": "Current Lambda function version", 152 | "Value": { 153 | "Ref": "HelloLambdaVersionhPN0gscNhYCtCQfiOZ8ilfVqsB4yHYtcNmjgUL3oQg" 154 | } 155 | } 156 | } 157 | }, 158 | "coreCloudFormationTemplate": { 159 | "AWSTemplateFormatVersion": "2010-09-09", 160 | "Description": "The AWS CloudFormation template for this Serverless application", 161 | "Resources": { 162 | "ServerlessDeploymentBucket": { 163 | "Type": "AWS::S3::Bucket" 164 | } 165 | }, 166 | "Outputs": { 167 | "ServerlessDeploymentBucketName": { 168 | "Value": { 169 | "Ref": "ServerlessDeploymentBucket" 170 | } 171 | } 172 | } 173 | }, 174 | "vpc": {} 175 | }, 176 | "pluginsData": {}, 177 | "functions": { 178 | "hello": { 179 | "handler": "handler.hello", 180 | "layers": [ 181 | "arn:aws:lambda:ap-southeast-2:687416365397:layer:selenium:32", 182 | "arn:aws:lambda:ap-southeast-2:687416365397:layer:chromedriver:13" 183 | ], 184 | "events": [], 185 | "name": "selenium-lambda-dev-hello", 186 | "package": {}, 187 | "memory": 1024, 188 | "timeout": 900, 189 | "runtime": "python3.6", 190 | "vpc": {} 191 | } 192 | }, 193 | "layers": {}, 194 | "artifact": "/Users/neaminational/works/Selenium-UI-testing-with-AWS-Lambda-Layers/lambda/.serverless/selenium-lambda.zip" 195 | }, 196 | "package": { 197 | "artifactDirectoryName": "serverless/selenium-lambda/dev/1547610682694-2019-01-16T03:51:22.694Z", 198 | "artifact": "selenium-lambda.zip" 199 | } 200 | } -------------------------------------------------------------------------------- /lambda/handler.py: -------------------------------------------------------------------------------- 1 | from selenium import webdriver 2 | from selenium.webdriver.chrome.options import Options 3 | 4 | def hello(event, context): 5 | options = Options() 6 | options.binary_location = '/opt/headless-chromium' 7 | options.add_argument('--headless') 8 | options.add_argument('--no-sandbox') 9 | options.add_argument('--single-process') 10 | options.add_argument('--disable-dev-shm-usage') 11 | 12 | driver = webdriver.Chrome('/opt/chromedriver',chrome_options=options) 13 | 14 | driver.get('https://www.neaminational.org.au/') 15 | body = f"Headless Chrome Initialized, Page title: {driver.title}" 16 | 17 | driver.close(); 18 | driver.quit(); 19 | 20 | response = { 21 | "statusCode": 200, 22 | "body": body 23 | } 24 | 25 | return response 26 | 27 | -------------------------------------------------------------------------------- /lambda/serverless.yml: -------------------------------------------------------------------------------- 1 | 2 | service: selenium-lambda 3 | 4 | 5 | provider: 6 | name: aws 7 | runtime: python3.6 8 | region: ap-southeast-2 9 | timeout: 900 10 | 11 | 12 | functions: 13 | hello: 14 | handler: handler.hello 15 | layers: 16 | - ${cf:selenium-layer-dev.SeleniumLayerExport} 17 | - ${cf:selenium-layer-dev.ChromedriverLayerExport} -------------------------------------------------------------------------------- /seleniumLayer/.gitignore: -------------------------------------------------------------------------------- 1 | # Distribution / packaging 2 | .Python 3 | env/ 4 | build/ 5 | develop-eggs/ 6 | dist/ 7 | downloads/ 8 | eggs/ 9 | .eggs/ 10 | lib/ 11 | lib64/ 12 | parts/ 13 | sdist/ 14 | var/ 15 | *.egg-info/ 16 | .installed.cfg 17 | *.egg 18 | 19 | # Serverless directories 20 | .serverless -------------------------------------------------------------------------------- /seleniumLayer/serverless.yml: -------------------------------------------------------------------------------- 1 | 2 | service: selenium-layer 3 | 4 | 5 | provider: 6 | name: aws 7 | runtime: python3.6 8 | region: ap-southeast-2 9 | timeout: 900 10 | 11 | layers: 12 | selenium: 13 | path: selenium 14 | compatibleRuntimes: 15 | - "python3.6" 16 | chromedriver: 17 | path: chromedriver 18 | description: chrome driver layer 19 | compatibleRuntimes: 20 | - "python3.6" 21 | resources: 22 | Outputs: 23 | SeleniumLayerExport: 24 | Value: 25 | Ref: SeleniumLambdaLayer 26 | Export: 27 | Name: SeleniumLambdaLayer 28 | ChromedriverLayerExport: 29 | Value: 30 | Ref: ChromedriverLambdaLayer 31 | Export: 32 | Name: ChromedriverLambdaLayer --------------------------------------------------------------------------------