I am 27 y.o. and I rarely play video games now. Believe me, I am such a boring 💤 guy, that in 90% of cases I would choose to open my favorite code editor instead of Steam, which is covered in dust already. And definetly I do not miss the times when I could be lost for weeks in The Elder Scrolls or Grand Theft Auto. But each time something reminds me of a good old gaming title, a wide-wide smile spreads across my face. Even now, when I write this lines, I can’t stop myself from smiling :)
Games always had something in them: you felt the real spirit of exploration, you rushed with joy to your friends just to seat nearby and watch them play! And I bet that all of us had a brilliant idea of a game that everyone would love (if we had a chance to implement it). Becoming an astronaut was never an option, as there was always a choice to become a Game Developer.
Now, 15 years later, I am actually happy that my dreams have only partially come true. I am a developer, but with less “blood, sweat and pixels”. Instead of creating fancy drawn worlds, I have been creating new web ecosystems, platforms and APIs. It definetly sounds like a more boring job, but games have taught me that there is always field for fantasy and exploration. Especially, where it’s least expected.
So what if I told you, that APIs could also have some Easter Eggs? 🐣
Of course I am not talking about unplanned security holes, 0-day vulnerabilities or public APIs, where the main easter egg is the outdated documentation. Technically they count, but I would suggest a more positive approach. Let’s include some fun in our API responses!
In order to do that, we will write our own Lua plugin for Kong API Gateway. Don’t worry if you are not familiar with Lua or Kong. It took me just one working day to write a much more complex working plugin without any previous knowledge. Plus I’ll give a brief explanation and share some useful links to make your life easier. In any case, these tools may be a great addition to your technical arsenal, so I recommend you to spend some time afterwards to get to know them closer. If you are familiar with Kong and Lua, you may skip the next abstract and jump straight to Preparing to cook.
Kong and Lua
Kong Gateway 🦍 is the the world’s most popular open source API gateway. It is cloud-native, super fast, scalable and extendable. Kong itself extends underlying OpenResty web platform (based on LuaJIT), which runs on the ultra-performant Nginx core (written in C). Kong can run on any platform and in any environment: for example, it’s widely used as an Ingress controller for Kubernetes cluster. But the true power of Kong Gateway is the rich ecosystem of ready-to-use plugins which cover all the important cases: authentication, bot detection, proxy caching, rate limiting, request termination and so on. There are plenty of various useful examples on Kong Github. Though, before diving into source code I would strongly recommend to read official plugin development guide together with an article on declarative configuration and dbless mode.
It’s very important to understand, that Kong is much more than a perfect API Gateway. Kong is a full-fledged open-core API platform for powering of all your APIs and microservices. Kong is a prefect example of an innovative company which moves forward together with its open source community.
Lua 🌝 is a powerful, efficient, lightweight, embeddable scripting language. It is widely used in web (OpenResty, Redis, etc.) and is loved in gaming industry (WoW, Dark Souls, Star Wars series, etc.). As for me, that sounds like a perfect combination!
Here is a simple Lua tutorial to get started, but for more detailed information you can use official reference manual. It’s not required to read any of the above as our plugin code will be deadly simple. And last but not least, we will use LuaRocks package manager for plugin integration into Kong.
Preparing to cook
Cooking API easter eggs doesn’t require much knowledge or skill. Here is what we need to get started:
- one pot with water;
- two easter eggs: “#1 Disco” (3 min) and “#2 Jackpot” (6 min);
- cloned recipe from GitHub.
#1 Disco 💃🏿🕺: Any good APIs must know how to make you dance (and I am not talking about dancing around with their documentation). When anyone will add “Make-Me: dance” header to GET or HEAD API request, we will return them a link with an incendiary dance.
#2 Jackpot 💰: We will count all the incoming requests and every N responses will contain a special header “You-Have-Won: a prize”. A prize must be randomly chosen and must be something trendy that anyone would like to have.
Water is boiling
Initially our plugin has the following structure:
Plugin sources live in
src folder only.
.rockspec file is a specification for luarocks package manager.
run.sh are used in plugin development and testing.
kong.tpl.yml is a template for our declarative configuration.
Kong plugin file structure has some strict rules. There must always
handler.luaentry point file and
schema.lua, where plugin configuration is described.
header_filter.luaare our custom plugin files. They could have any name, but it’s a good practice to name them like that to reflect the lifecycle of requests in Kong.
After setting of project layout we need to create declarative configuration for Kong to start with:
cp kong.tpl.yml kong.yml
We will now modify
kong.yml to have the following configuration:
We have just added Nanoservice with named
/root to our Kong API Configuration. We have also set some parameters like
dance_link to our
Note: Microservices are a doubtful trend now, so we need a fresh cool title. Nanoservice title has nothing to do with the size of a service, it’s just a great API service created in the Nano text editor.
All of kong plugin parameters must be described in
schema.lua file. Parameters may have different types (string, number, array, etc.), they may be required or have default values.
Let’s now look at our main entry point
Handler contains imports from other files. We also set plugin priority and initialize plugin instance. Our plugin doesn’t depend on any other plugin, so priority value doesn’t matter much. But if we would want to include easter eggs only after authentication, then we would need to choose priority wisely, according to this priority execution list. Within plugin scope execution sequence will be the following:
We are totally ready to cook, so let’s start Kong Gateway immediately.
If everything is fine, we will see something like this in console:
kong_1 | No existing manifest. Attempting to rebuild...
kong_1 | kong-easter-eggs 1.0.0-1 is now installed in /home/kong/.luarocks (license: MIT)
kong_1 | 2020/05/31 16:49:36 [notice] 67#0: using the "epoll" event method
kong_1 | 2020/05/31 16:49:36 [notice] 67#0: openresty/18.104.22.168
kong_1 | 2020/05/31 16:49:36 [notice] 67#0: built by gcc 9.2.0 (Alpine 9.2.0)
kong_1 | 2020/05/31 16:49:36 [notice] 67#0: OS: Linux 4.9.184-linuxkit
kong_1 | 2020/05/31 16:49:36 [notice] 67#0: getrlimit(RLIMIT_NOFILE): 1048576:1048576
kong_1 | 2020/05/31 16:49:36 [notice] 67#0: start worker processes
kong_1 | 2020/05/31 16:49:36 [notice] 67#0: start worker process 71
To be totally sure, we can already make a request to Kong, which runs on port 8000 as described in our
curl localhost:8000 -IHTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Cooking Disco (3 min)
When we get “Make-Me” header, we don’t even want to make a call to the upstream service. We need to interfere into requests lifecycle as soon as possible and
access phase is one of the options to do that. Let’s look at our sources:
To understand code fully, you need to know that Kong is shipped with a set of helpful tools called PDK — Plugin Development Kit.
kong.response are examples of modules, provided by Kong to access request information and process responses. Let’s check if our egg is ready:
curl localhost:8000 -H "Make-Me: rich"I'm a teapot. I can't make you rich!
Well… Could be better, but this was expected, as we only taught our APIs dancing techniques. Let’s try again:
curl localhost:8000 -H "Make-Me: dance" -IHTTP/1.1 302 Moved Temporarily
Date: Sun, 31 May 2020 22:19:24 GMT
Awesome! We are redirected to our dancing video in
Location header. Disco is ready and now we can check Jackpot.
Hitting Jackpot (6 mins)
Let’s dive into code straight away:
You may have spotted, that we use some dirty magic in this line
ngx.shared.kong:incr(“requests_count”, 1, 0). Kong runs several worker processes, which run simultaneously. To count incoming requests we need to use the shared memory provided by OpenResty. Kong provides a caching abstraction via
kong.cache , but I have chosen to demonstrate another approach. From code we can see that we give out the prize only in case of successful response (≤399) and if there are any prizes. Let’s go and hit our well-deserved jackpot!
curl localhost:8000 -I...
You-Have-Won: Ticket to Crew Dragon
In every third response you will see “You-Have-Won” header. My jackpot is the Ticket to Crew Dragon. Nice one, but I am sure you can do better. Try yourself to see what you will win! APIs must always be rewarding as they really love to be used.
Setting the table
Now, when our easter eggs are ready, we need to color and pack them, so we could share them with the world. Luarocks helps us with that and you can already use them in your Kong API Gateways:
luarocks install kong-easter-eggs
There are several options on how plugins can be installed into Kong. As plugins need to be applied before the start of Kong, you can always create a custom Dockerfile with plugin instructions, based on the official Kong docker images.
Anyway, my fantasy is limited and you may want to add more cool things to your APIs. Here is the GitHub repository for you:
This is an official Kong Easter Eggs plugin (adding 'official' always makes it more powerful). It's production-ready (2…
I hope you liked the article as much as I loved writing it. Kong API Gateway is an awesome open source project, which is definetly worth reading and writing about. I hope that creation of even funny plugins, like the one from the article, helps them to expand their ecosystem.