├── .gitignore ├── LICENSE ├── README.md ├── bin └── slsperf.js ├── index.js ├── package.json └── providers ├── alphabet ├── index.js └── serverless.yml ├── amazon ├── handler.js └── serverless.yml ├── ibm ├── handler.js └── serverless.yml ├── index.js ├── microsoft ├── .gitignore ├── handler.js └── serverless.yml └── prototype ├── index.js └── serverless.yml /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | *.log 3 | npm-debug.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | dist 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 28 | node_modules 29 | 30 | # IDE stuff 31 | **/.idea 32 | 33 | # OS stuff 34 | .DS_Store 35 | .tmp 36 | 37 | # Serverless stuff 38 | .serverless 39 | admin.env 40 | .env 41 | tmp 42 | .coveralls.yml 43 | tmpdirs-serverless 44 | 45 | # ESLint cache 46 | .eslintcache 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Garrett McGrath 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Serverless Performance 2 | 3 | This package allows you to measure performance characteristics of serverless platforms such as AWS Lambda, Azure Functions, Google Cloud Functions, and IBM OpenWhisk. 4 | 5 | The Serverless Framework is used as the deployment engine for this package. 6 | 7 | Note: this project is in its early stages, proceed cautiously. If you encounter issues, please report them and they will be fixed promptly. 8 | 9 | ## Getting Started 10 | 11 | ### Install Package 12 | 13 | This package can be installed via npm: 14 | 15 | ```bash 16 | npm install serverless-performance 17 | ``` 18 | 19 | ### Configure Platforms 20 | 21 | You must configure credentials before using any of the supported platforms: 22 | 23 | * **AWS Lambda** can be configured by following this guide on the Serverless Framework website. 24 | * **IBM OpenWhisk** can be configured by following this guide on the Serverless Framework website. 25 | * **Azure Functions** can be configured by following this guide on the Serverless Framework website. 26 | * **Google Cloud Functions** can be configured by creating a `keyfile.json` file as described here. 27 | * **Prototype Platform** also compatible with a new prototype platform using a `prototypeServiceBaseUri` environment variable. 28 | 29 | ### Command Line Interface 30 | 31 | A command line tool for this package is available at `bin/slsperf.js`: 32 | 33 | ``` 34 | Usage: slsperf [options] 35 | 36 | Options: 37 | 38 | -h, --help output usage information 39 | -p, --provider Serverless platform to target (amazon, ibm, microsoft, google, prototype) 40 | --project Name of the project to deploy Google Cloud Functions to 41 | --credentials Path of the file holding Google Cloud credentials 42 | -d, --duration Number of milliseconds the function should execute before returning 43 | -b, --backoff Runs a backoff test on the specified provider 44 | -c, --concurrency Runs a concurrency test on the specified provider 45 | -k, --keep-alive Maintains an invocation call to the specified provider 46 | -i, --iterations Number of times to run the test 47 | 48 | Examples: 49 | 50 | node slsperf.js -p amazon -d 0 -c -i 1 . 51 | ``` 52 | 53 | ## Example Results 54 | 55 | The following are example results of running the performance tool on various serverless platforms. Details of the experimental setup can be found here. 56 | 57 | ### Concurrency Test 58 | 59 |

60 | Concurrency Test Results 61 |

62 | 63 | The concurrency test is designed to measure the ability of serverless platforms to performantly scale and execute a function. The tool maintains invocation calls to the test function by reissuing each request immediately after receiving the response from the previous call. The test begins by maintaining a single invocation call in this way, and every 10 seconds adds an additional concurrent call, up to a maximum of 15 concurrent requests to the test function. 64 | 65 | ### Backoff Test 66 | 67 |

68 | Backoff Test Results 69 |

