261 karma · joined March 4, 2013
http://ysoftware.se http://andreineculau.com
I only had a PoC and then told myself this would be brilliant as an abstraction
So - reverse engineer and monitor in-transit behavior it is...
When you write "your live stack", my brain freezes. Even a simple "webapp" project in my team had 4 common stacks (prod, staging, dev, git-master which had every push to master getting deployed) plus individual stacks, one per developer, with the ability to create extra stacks for specific feature development.
Only prod had real data. Obviously (to me) you wouldn't shorcircuit lambdas in the prod env. Not even staging. Not even dev. Not even git-master. Short-circuit development environments where you explore. Very low risks there. We need to learn it's ok to fuck up, as long as we fuck up in a safe environment, nowhere near the nuclear button ;)
Back to the point: reality is that many build their AWS environment (prod) manually, maybe they duplicate once (dev) also manually, maybe they use some automation for their "code" (lambda) but that's it. This implies it's practically impossible to run end-to-end tests. You can't do that in prod for obvious reasons and you can't do it in dev either - you have many devs queueing, maybe dev is not in sync with prod etc.
My team ran cloudformation end-to-end. We actually orchestrated and wrapped cloudformation (this is yet another topic for not using terraform etc) so that if smth couldn't be done in CFN, it would still be automated and reproducible. Long story short, in 30 minutes (it was this long because we had to wait for cloudfront etc) we had a new environment, ready to play with. A total sandbox. Every dev had their own and it was easy to deploy from a release artifact or a git branch to this environment. Similarly you could create a separate env for more elaborate changes to the architecture. And test in a live environment.
Finally to your question: how do you test end-to-end?
If we talk about lambdas because that's where the business logic lies in a "serverless" architecture, then the answer is by calling the system which will eventually call your lambda/s along the way. If your lambda ia sitting behind AWS gateway, then fire an http request. Is it triggered when objects land on S3? Then push some object to S3. How do you assert? Just the same - http response, S3 changes etc. Not to mention you can also check cloudwatch for specific log entries (though they are not instant).
With this type of a setup, which sounds complex, but it is not since it is 100% reproducible (also from project to project - I had several), adding this proxy-to-my-dev-machine lambda would mean I can make local changes and then fire unit AND end-to-end tests without any changes pushed to AWS, which is the main time/energy consumer imo.
PS: sorry for the wall of text. Like i said i recently realized that the development realities have huge discrepancies, so i tried to summarize my reality :)
When it comes to Lambdas, given the reasons above, there's only one thing that can improve the experience: PROXY. Before i went on parental leave i had the idea is creating a proxy lambda which can be configured with an IP and port number. That IP and port is for your local dev environment. This way, when developing you can instruct a live system to short-circuit and to proxy calls to a local lambda available om the respective port. Trigger end-to-end tests by invoking AWS services that will eventually call the proxy lambda, and then your local lambda with the same environment/context/input, reply with output which will reach the proxy lambda, which will output the same content forward.
Cfn2dot source https://github.com/tobiipro/support-firecloud/blob/master/bi...
Used like this https://github.com/tobiipro/support-firecloud/blob/master/re...
One problem is that you need meta-data to give weights to different resources so that you highlight a lambda resource and not there associated cloudwatch loggroup resource, and its iam role, and iam policy, and its permission resource, etc
The other problem is the graph itself shows how the cloudformation resources depend on one another, not how your data/execution pipeline flows.
So you need to do it manually or maybe try to add lots of meta-data to the types of resources.
target1: ## Do this
target2:
target3: target2 ## Do that
would be nicely formatted as the output of `make help` into Available targets:
target1 Do this
target2 Do that
and if you want to see all targets along with their origin file, even those without help messages, type `make help-all` to render Available targets:
target1 path/to/Makefile Do this
target2 path/to/Makefile
target3 path/to/Makefile Do thatSoon after i gave up. Too many silly bugs, and no fixes.
Reference: https://github.com/andreineculau/fl-aws
for small repos, running linters and such on pre-push is great because you definitely don't have unlimited CI agents, so a lag of 10 seconds for a "git push" might mean you actually gain 30 minutes and not wait for an agent to be available and you also save your team some good minutes with a silly CI job failing with "missing whitespace".
PS: if you want to skip the pre-push hook once in a while, just run "git push --no-verify"
A simple tool like https://github.com/andreineculau/npm-publish-git does the job (it's regularly used and tested within my current company TobiiPro https://github.com/tobiipro).
We publish npm packages to git tags that get installed exactly the same as packages received from a registry, no build step necessary.
Take the JS world for instance, npm is many good&bad things, but one thing that was squeezed in the package.json spec is the ability to install packages from git repositories. And so, github already had a "package registry" for npm, and we publish npm packages to github without needing extra credentials, etc.
Granted npm could add a command "publish-to-git", or allow setting the repository to a github url, but a simple tool like https://github.com/andreineculau/npm-publish-git does the job (it's regularly used and tested within my current company TobiiPro https://github.com/tobiipro).
Who is interchanging information? In 99% of the cases, which is the target of the blog post, it's a human/developer and some software writing and reading a JSON file ON THE SAME MACHINE, IN THE SAME ENVIRONMENT. That is coding! You can do a far better job controlling a piece of software by actually writing code, telling it how to run.
Reversely, if you need to configure two pieces of software via the same config file, you need something that both can read, so you might go with JSON, XML, YAML, your flavour of the day.
But think for a second: does that happen more than 1% of the times? It doesn't!
Software like eslint (js), babel (js), emacs, etc are using configuration written in their appropriate languages, not in a serialized format. This allows for huge benefits.
If you had the chance to work with AWS for instance, you would have noticed how their Cloudformation templates are insane. They have pseudo functions, control statements IN JSON (and YAML FWIW)! On the other side of the globe, GCP teaches its customers to use python or jinja (a python template engine) to GENERATE YAML, though you can obviously use whatever language you want, which is then consumed by GCP.
You can let editorconfig/linters/formatters to take care of code style, and prettify-symbols-mode of your preferred presentation, which should be stable as well given a stable code style.
An example of a KATT spec: https://github.com/for-GET/katt/blob/master/doc/example-http...
This!
for reasons that I've explained in a README section https://github.com/tobiipro/http-lambda#a-word-on-httpsgithu...
Your snippet becomes
const {httpLambda} = require('http-lambda');
const app = require('./app');
exports.handler = httpLambda((http, _event, _context) => (
http.createServer(app);
));