├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── MANIFEST.in ├── README.rst ├── bin └── lt ├── build.sh ├── docs ├── Makefile ├── conf.py ├── debug.rst ├── examples.rst ├── index.rst └── start.rst ├── lambda_toolkit ├── __init__.py ├── data │ ├── helps │ │ ├── cli.txt │ │ ├── invoke.txt │ │ ├── list.txt │ │ ├── project.txt │ │ ├── proxy.txt │ │ ├── queue.txt │ │ ├── receiver.txt │ │ └── role.txt │ ├── lambda-proxy │ │ ├── index.js │ │ └── index.py │ ├── lambda-toolkit.json │ ├── standard-folder │ │ ├── invoke │ │ │ ├── contexts │ │ │ │ ├── js.json │ │ │ │ └── python.json │ │ │ └── events │ │ │ │ ├── cloudformation.json │ │ │ │ ├── cloudwatch_events.json │ │ │ │ ├── cloudwatch_logs.json │ │ │ │ ├── codepipeline.json │ │ │ │ ├── cognito_sync_trigger.json │ │ │ │ ├── config_rule.json │ │ │ │ ├── dynamodb_update.json │ │ │ │ ├── helloworld.json │ │ │ │ ├── kinesis_stream.json │ │ │ │ ├── s3_delete.json │ │ │ │ ├── s3_put.json │ │ │ │ ├── scheduled.json │ │ │ │ ├── ses.json │ │ │ │ └── sns.json │ │ └── lambdas │ │ │ └── README.txt │ └── standard-lambda │ │ ├── index.js │ │ └── index.py └── modules │ ├── __init__.py │ ├── cli.py │ ├── conf.py │ ├── invoke.py │ ├── lambdacontext.py │ ├── logger.py │ ├── project.py │ ├── proxy.py │ ├── queue.py │ ├── receiver.py │ ├── role.py │ └── utils.py ├── requirements-dev.txt ├── requirements-user.txt ├── setup.cfg └── setup.py /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | python: 4 | - 2.7 5 | 6 | install: 7 | - python setup.py install 8 | 9 | script: nosetests 10 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | #### Version 0.3.4 2 | 3 | * Added project command: list-variables 4 | 5 | #### Version 0.3.3 6 | 7 | * Add support to set/unset variables to lambda projects 8 | * Importing and deploying variables 9 | * Using variables when lambda is invoking local and on receiver 10 | 11 | #### Version 0.3 12 | 13 | * Compatible with Python 2 and 3. 14 | * Compatible with Windows. 15 | * Major bugs fixed 16 | * Multi region support improved 17 | 18 | #### Version 0.2 19 | 20 | * Rewrited from scratch 21 | * Supporting multiples region 22 | * Added Click as CLI handler 23 | * Removed ConfigParser as Conf handler 24 | * Changed the config file to use Json instead properties. 25 | 26 | 27 | #### Version 0.1 28 | 29 | * Initial version 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include lambda_toolkit/data/ * 2 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ===================== 2 | lambda-toolkit 3 | ===================== 4 | 5 | .. image:: https://readthedocs.org/projects/lambda-toolkit/badge/?version=trunk 6 | :target: http://lambda-toolkit.readthedocs.io/en/latest/?badge=latest 7 | :alt: Documentation Status 8 | 9 | .. image:: https://travis-ci.org/lucioveloso/lambda-toolkit.svg?branch=trunk 10 | :target: https://travis-ci.org/lucioveloso/lambda-toolkit 11 | :alt: Build Status 12 | 13 | .. image:: https://img.shields.io/pypi/pyversions/lambda-toolkit.svg 14 | :alt: Compatible 15 | 16 | * Now compatible with Python 2 and 3. 17 | * Now compatible with Linux, Mac and Windows. 18 | 19 | Top Features 20 | ------------ 21 | 22 | * Invoke locally lambdas in your machine with simulated events or real events 23 | * Easy import and deploy your lambdas in different regions (v0.3.0) 24 | * Supporting Lambda Environment Variables (Even in local execution) (v0.3.3) 25 | 26 | Contributing 27 | ------------ 28 | 29 | We are open to contributions. Also let we know if you need some feature that is not implemented or it's not working properly. Please feel free to open an Issue in the Github project requesting fixes or new features. 30 | 31 | Easy to use and install 32 | ------------------------ 33 | 34 | We provide a step-by-step installation guide with installation manual and how to use. 35 | 36 | Access now `here `_. 37 | 38 | 39 | .. _start: http://lambda-toolkit.readthedocs.io/en/trunk/start.html 40 | 41 | Documentation 42 | ------------- 43 | http://lambda-toolkit.readthedocs.io/ 44 | 45 | GitHub Project 46 | -------------- 47 | https://github.com/lucioveloso/lambda-toolkit 48 | 49 | Installation: 50 | ------------- 51 | 52 | $ pip install lambda-toolkit -U 53 | 54 | ===================================== 55 | Lambda-toolkit offers a lambda proxy 56 | ===================================== 57 | 58 | Using lambda-toolkit proxy you're able to debug real events in real time in your own machine. 59 | 60 | See how it works. 61 | 62 | .. image:: https://s3-eu-west-1.amazonaws.com/lucio-public-bucket/lambda-proxy-diagram.png 63 | 64 | After you create a proxy, you're able even to put breakpoints in your code, like the example below: 65 | 66 | .. image:: https://s3-eu-west-1.amazonaws.com/lucio-public-bucket/debugging-lambda.jpg 67 | 68 | ======================================= 69 | Lambda-toolkit offers lambda tailing 70 | ======================================= 71 | 72 | .. image:: https://s3-eu-west-1.amazonaws.com/lucio-public-bucket/lambda-tail.gif 73 | 74 | -------------------------------------------------------------------------------- /bin/lt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | 4 | import sys 5 | import os 6 | 7 | sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 8 | 9 | if os.environ.get('LC_CTYPE', '') == 'UTF-8': 10 | os.environ['LC_CTYPE'] = 'en_US.UTF-8' 11 | 12 | if __name__ == '__main__': 13 | from lambda_toolkit.modules import cli 14 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ "$1" == "make" ]; then 4 | $0 clean > /dev/null 5 | virtualenv build_env 6 | source build_env/bin/activate 7 | python2.7 setup.py sdist 8 | pip install -r requirements-dev.txt 9 | python2.7 setup.py bdist_wheel 10 | elif [ "$1" == "clean" ]; then 11 | echo -n "Roger that. " 12 | rm -rf build_env/ 13 | rm -rf build/ 14 | rm -rf dist/ 15 | rm -rf *egg-info* 16 | echo "Sector clear." 17 | elif [ "$1" == "install" ]; then 18 | $0 make 19 | twine upload dist/* 20 | $0 clean > /dev/null 21 | else 22 | echo "No action defined. Options: clean,make,install." 23 | fi 24 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = python -msphinx 7 | SPHINXPROJ = lambda-toolkit 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # lambda-toolkit documentation build configuration file, created by 4 | # sphinx-quickstart on Mon Jul 10 18:45:01 2017. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | # If extensions (or modules to document with autodoc) are in another directory, 16 | # add these directories to sys.path here. If the directory is relative to the 17 | # documentation root, use os.path.abspath to make it absolute, like shown here. 18 | # 19 | # import os 20 | # import sys 21 | # sys.path.insert(0, os.path.abspath('.')) 22 | 23 | 24 | # -- General configuration ------------------------------------------------ 25 | 26 | # If your documentation needs a minimal Sphinx version, state it here. 27 | # 28 | # needs_sphinx = '1.0' 29 | 30 | # Add any Sphinx extension module names here, as strings. They can be 31 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 32 | # ones. 33 | extensions = [] 34 | 35 | # Add any paths that contain templates here, relative to this directory. 36 | templates_path = ['_templates'] 37 | 38 | # The suffix(es) of source filenames. 39 | # You can specify multiple suffix as a list of string: 40 | # 41 | # source_suffix = ['.rst', '.md'] 42 | source_suffix = '.rst' 43 | 44 | # The master toctree document. 45 | master_doc = 'index' 46 | 47 | # General information about the project. 48 | project = u'lambda-toolkit' 49 | copyright = u'2017, Lucio Veloso' 50 | author = u'Lucio Veloso' 51 | 52 | # The version info for the project you're documenting, acts as replacement for 53 | # |version| and |release|, also used in various other places throughout the 54 | # built documents. 55 | # 56 | # The short X.Y version. 57 | version = u'0.3' 58 | # The full version, including alpha/beta/rc tags. 59 | release = u'0.3' 60 | 61 | # The language for content autogenerated by Sphinx. Refer to documentation 62 | # for a list of supported languages. 63 | # 64 | # This is also used if you do content translation via gettext catalogs. 65 | # Usually you set "language" from the command line for these cases. 66 | language = None 67 | 68 | # List of patterns, relative to source directory, that match files and 69 | # directories to ignore when looking for source files. 70 | # This patterns also effect to html_static_path and html_extra_path 71 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 72 | 73 | # The name of the Pygments (syntax highlighting) style to use. 74 | pygments_style = 'sphinx' 75 | 76 | # If true, `todo` and `todoList` produce output, else they produce nothing. 77 | todo_include_todos = False 78 | 79 | 80 | # -- Options for HTML output ---------------------------------------------- 81 | 82 | # The theme to use for HTML and HTML Help pages. See the documentation for 83 | # a list of builtin themes. 84 | # 85 | html_theme = 'sphinx_rtd_theme' 86 | 87 | # Theme options are theme-specific and customize the look and feel of a theme 88 | # further. For a list of options available for each theme, see the 89 | # documentation. 90 | # 91 | # html_theme_options = {} 92 | 93 | # Add any paths that contain custom static files (such as style sheets) here, 94 | # relative to this directory. They are copied after the builtin static files, 95 | # so a file named "default.css" will overwrite the builtin "default.css". 96 | html_static_path = ['_static'] 97 | 98 | # Custom sidebar templates, must be a dictionary that maps document names 99 | # to template names. 100 | # 101 | # This is required for the alabaster theme 102 | # refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars 103 | html_sidebars = { 104 | '**': [ 105 | 'about.html', 106 | 'navigation.html', 107 | 'relations.html', # needs 'show_related': True theme option to display 108 | 'searchbox.html', 109 | 'donate.html', 110 | ] 111 | } 112 | 113 | 114 | # -- Options for HTMLHelp output ------------------------------------------ 115 | 116 | # Output file base name for HTML help builder. 117 | htmlhelp_basename = 'lambda-toolkitdoc' 118 | 119 | 120 | # -- Options for LaTeX output --------------------------------------------- 121 | 122 | latex_elements = { 123 | # The paper size ('letterpaper' or 'a4paper'). 124 | # 125 | # 'papersize': 'letterpaper', 126 | 127 | # The font size ('10pt', '11pt' or '12pt'). 128 | # 129 | # 'pointsize': '10pt', 130 | 131 | # Additional stuff for the LaTeX preamble. 132 | # 133 | # 'preamble': '', 134 | 135 | # Latex figure (float) alignment 136 | # 137 | # 'figure_align': 'htbp', 138 | } 139 | 140 | # Grouping the document tree into LaTeX files. List of tuples 141 | # (source start file, target name, title, 142 | # author, documentclass [howto, manual, or own class]). 143 | latex_documents = [ 144 | (master_doc, 'lambda-toolkit.tex', u'lambda-toolkit Documentation', 145 | u'Lucio Veloso', 'manual'), 146 | ] 147 | 148 | 149 | # -- Options for manual page output --------------------------------------- 150 | 151 | # One entry per manual page. List of tuples 152 | # (source start file, name, description, authors, manual section). 153 | man_pages = [ 154 | (master_doc, 'lambda-toolkit', u'lambda-toolkit Documentation', 155 | [author], 1) 156 | ] 157 | 158 | 159 | # -- Options for Texinfo output ------------------------------------------- 160 | 161 | # Grouping the document tree into Texinfo files. List of tuples 162 | # (source start file, target name, title, author, 163 | # dir menu entry, description, category) 164 | texinfo_documents = [ 165 | (master_doc, 'lambda-toolkit', u'lambda-toolkit Documentation', 166 | author, 'lambda-toolkit', 'One line description of project.', 167 | 'Miscellaneous'), 168 | ] 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /docs/debug.rst: -------------------------------------------------------------------------------- 1 | Debugging a Lambda 2 | ------------------ 3 | 4 | To debug real events in real time, you should have at least: 5 | 6 | * A queue (to store the events) 7 | * A proxy (to forward the events to the queue) 8 | * A lambda project (to process the events) 9 | 10 | Creating a queue 11 | ---------------- 12 | 13 | To create a queue, you can use the command ``lt project queue create``:: 14 | 15 | $ lt queue create -q myQueue 16 | Initializing lambda-toolkit CLI (v0.2.11) - Region: eu-west-1 - Auth: file 17 | [INFO] The queue 'myQueue.fifo' has been created. 18 | 19 | .. hint:: 20 | 21 | In any moment, you can use ``lt list`` to see all resources in your lambda-toolkit environment. 22 | 23 | 24 | Deploying a proxy 25 | ----------------- 26 | 27 | To deploy a proxy, you can use the command ``lt project proxy deploy``:: 28 | 29 | $ lt proxy deploy -p myProxy -q myQueue 30 | Initializing lambda-toolkit CLI (v0.2.11) - Region: eu-west-1 - Auth: file 31 | [INFO] Lambda proxy myProxy created proxying requests to myQueue.fifo 32 | 33 | 34 | Verifying requisites 35 | -------------------- 36 | 37 | Let's list our environment, to check if we already have the **queue**, the **proxy** and the **lambda project**. :: 38 | 39 | $ lt list 40 | Initializing lambda-toolkit CLI (v0.2.11) - Region: eu-west-1 - Auth: file 41 | [INFO] 42 | [INFO] User Projects (Lambda Functions): 43 | [INFO] - Project: s3_resources Deployed: True Runtime: python2.7 44 | [INFO] - Project: myLambdaProject Deployed: True Runtime: python2.7 45 | [INFO] - Project: lambda-list-buckets Deployed: True Runtime: python2.7 46 | [INFO] 47 | [INFO] SQS (Queues): 48 | [INFO] - Queue name: myQueue.fifo Used by: myProxy 49 | [INFO] 50 | [INFO] Proxies (Lambda proxies): 51 | [INFO] - Proxy name: myProxy Queue: myQueue.fifo Runtime: python2.7 52 | 53 | .. note:: 54 | 55 | Note that now we have the proxy ``myProxy`` forwarding the event to ``myQueue``. 56 | 57 | Debugging 58 | --------- 59 | 60 | Now that you already have the all requisites to run a ``receiver``, we can execute it to start to collect our real data in real time:: 61 | 62 | $ lt receiver collect --projectname myLambdaProject --sqsname myQueue 63 | Initializing lambda-toolkit CLI (v0.2.11) - Region: eu-west-1 - Auth: file 64 | [INFO] Importing project myLambdaProject 65 | [INFO] Starting the receiver using the queue myQueue.fifo 66 | ......... 67 | 68 | 69 | .. attention:: 70 | 71 | You can run this command inside your IDE in Debug Mode. By this way you will be able to set break points and debug all data. If you want to just see the logs in real time, you can use the ``lt tail`` instead the ``lt receiver``. -------------------------------------------------------------------------------- /docs/examples.rst: -------------------------------------------------------------------------------- 1 | Creating a lambda project 2 | ========================= 3 | 4 | The first step to start to use the lambda-toolkit is creating or importing a lambda project. Lambda-toolkit provides a command called ``project``, that you can invoke to create a lambda project running the command ``lt project create``:: 5 | 6 | $ lt project create -p myLambdaProject 7 | Initializing lambda-toolkit CLI (v0.2.10) - Region: eu-west-1 - Auth: file 8 | [INFO] Creating the project lambda-toolkit folder '/Users/glucio/lambda-toolkit/lambdas/myLambdaProject_eu-west-1' 9 | [INFO] Project 'myLambdaProject' [python2.7] has been created. 10 | 11 | Now I have my project pre-configured in ``lambdas/myLambdaProject_eu-west-1``:: 12 | 13 | $ ls /Users/glucio/lambda-toolkit/lambdas/myLambdaProject_eu-west-1 14 | __init__.py index.py 15 | 16 | .. note:: 17 | Lambda-toolkit created also the file ``__init__.py``. Lambda-toolkit creates this file to make sure that it will be possible to load this project as module. 18 | 19 | Now, if we list our environment, we already can see this project:: 20 | 21 | $ lt list 22 | Initializing lambda-toolkit CLI (v0.2.10) - Region: eu-west-1 - Auth: file 23 | [INFO] 24 | [INFO] User Projects (Lambda Functions): 25 | [INFO] - Project: lambda-list-buckets Deployed: True Runtime: python2.7 26 | [INFO] - Project: myLambdaProject Deployed: False Runtime: python2.7 27 | 28 | To see the project command help, just type ``lt project --help``: 29 | 30 | .. literalinclude:: ../lambda_toolkit/data/helps/project.txt 31 | 32 | .. hint:: 33 | 34 | For all the commands, you can use ``--help``. For example: ``lt invoke --help`` or ``lt queue --help``. 35 | 36 | Listing AWS lambda projects 37 | ==================================== 38 | 39 | To list all existing AWS lambda projects in your AWS environment, you can use the command ``lt project list-aws``:: 40 | 41 | $ lt project list-aws 42 | Initializing lambda-toolkit CLI (v0.2.10) - Region: eu-west-1 - Auth: file 43 | [INFO] AWS Projects (Lambda Functions): 44 | [INFO] - Project: abcd Imported: False Runtime: python2.7 45 | [INFO] - Project: s3_resources Imported: False Runtime: python2.7 46 | [INFO] - Project: lambda-list-buckets Imported: True Runtime: python2.7 47 | 48 | Importing an existing AWS lambda project 49 | ======================================== 50 | 51 | To import an existing lambda project in your AWS environment, you can use the command ``lt project import``:: 52 | 53 | $ lt project import -p s3_resources 54 | Initializing lambda-toolkit CLI (v0.2.10) - Region: eu-west-1 - Auth: file 55 | [INFO] Creating the project lambda-toolkit folder '/Users/glucio/lambda-toolkit/lambdas/s3_resources_eu-west-1' 56 | [INFO] Project s3_resources imported. 57 | 58 | 59 | .. warning:: 60 | 61 | Note that if you import an proxy that you already have inside lambda-toolkit, it will overwrite your local project. It can be very useful if you wish to update your local project from the lambda in the AWS, but it also can make you lose data. 62 | 63 | Deploying a project in AWS 64 | ========================== 65 | 66 | To deploy a project to your AWS environment, you can use the command ``lt project deploy``:: 67 | 68 | $ lt project deploy -p myLambdaProject 69 | Initializing lambda-toolkit CLI (v0.2.10) - Region: eu-west-1 - Auth: file 70 | [INFO] Lambda project myLambdaProject was created and deployed. 71 | 72 | .. note:: 73 | 74 | Note that I didn't provide the option ``--rolename`` that is required. It happened due I previously had configured a default role using the command ``lt role --set-default``. 75 | 76 | .. warning:: 77 | 78 | If you already have a lambda project with this name in your AWS environment, it will be overwritten. 79 | 80 | Setting a default role 81 | ======================= 82 | 83 | To set a default role to be used always that you do not provide a rolename to deploy a proxy or a lambda project, you can use the command ``lt role``:: 84 | 85 | $ lt role set-default --rolename arn:aws:iam::432811670411:role/service-role/myRoleLambda 86 | Initializing lambda-toolkit CLI (v0.2.10) - Region: eu-west-1 - Auth: file 87 | [INFO] Role 'arn:aws:iam::123456789000:role/service-role/myRoleLambda' is set as default now. 88 | 89 | .. hint:: 90 | 91 | If you define a default role, but you use the ``--rolename`` to deploy a lambda project, the rolename that you specify will preponderate. 92 | 93 | Invoking a local lambda 94 | ======================== 95 | 96 | To invoke a lambda project locally in your machine, you can use the command ``lt project invoke``:: 97 | 98 | $ lt invoke local --projectname myLambdaProject --event-file helloworld.json 99 | Initializing lambda-toolkit CLI (v0.2.10) - Region: eu-west-1 - Auth: file 100 | [INFO] Importing project myLambdaProject 101 | Hi, I'm here. Lambda-proxy is working. =) 102 | AWS Event ID: 11111111-1111-1111-1111-111111111111 103 | Event Body: {"key3": "value3", "key2": "value2", "key1": "value1"} 104 | 105 | .. hint:: 106 | 107 | You can customize or add new events file in the folder ``~/lambda-toolkit/invoke/events/`` 108 | 109 | Invoking a remote lambda 110 | ======================== 111 | 112 | To invoke a lambda project remotely, you can use the command ``lt project invoke remote``:: 113 | 114 | $ lt invoke remote --event-file helloworld.json --projectname myLambdaProject 115 | Initializing lambda-toolkit CLI (v0.2.11) - Region: eu-west-1 - Auth: file 116 | [INFO] Invoking the project myLambdaProject 117 | START RequestId: 5a380d00-66c6-11e7-8119-9b430b7e8688 Version: $LATEST 118 | Hi, I'm here. Lambda-proxy is working. =) 119 | AWS Event ID: 5a380d00-66c6-11e7-8119-9b430b7e8688 120 | Event Body: {"key3": "value3", "key2": "value2", "key1": "value1"} 121 | END RequestId: 5a380d00-66c6-11e7-8119-9b430b7e8688 122 | REPORT RequestId: 5a380d00-66c6-11e7-8119-9b430b7e8688 Duration: 0.57 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 29 MB 123 | 124 | .. hint:: 125 | 126 | You can invoke remotely your lambda-toolkit proxy, providing the argument ``--proxyname`` instead ``--projectname``. 127 | 128 | Tailing a remote lambda 129 | ======================= 130 | 131 | To tail a remote lambda project, you can use the command ``lt tail cloudwatch``:: 132 | 133 | $ lt tail cloudwatch --loggroupname "/aws/lambda/myLambdaProject" 134 | Initializing tail-toolkit CLI (v0.0.5) - Region: eu-west-1 135 | Collecting logs in real time, starting from 5 minutes ago 136 | START RequestId: 8b690d74-66de-11e7-b54e-2d48a73dcaf9 Version: $LATEST 137 | Hi, I'm here. Lambda-proxy is working. =) 138 | AWS Event ID: 8b690d74-66de-11e7-b54e-2d48a73dcaf9 139 | Event Body: {"account": "123456789000", "region": "eu-west-1", "detail": {"state": "running", "instance-id": "i-03169cf0533d7d000"}, "detail-type": "EC2 Instance State-change Notification", "source": "aws.ec2", "version": "0", "time": "2017-07-12T08:46:05Z", "id": "812d642c-5f46-4588-9dde-bfa4478a4e78", "resources": ["arn:aws:ec2:eu-west-1:123456789000:instance/i-03169cf0533d7d000"]} 140 | END RequestId: 8b690d74-66de-11e7-b54e-2d48a73dcaf9 141 | REPORT RequestId: 8b690d74-66de-11e7-b54e-2d48a73dcaf9 Duration: 0.69 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 29 MB 142 | ************* 143 | 144 | .. important:: 145 | 146 | Please note that tail can be used to any log group name in your cloudwatch environment. To tail your lambda functions you should append the lambda log group prefix ``/aws/lambda/`` 147 | 148 | .. note:: 149 | 150 | If you want to debug your remote lambda function, you should use the ``receiver`` command instead the ``tail``. -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. lambda-toolkit documentation master file, created by 2 | sphinx-quickstart on Mon Jul 10 18:45:01 2017. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to lambda-toolkit's documentation! 7 | ========================================== 8 | 9 | .. include:: ../README.rst 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | :caption: Getting started: 14 | 15 | start 16 | 17 | .. toctree:: 18 | :maxdepth: 2 19 | :caption: Examples: 20 | 21 | examples 22 | 23 | .. toctree:: 24 | :maxdepth: 2 25 | :caption: Debugging Lambda: 26 | 27 | debug 28 | 29 | 30 | Indices and tables 31 | ================== 32 | 33 | * :ref:`genindex` 34 | * :ref:`modindex` 35 | * :ref:`search` 36 | -------------------------------------------------------------------------------- /docs/start.rst: -------------------------------------------------------------------------------- 1 | Basically you're able to install the lambda-toolkit using pip. You can also install it manually, cloning the project from GitHub. 2 | 3 | .. hint:: 4 | 5 | If you're not a developer and you're not planning to contribute developing in lambda-toolkit, **we do recommend to use pip** installation mode.. 6 | 7 | --------------------- 8 | General Prerequisites 9 | --------------------- 10 | 11 | Basically, to start to use the lambda-toolkit, you must have at least: 12 | 13 | * Python 2 or 3 14 | * Package Management System (``pip``) 15 | * AWS Credentials 16 | 17 | Python 18 | ========== 19 | 20 | Python is required to use lambda-toolkit. By this way, you should make sure that your system has a proper Python installation available:: 21 | 22 | $ python --version 23 | Python x.x.x 24 | 25 | If your system does not support Python, you can install `here `_. 26 | 27 | .. _python: https://www.python.org/download 28 | 29 | Package Management System (pip) 30 | =============================== 31 | 32 | pip is required to use lambda-toolkit. By this way, you should make sure that your system has a proper pip installed:: 33 | 34 | $ pip --version 35 | pip 9.0.1 from /Library/Python/2.7/site-packages/pip-9.0.1-py2.7.egg (python 2.7) 36 | 37 | If you system does not has pip installed, you can install `here `_. 38 | 39 | .. _pip: https://pip.pypa.io/en/stable/installing/ 40 | 41 | AWS Credentials 42 | =============== 43 | 44 | Lambda-toolkit can read yours credentials from the **system environment variables** or **credential files**. 45 | 46 | If you're already using aws cli for example, credential files were generated when the command ``aws configure`` was executed. 47 | 48 | .. hint:: 49 | 50 | Lambda-toolkit tries first to read the environment variables, and if it is not configured, lambda-toolkit reads the credential files. 51 | 52 | .. warning:: 53 | 54 | We do recommend to use credential files. 55 | 56 | Using credential files 57 | ------------------------------ 58 | 59 | Make sure that you have the files: 60 | 61 | * ``~/.aws/credentials`` 62 | * ``~/.aws/config`` 63 | 64 | For example: 65 | 66 | ~/.aws/credentials:: 67 | 68 | [default] 69 | aws_access_key_id = AAAAAAAAAAAAAAAAAAAA 70 | aws_secret_access_key = AWS_ACCESS_SECRET_KEY__KEEP_IT_SAFE 71 | 72 | ~/.aws/config:: 73 | 74 | [default] 75 | region = eu-west-1 76 | 77 | .. hint:: 78 | 79 | As you can see, the example shows how to create a default credential. You can create others, and then, you can use the environment variable ``AWS_PROFILE`` to choice a specific one. 80 | You can also overwrite your region option, setting the environment variable ``AWS_REGION``. 81 | 82 | Using environments variables 83 | ---------------------------- 84 | 85 | Specify the env variables below: 86 | 87 | * ``AWS_ACCESS_KEY_ID`` 88 | * ``AWS_SECRET_ACCESS_KEY`` 89 | * ``AWS_REGION`` 90 | 91 | For example:: 92 | 93 | $ export AWS_ACCESS_KEY_ID="AAAAAAAAAAAAAAAAAAAA" 94 | $ export AWS_SECRET_ACCESS_KEY="AWS_ACCESS_SECRET_KEY__KEEP_IT_SAFE" 95 | $ export AWS_REGION="us-east-1" 96 | 97 | .. hint:: 98 | 99 | Remember that you must ``export`` the variables. 100 | 101 | .. warning:: 102 | 103 | Lambda-toolkit only uses the environment variables, if the 3 variables are available. 104 | 105 | --------------------- 106 | Installing 107 | --------------------- 108 | 109 | Installing using pip 110 | ==================== 111 | 112 | The installation with pip is quick and simple. For common installation, use the command below:: 113 | 114 | $ sudo -H pip install lambda-toolkit 115 | $ lt --help 116 | 117 | .. hint:: 118 | 119 | If you want uninstall lambda-toolkit, just run ``sudo pip uninstall lambda-toolkit``. 120 | To update, run ``sudo pip install lambda-toolkit -U`` 121 | 122 | Cloning the repository manually 123 | =============================== 124 | 125 | Installing from repository is not to common users, but it is also another option. To install from repository you also need to have the ``git`` client installed. 126 | 127 | The first step is clone the repository:: 128 | 129 | $ git clone https://github.com/lucioveloso/lambda-toolkit 130 | 131 | Install the requirements using pip:: 132 | 133 | $ pip install -r lambda-toolkit/requirements-user.txt 134 | $ lambda-toolkit/bin/lt --help 135 | 136 | And then, you are able to run the lambda-toolkit from current user: 137 | -------------------------------------------------------------------------------- /lambda_toolkit/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.3.7' 2 | -------------------------------------------------------------------------------- /lambda_toolkit/data/helps/cli.txt: -------------------------------------------------------------------------------- 1 | The lambda-toolkit provides several commands and options. 2 | 3 | For detailed information about each command you can use: 4 | 5 | lt [COMMAND] --help 6 | 7 | Usage examples: 8 | 9 | lt invoke --help 10 | lt project --help -------------------------------------------------------------------------------- /lambda_toolkit/data/helps/invoke.txt: -------------------------------------------------------------------------------- 1 | Invoke yours lambdas functions locally or remotely 2 | -------------------------------------------------------------------------- 3 | 4 | Using invoke command, you can invoke yours lambda functions. 5 | The command invoke has the following actions: 6 | 7 | - local 8 | For local execution, you must specify: 9 | * An event file (-f) 10 | * An existing project (-p) 11 | 12 | - remote 13 | For remote execution, you must specify: 14 | * An event file (-f). 15 | * An deployed project (-p) or any existing proxy (-pp). 16 | 17 | Examples: 18 | lt invoke remote -f helloworld.json --proxyname test 19 | lt invoke local -f helloworld.json -p s3_resources -------------------------------------------------------------------------------- /lambda_toolkit/data/helps/list.txt: -------------------------------------------------------------------------------- 1 | List all configuration 2 | -------------------------------------------------------------------------- 3 | 4 | List command does not provide any internal parameters. -------------------------------------------------------------------------------- /lambda_toolkit/data/helps/project.txt: -------------------------------------------------------------------------------- 1 | Manage Projects 2 | ****************************************** 3 | 4 | Examples: 5 | 6 | $ lt project list 7 | $ lt project create -p myProject [--runtime nodejs6.10] 8 | $ lt project delete -p myProject 9 | $ lt project deploy -p myProject [--rolename arn:xxx:yyyy] 10 | $ lt project undeploy -p myProject 11 | $ lt project deploy-all [--rolename arn:xxx:yyyy] 12 | $ lt project undeploy-all 13 | $ lt project import -p myAwsProject 14 | $ lt project import-all 15 | $ lt project list-aws -------------------------------------------------------------------------------- /lambda_toolkit/data/helps/proxy.txt: -------------------------------------------------------------------------------- 1 | Manage Proxies : 2 | 3 | project proxy --help for deep information. 4 | 5 | Examples: -------------------------------------------------------------------------------- /lambda_toolkit/data/helps/queue.txt: -------------------------------------------------------------------------------- 1 | Manage Queues : 2 | 3 | project queue --help for deep information. 4 | 5 | Examples: -------------------------------------------------------------------------------- /lambda_toolkit/data/helps/receiver.txt: -------------------------------------------------------------------------------- 1 | Execute Receiver : 2 | 3 | project receiver --help for deep information. 4 | 5 | Examples: 6 | -------------------------------------------------------------------------------- /lambda_toolkit/data/helps/role.txt: -------------------------------------------------------------------------------- 1 | Manage Roles : 2 | 3 | project role --help for deep information. 4 | 5 | Examples: -------------------------------------------------------------------------------- /lambda_toolkit/data/lambda-proxy/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by glucio on 7/5/17. 3 | * Default lambda-toolkit proxy to JS projects. 4 | */ 5 | var AWS = require('aws-sdk'); 6 | var sqs = new AWS.SQS({region : process.env.AWS_REGION}); 7 | 8 | exports.handler = function(event, context, callback) { 9 | var output = {}; 10 | output['event'] = JSON.stringify(event); 11 | output['context'] = JSON.stringify(context); 12 | 13 | var params = { 14 | QueueName: 'TEMPLATEQUEUENAME' 15 | }; 16 | sqs.getQueueUrl(params, function(err, data) { 17 | if (err) console.log(err, err.stack); 18 | else { 19 | var params = { 20 | MessageBody: JSON.stringify(output), 21 | QueueUrl: data.QueueUrl, 22 | MessageDeduplicationId: context.awsRequestId, 23 | MessageGroupId: 'sameallthetime' 24 | }; 25 | sqs.sendMessage(params, function(err, data) { 26 | if (err) console.log(err, err.stack); 27 | }); 28 | } 29 | }); 30 | }; -------------------------------------------------------------------------------- /lambda_toolkit/data/lambda-proxy/index.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import json 3 | import os 4 | 5 | 6 | class NormalizeJson(json.JSONEncoder): 7 | def default(self, obj): 8 | return obj.__repr__() 9 | 10 | 11 | def lambda_handler(event, context): 12 | 13 | sqs = boto3.resource('sqs') 14 | 15 | queue = sqs.get_queue_by_name(QueueName=os.environ['sqsname']) 16 | queue.send_message(MessageDeduplicationId=context.aws_request_id, MessageGroupId="sametoall", MessageBody="{ \"event\": " + json.dumps(event) + ", \"context\": " + json.dumps(vars(context), cls=NormalizeJson) + " }") 17 | -------------------------------------------------------------------------------- /lambda_toolkit/data/lambda-toolkit.json: -------------------------------------------------------------------------------- 1 | { 2 | "cli": { 3 | "invoke": { 4 | "commands": { 5 | "local": [ "event-file", "projectname" ], 6 | "remote": [ "event-file", [ "projectname", "proxyname"]] 7 | } 8 | }, 9 | "receiver": { 10 | "commands": { 11 | "collect": [ "projectname", "sqsname" ] 12 | } 13 | }, 14 | "role": { 15 | "commands": { 16 | "set-default": [ "rolename" ], 17 | "unset-default": [] 18 | } 19 | }, 20 | "proxy": { 21 | "commands": { 22 | "deploy": [ "sqsname", "proxyname", "rolename"], 23 | "list": [], 24 | "undeploy": [ "proxyname" ], 25 | "undeploy-all": [] 26 | } 27 | }, 28 | "queue": { 29 | "commands": { 30 | "purge": [ "sqsname" ], 31 | "create": [ "sqsname" ], 32 | "list": [], 33 | "delete": [ "sqsname" ], 34 | "delete-all": [] 35 | } 36 | }, 37 | "project": { 38 | "commands": { 39 | "list-aws-all" : [], 40 | "list-aws": [], 41 | "list-all": [], 42 | "undeploy": [ "projectname" ], 43 | "deploy": [ "projectname" ], 44 | "create": [ "projectname" ], 45 | "list": [], 46 | "import-all": [], 47 | "undeploy-all": [], 48 | "import": [ "projectname" ], 49 | "deploy-all": [], 50 | "delete": [ "projectname" ], 51 | "delete-all": [], 52 | "check-regions-delay": [], 53 | "import-all-regions": [], 54 | "delete-all-regions": [], 55 | "undeploy-all-regions": [], 56 | "deploy-all-regions": [ "rolename" ], 57 | "set-variable": [ "projectname", "variable", "value" ], 58 | "unset-variable": [ "projectname", "variable" ], 59 | "list-variables": [ "projectname" ] 60 | } 61 | } 62 | }, 63 | "aws-regions": ["ap-south-1", "eu-west-2", "eu-west-1", "ap-northeast-2", "ap-northeast-1", "sa-east-1", "ca-central-1", "ap-southeast-1", "ap-southeast-2", "eu-central-1", "us-east-1", "us-east-2", "us-west-1", "us-west-2"], 64 | "settings": { 65 | "C_DEFAULT_ROLE": "", 66 | "C_LAMBDAS_DIR": "lambdas/", 67 | "C_STANDARD_FOLDER_DIR": "data/standard-folder", 68 | "C_LAMBDAS_ZIP_DIR": ".zips", 69 | "C_LAMBDAPROXY_FUNC": "data/lambda-proxy/index.py", 70 | "C_LAMBDASTANDARD_FUNC_PY": "data/standard-lambda/index.py", 71 | "C_LAMBDASTANDARD_FUNC_JS": "data/standard-lambda/index.js", 72 | "C_LAMBDACONTEXT_FUNC_PY": "data/contexts/", 73 | "C_LAMBDAEVENTS_FUNC": "data/events/", 74 | "C_INVOKE_DIR": "invoke/", 75 | "C_INVOKE_DIR_EVT": "invoke/events/", 76 | "C_INVOKE_DIR_CTX": "invoke/contexts/", 77 | "C_INVOKE_CTX_FILE": "python.json", 78 | "QUEUE_CREATEQUEUE_VISIBILITY_TIMEOUT": 3, 79 | "C_LAMBDASTANDERD_FUNC_VAR_REPLACE": "TEMPLATEQUEUENAME", 80 | "QUEUE_GETMESSAGE_VISIBILITY_TIMEOUT": 10, 81 | "QUEUE_GETMESSAGE_MAXNUMBEROFMESSAGES": 10, 82 | "C_BASE_DIR": "~/lambda-toolkit/", 83 | "QUEUE_GETMESSAGE_WAITTIMESECONDS": 20, 84 | "C_HELPS_FILES": "data/helps/" 85 | } 86 | } -------------------------------------------------------------------------------- /lambda_toolkit/data/standard-folder/invoke/contexts/js.json: -------------------------------------------------------------------------------- 1 | { 2 | "callbackWaitsForEmptyEventLoop": true, 3 | "logGroupName": "/aws/lambda/log_group_name", 4 | "logStreamName": "2017/07/09/[$LATEST]log_stream_name_id", 5 | "functionName": "FUNC_NAME", 6 | "memoryLimitInMB": "128", 7 | "functionVersion": "$LATEST", 8 | "invokeid": "11111111-1111-1111-1111-111111111111", 9 | "awsRequestId": "11111111-1111-1111-1111-111111111111", 10 | "invokedFunctionArn": "arn:aws:lambda:eu-west-1:432811670411:function:FUNC_NAME" 11 | } -------------------------------------------------------------------------------- /lambda_toolkit/data/standard-folder/invoke/contexts/python.json: -------------------------------------------------------------------------------- 1 | { 2 | "aws_request_id": "11111111-1111-1111-1111-111111111111", 3 | "log_stream_name": "2017/07/09/[$LATEST]log_stream_name_id", 4 | "invoked_function_arn": "arn:aws:lambda:region:123456789000:function:FUNC_NAME", 5 | "client_context": null, 6 | "log_group_name": "/aws/lambda/log_group_name", 7 | "function_name": "FUNC_NAME", 8 | "function_version": "$LATEST", 9 | "identity": "", 10 | "memory_limit_in_mb": "128" 11 | } -------------------------------------------------------------------------------- /lambda_toolkit/data/standard-folder/invoke/events/cloudformation.json: -------------------------------------------------------------------------------- 1 | { 2 | "StackId": "arn:aws:cloudformation:us-east-1:123456789000:stack/Test/e97df1b0-92d6-11e5-b0f6-50d501114c2a", 3 | "ResponseURL": "http://pre-signed-S3-url-for-response", 4 | "ResourceProperties": { 5 | "StackName": "stack-name", 6 | "List": [ 7 | "1", 8 | "2", 9 | "3" 10 | ] 11 | }, 12 | "RequestType": "Create", 13 | "ResourceType": "Custom::TestResource", 14 | "RequestId": "unique id for this create request", 15 | "LogicalResourceId": "MyTestResource" 16 | } -------------------------------------------------------------------------------- /lambda_toolkit/data/standard-folder/invoke/events/cloudwatch_events.json: -------------------------------------------------------------------------------- 1 | { 2 | "account": "123456789000", 3 | "region": "us-east-1", 4 | "detail": { 5 | "state": "terminated", 6 | "instance-id": "i-99999999" 7 | }, 8 | "detail-type": "EC2 Instance State-change Notification", 9 | "source": "aws.ec2", 10 | "version": "0", 11 | "time": "2016-01-01T00:00:00", 12 | "id": "f4e480cb-307b-46ed-b290-07a047534ac1", 13 | "resources": ["arn:aws:ec2:us-east-1:123456789000:instance/i-99999999"] 14 | } -------------------------------------------------------------------------------- /lambda_toolkit/data/standard-folder/invoke/events/cloudwatch_logs.json: -------------------------------------------------------------------------------- 1 | { 2 | "awslogs": { 3 | "data": "H4sIAAAAAAAAAHWPwQqCQBCGX0Xm7EFtK+smZBEUgXoLCdMhFtKV3akI8d0bLYmibvPPN3wz00CJxmQnTO41whwWQRIctmEcB6sQbFC3CjW3XW8kxpOpP+OC22d1Wml1qZkQGtoMsScxaczKN3plG8zlaHIta5KqWsozoTYw3/djzwhpLwivWFGHGpAFe7DL68JlBUk+l7KSN7tCOEJ4M3/qOI49vMHj+zCKdlFqLaU2ZHV2a4Ct/an0/ivdX8oYc1UVX860fQDQiMdxRQEAAA==" 4 | } 5 | } -------------------------------------------------------------------------------- /lambda_toolkit/data/standard-folder/invoke/events/codepipeline.json: -------------------------------------------------------------------------------- 1 | { 2 | "CodePipeline.job": { 3 | "data": { 4 | "artifactCredentials": { 5 | "secretAccessKey": "shhhh_its_a_secret", 6 | "accessKeyId": "keys_to_the_whip", 7 | "sessionToken": "some_really_long_string" 8 | }, 9 | "actionConfiguration": { 10 | "configuration": { 11 | "FunctionName": "gucci__main__" 12 | } 13 | }, 14 | "inputArtifacts": [{ 15 | "location": { 16 | "type": "S3", 17 | "s3Location": { 18 | "objectKey": "pipeline/MyApp/random.zip", 19 | "bucketName": "codepipeline-us-east-1-123456789000" 20 | } 21 | }, 22 | "name": "MyApp", 23 | "revision": null 24 | }], 25 | "outputArtifacts": [] 26 | }, 27 | "id": "7c878283-f6d8-42b8-865c-d43a949cd902", 28 | "accountId": "123456789000" 29 | } 30 | } -------------------------------------------------------------------------------- /lambda_toolkit/data/standard-folder/invoke/events/cognito_sync_trigger.json: -------------------------------------------------------------------------------- 1 | { 2 | "datasetName": "datasetName", 3 | "eventType": "SyncTrigger", 4 | "region": "us-east-1", 5 | "identityId": "identityId", 6 | "datasetRecords": { 7 | "SampleKey2": { 8 | "newValue": "newValue2", 9 | "oldValue": "oldValue2", 10 | "op": "replace" 11 | }, 12 | "SampleKey1": { 13 | "newValue": "newValue1", 14 | "oldValue": "oldValue1", 15 | "op": "replace" 16 | } 17 | }, 18 | "identityPoolId": "identityPoolId", 19 | "version": 2 20 | } -------------------------------------------------------------------------------- /lambda_toolkit/data/standard-folder/invoke/events/config_rule.json: -------------------------------------------------------------------------------- 1 | { 2 | "configRuleId": "config-rule-lrmtsa", 3 | "version": "1.0", 4 | "configRuleName": "test", 5 | "configRuleArn": "arn:aws:config:us-east-1:123456789000:config-rule/config-rule-lrmtsa", 6 | "invokingEvent": { 7 | "configurationItemDiff": null, 8 | "configurationItem": { 9 | "configurationItemVersion": "1.1", 10 | "relatedEvents": [ 11 | "a3336d74-15a5-48c9-bd72-b72e544e110a" 12 | ], 13 | "tags": { 14 | "aws:cloudformation:stack-name": "awseb-e-fpare4evqj-stack", 15 | "aws:autoscaling:groupName": "awseb-e-fpare4evqj-stack-AWSEBAutoScalingGroup-1PT7D8M0ELAIY", 16 | "aws:cloudformation:stack-id": "arn:aws:cloudformation:us-east-1:123456789000:stack/awseb-e-fpare4evqj-stack/499b0420-871f-11e5-b799-50fa1dbb2c64", 17 | "elasticbeanstalk:environment-name": "test", 18 | "aws:cloudformation:logical-id": "AWSEBAutoScalingGroup", 19 | "elasticbeanstalk:environment-id": "e-fpare4evqj", 20 | "Name": "test" 21 | }, 22 | "relationships": [{ 23 | "resourceId": "eni-7755cc55", 24 | "resourceName": null, 25 | "resourceType": "AWS::EC2::NetworkInterface", 26 | "name": "Contains Network Interface" 27 | }, { 28 | "resourceId": "sg-ca9a13ad", 29 | "resourceName": null, 30 | "resourceType": "AWS::EC2::SecurityGroup", 31 | "name": "Is associated with Security Group" 32 | }, { 33 | "resourceId": "subnet-c75c6ced", 34 | "resourceName": null, 35 | "resourceType": "AWS::EC2::Subnet", 36 | "name": "Is contained in Subnet" 37 | }, { 38 | "resourceId": "vol-43ac259d", 39 | "resourceName": null, 40 | "resourceType": "AWS::EC2::Volume", 41 | "name": "Is attached to Volume" 42 | }, { 43 | "resourceId": "vpc-a19f2bcd", 44 | "resourceName": null, 45 | "resourceType": "AWS::EC2::VPC", 46 | "name": "Is contained in Vpc" 47 | }], 48 | "configuration": { 49 | "instanceId": "i-9d8af31e", 50 | "imageId": "ami-5578243f", 51 | "state": { 52 | "code": 16, 53 | "name": "running" 54 | }, 55 | "privateDnsName": "ip-172-31-60-10.ec2.internal", 56 | "publicDnsName": "ec2-54-173-142-236.compute-1.amazonaws.com", 57 | "stateTransitionReason": "", 58 | "keyName": "test", 59 | "amiLaunchIndex": 0, 60 | "productCodes": [], 61 | "instanceType": "t2.small", 62 | "launchTime": "2016-02-11T20:42:29.000Z", 63 | "placement": { 64 | "availabilityZone": "us-east-1d", 65 | "groupName": "", 66 | "tenancy": "default", 67 | "hostId": null, 68 | "affinity": null 69 | }, 70 | "kernelId": null, 71 | "ramdiskId": null, 72 | "platform": null, 73 | "monitoring": { 74 | "state": "enabled" 75 | }, 76 | "subnetId": "subnet-c75c6ced", 77 | "vpcId": "vpc-a19f2bcd", 78 | "privateIpAddress": "172.31.60.10", 79 | "publicIpAddress": "54.173.142.236", 80 | "stateReason": null, 81 | "architecture": "x86_64", 82 | "rootDeviceType": "ebs", 83 | "rootDeviceName": "/dev/xvda", 84 | "blockDeviceMappings": [{ 85 | "deviceName": "/dev/xvda", 86 | "ebs": { 87 | "volumeId": "vol-43ac259d", 88 | "status": "attached", 89 | "attachTime": "2016-02-11T20:42:33.000Z", 90 | "deleteOnTermination": true 91 | } 92 | }], 93 | "virtualizationType": "hvm", 94 | "instanceLifecycle": null, 95 | "spotInstanceRequestId": null, 96 | "clientToken": "5e9a9f6f-47fe-4f3e-b083-6b0da395d229_us-east-1d_1", 97 | "tags": [{ 98 | "key": "aws:cloudformation:stack-name", 99 | "value": "awseb-e-fpare4evqj-stack" 100 | }, { 101 | "key": "aws:autoscaling:groupName", 102 | "value": "awseb-e-fpare4evqj-stack-AWSEBAutoScalingGroup-1PT7D8M0ELAIY" 103 | }, { 104 | "key": "elasticbeanstalk:environment-id", 105 | "value": "e-fpare4evqj" 106 | }, { 107 | "key": "elasticbeanstalk:environment-name", 108 | "value": "test" 109 | }, { 110 | "key": "aws:cloudformation:logical-id", 111 | "value": "AWSEBAutoScalingGroup" 112 | }, { 113 | "key": "aws:cloudformation:stack-id", 114 | "value": "arn:aws:cloudformation:us-east-1:123456789000:stack/awseb-e-fpare4evqj-stack/499b0420-871f-11e5-b799-50fa1dbb2c64" 115 | }, { 116 | "key": "Name", 117 | "value": "test" 118 | }], 119 | "securityGroups": [{ 120 | "groupName": "awseb-e-fpare4evqj-stack-AWSEBSecurityGroup-1NIQ6P36BTNUR", 121 | "groupId": "sg-ca9a13ad" 122 | }], 123 | "sourceDestCheck": true, 124 | "hypervisor": "xen", 125 | "networkInterfaces": [{ 126 | "networkInterfaceId": "eni-7755cc55", 127 | "subnetId": "subnet-c75c6ced", 128 | "vpcId": "vpc-a19f2bcd", 129 | "description": "", 130 | "ownerId": "123456789000", 131 | "status": "in-use", 132 | "macAddress": "12:ff:b2:ba:06:55", 133 | "privateIpAddress": "172.31.60.10", 134 | "privateDnsName": "ip-172-31-60-10.ec2.internal", 135 | "sourceDestCheck": true, 136 | "groups": [{ 137 | "groupName": "awseb-e-fpare4evqj-stack-AWSEBSecurityGroup-1NIQ6P36BTNUR", 138 | "groupId": "sg-ca9a13ad" 139 | }], 140 | "attachment": { 141 | "attachmentId": "eni-attach-41f0d5a0", 142 | "deviceIndex": 0, 143 | "status": "attached", 144 | "attachTime": "2016-02-11T20:42:29.000Z", 145 | "deleteOnTermination": true 146 | }, 147 | "association": { 148 | "publicIp": "54.173.142.236", 149 | "publicDnsName": "ec2-54-173-142-236.compute-1.amazonaws.com", 150 | "ipOwnerId": "amazon" 151 | }, 152 | "privateIpAddresses": [{ 153 | "privateIpAddress": "172.31.60.10", 154 | "privateDnsName": "ip-172-31-60-10.ec2.internal", 155 | "primary": true, 156 | "association": { 157 | "publicIp": "54.173.142.236", 158 | "publicDnsName": "ec2-54-173-142-236.compute-1.amazonaws.com", 159 | "ipOwnerId": "amazon" 160 | } 161 | }] 162 | }], 163 | "iamInstanceProfile": { 164 | "arn": "arn:aws:iam::123456789000:instance-profile/aws-elasticbeanstalk-ec2-role", 165 | "id": "AIPAJVHIC3NYNK2GWV3XK" 166 | }, 167 | "ebsOptimized": false, 168 | "sriovNetSupport": null 169 | }, 170 | "configurationItemCaptureTime": "2016-02-11T20:46:47.863Z", 171 | "configurationStateId": 1, 172 | "awsAccountId": "123456789000", 173 | "configurationItemStatus": "ResourceDiscovered", 174 | "resourceType": "AWS::EC2::Instance", 175 | "resourceId": "i-9d8af31e", 176 | "resourceName": null, 177 | "ARN": "arn:aws:ec2:us-east-1:123456789000:instance/i-9d8af31e", 178 | "awsRegion": "us-east-1", 179 | "availabilityZone": "us-east-1d", 180 | "configurationStateMd5Hash": "b748dcf5f848f96efcea6dc5af00cb08", 181 | "resourceCreationTime": "2016-02-11T20:42:29.000Z" 182 | }, 183 | "notificationCreationTime": "2016-03-29T02:02:02.777Z", 184 | "messageType": "ConfigurationItemChangeNotification", 185 | "recordVersion": "1.2" 186 | }, 187 | "resultToken": "eyJlbmNyeXB0ZWREYXRhIjpbODksMTA0LDIxLDc0LC0zNyw5OCwtNzcsLTY2LDk0LDEyMiwtMTEzLDg1LDg4LDEyMCwtMTE0LC02NSwxMjMsLTgwLDgyLC03MSwzMywyLC02MiwtMTE0LDI4LC01OSwtMTIxLDQ5LDEzLC01NCwxMDMsNzIsLTExLDEzLC04MCwtMjQsNDIsODUsNTgsNTIsLTExOCwtOTYsMTMsLTgxLC0xMDgsLTIsLTEyNywzMywtOTEsMiwtMiwtNzEsNCwtNjksLTM5LC00NywyMSwtMTAxLC0xMyw5NCw1Niw5NSw3MSwxMjYsMTEzLC0xMCwtMzcsLTc2LDI2LDAsMTAzLC03LC0zNiwtMjQsMTE4LC05MCwxMjMsMzIsNjcsLTExMiwtOTksMTE4LDc0LDY5LDgyLC0xMDcsMSwxMDgsNiw4MCwtNywxMDcsOTEsNTQsLTg2LC0xMDMsLTgsLTk5LC0xOCwtNTMsLTc2LC02NCwtMTEwLDkxLC0xMTQsMTE3LC00MCwtNjIsMjEsODMsNDYsMzEsNTUsOSwtODksLTEyMSwtOTUsLTExMCwtMzEsNjYsLTkzLDIyLC04Niw4OSwyMywyNiw3MywxMjQsNTYsNTIsLTQwLC02MSwyOSwtMzgsOSwtNzMsNDEsMTE3LDc0LC01OCwxMTUsNDIsLTE3LDExNiwtNTIsLTc4LC0xMTEsLTEwOCwxMTMsLTIwLC04MiwxMDYsMTEyLC04MCwxNSwtMzMsMTAyLDIzLDYxLC0xMiwtMTI3LC0yMiwtMzgsMTExLC0zNSwtMTcsLTE0LC04MiwtMjYsNDIsLTksLTEyMCwtMTA3LC0xLDIwLC0zMyw4MiwtODEsNDEsNzUsNjAsNTcsLTExOCwyNyw1OCw3NSwtOTIsLTMyLDk1LDgzLC0xNCwtOTAsMTIyLC0xMTQsNiwtMTExLDEzLDI1LDEwMyw3MywtMzIsOTQsLTk2LDUwLDExOSwtNTksLTExNCwtMTIxLDU1LC0xMTIsMyw3MywtMzAsLTI3LC0yLDk2LC04NCw4MiwzNSw3LDM3LC02NSwxOSwxOCwtOTgsLTEyNCwtMTE2LC0zLDEzLC0xMjgsNjIsMTEyLC0yMSw5OSw3OCw3MywtMjMsMjQsOTcsMzIsNjQsLTkwLC00NSwtNjAsLTU3LC01MiwtNDIsODgsMTIwLDgwLDQ5LDc1LC00LC01NSwtNTEsLTExNywyMywtMjcsMTI3LC0xMTUsLTQxLDEwMiwtNzgsLTEwNCw3OCwxMTksMTEyLDEwMywtMTE3LDY3LC02NCw0MywwLDQ1LC04MCwtMTksLTQ0LC05MywtNyw1NywtMTI2LDYwLDg4LC00NCwtNzEsODAsLTYsMzgsLTc4LC0xNSwzNiwtMTYsMjYsMCwtNzcsNjgsLTYsNDQsNywxMywxMjQsNTMsMjksLTQzLC0xMjEsMjcsOTcsMTIzLDExMSwzMCwxMCw0NSwtNTYsLTc2LDcsNDUsLTEyLDI3LC00NCwtMTA3LDc3LDQ1LC0xMDEsMzMsNzYsLTQ1LDExMiwtMTI1LDQ5LDc4LDgyLC04LDMxLC0xMjcsMTE3LDQzLDc3LDEyMSw5M10sIm1hdGVyaWFsU2V0U2VyaWFsTnVtYmVyIjoxLCJpdlBhcmFtZXRlclNwZWMiOnsiaXYiOlszMCw5NiwtNSw5NywtMjcsLTEwMywxMTYsLTgwLC0xMywtMTA4LC01MywtNDksMiw1MiwtNTAsMjldfX0=", 188 | "eventLeftScope": "False", 189 | "ruleParameters": "{}", 190 | "executionRoleArn": "arn:aws:iam::123456789000:role/config-role", 191 | "accountId": "123456789000" 192 | } -------------------------------------------------------------------------------- /lambda_toolkit/data/standard-folder/invoke/events/dynamodb_update.json: -------------------------------------------------------------------------------- 1 | { 2 | "Records": [{ 3 | "eventID": "1", 4 | "eventVersion": "1.0", 5 | "dynamodb": { 6 | "Keys": { 7 | "Id": { 8 | "N": "101" 9 | } 10 | }, 11 | "NewImage": { 12 | "Message": { 13 | "S": "New item!" 14 | }, 15 | "Id": { 16 | "N": "101" 17 | } 18 | }, 19 | "StreamViewType": "NEW_AND_OLD_IMAGES", 20 | "SequenceNumber": "111", 21 | "SizeBytes": 26 22 | }, 23 | "awsRegion": "us-west-2", 24 | "eventName": "INSERT", 25 | "eventSourceARN": "arn:aws:dynamodb:us-east-1:123456789000:table/tablename", 26 | "eventSource": "aws:dynamodb" 27 | }, { 28 | "eventID": "2", 29 | "eventVersion": "1.0", 30 | "dynamodb": { 31 | "OldImage": { 32 | "Message": { 33 | "S": "New item!" 34 | }, 35 | "Id": { 36 | "N": "101" 37 | } 38 | }, 39 | "SequenceNumber": "222", 40 | "Keys": { 41 | "Id": { 42 | "N": "101" 43 | } 44 | }, 45 | "SizeBytes": 59, 46 | "NewImage": { 47 | "Message": { 48 | "S": "This item has changed" 49 | }, 50 | "Id": { 51 | "N": "101" 52 | } 53 | }, 54 | "StreamViewType": "NEW_AND_OLD_IMAGES" 55 | }, 56 | "awsRegion": "us-west-2", 57 | "eventName": "MODIFY", 58 | "eventSourceARN": "arn:aws:dynamodb:us-east-1:123456789000:table/tablename", 59 | "eventSource": "aws:dynamodb" 60 | }, { 61 | "eventID": "3", 62 | "eventVersion": "1.0", 63 | "dynamodb": { 64 | "Keys": { 65 | "Id": { 66 | "N": "101" 67 | } 68 | }, 69 | "SizeBytes": 38, 70 | "SequenceNumber": "333", 71 | "OldImage": { 72 | "Message": { 73 | "S": "This item has changed" 74 | }, 75 | "Id": { 76 | "N": "101" 77 | } 78 | }, 79 | "StreamViewType": "NEW_AND_OLD_IMAGES" 80 | }, 81 | "awsRegion": "us-west-2", 82 | "eventName": "REMOVE", 83 | "eventSourceARN": "arn:aws:dynamodb:us-east-1:123456789000:table/tablename", 84 | "eventSource": "aws:dynamodb" 85 | }] 86 | } -------------------------------------------------------------------------------- /lambda_toolkit/data/standard-folder/invoke/events/helloworld.json: -------------------------------------------------------------------------------- 1 | { 2 | "key3": "value3", 3 | "key2": "value2", 4 | "key1": "value1" 5 | } -------------------------------------------------------------------------------- /lambda_toolkit/data/standard-folder/invoke/events/kinesis_stream.json: -------------------------------------------------------------------------------- 1 | { 2 | "Records": [{ 3 | "eventID": "shardId-000000000000:49545115243490985018280067714973144582180062593244200961", 4 | "eventVersion": "1.0", 5 | "kinesis": { 6 | "partitionKey": "partitionKey-3", 7 | "data": "SGVsbG8sIHRoaXMgaXMgYSB0ZXN0IDEyMy4=", 8 | "kinesisSchemaVersion": "1.0", 9 | "sequenceNumber": "49545115243490985018280067714973144582180062593244200961" 10 | }, 11 | "invokeIdentityArn": "arn:aws:iam::123456789000:role/test", 12 | "eventName": "aws:kinesis:record", 13 | "eventSourceARN": "arn:aws:kinesis:us-east-1:123456789000:stream/test", 14 | "eventSource": "aws:kinesis", 15 | "awsRegion": "us-east-1" 16 | }] 17 | } -------------------------------------------------------------------------------- /lambda_toolkit/data/standard-folder/invoke/events/s3_delete.json: -------------------------------------------------------------------------------- 1 | { 2 | "Records": [{ 3 | "eventVersion": "2.0", 4 | "eventTime": "1970-01-01T00:00:00.000Z", 5 | "requestParameters": { 6 | "sourceIPAddress": "127.0.0.1" 7 | }, 8 | "s3": { 9 | "configurationId": "testConfigRule", 10 | "object": { 11 | "sequencer": "0A1B2C3D4E5F678901", 12 | "key": "HappyFace.jpg" 13 | }, 14 | "bucket": { 15 | "arn": "arn:aws:s3:::bucket_name", 16 | "name": "sourcebucket", 17 | "ownerIdentity": { 18 | "principalId": "EXAMPLE" 19 | } 20 | }, 21 | "s3SchemaVersion": "1.0" 22 | }, 23 | "responseElements": { 24 | "x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH", 25 | "x-amz-request-id": "EXAMPLE123456789" 26 | }, 27 | "awsRegion": "us-east-1", 28 | "eventName": "ObjectRemoved:Delete", 29 | "userIdentity": { 30 | "principalId": "EXAMPLE" 31 | }, 32 | "eventSource": "aws:s3" 33 | }] 34 | } -------------------------------------------------------------------------------- /lambda_toolkit/data/standard-folder/invoke/events/s3_put.json: -------------------------------------------------------------------------------- 1 | { 2 | "Records": [{ 3 | "eventVersion": "2.0", 4 | "eventTime": "1970-01-01T00:00:00.000Z", 5 | "requestParameters": { 6 | "sourceIPAddress": "127.0.0.1" 7 | }, 8 | "s3": { 9 | "configurationId": "testConfigRule", 10 | "object": { 11 | "eTag": "0123456789abcdef0123456789abcdef", 12 | "sequencer": "0A1B2C3D4E5F678901", 13 | "key": "HappyFace.jpg", 14 | "size": 1024 15 | }, 16 | "bucket": { 17 | "arn": "arn:aws:s3:::bucket_name", 18 | "name": "sourcebucket", 19 | "ownerIdentity": { 20 | "principalId": "EXAMPLE" 21 | } 22 | }, 23 | "s3SchemaVersion": "1.0" 24 | }, 25 | "responseElements": { 26 | "x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH", 27 | "x-amz-request-id": "EXAMPLE123456789" 28 | }, 29 | "awsRegion": "us-east-1", 30 | "eventName": "ObjectCreated:Put", 31 | "userIdentity": { 32 | "principalId": "EXAMPLE" 33 | }, 34 | "eventSource": "aws:s3" 35 | }] 36 | } -------------------------------------------------------------------------------- /lambda_toolkit/data/standard-folder/invoke/events/scheduled.json: -------------------------------------------------------------------------------- 1 | { 2 | "account": "123456789000", 3 | "region": "us-east-1", 4 | "detail": {}, 5 | "detail-type": "Scheduled Event", 6 | "source": "aws.events", 7 | "version": "0", 8 | "time": "2016-03-29T00:39:31Z", 9 | "id": "c714fbf3-d4ca-443d-a0f6-b1b1472d0291", 10 | "resources": ["arn:aws:events:us-east-1:123456789000:rule/Test"] 11 | } -------------------------------------------------------------------------------- /lambda_toolkit/data/standard-folder/invoke/events/ses.json: -------------------------------------------------------------------------------- 1 | { 2 | "Records": [{ 3 | "eventVersion": "1.0", 4 | "ses": { 5 | "mail": { 6 | "commonHeaders": { 7 | "from": [ 8 | "Jane Doe " 9 | ], 10 | "to": [ 11 | "johndoe@example.com" 12 | ], 13 | "returnPath": "janedoe@example.com", 14 | "messageId": "<0123456789example.com>", 15 | "date": "Wed, 7 Oct 2015 12:34:56 -0700", 16 | "subject": "Test Subject" 17 | }, 18 | "source": "janedoe@example.com", 19 | "timestamp": "1970-01-01T00:00:00.000Z", 20 | "destination": [ 21 | "johndoe@example.com" 22 | ], 23 | "headers": [{ 24 | "name": "Return-Path", 25 | "value": "" 26 | }, { 27 | "name": "Received", 28 | "value": "from mailer.example.com (mailer.example.com [203.0.113.1]) by inbound-smtp.us-east-1.amazonaws.com with SMTP id o3vrnil0e2ic28trm7dfhrc2v0cnbeccl4nbp0g1x for johndoe@example.com; Wed, 07 Oct 2015 12:34:56 +0000 (UTC)" 29 | }, { 30 | "name": "DKIM-Signature", 31 | "value": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=example.com; s=example; h=mime-version:from:date:message-id:subject:to:content-type; bh=jX3F0bCAI7sIbkHyy3mLYO28ieDQz2R0P8HwQkklFj4x=; b=sQwJ+LMe9RjkesGu+vqU56asvMhrLRRYrWCbVt6WJulueecwfEwRf9JVWgkBTKiL6m2hr70xDbPWDhtLdLO+jB3hzjVnXwK3pYIOHw3vxG6NtJ6o61XSUwjEsp9tdyxQjZf2HNYee873832l3K1EeSXKzxYk9Pwqcpi3dMC74ct9GukjIevf1H46hm1L2d9VYTL0LGZGHOAyMnHmEGB8ZExWbI+k6khpurTQQ4sp4PZPRlgHtnj3Zzv7nmpTo7dtPG5z5S9J+L+Ba7dixT0jn3HuhaJ9b+VThboo4YfsX9PMNhWWxGjVksSFOcGluPO7QutCPyoY4gbxtwkN9W69HA==" 32 | }, { 33 | "name": "MIME-Version", 34 | "value": "1.0" 35 | }, { 36 | "name": "From", 37 | "value": "Jane Doe " 38 | }, { 39 | "name": "Date", 40 | "value": "Wed, 7 Oct 2015 12:34:56 -0700" 41 | }, { 42 | "name": "Message-ID", 43 | "value": "<0123456789example.com>" 44 | }, { 45 | "name": "Subject", 46 | "value": "Test Subject" 47 | }, { 48 | "name": "To", 49 | "value": "johndoe@example.com" 50 | }, { 51 | "name": "Content-Type", 52 | "value": "text/plain; charset=UTF-8" 53 | }], 54 | "headersTruncated": false, 55 | "messageId": "o3vrnil0e2ic28trm7dfhrc2v0clambda4nbp0g1x" 56 | }, 57 | "receipt": { 58 | "recipients": [ 59 | "johndoe@example.com" 60 | ], 61 | "timestamp": "1970-01-01T00:00:00.000Z", 62 | "spamVerdict": { 63 | "status": "PASS" 64 | }, 65 | "dkimVerdict": { 66 | "status": "PASS" 67 | }, 68 | "processingTimeMillis": 574, 69 | "action": { 70 | "type": "Lambda", 71 | "invocationType": "Event", 72 | "functionArn": "arn:aws:ses:us-east-1:888888888888:identity/example.com" 73 | }, 74 | "spfVerdict": { 75 | "status": "PASS" 76 | }, 77 | "virusVerdict": { 78 | "status": "PASS" 79 | } 80 | } 81 | }, 82 | "eventSource": "aws:ses" 83 | }] 84 | } -------------------------------------------------------------------------------- /lambda_toolkit/data/standard-folder/invoke/events/sns.json: -------------------------------------------------------------------------------- 1 | { 2 | "Records": [{ 3 | "EventVersion": "1.0", 4 | "EventSubscriptionArn": "arn:aws:sns:us-east-1:123456789000:Test:4f43f32f-0e4f-4b14-ae25-df03b93652f9", 5 | "EventSource": "aws:sns", 6 | "Sns": { 7 | "SignatureVersion": "1", 8 | "Timestamp": "2016-01-25T22:35:26.578Z", 9 | "Signature": "ZUZZ8wdt9pD7dj1GObPvoi4ARbMouZfhseuYV316HRaHqZkNdBFBjDQvYtRhXmH0+hF0CcJxxw9fa7oPjBOeYrSeKwYAoDP8CXvD7PxA4S3x2Md1pzHNbNB36cNJ5zhAloFF1T0ifG3fB12CnFhwK+xT7C5VvD0fwswzToaF6YJVMLOMAtTAQc6Ccud0gRF6S4IgwFqxBwsnm1VM9pswosdn2xobVwE+H+nx+dJ+G+7UEsaDerv4K/h8Cl+xy/cDEL7eHhXJuwIpEUM+ENLRr7Py3P8x6EV/09OqZrtbC+xjQJrY90laPrvu1B1gScBiLMbUA7lGPU4UINYMgA/icA==", 10 | "SigningCertUrl": "https://sns.us-east-1.amazonaws.com/SimpleNotificationService-bb750dd426d95ee9390147a5624348ee.pem", 11 | "MessageId": "5e2abd9f-e569-5084-9233-f2bf4007c295", 12 | "Message": { 13 | "AutoScalingGroupName": "some_asg", 14 | "Service": "AWS Auto Scaling", 15 | "Time": "2016-01-01T00:00:00Z", 16 | "AccountId": "123456789000 ", 17 | "Event": "autoscaling: TEST_NOTIFICATION", 18 | "RequestId": "ee09301f-c3b3-11e5-94d9-3b439ffa9270", 19 | "AutoScalingGroupARN": "arn:aws:autoscaling:us-east-1:123456789000:autoScalingGroup:f9c94272-5d4a-47f4-98fd-b45e82c65a89:autoScalingGroupName/some_asg" 20 | }, 21 | "MessageAttributes": {}, 22 | "Type": "Notification", 23 | "UnsubscribeUrl": "https://sns.us-east-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-1:123456789000:test:4f43f32f-0e4f-4b14-ae25-df03b93652f9", 24 | "TopicArn": "arn:aws:sns:us-east-1:123456789000:Test", 25 | "Subject": "Auto Scaling: test notification" 26 | } 27 | }] 28 | } -------------------------------------------------------------------------------- /lambda_toolkit/data/standard-folder/lambdas/README.txt: -------------------------------------------------------------------------------- 1 | This folder stores your lambda functions projects. -------------------------------------------------------------------------------- /lambda_toolkit/data/standard-lambda/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by glucio on 7/5/17. 3 | * Default lambda JS project. 4 | */ 5 | var AWS = require('aws-sdk'); 6 | 7 | exports.handler = function(event, context, callback) { 8 | 9 | console.log("Hi, I'm here. Lambda-proxy is working. =)"); 10 | console.log("AWS Event ID: " + context.awsRequestId); 11 | console.log("Event Body: " + JSON.stringify(event)); 12 | 13 | 14 | /* NodeJS v0.10.42 15 | context.succeed("Done"); */ 16 | 17 | /* NodeJS v4.3 or v6.10 */ 18 | context.callbackWaitsForEmptyEventLoop = false; 19 | callback(null, true); 20 | }; -------------------------------------------------------------------------------- /lambda_toolkit/data/standard-lambda/index.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import json 3 | 4 | 5 | def lambda_handler(event, context): 6 | print("Hi, I'm here. Lambda-proxy is working. =)") 7 | print("AWS Event ID: " + context.aws_request_id) 8 | print("Event Body: " + json.dumps(event)) 9 | 10 | return True # Remove message from SQS 11 | -------------------------------------------------------------------------------- /lambda_toolkit/modules/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucioveloso/lambda-toolkit/6869a82f9f0bcc3d91e16b7965cf0f60b8521830/lambda_toolkit/modules/__init__.py -------------------------------------------------------------------------------- /lambda_toolkit/modules/cli.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from lambda_toolkit.modules.conf import Conf 4 | from lambda_toolkit.modules.utils import Utils 5 | import click 6 | from lambda_toolkit import __version__ 7 | import sys 8 | 9 | 10 | if len(sys.argv) > 1: 11 | if sys.argv[1] == "tail": 12 | # External modudle 13 | import tail_toolkit.modules.cli 14 | else: 15 | sys.argv[0] == "--help" 16 | 17 | def execute_cli(args): 18 | Utils.click_validate_required_options(click.get_current_context(), conf) 19 | module = click.get_current_context().info_name 20 | myclass = __import__("lambda_toolkit.modules." + module) 21 | clazz = getattr(getattr(myclass.modules, module), module.title()) 22 | getattr(clazz(conf, args), args['action'].replace("-", "_") + "_" + module)().save_config() 23 | 24 | conf = Conf() 25 | 26 | @click.group() 27 | @click.option('--debug/--no-debug', default=False) 28 | def cli(**kwargs): 29 | pass 30 | 31 | 32 | 33 | @cli.command() 34 | @click.argument('action', required=True, type=click.Choice(Utils.click_get_command_choice("queue", conf))) 35 | @click.option('--sqsname', '-q', callback=Utils.click_append_fifo_in_queue, help="Define the queue.") 36 | @click.option('--region', '-R', type=click.Choice(conf.aws_regions), help="Define the optional region.") 37 | @Utils.docstring_parameter(conf) 38 | def queue(**kwargs): 39 | execute_cli(kwargs) 40 | 41 | @cli.command() 42 | @click.argument('action', required=True, type=click.Choice(Utils.click_get_command_choice("proxy", conf))) 43 | @click.option('--proxyname', '-p', help="Define the proxy name.") 44 | @click.option('--sqsname', '-q', callback=Utils.click_append_fifo_in_queue, help="Define the queue name.", 45 | type=click.Choice(Utils.click_list_queues_without_fifo(conf))) 46 | @click.option('--rolename', '-r', default=conf.sett['C_DEFAULT_ROLE'], callback=Utils.click_verify_role_exists, 47 | help="Define the role or try to get the default.") 48 | @click.option('--runtime','-e', default="python2.7", help="Define runtime. (Default: Python2.7)", 49 | type=click.Choice(Utils.click_list_runtime())) 50 | @click.option('--region', '-R', type=click.Choice(conf.aws_regions), help="Define the optional region.") 51 | @Utils.docstring_parameter(conf) 52 | def proxy(**kwargs): 53 | execute_cli(kwargs) 54 | 55 | @cli.command() 56 | @click.argument('action', required=True, type=click.Choice(Utils.click_get_command_choice("project", conf))) 57 | @click.option( '--projectname', '-p', help="Define the project.") 58 | @click.option('--rolename', '-r', default=conf.sett['C_DEFAULT_ROLE'], callback=Utils.click_verify_role_exists, 59 | help="Define the role or try to get the default.") 60 | @click.option('--runtime','-e', default="python2.7", help="Define runtime. (Default: Python2.7)", 61 | type=click.Choice(Utils.click_list_runtime())) 62 | @click.option('--region', '-R', type=click.Choice(conf.aws_regions), help="Define the optional region.") 63 | @click.option( '--value', '-V', help="Define the a value to a variable") 64 | @click.option( '--variable', '-v', help="Define the a variable name") 65 | @Utils.docstring_parameter(conf) 66 | def project(**kwargs): 67 | execute_cli(kwargs) 68 | 69 | @cli.command() 70 | @click.argument('action', required=True, type=click.Choice(Utils.click_get_command_choice("role", conf))) 71 | @click.option('--rolename', '-r', 72 | help="Define the role or try to get the default.") 73 | @Utils.docstring_parameter(conf) 74 | def role(**kwargs): 75 | execute_cli(kwargs) 76 | 77 | @cli.command() 78 | @click.argument('action', required=True, type=click.Choice(Utils.click_get_command_choice("receiver", conf))) 79 | @click.option('--sqsname', '-q', callback=Utils.click_append_fifo_in_queue, help="Define the queue name.", 80 | type=click.Choice(Utils.click_list_queues_without_fifo(conf))) 81 | @click.option( '--projectname', '-p', help="Define the project.", type=click.Choice(conf.projects.keys())) 82 | @Utils.docstring_parameter(conf) 83 | def receiver(**kwargs): 84 | execute_cli(kwargs) 85 | 86 | @cli.command() 87 | @click.argument('action', required=True, type=click.Choice(Utils.click_get_command_choice("invoke", conf))) 88 | @Utils.docstring_parameter(conf) 89 | @click.option( '--projectname', '-p', help="Define the project.", type=click.Choice(conf.projects.keys())) 90 | @click.option( '--event-file', '-f', help="Define a file.", 91 | type=click.Choice(Utils.click_list_event_files(conf))) 92 | @click.option( '--proxyname', '-pp', help="Define the proxy.", type=click.Choice(conf.proxies.keys())) 93 | def invoke(**kwargs): 94 | execute_cli(kwargs) 95 | 96 | @cli.command() 97 | def tail(**kwargs): 98 | """Forward to tail-toolkit""" 99 | pass 100 | 101 | @cli.command() 102 | @Utils.docstring_parameter(conf) 103 | def list(**kwargs): 104 | modules = ['project', 'queue', 'proxy'] 105 | 106 | for m in modules: 107 | conf.log.info("") 108 | myclass = __import__("lambda_toolkit.modules." + m) 109 | args = {} 110 | args['action'] = "list" 111 | clazz = getattr(getattr(myclass.modules, m), m.title()) 112 | getattr(clazz(conf, args), args['action'].replace("-", "_") + "_" + m)().save_config() 113 | 114 | 115 | print("Initializing lambda-toolkit CLI (v" + __version__ + ") - Region: " + conf.region + " - Auth: " + conf.auth_mode) 116 | cli() 117 | -------------------------------------------------------------------------------- /lambda_toolkit/modules/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import lambda_toolkit.modules.logger as logger 4 | from lambda_toolkit.modules.utils import Utils 5 | import os 6 | import json 7 | import pkgutil 8 | import boto3 9 | from shutil import copytree 10 | 11 | 12 | class Conf: 13 | 14 | def __init__(self): 15 | self.config_file = os.path.join(os.path.expanduser('~'), ".lambda-toolkit.json") 16 | self.log = logger.get_my_logger("lambda-toolkit") 17 | # Get default configuration 18 | default_conf = json.loads(pkgutil.get_data("lambda_toolkit", "data/lambda-toolkit.json")) 19 | self.cli = default_conf['cli'] 20 | self.aws_regions = default_conf['aws-regions'] 21 | 22 | # Keep compatibility with previous versions 23 | self.sett = self._sync_settings(default_conf) 24 | 25 | self._copy_default_folder() 26 | self._set_authmode_and_default_region() 27 | 28 | def set_region(self, region): 29 | self.region = region 30 | if 'configurations' not in self.json_conf: 31 | self.json_conf['configurations'] = {} 32 | 33 | if self.region not in self.json_conf['configurations']: 34 | self.json_conf['configurations'][self.region] = {} 35 | 36 | confs = list(self.cli) 37 | # plural issue 38 | confs.remove("proxy") 39 | confs.append("proxie") 40 | for c in confs: 41 | c = c + "s" 42 | if c not in self.json_conf['configurations'][self.region]: 43 | self.json_conf['configurations'][self.region][c] = {} 44 | setattr(self, c, self.json_conf['configurations'][self.region][c]) 45 | 46 | return self 47 | 48 | def save_config(self): 49 | with open(self.config_file, "w") as f: 50 | f.write(json.dumps(self.json_conf, indent=4)) 51 | 52 | def get_boto3(self, api_name, api_method): 53 | func = getattr(__import__("boto3"), api_method) 54 | if self.auth_mode == "env": 55 | return func( 56 | api_name, 57 | aws_access_key_id=os.environ['AWS_ACCESS_KEY_ID'], 58 | aws_secret_access_key=os.environ['AWS_SECRET_ACCESS_KEY'], 59 | region_name=self.region 60 | ) 61 | else: 62 | return func(api_name, region_name=self.region) 63 | 64 | def _set_authmode_and_default_region(self): 65 | self.auth_mode = "env" 66 | for env_var in ['AWS_REGION', 'AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY']: 67 | if env_var not in os.environ: 68 | self.auth_mode = "file" 69 | s = boto3.session.Session().region_name 70 | if s is None: 71 | self.log.critical("Cannot read 'region' from env or credential file") 72 | self.set_region(boto3.session.Session().region_name) 73 | break 74 | 75 | if self.auth_mode == "env": 76 | self.set_region(os.environ['AWS_REGION']) 77 | 78 | 79 | def _copy_default_folder(self): 80 | if not os.path.exists(Utils.fixpath(self.sett['C_BASE_DIR'])): 81 | path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 82 | if not os.path.exists(Utils.fixpath(self.sett['C_BASE_DIR'])): 83 | copytree(os.path.join(path, Utils.fixpath(self.sett['C_STANDARD_FOLDER_DIR'])), 84 | Utils.fixpath(self.sett['C_BASE_DIR'])) 85 | 86 | def _sync_settings(self, default_conf): 87 | if os.path.isfile(self.config_file): 88 | with open(self.config_file, "r") as f: 89 | self.json_conf = json.loads(f.read()) 90 | # Check for new settings (Make compatible with older versions) 91 | for setting in default_conf['settings']: 92 | if setting not in self.json_conf['settings']: 93 | self.log.debug("Adding new setting: " + setting) 94 | self.json_conf['settings'][setting] = default_conf['settings'][setting] 95 | # Remove deprecated settings 96 | remove_list = [] 97 | for setting in self.json_conf['settings']: 98 | if setting not in default_conf['settings']: 99 | self.log.debug("Removing old setting: " + setting) 100 | remove_list.append(setting) 101 | for r in remove_list: 102 | self.json_conf['settings'].pop(r) 103 | else: 104 | self.json_conf = {} 105 | self.json_conf['settings'] = default_conf['settings'] 106 | 107 | return self.json_conf['settings'] 108 | -------------------------------------------------------------------------------- /lambda_toolkit/modules/invoke.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | import json 6 | import base64 7 | from lambda_toolkit.modules.lambdacontext import LambdaContext 8 | 9 | class Invoke: 10 | def __init__(self, conf, kwargs): 11 | self.lbs = conf.get_boto3("lambda", "client") 12 | self.log = conf.log 13 | self.conf = conf 14 | self.kwargs = kwargs 15 | 16 | def local_invoke(self): 17 | if 'python' not in self.conf.projects[self.kwargs['projectname']]['runtime']: 18 | self.log.error("Cannot invoke locally a JS runtime project in Python lambda-toolkit environment.") 19 | self.log.critical("Please find: lambda-toolkit-js project.") 20 | else: 21 | self.log.info("Importing project " + self.kwargs['projectname']) 22 | pp = os.path.join(os.path.expanduser(self.conf.sett['C_BASE_DIR']), 23 | self.conf.sett['C_LAMBDAS_DIR'], 24 | self.conf.region, 25 | self.kwargs['projectname']) 26 | 27 | self.log.debug("Using project dir: " + pp) 28 | sys.path.append(pp) 29 | 30 | if 'variables' in self.conf.projects[self.kwargs['projectname']]: 31 | vars = self.conf.projects[self.kwargs['projectname']]['variables'] 32 | 33 | for v in vars: 34 | self.log.info("Injecting lambda variable '" + v + "' with value '" + vars[v] + "'.") 35 | os.environ[v] = vars[v] 36 | 37 | ctx = LambdaContext(json.loads(open(os.path.join(os.path.expanduser(self.conf.sett['C_BASE_DIR']), 38 | self.conf.sett['C_INVOKE_DIR_CTX'], 39 | self.conf.sett['C_INVOKE_CTX_FILE'])).read())) 40 | 41 | func = getattr(__import__("index"), "lambda_handler") 42 | func(self._get_event(), ctx) 43 | 44 | return self.conf 45 | 46 | def remote_invoke(self): 47 | if "projectname" in self.kwargs and self.kwargs['projectname'] is not None: 48 | invoke_lambda = self.kwargs['projectname'] 49 | if self.conf.projects[invoke_lambda]['deployed'] == False: 50 | self.log.critical("Project '" + invoke_lambda + "' is not deployed.") 51 | else: 52 | invoke_lambda = self.kwargs['proxyname'] 53 | 54 | self.log.info("Invoking the project " + invoke_lambda) 55 | ret = self.lbs.invoke( 56 | FunctionName=invoke_lambda, 57 | LogType='Tail', 58 | InvocationType='RequestResponse', 59 | Payload=open(os.path.join(os.path.expanduser(self.conf.sett['C_BASE_DIR']), 60 | self.conf.sett['C_INVOKE_DIR_EVT'], 61 | self.kwargs['event_file'])).read() 62 | ) 63 | 64 | print(base64.b64decode(ret['LogResult']).decode()) 65 | 66 | return self.conf 67 | 68 | def _get_event(self): 69 | with open(os.path.join(os.path.expanduser(self.conf.sett['C_BASE_DIR']), 70 | self.conf.sett['C_INVOKE_DIR_EVT'], 71 | self.kwargs['event_file']), 72 | 'r') as f: 73 | return json.loads(f.read()) 74 | -------------------------------------------------------------------------------- /lambda_toolkit/modules/lambdacontext.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class LambdaContext: 4 | def __init__(self, context_json): 5 | self.aws_request_id = context_json['aws_request_id'] 6 | self.log_stream_name = context_json['log_stream_name'] 7 | self.invoked_function_arn = context_json['invoked_function_arn'] 8 | self.client_context = context_json['client_context'] 9 | self.log_group_name = context_json['log_group_name'] 10 | self.function_name = context_json['function_name'] 11 | self.function_version = context_json['function_version'] 12 | self.identity = context_json['aws_request_id'] 13 | self.memory_limit_in_mb = context_json['memory_limit_in_mb'] 14 | 15 | def get_remaining_time_in_millis(self): 16 | return 50000 -------------------------------------------------------------------------------- /lambda_toolkit/modules/logger.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import logging 4 | import sys 5 | 6 | 7 | class ShutdownHandler(logging.Handler): 8 | def emit(self, record): 9 | logging.shutdown() 10 | sys.exit(1) 11 | 12 | 13 | def get_my_logger(name): 14 | handler = logging.StreamHandler(sys.stdout) 15 | handler.setFormatter(logging.Formatter('[%(levelname)s] %(message)s')) 16 | 17 | logger = logging.getLogger(name) 18 | logger.handlers[:] = [handler] 19 | logger.setLevel(logging.DEBUG) 20 | logger.addHandler(handler) 21 | logger.addHandler(ShutdownHandler(level=50)) 22 | 23 | return logger 24 | -------------------------------------------------------------------------------- /lambda_toolkit/modules/project.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import pkgutil 5 | from lambda_toolkit.modules.utils import Utils 6 | from shutil import make_archive 7 | from shutil import rmtree 8 | import sys 9 | import time 10 | import json 11 | 12 | class Project: 13 | def __init__(self, conf, kwargs): 14 | self.log = conf.log 15 | 16 | if Utils.check_kwargs(kwargs, "region"): 17 | self.log.info("Updating region to '" + kwargs['region'] + "'.") 18 | self.conf = conf.set_region(kwargs['region']) 19 | else: 20 | self.conf = conf 21 | 22 | 23 | if Utils.check_kwargs(kwargs, "projectname"): 24 | self.projectname = kwargs['projectname'] 25 | self._set_project(self.projectname) 26 | 27 | self.kwargs = kwargs 28 | self.projects = self.conf.projects.keys() 29 | 30 | 31 | def _update_new_region(self, reg): 32 | self.conf.set_region(reg) 33 | self.projects = self.conf.projects.keys() 34 | 35 | def import_all_project(self): 36 | lambdas = self.conf.get_boto3("lambda", "client").list_functions() 37 | for mylb in lambdas['Functions']: 38 | if mylb['FunctionName'] not in self.conf.proxies: 39 | self._set_project(mylb['FunctionName']) 40 | self.import_project() 41 | 42 | self.log.info("Imported all projects in " + self.conf.region + ".") 43 | return self.conf 44 | 45 | def deploy_all_project(self): 46 | for s in list(self.projects): 47 | self._set_project(s) 48 | self.deploy_project() 49 | 50 | self.log.info("Deployed all projects in " + self.conf.region + ".") 51 | return self.conf 52 | 53 | def undeploy_all_project(self): 54 | for s in list(self.projects): 55 | self._set_project(s) 56 | self.undeploy_project() 57 | 58 | self.log.info("Undeployed all projects in " + self.conf.region + ".") 59 | return self.conf 60 | 61 | def create_project(self): 62 | if self.projectname in self.conf.projects: 63 | self.log.warn("Project '" + self.projectname + "' already exists in lambda-toolkit.") 64 | exit(1) 65 | self.conf.projects[self.projectname] = {} 66 | self.conf.projects[self.projectname]['variables'] = {} 67 | self._create_project_folders() 68 | 69 | if 'python' in self.kwargs['runtime']: 70 | open(self.project_dir + "/__init__.py", 'a').close() 71 | with open(os.path.join(self.project_dir, "index.py"), "wb") as text_file: 72 | text_file.write(pkgutil.get_data("lambda_toolkit", self.conf.sett['C_LAMBDASTANDARD_FUNC_PY'])) 73 | elif 'nodejs' in self.kwargs['runtime']: 74 | with open(os.path.join(self.project_dir, "index.js"), "wb") as text_file: 75 | text_file.write(pkgutil.get_data("lambda_toolkit", self.conf.sett['C_LAMBDASTANDARD_FUNC_JS'])) 76 | 77 | self.log.info("Project '" + self.projectname + "' " 78 | "[" + self.kwargs['runtime'] + "] has been created.") 79 | self.conf.projects[self.projectname]['deployed'] = False 80 | self.conf.projects[self.projectname]['runtime'] = self.kwargs['runtime'] 81 | 82 | return self.conf 83 | 84 | def delete_all_project(self): 85 | for s in list(self.projects): 86 | self._set_project(s) 87 | self.delete_project() 88 | 89 | self.log.info("Deleted all projects in " + self.conf.region + ".") 90 | return self.conf 91 | 92 | def delete_project(self): 93 | if self.projectname in self.conf.projects: 94 | self.conf.projects.pop(self.projectname) 95 | if not os.path.exists(self.project_dir): 96 | self.log.warn("The folder '" + self.project_dir + "' does not exist. Ignoring folder removing.") 97 | else: 98 | rmtree(self.project_dir) 99 | self.log.info("Project '" + self.projectname + "' has been deleted.") 100 | else: 101 | self.log.warn("Project '" + self.projectname + "' does not exist.") 102 | 103 | return self.conf 104 | 105 | def import_project(self): 106 | if self.projectname in self.conf.proxies.keys(): 107 | self.log.warn("Project '" + self.projectname + "' is a proxy. Ignoring import.") 108 | return self.conf 109 | 110 | try: 111 | lambda_function = self.conf.get_boto3("lambda", "client").get_function(FunctionName=self.projectname) 112 | 113 | if self.projectname in self.conf.projects: 114 | self.log.info("Project '" + self.projectname + "' already exists in your configuration. Updating.") 115 | else: 116 | self.conf.projects[self.projectname] = {} 117 | self.conf.projects[self.projectname]['variables'] = {} 118 | self._create_project_folders() 119 | open(self.project_dir + "/__init__.py", 'a').close() 120 | self.log.info("Project " + self.projectname + " imported.") 121 | 122 | self.conf.projects[self.projectname]['deployed'] = True 123 | self.conf.projects[self.projectname]['runtime'] = lambda_function['Configuration']['Runtime'] 124 | if 'Environment' in lambda_function['Configuration']: 125 | if 'Variables' in lambda_function['Configuration']['Environment']: 126 | vars = lambda_function['Configuration']['Environment']['Variables'] 127 | for v in vars: 128 | self.log.info("Importing lambda variable '" + v + "' with value '" + vars[v] + "'.") 129 | self.conf.projects[self.projectname]['variables'][v] = vars[v] 130 | 131 | import requests, zipfile 132 | if sys.version_info[0] == 3: 133 | import io 134 | r = requests.get(lambda_function['Code']['Location'], stream=True) 135 | z = zipfile.ZipFile(io.BytesIO(r.content)) 136 | else: 137 | import StringIO 138 | r = requests.get(lambda_function['Code']['Location'], stream=True) 139 | z = zipfile.ZipFile(StringIO.StringIO(r.content)) 140 | 141 | z.extractall(self.project_dir) 142 | z.close() 143 | except Exception as e: 144 | self.log.error(str(e)) 145 | self.log.warn("The project '" + self.projectname + "' does not exist in AWS environment.") 146 | 147 | return self.conf 148 | 149 | def deploy_project(self): 150 | if self.projectname not in self.projects: 151 | self.log.critical("Project '" + self.projectname + "' does not exist.") 152 | 153 | make_archive(self.project_zip_file_without_ext, "zip", self.project_dir) 154 | 155 | try: 156 | self.conf.get_boto3("lambda", "client").get_function(FunctionName=self.projectname) 157 | replace = True 158 | except Exception: 159 | replace = False 160 | 161 | try: 162 | 163 | if replace: 164 | self.conf.get_boto3("lambda", "client").update_function_code( 165 | FunctionName=self.projectname, 166 | ZipFile=open(self.project_zip_file, "rb").read(), 167 | ) 168 | self.log.info("Lambda project " + self.projectname + " was redeployed.") 169 | else: 170 | if self.kwargs['rolename'] is None: 171 | self.log.error("Project '" + self.projectname + "' is new. The parameter --rolename is required.") 172 | exit(1) 173 | return self.conf 174 | 175 | self.conf.get_boto3("lambda", "client").create_function( 176 | FunctionName=self.projectname, 177 | Runtime=self.kwargs['runtime'], 178 | Role=self.kwargs['rolename'], 179 | Handler='index.lambda_handler', 180 | Description="Lambda project " + self.projectname + " deployed by lambda-proxy", 181 | Code={ 182 | 'ZipFile': open(self.project_zip_file, "rb").read() 183 | }, 184 | ) 185 | self.log.info("Lambda project " + self.projectname + " was deployed.") 186 | 187 | if 'variables' in self.conf.projects[self.projectname]: 188 | self.conf.get_boto3("lambda", "client").update_function_configuration( 189 | FunctionName=self.projectname, 190 | Environment={ 191 | 'Variables': self.conf.projects[self.projectname]['variables'] 192 | } 193 | ) 194 | 195 | self.conf.projects[self.projectname]['deployed'] = True 196 | 197 | except Exception as e: 198 | self.log.error(str(e)) 199 | self.log.critical("Failed to deploy the lambda project.") 200 | 201 | return self.conf 202 | 203 | def undeploy_project(self): 204 | if self.projectname not in self.projects: 205 | self.log.critical("Project '" + self.projectname + "' does not exist.") 206 | 207 | try: 208 | self.conf.get_boto3("lambda", "client").delete_function(FunctionName=self.projectname) 209 | self.log.info("Project '" + self.projectname + "' is now undeployed.") 210 | except Exception as e: 211 | self.log.warn("Project '" + self.projectname + "' is not deployed.") 212 | 213 | self.conf.projects[self.projectname]['deployed'] = False 214 | 215 | return self.conf 216 | 217 | def list_variables_project(self): 218 | if self.projectname in self.conf.projects: 219 | vars = self.conf.projects[self.projectname]['variables'] 220 | self.log.info("Lambda environment variables:") 221 | for var in vars: 222 | self.log.info("Variable: " + var + " Value: " + vars[var]) 223 | 224 | return self.conf 225 | 226 | self.log.warn("Project does not exist") 227 | 228 | def set_variable_project(self): 229 | if self.projectname in self.conf.projects: 230 | self.conf.projects[self.projectname]['variables'][self.kwargs['variable']] = self.kwargs['value'] 231 | self.log.info("Variable " + self.kwargs['variable'] + " now is: " + self.kwargs['value']) 232 | return self.conf 233 | 234 | self.log.warn("Project does not exist") 235 | 236 | def unset_variable_project(self): 237 | if self.projectname in self.conf.projects: 238 | self.conf.projects[self.projectname]['variables'].pop(self.kwargs['variable']) 239 | self.log.info("Variable " + self.kwargs['variable'] + " has been removed.") 240 | return self.conf 241 | 242 | self.log.warn("Project does not exist") 243 | def delete_all_regions_project(self): 244 | for reg in self.conf.aws_regions: 245 | self._update_new_region(reg) 246 | self.delete_all_project() 247 | 248 | return self.conf 249 | 250 | def deploy_all_regions_project(self): 251 | for reg in self.conf.aws_regions: 252 | self._update_new_region(reg) 253 | self.deploy_all_project() 254 | 255 | return self.conf 256 | 257 | def undeploy_all_regions_project(self): 258 | for reg in self.conf.aws_regions: 259 | self._update_new_region(reg) 260 | self.undeploy_all_project() 261 | 262 | return self.conf 263 | 264 | def import_all_regions_project(self): 265 | for reg in self.conf.aws_regions: 266 | self._update_new_region(reg) 267 | self.import_all_project() 268 | 269 | return self.conf 270 | 271 | def list_aws_all_project(self): 272 | for reg in self.conf.aws_regions: 273 | self._update_new_region(reg) 274 | self.list_aws_project() 275 | 276 | return self.conf 277 | 278 | def list_all_project(self): 279 | for reg in self.conf.aws_regions: 280 | self._update_new_region(reg) 281 | self.list_project() 282 | 283 | return self.conf 284 | 285 | def check_regions_delay_project(self): 286 | for reg in self.conf.aws_regions: 287 | self._update_new_region(reg) 288 | start_time = time.time() 289 | self.conf.get_boto3("lambda", "client").list_functions() 290 | self.log.info('{0: <{1}}'.format(self.conf.region + ": ", 20) + 291 | str((time.time() - start_time))) 292 | 293 | return self.conf 294 | 295 | def list_aws_project(self): 296 | self.log.info("AWS Projects (Lambda Functions in " + self.conf.region + "):") 297 | lambdas = self.conf.get_boto3("lambda", "client").list_functions() 298 | for mylb in lambdas['Functions']: 299 | imported = False 300 | if mylb['FunctionName'] in self.projects: 301 | imported = True 302 | self.log.info('{0: <{1}}'.format("- Project:", 15) + 303 | '{0: <{1}}'.format(mylb['FunctionName'], 25) + 304 | '{0: <{1}}'.format("Imported:", 10) + 305 | '{0: <{1}}'.format(str(imported), 25) + 306 | '{0: <{1}}'.format("Runtime:", 10) + 307 | mylb['Runtime']) 308 | 309 | return self.conf 310 | 311 | def list_project(self): 312 | if len(self.projects) > 0: 313 | self.log.info("User Projects (Lambda Functions in " + self.conf.region + "):") 314 | for p in self.projects: 315 | self.log.info('{0: <{1}}'.format("- Project:", 15) + 316 | '{0: <{1}}'.format(p, 25) + 317 | '{0: <{1}}'.format("Deployed:", 10) + 318 | '{0: <{1}}'.format(str(self.conf.projects[p]["deployed"]), 25) + 319 | '{0: <{1}}'.format("Runtime:", 10) + 320 | self.conf.projects[p]["runtime"]) 321 | 322 | return self.conf 323 | 324 | def _create_project_folders(self): 325 | if not os.path.exists(self.project_dir): 326 | self.log.info("Creating the project lambda-toolkit folder '" + self.project_dir + "'") 327 | os.makedirs(self.project_dir) 328 | 329 | if not os.path.exists(self.project_zip_dir): 330 | self.log.info("Creating the zip lambda-toolkit folder '" + self.project_zip_dir + "'") 331 | os.makedirs(self.project_zip_dir) 332 | 333 | 334 | def _set_project(self, projectname): 335 | self.log.debug("Updating project environment to: '" + projectname + "'") 336 | if projectname in self.conf.proxies.keys(): 337 | self.log.critical("You cannot act in a project with the same name of an existing proxy.") 338 | self.projectname = projectname 339 | 340 | self.project_dir = os.path.join(Utils.fixpath(self.conf.sett['C_BASE_DIR']), 341 | Utils.fixpath(self.conf.sett['C_LAMBDAS_DIR']), 342 | self.conf.region, projectname) 343 | self.project_zip_dir = os.path.join(Utils.fixpath(self.conf.sett['C_BASE_DIR']), 344 | Utils.fixpath(self.conf.sett['C_LAMBDAS_DIR']), 345 | self.conf.region, 346 | Utils.fixpath(self.conf.sett['C_LAMBDAS_ZIP_DIR'])) 347 | self.project_zip_file = os.path.join(self.project_zip_dir, projectname + ".zip") 348 | self.project_zip_file_without_ext = os.path.join(self.project_zip_dir, projectname) 349 | 350 | # TODO -> ZIP (ALL / SPECIFIC) (local / remote) 351 | # TODO -> SYNC ONLY STATUS (ALL / SPECIFIC) 352 | # TODO -> SYNC FILES -> OVERWRITE LOCAL OR REMOTE (same that import and deploy?) 353 | # TODO -> COMPARE MD5 (ALL / SPECIFIC) 354 | # TODO -> IMPORT/DEPLOY WITH OVERWRITE OPTION 355 | # TODO -> import from a .zip (only local, only deploy, both) 356 | # TODO -> Change runtime 357 | # TODO -> Copy project among regions 358 | -------------------------------------------------------------------------------- /lambda_toolkit/modules/proxy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import pkgutil 5 | import pickle 6 | from shutil import make_archive 7 | from shutil import rmtree 8 | from lambda_toolkit.modules.utils import Utils 9 | 10 | 11 | class Proxy: 12 | def __init__(self, conf, kwargs): 13 | self.lbs = conf.get_boto3("lambda", "client") 14 | self.log = conf.log 15 | 16 | if Utils.check_kwargs(kwargs, "region"): 17 | self.log.info("Updating region to '" + kwargs['region'] + "'.") 18 | self.conf = conf.set_region(kwargs['region']) 19 | else: 20 | self.conf = conf 21 | 22 | if Utils.check_kwargs(kwargs, "proxyname"): 23 | self.proxyname = kwargs['proxyname'] 24 | self._set_proxyname(kwargs['proxyname']) 25 | 26 | self.proxies = self.conf.proxies.keys() 27 | self.queues = self.conf.queues.keys() 28 | self.kwargs = kwargs 29 | 30 | def list_proxy(self): 31 | if len(self.proxies) > 0: 32 | self.log.info("Proxies (Lambda proxies):") 33 | for q in self.proxies: 34 | self.log.info('{0: <{1}}'.format("- Proxy name:", 15) + 35 | '{0: <{1}}'.format(q, 25) + 36 | '{0: <{1}}'.format("Queue:", 10) + 37 | '{0: <{1}}'.format(self.conf.proxies[q]['sqsname'], 25) + 38 | '{0: <{1}}'.format("Runtime:", 10) + 39 | self.conf.proxies[q]['runtime']) 40 | 41 | return self.conf 42 | 43 | def undeploy_all_proxy(self): 44 | for q in list(self.proxies): 45 | self._set_proxyname(q) 46 | self.undeploy_proxy() 47 | 48 | self.log.info("Undeployed all proxies.") 49 | return self.conf 50 | 51 | def deploy_proxy(self): 52 | if self.proxyname in self.proxies: 53 | self.log.critical("The proxy '" + self.proxyname + "' already exists.") 54 | 55 | if self.kwargs['sqsname'] not in self.queues: 56 | self.log.critical("The queue '" + self.proxyname + "' does not exist.") 57 | 58 | try: 59 | os.makedirs(self.lambdaproxy_dir) 60 | except Exception as a: 61 | self.log.debug("Proxy temp folder already exists") 62 | 63 | f1 = pkgutil.get_data("lambda_toolkit", self.conf.sett['C_LAMBDAPROXY_FUNC']) 64 | if 'python' in self.kwargs['runtime']: 65 | index_file = "index.py" 66 | elif 'nodejs' in self.kwargs['runtime']: 67 | index_file = "index.js" 68 | 69 | f2 = open(os.path.join(self.lambdaproxy_dir, index_file), "w") 70 | f2.write(str(f1)) 71 | f2.close() 72 | 73 | make_archive(os.path.splitext(self.lambdaproxy_zip_file)[0], "zip", self.lambdaproxy_dir) 74 | 75 | try: 76 | self.lbs.create_function( 77 | FunctionName=self.proxyname, 78 | Runtime=self.kwargs['runtime'], 79 | Role=self.kwargs['rolename'], 80 | Handler='index.lambda_handler', 81 | Description="Proxy lambda function " + self.proxyname + "proxying requests to " + self.kwargs[ 82 | 'sqsname'], 83 | Code={ 84 | 'ZipFile': open(self.lambdaproxy_zip_file, "rb").read() 85 | }, 86 | Environment={ 87 | 'Variables': { 88 | 'sqsname': self.kwargs['sqsname'], 89 | } 90 | } 91 | ) 92 | self.log.info("Lambda proxy " + self.proxyname + " created proxying requests to " + self.kwargs['sqsname']) 93 | self.conf.proxies[self.proxyname] = {} 94 | self.conf.proxies[self.proxyname]['sqsname'] = self.kwargs['sqsname'] 95 | self.conf.proxies[self.proxyname]['runtime'] = self.kwargs['runtime'] 96 | 97 | except Exception as e: 98 | self.log.error(str(e)) 99 | self.log.critical("Failed to create the lambda function") 100 | 101 | rmtree(self.lambdaproxy_dir) 102 | 103 | return self.conf 104 | 105 | def undeploy_proxy(self): 106 | if self.proxyname not in self.proxies: 107 | self.log.critical("The proxy '" + self.proxyname + "' does not exist.") 108 | 109 | try: 110 | self.lbs.delete_function(FunctionName=self.proxyname) 111 | self.log.info("Lambda proxy '" + self.proxyname + "' has been removed.") 112 | os.remove(self.lambdaproxy_zip_file) 113 | except Exception as e: 114 | self.log.error(str(e)) 115 | self.log.error("Failed to delete the lambda proxy.") 116 | 117 | self.conf.proxies.pop(self.proxyname) 118 | 119 | return self.conf 120 | 121 | def _set_proxyname(self, proxyname): 122 | self.log.debug("Updating proxy environment to: '" + proxyname + "'") 123 | if proxyname in self.conf.projects.keys(): 124 | self.log.critical("You cannot create a proxy with the same name of an existing project.") 125 | 126 | self.proxyname = proxyname 127 | self.lambdaproxy_dir = os.path.join(Utils.fixpath(self.conf.sett['C_BASE_DIR']), 128 | Utils.fixpath(self.conf.sett['C_LAMBDAS_DIR']), 129 | self.conf.region, 130 | proxyname) 131 | self.lambdaproxy_zip_dir = os.path.join(Utils.fixpath(self.conf.sett['C_BASE_DIR']), 132 | Utils.fixpath(self.conf.sett['C_LAMBDAS_DIR']), 133 | self.conf.region, 134 | Utils.fixpath(self.conf.sett['C_LAMBDAS_ZIP_DIR'])) 135 | self.lambdaproxy_zip_file = os.path.join(self.lambdaproxy_zip_dir, proxyname + ".zip") 136 | -------------------------------------------------------------------------------- /lambda_toolkit/modules/queue.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from lambda_toolkit.modules.utils import Utils 4 | 5 | 6 | class Queue: 7 | def __init__(self, conf, kwargs): 8 | self.sqs = conf.get_boto3("sqs", "client") 9 | self.log = conf.log 10 | 11 | if Utils.check_kwargs(kwargs, "region"): 12 | self.log.info("Updating region to '" + kwargs['region'] + "'.") 13 | self.conf = conf.set_region(kwargs['region']) 14 | else: 15 | self.conf = conf 16 | 17 | if Utils.check_kwargs(kwargs, "sqsname"): 18 | self.sqsname = kwargs['sqsname'] 19 | 20 | self.queues = self.conf.queues.keys() 21 | 22 | def create_queue(self): 23 | if self.sqsname in self.conf.queues: 24 | self.log.critical("The queue '" + self.sqsname + "' already exists.") 25 | else: 26 | try: 27 | self.sqs.create_queue(QueueName=self.sqsname, 28 | Attributes={'VisibilityTimeout': '3', 29 | 'FifoQueue': 'true'}) 30 | 31 | self.conf.queues[self.sqsname] = {} 32 | 33 | self.log.info("The queue '" + self.sqsname + "' has been created.") 34 | except Exception as e: 35 | self.log.error(str(e)) 36 | self.log.critical("Failed to create the queue '" + self.sqsname + "'.") 37 | 38 | return self.conf 39 | 40 | def purge_queue(self): 41 | if self.sqsname in self.queues: 42 | try: 43 | response = self.sqs.get_queue_url(QueueName=self.sqsname) 44 | self.sqs.purge_queue(QueueUrl=response['QueueUrl']) 45 | self.log.info("The queue '" + self.sqsname + "' has been purged.") 46 | except Exception as e: 47 | self.log.error(str(e)) 48 | self.log.critical("Failed to purge the queue '" + self.sqsname + "'.") 49 | else: 50 | self.log.critical("The queue '" + self.sqsname + "' does not exist.") 51 | 52 | return self.conf 53 | 54 | def delete_all_queue(self): 55 | for q in list(self.queues): 56 | self.sqsname = q 57 | self.delete_queue() 58 | self.log.info("Deleted all queues.") 59 | return self.conf 60 | 61 | def delete_queue(self): 62 | uses = self._verify_queue_in_use() 63 | if uses is not None and len(uses) > 0: 64 | self.log.warn("Impossible to remove '" + self.sqsname + "'." 65 | + " The lambda-proxy '" 66 | + ', '.join(map(str, uses)) + "' is using it.") 67 | return self.conf 68 | 69 | if self.sqsname in self.queues: 70 | try: 71 | response = self.sqs.get_queue_url(QueueName=self.sqsname) 72 | self.sqs.delete_queue(QueueUrl=response['QueueUrl']) 73 | self.log.info("The queue '" + self.sqsname + "' has been removed.") 74 | except Exception as e: 75 | self.log.error(str(e)) 76 | self.log.error("Failed to remove the queue '" + self.sqsname + "' in AWS.") 77 | 78 | self.conf.queues.pop(self.sqsname) 79 | else: 80 | self.log.warn("The queue '" + self.sqsname + "' does not exist in lambda-toolkit.") 81 | 82 | return self.conf 83 | 84 | def list_queue(self): 85 | if len(self.queues) > 0: 86 | self.log.info("SQS (Queues):") 87 | for q in self.queues: 88 | self.sqsname = q 89 | uses = self._verify_queue_in_use() 90 | display_uses = "" 91 | if uses is not None and len(uses) > 0: 92 | display_uses = ', '.join(map(str, uses)) 93 | 94 | self.log.info('{0: <{1}}'.format("- Queue name:", 15) + 95 | '{0: <{1}}'.format(q, 25) + 96 | '{0: <{1}}'.format("Used by:", 10) + 97 | display_uses) 98 | 99 | return self.conf 100 | 101 | def _verify_queue_in_use(self): 102 | used = [] 103 | for q in self.conf.proxies.keys(): 104 | if self.conf.proxies[q]['sqsname'] == self.sqsname: 105 | used.append(q) 106 | 107 | return used -------------------------------------------------------------------------------- /lambda_toolkit/modules/receiver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import json 4 | import os 5 | import sys 6 | import signal 7 | from lambda_toolkit.modules.utils import Utils 8 | from lambda_toolkit.modules.lambdacontext import LambdaContext 9 | 10 | def signal_handler(signal, frame): 11 | sys.exit(0) 12 | 13 | signal.signal(signal.SIGINT, signal_handler) 14 | 15 | class Receiver: 16 | def __init__(self, conf, kwargs): 17 | self.log = conf.log 18 | self.conf = conf 19 | self.sqsname = kwargs['sqsname'] 20 | self.projectname = kwargs['projectname'] 21 | self.sqs = conf.get_boto3("sqs", "resource") 22 | 23 | def collect_receiver(self): 24 | 25 | queue = self.sqs.get_queue_by_name(QueueName=self.sqsname) 26 | self.log.info("Importing project " + self.projectname) 27 | pp = os.path.join(Utils.fixpath(self.conf.sett['C_BASE_DIR']), 28 | Utils.fixpath(self.conf.sett['C_LAMBDAS_DIR']), 29 | self.conf.region, self.projectname) 30 | self.log.debug("Using project dir: " + pp) 31 | sys.path.append(pp) 32 | a = __import__("index") 33 | func = getattr(a, "lambda_handler") 34 | 35 | self.log.info("Starting the receiver using the queue " + self.sqsname) 36 | 37 | if 'variables' in self.conf.projects[self.projectname]: 38 | vars = self.conf.projects[self.projectname]['variables'] 39 | 40 | for v in vars: 41 | self.log.info("Injecting lambda variable '" + v + "' with value '" + vars[v] + "'.") 42 | os.environ[v] = vars[v] 43 | 44 | while True: 45 | try: 46 | sys.stdout.write(".") 47 | sys.stdout.flush() 48 | msg_list = queue.receive_messages( 49 | VisibilityTimeout=int(self.conf.sett['QUEUE_GETMESSAGE_VISIBILITY_TIMEOUT']), 50 | MaxNumberOfMessages=int(self.conf.sett['QUEUE_GETMESSAGE_MAXNUMBEROFMESSAGES']), 51 | WaitTimeSeconds=int(self.conf.sett['QUEUE_GETMESSAGE_WAITTIMESECONDS'])) 52 | for msg in msg_list: 53 | jsonmsg = json.loads(msg.body) 54 | self.log.info("=======================================") 55 | self.log.info("* New message. Sending to " + self.projectname) 56 | 57 | if func(jsonmsg["event"], LambdaContext(jsonmsg["context"])): 58 | try: 59 | msg.delete() 60 | self.log.info("* Message deleted.") 61 | except Exception as e: 62 | self.log.warn("* Failed to delete the message. Expired.") 63 | self.log.warn("Configured timeout [QUEUE_GETMESSAGE_VISIBILITY_TIMEOUT]: " + str(self.conf.sett[ 64 | 'QUEUE_GETMESSAGE_VISIBILITY_TIMEOUT'])) 65 | 66 | else: 67 | self.log.info("* Project " + self.projectname + " returned False. Keeping message in the queue.") 68 | 69 | self.log.info("=======================================") 70 | 71 | except Exception as a: 72 | print(a) 73 | -------------------------------------------------------------------------------- /lambda_toolkit/modules/role.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from lambda_toolkit.modules.utils import Utils 4 | 5 | class Role: 6 | def __init__(self, conf, kwargs): 7 | self.log = conf.log 8 | self.conf = conf 9 | self.rolename = kwargs['rolename'] 10 | 11 | def set_default_role(self): 12 | Utils.click_verify_role_exists(None, None, self.rolename) 13 | 14 | if Utils.click_verify_role_exists(None, None, self.rolename): 15 | self.log.info("Role '" + self.rolename + "' is set as default now.") 16 | self.conf.sett['C_DEFAULT_ROLE'] = self.rolename 17 | return self.conf 18 | 19 | def unset_default_role(self): 20 | if self.conf.sett['C_DEFAULT_ROLE'] is not None: 21 | self.log.info("Unset the default role '" + self.conf.sett['C_DEFAULT_ROLE'] + "'.") 22 | self.conf.sett['C_DEFAULT_ROLE'] = "" 23 | else: 24 | self.log.warn("There isn't a default role configured.") 25 | 26 | return self.conf 27 | -------------------------------------------------------------------------------- /lambda_toolkit/modules/utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import lambda_toolkit.modules.logger as logger 4 | import boto3 5 | import pkgutil 6 | import os 7 | import sys 8 | 9 | 10 | class Utils: 11 | 12 | def __init__(self): 13 | pass 14 | 15 | @staticmethod 16 | def fixpath(path): 17 | path = os.path.normpath(os.path.expanduser(path)) 18 | if path.startswith("\\"): 19 | return "C:" + path 20 | return path 21 | 22 | @staticmethod 23 | def docstring_parameter(*sub): 24 | def dec(obj): 25 | obj.__doc__ = pkgutil.get_data("lambda_toolkit", os.path.join(sub[0].sett['C_HELPS_FILES'], obj.__name__ + ".txt")) 26 | return obj 27 | return dec 28 | 29 | @staticmethod 30 | def click_list_runtime(): 31 | return ['python2.7', 'python3.6', 'nodejs6.10', 'nodejs4.3', 'nodejs4.3-edge'] 32 | 33 | @staticmethod 34 | def click_get_command_choice(command, conf): 35 | opts = [''] 36 | if command in conf.cli: 37 | for opt in conf.cli[command]['commands']: 38 | opts.append(opt) 39 | 40 | return opts 41 | 42 | @staticmethod 43 | def click_list_event_files(conf): 44 | opts = [''] 45 | for f in os.listdir(os.path.join(os.path.expanduser(conf.sett['C_BASE_DIR']), conf.sett['C_INVOKE_DIR_EVT'])): 46 | opts.append(f) 47 | return opts 48 | 49 | 50 | @staticmethod 51 | def click_validate_required_options(ctx,conf): 52 | if ctx.info_name in conf.cli: 53 | if ctx.params['action'] in conf.cli[ctx.info_name]['commands']: 54 | for check in conf.cli[ctx.info_name]['commands'][ctx.params['action']]: 55 | if sys.version_info[0] == 3: 56 | instance_type = str 57 | else: 58 | instance_type = unicode 59 | if isinstance(check, instance_type): 60 | # For single parameters 61 | c = check.replace("-", "_") 62 | if ctx.params[c] is None: 63 | logger.get_my_logger("Utils").critical("The option '--" + check + "' is required"); 64 | elif isinstance(check, list): 65 | # At least one parameter in the list should be informed (combined) 66 | find = False 67 | for c in check: 68 | c = c.replace("-", "_") 69 | if c in ctx.params and ctx.params[c] is not None: 70 | find = True 71 | break 72 | if find: 73 | continue 74 | logger.get_my_logger("Utils").critical("One of those parameters should be included: " + ', '.join(map(str, check))) 75 | else: 76 | logger.get_my_logger("Utils").critical("Invalid cli conf file in: " + check) 77 | 78 | 79 | @staticmethod 80 | def click_list_queues_without_fifo(conf): 81 | opts = [''] 82 | for c in conf.queues.keys(): 83 | if c.endswith(".fifo"): 84 | opts.append(c.replace(".fifo", "")) 85 | else: 86 | opts.append(c) 87 | return opts 88 | 89 | 90 | @staticmethod 91 | def click_append_fifo_in_queue(ctx, param, value): 92 | if value is None: 93 | return None 94 | elif value.endswith(".fifo"): 95 | return value 96 | else: 97 | return value + ".fifo" 98 | 99 | @staticmethod 100 | def check_kwargs(kwargs, value): 101 | if value in kwargs and kwargs[value] is not None: 102 | return True 103 | 104 | return False 105 | 106 | @staticmethod 107 | def click_verify_role_exists(ctx, param, value): 108 | if value == "": 109 | return None 110 | client = boto3.client('iam') 111 | try: 112 | response = client.get_role( 113 | RoleName=value.split('/')[-1] 114 | ) 115 | return value 116 | except Exception as e: 117 | logger.get_my_logger("Utils").debug(e) 118 | logger.get_my_logger("Utils").critical("The role '" + value + "' does not exist.") 119 | 120 | return None 121 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | pip>=9.0.1 2 | setuptools>=36.0.1 3 | wheel>=0.29.0 4 | twine>=1.3.1 5 | boto3>=1.4.4 6 | tail-toolkit>=0.0.7 7 | click>=6.7 8 | sphinx>=1.6.3 9 | requests>=2.18.1 10 | -------------------------------------------------------------------------------- /requirements-user.txt: -------------------------------------------------------------------------------- 1 | boto3>=1.4.4 2 | botocore>=1.5.78 3 | tail-toolkit>=0.0.7 4 | click>=6.7 5 | requests>=2.18.1 6 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [wheel] 2 | universal = 1 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import codecs 3 | import os.path 4 | import re 5 | 6 | from setuptools import setup, find_packages 7 | 8 | here = os.path.abspath(os.path.dirname(__file__)) 9 | 10 | 11 | def read(*parts): 12 | return codecs.open(os.path.join(here, *parts), 'r').read() 13 | 14 | 15 | def find_version(*file_paths): 16 | version_file = read(*file_paths) 17 | version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", 18 | version_file, re.M) 19 | if version_match: 20 | return version_match.group(1) 21 | raise RuntimeError("Unable to find version string.") 22 | 23 | 24 | requires = ['boto3>=1.4.4', 25 | 'botocore>=1.5.78', 26 | 'tail-toolkit>=0.0.7', 27 | 'click>=6.7.0', 28 | 'requests>=2.18.1'] 29 | 30 | setup_options = dict( 31 | name='lambda-toolkit', 32 | version=find_version("lambda_toolkit", "__init__.py"), 33 | description='An AWS Lambda command line interface (CLI). It helps you in creating, building, testing and deploying your lambda functions.', 34 | long_description=open('README.rst').read(), 35 | author='Lucio Veloso Guimaraes', 36 | author_email='lucio.veloso@gmail.com', 37 | url='https://github.com/lucioveloso/lambda-toolkit', 38 | scripts=['bin/lt'], 39 | packages=find_packages(exclude=['tests*']), 40 | include_package_data=True, 41 | install_requires=requires, 42 | classifiers=( 43 | 'Development Status :: 4 - Beta', 44 | 'Intended Audience :: Developers', 45 | 'Intended Audience :: System Administrators', 46 | 'Natural Language :: English', 47 | 'License :: OSI Approved :: Apache Software License', 48 | 'Programming Language :: Python', 49 | 'Programming Language :: Python :: 2', 50 | 'Programming Language :: Python :: 3', 51 | ), 52 | ) 53 | 54 | setup(**setup_options) 55 | --------------------------------------------------------------------------------