70 | 71 | The backoff test is designed to study the cold start times and expiration behaviors of function instances in the various platforms. The backoff test sends single invocation call to the test function at increasing intervals, ranging from one to thirty minutes. 72 | -------------------------------------------------------------------------------- /bin/slsperf.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // TODO: this script sucks; usage on empty, actual validation, ect. 4 | 5 | const fs = require('fs'); 6 | const path = require('path'); 7 | 8 | const program = require('commander'); 9 | 10 | const slsperf = require(path.join(__dirname, '..')); 11 | 12 | let config = { 13 | provider: {}, 14 | function: {}, 15 | test: {} 16 | }; 17 | 18 | let latencyTest = { 19 | type: 'latency', 20 | delayCallback: i => 60000 * (Math.floor((i - 1) / 5) + 1), 21 | maxDelay: 1800000 22 | }; 23 | 24 | let throughputTest = { 25 | type: 'throughput', 26 | width: 15, 27 | duration: 10000, 28 | }; 29 | 30 | let keepAliveTest = { 31 | type: 'latency', 32 | delayCallback: i => i ? 0 : 100, 33 | maxDelay: 101 34 | }; 35 | 36 | program 37 | .usage('[options] ') 38 | .option( 39 | '-p, --provider ', 40 | 'Serverless platform to target (amazon, ibm, microsoft, google, prototype)', 41 | name => config.provider.name = name == 'google' ? 'alphabet' : name) 42 | .option( 43 | '--project ', 44 | 'Name of the project to deploy Google Cloud Functions to', 45 | name => config.provider.project = name) 46 | .option( 47 | '--credentials ', 48 | 'Path of the file holding Google Cloud credentials', 49 | path => config.provider.credentials = path) 50 | .option( 51 | '-d, --duration ', 52 | 'Number of milliseconds the function should execute before returning', 53 | ms => config.function.duration = parseInt(ms)) 54 | .option( 55 | '-b, --backoff', 56 | 'Runs a backoff test on the specified provider', 57 | () => config.test = latencyTest) 58 | .option( 59 | '-c, --concurrency', 60 | 'Runs a concurrency test on the specified provider', 61 | () => config.test = throughputTest) 62 | .option( 63 | '-k, --keep-alive', 64 | 'Runs a keep-alive test on the specified provider', 65 | () => config.test = keepAliveTest) 66 | .option( 67 | '-i, --iterations ', 68 | 'Number of times to run the test', 69 | n => config.test.iterations = parseInt(n)) 70 | .parse(process.argv); 71 | 72 | config.resultsDir = program.args[0]; 73 | 74 | if (!fs.existsSync(config.resultsDir)) { 75 | fs.mkdirSync(config.resultsDir); 76 | } 77 | 78 | fs.writeFileSync(path.join(config.resultsDir, `${config.provider.name}_${config.test.type}_config.json`), JSON.stringify(config)); 79 | 80 | let iteration = 0; 81 | fs.readdirSync(config.resultsDir).forEach(file => { 82 | if (file.match(/[a-z]+_[a-z]+_[0-9]+\.json/) != null) { 83 | let left = file.split('.')[0].split('_'); 84 | if (left[0] == config.provider.name && left[1] == config.test.type) { 85 | let iterationFound = parseInt(left[2]); 86 | if (iterationFound >= iteration) { 87 | console.log(`Iteration ${iterationFound} already complete`); 88 | iteration = iterationFound + 1; 89 | } 90 | } 91 | } 92 | }); 93 | 94 | if (iteration < config.test.iterations) { 95 | console.log(`Starting iteration ${iteration}`); 96 | slsperf.run(config, false, function processOutput(output) { 97 | let outputFile = path.join(config.resultsDir, `${config.provider.name}_${config.test.type}_${iteration}.json`); 98 | fs.writeFileSync(outputFile, JSON.stringify(output, null, 4)); 99 | console.log(`Finished iteration ${iteration}`); 100 | if (++iteration < config.test.iterations) { 101 | console.log(`Starting iteration ${iteration}`); 102 | slsperf.run(config, true, processOutput); 103 | } 104 | }); 105 | } 106 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | 5 | const request = require('request'); 6 | 7 | const providers = require('./providers'); 8 | 9 | function run(config, verifyRemoval, callback) { 10 | providers.prepareFunctions(config.provider, () => { 11 | providers.removeFunctions(config.provider, removeCode => { 12 | if (verifyRemoval && removeCode != 0) { 13 | throw new Error('Function removal failed with code ' + removeCode); 14 | } 15 | 16 | providers.deployFunctions(config.provider, (deployCode, uri) => { 17 | if (deployCode != 0) { 18 | throw new Error('Function deployment failed with code ' + deployCode); 19 | } 20 | 21 | switch (config.test.type) { 22 | case 'latency': 23 | executeLatencyTest( 24 | [], 25 | 0, 26 | config.test.delayCallback, 27 | config.test.maxDelay, 28 | config.function.duration, 29 | uri, 30 | callback); 31 | break; 32 | case 'throughput': 33 | executeThroughputTest( 34 | [], 35 | 1, 36 | config.test.width, 37 | config.test.duration, 38 | config.function.duration, 39 | uri, 40 | callback); 41 | break; 42 | } 43 | }); 44 | }); 45 | }); 46 | } 47 | 48 | function executeLatencyTest(results, delay, delayCallback, maxDelay, functionDuration, uri, callback) { 49 | executeFunction(uri, functionDuration, result => { 50 | results.delay = delay; 51 | results.push(result); 52 | 53 | if (delay >= maxDelay) { 54 | callback(results); 55 | return; 56 | } 57 | 58 | let nextDelay = delay + delayCallback(results.length); 59 | console.log(`Delaying for ${nextDelay}ms`); 60 | 61 | setTimeout( 62 | executeLatencyTest, 63 | nextDelay, 64 | results, nextDelay, delayCallback, maxDelay, functionDuration, uri, callback); 65 | }); 66 | } 67 | 68 | function executeThroughputTest(results, currentWidth, maxWidth, testDuration, functionDuration, uri, callback) { 69 | if (currentWidth > maxWidth) { 70 | callback(results); 71 | return; 72 | } 73 | console.log(`Execution throughput test with width: ${currentWidth}`); 74 | results[currentWidth] = results[currentWidth] || []; 75 | 76 | let startTime = null; 77 | let stageCompleted = false; 78 | 79 | for (let i = 0; i < currentWidth; i++) { 80 | executeFunction(uri, functionDuration, function processResult(result) { 81 | if (startTime == null) { 82 | startTime = Date.now(); 83 | } 84 | 85 | results[currentWidth].push(result); 86 | 87 | if (Date.now() - startTime > testDuration) { 88 | if (!stageCompleted) { 89 | stageCompleted = true; 90 | 91 | executeThroughputTest( 92 | results, 93 | currentWidth + 1, 94 | maxWidth, 95 | testDuration, 96 | functionDuration, 97 | uri, 98 | callback); 99 | } 100 | } else { 101 | executeFunction(uri, functionDuration, processResult); 102 | } 103 | }); 104 | } 105 | } 106 | 107 | function executeFunction(uri, duration, callback) { 108 | request.post({ 109 | url: uri, 110 | body: JSON.stringify({ duration: duration }), 111 | headers: { 'Content-Type': 'application/json' }, 112 | time: true 113 | }, (err, res, body) => { 114 | if (err) throw err; 115 | if (res.statusCode != 200) { 116 | throw new Error('Unexpected response. Status code: ' + res.statusCode + '. Body: ' + body); 117 | } 118 | 119 | let parsedBody = JSON.parse(body); 120 | 121 | let actualDuration = parsedBody.duration != undefined ? parsedBody.duration : parsedBody.output.duration; 122 | let instanceId = parsedBody.id || parsedBody.output.id; 123 | 124 | let overhead = res.elapsedTime - actualDuration; 125 | 126 | console.log(`Execution latency of ${overhead}ms`); 127 | 128 | callback({ 129 | executionOverhead: overhead, 130 | requestStart: res.request.startTime, 131 | requestDuration: res.elapsedTime, 132 | functionDuration: actualDuration, 133 | instanceId: instanceId, 134 | body: parsedBody 135 | }); 136 | }); 137 | } 138 | 139 | module.exports = { 140 | run: run 141 | }; 142 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serverless-performance", 3 | "version": "0.0.46", 4 | "description": "This package allows you to measure performance characteristics of serverless platforms such as AWS Lambda, Azure Functions, IBM OpenWhisk, and Google Cloud Functions.", 5 | "main": "index.js", 6 | "dependencies": { 7 | "request": "^2.79.0", 8 | "serverless": "git+https://github.com/mgarrettm/serverless", 9 | "serverless-azure-functions": "git+https://github.com/serverless/serverless-azure-functions", 10 | "serverless-google-cloudfunctions": "git+https://github.com/serverless/serverless-google-cloudfunctions.git", 11 | "serverless-openwhisk": "^0.4.4", 12 | "serverless-prototype-plugin": "0.0.20", 13 | "url-regex": "^4.0.0", 14 | "yamljs": "^0.2.8" 15 | }, 16 | "devDependencies": {}, 17 | "scripts": { 18 | "test": "echo \"Error: no test specified\" && exit 1" 19 | }, 20 | "bin": { 21 | "serverless-performance": "bin/slsperf.js", 22 | "slsperf": "bin/slsperf.js" 23 | }, 24 | "repository": { 25 | "type": "git", 26 | "url": "git+https://github.com/mgarrettm/serverless-performance.git" 27 | }, 28 | "keywords": [ 29 | "serverless", 30 | "performance" 31 | ], 32 | "author": "Garrett McGrath", 33 | "license": "MIT", 34 | "bugs": { 35 | "url": "https://github.com/mgarrettm/serverless-performance/issues" 36 | }, 37 | "homepage": "https://github.com/mgarrettm/serverless-performance#readme" 38 | } 39 | -------------------------------------------------------------------------------- /providers/alphabet/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var id; 4 | 5 | exports.test = (request, response) => { 6 | var start = Date.now(); 7 | 8 | if (typeof id === 'undefined') { 9 | id = uuid(); 10 | } 11 | 12 | while (Date.now() < start + request.body.duration) {} 13 | 14 | response.status(200).send({ 15 | duration: Date.now() - start, 16 | id: id 17 | }); 18 | }; 19 | 20 | function uuid(a) { 21 | return a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,uuid); 22 | } -------------------------------------------------------------------------------- /providers/alphabet/serverless.yml: -------------------------------------------------------------------------------- 1 | service: test 2 | provider: 3 | name: google 4 | runtime: nodejs4.3 5 | project: your-project-name 6 | credentials: '~/.gcloud/keyfile.json' 7 | plugins: 8 | - serverless-google-cloudfunctions 9 | package: 10 | exclude: ['node_modules/**', .gitignore, package.json, '.git/**'] 11 | functions: 12 | test0: {handler: test, availableMemoryMb: 512, events: [{http: /}]} 13 | -------------------------------------------------------------------------------- /providers/amazon/handler.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var id; 4 | 5 | module.exports.test = (event, context, callback) => { 6 | var start = Date.now(); 7 | 8 | if (typeof id === 'undefined') { 9 | id = uuid(); 10 | } 11 | 12 | let duration = JSON.parse(event.body).duration; 13 | while (Date.now() < start + duration) {} 14 | 15 | callback(null, { 16 | statusCode: 200, 17 | body: JSON.stringify({ 18 | duration: Date.now() - start, 19 | id: id 20 | }) 21 | }); 22 | }; 23 | 24 | function uuid(a) { 25 | return a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,uuid); 26 | } -------------------------------------------------------------------------------- /providers/amazon/serverless.yml: -------------------------------------------------------------------------------- 1 | service: amazon 2 | provider: 3 | name: aws 4 | runtime: nodejs4.3 5 | stage: dev 6 | region: us-east-1 7 | memorySize: 512 8 | functions: 9 | test-fppbjs: {handler: handler.test, events: [{http: {path: /test, method: post, private: false}}]} 10 | -------------------------------------------------------------------------------- /providers/ibm/handler.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var id; 4 | 5 | function test(params) { 6 | var start = Date.now(); 7 | 8 | if (typeof id === 'undefined') { 9 | id = uuid(); 10 | } 11 | 12 | while (Date.now() < start + params.duration) {} 13 | 14 | return { 15 | duration: Date.now() - start, 16 | id: id 17 | }; 18 | } 19 | 20 | function uuid(a) { 21 | return a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,uuid); 22 | } 23 | 24 | exports.test = test; 25 | -------------------------------------------------------------------------------- /providers/ibm/serverless.yml: -------------------------------------------------------------------------------- 1 | service: ibm 2 | provider: 3 | name: openwhisk 4 | functions: 5 | test: {handler: handler.test, memory: 512, events: [{http: 'POST test'}]} 6 | plugins: 7 | - serverless-openwhisk 8 | -------------------------------------------------------------------------------- /providers/index.js: -------------------------------------------------------------------------------- 1 | // TODO: have you ever heard of promises? 2 | // TODO: better way to call serverless? not exec? 3 | 4 | 'use strict'; 5 | 6 | const exec = require('child_process').exec; 7 | const fs = require('fs'); 8 | const path = require('path'); 9 | 10 | const urlRegex = require('url-regex'); 11 | const YAML = require('yamljs'); 12 | 13 | const serverlessPath = path.join(require.resolve('serverless'), '../../bin/serverless'); 14 | 15 | let env = Object.create(process.env); 16 | env.SLS_IGNORE_WARNING = '*'; 17 | env.SLS_DEBUG = '*'; 18 | 19 | function prepareFunctions(provider, callback) { 20 | let ymlPath = path.join(__dirname, provider.name, 'serverless.yml'); 21 | 22 | YAML.load(ymlPath, yml => { 23 | yml = modifyYAML(provider, yml); 24 | 25 | let ymlString = YAML.stringify(yml, null, 4); 26 | 27 | fs.writeFile(ymlPath, ymlString, err => { 28 | if (err) throw err; 29 | callback(); 30 | }); 31 | }); 32 | } 33 | 34 | function deployFunctions(provider, callback) { 35 | let deploymentPath = path.join(__dirname, provider.name); 36 | 37 | let child = exec('cd ' + deploymentPath + '&& node ' + serverlessPath + ' deploy', { env: env }); 38 | 39 | let stdout = ''; 40 | child.stdout.on('data', data => { 41 | stdout += data; 42 | process.stdout.write(data); 43 | }); 44 | 45 | child.stderr.on('data', data => process.stderr.write(data)); 46 | 47 | child.on('close', code => { 48 | let uri = []; 49 | if (code == 0) { 50 | uri = extractUri(provider, stdout.match(urlRegex())); 51 | } 52 | 53 | callback(code, uri); 54 | }); 55 | } 56 | 57 | function removeFunctions(provider, callback) { 58 | let deploymentPath = path.join(__dirname, provider.name); 59 | 60 | let child = exec('cd ' + deploymentPath + '&& node ' + serverlessPath + ' remove', { env: env }); 61 | 62 | child.stdout.on('data', data => process.stdout.write(data)); 63 | child.stderr.on('data', data => process.stderr.write(data)); 64 | 65 | child.on('close', code => { 66 | callback(code); 67 | }) 68 | } 69 | 70 | function modifyYAML(provider, yml) { 71 | switch(provider.name) { 72 | case 'alphabet': 73 | provider.project = provider.project || generateRandomLetters(12); 74 | yml.provider.project = provider.project; 75 | provider.credentials = provider.credentials || '~/.gcloud/keyfile.json'; 76 | yml.provider.credentials = provider.credentials; 77 | break; 78 | case 'microsoft': 79 | provider.service = provider.service || generateRandomLetters(12); 80 | yml.service = provider.service; 81 | break; 82 | } 83 | 84 | yml.functions = generateFunctions(provider); 85 | 86 | return yml; 87 | } 88 | 89 | function generateFunctions(provider) { 90 | let functions = {}; 91 | 92 | let functionName = 'test'; 93 | if (provider.name == 'amazon') { 94 | functionName += '-' + generateRandomLetters(6); 95 | } 96 | 97 | switch(provider.name) { 98 | case 'alphabet': 99 | functions[functionName] = { 100 | handler: 'test', 101 | availableMemoryMb: 512, 102 | events: [{ 103 | http: '/' 104 | }] 105 | }; 106 | break; 107 | case 'amazon': 108 | functions[functionName] = { 109 | handler: 'handler.test', 110 | events: [{ 111 | http: { 112 | path: '/test', 113 | method: 'post', 114 | private: false 115 | } 116 | }] 117 | }; 118 | break; 119 | case 'ibm': 120 | functions[functionName] = { 121 | handler: 'handler.test', 122 | memory: 512, 123 | events: [{ 124 | http: 'POST test' 125 | }] 126 | }; 127 | break; 128 | case 'microsoft': 129 | functions[functionName] = { 130 | handler: 'handler.test', 131 | events: [{ 132 | http: true, 133 | 'x-azure-settings': { 134 | authLevel: 'anonymous' 135 | } 136 | }] 137 | }; 138 | break; 139 | case 'prototype': 140 | functions[functionName] = { 141 | handler: 'index.js', 142 | memorySize: 512 143 | }; 144 | break; 145 | } 146 | 147 | return functions; 148 | } 149 | 150 | function extractUri(provider, uris) { 151 | switch (provider.name) { 152 | case 'microsoft': 153 | return 'http://' + provider.service + '.azurewebsites.net/api/test'; 154 | default: 155 | return uris[0]; 156 | } 157 | } 158 | 159 | function generateRandomLetters(length) 160 | { 161 | let possibilities = "abcdefghijklmnopqrstuvwxyz"; 162 | let text = ""; 163 | for(let i = 0; i < length; i++) { 164 | text += possibilities.charAt(Math.floor(Math.random() * possibilities.length)); 165 | } 166 | return text; 167 | } 168 | 169 | module.exports = { 170 | prepareFunctions: prepareFunctions, 171 | deployFunctions: deployFunctions, 172 | removeFunctions: removeFunctions 173 | }; -------------------------------------------------------------------------------- /providers/microsoft/.gitignore: -------------------------------------------------------------------------------- 1 | # Hide functions directory 2 | functions -------------------------------------------------------------------------------- /providers/microsoft/handler.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var id; 4 | 5 | module.exports.test = function (context, req) { 6 | var start = Date.now(); 7 | 8 | if (typeof id === 'undefined') { 9 | id = uuid(); 10 | } 11 | 12 | while (Date.now() < start + req.body.duration) {} 13 | 14 | context.done(null, { body: { 15 | duration: Date.now() - start, 16 | id: id 17 | }}); 18 | }; 19 | 20 | function uuid(a) { 21 | return a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,uuid); 22 | } 23 | -------------------------------------------------------------------------------- /providers/microsoft/serverless.yml: -------------------------------------------------------------------------------- 1 | service: your-service-name 2 | provider: 3 | name: azure 4 | location: 'East US' 5 | plugins: 6 | - serverless-azure-functions 7 | package: 8 | exclude: ['node_modules/**', .gitignore, package.json, '.git/**'] 9 | functions: 10 | test0: {handler: handler.test, events: [{http: true, x-azure-settings: {authLevel: anonymous}}]} 11 | -------------------------------------------------------------------------------- /providers/prototype/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var id; 4 | 5 | module.exports = function (input) { 6 | var start = Date.now(); 7 | 8 | if (typeof id === 'undefined') { 9 | id = uuid(); 10 | } 11 | 12 | while (Date.now() < start + input.duration) {} 13 | 14 | return { 15 | duration: Date.now() - start, 16 | id: id 17 | }; 18 | }; 19 | 20 | function uuid(a) { 21 | return a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,uuid); 22 | } 23 | -------------------------------------------------------------------------------- /providers/prototype/serverless.yml: -------------------------------------------------------------------------------- 1 | service: prototype 2 | provider: 3 | name: prototype 4 | location: 'East US' 5 | plugins: 6 | - serverless-prototype-plugin 7 | package: 8 | exclude: ['node_modules/**', .gitignore, package.json, '.git/**'] 9 | functions: 10 | test0: {handler: index.js, memorySize: 512} 11 | --------------------------------------------------------------------------------