Maciej Opała — Senior Software Engineer

Enabling an outgoing traffic from AWS private subnets via a NAT gateway

In a project I’m currently working on, there was a need to add two slaves to our CI/CD server (which is Jenkins) to speed up the process of building the source code. CI server was already set up on an AWS infrastructure — unfortunately manually — but since we want to get rid off the infrastructure-related technical debt we decided to go with terraform scripts to prepare the slaves and set them up. A tricky part was that we didn’t want to expose these additional nodes to an external world. Hence they needed to be set up in a separate subnet which — through the subnet Jenkins master is defined in — allowed single way traffic with the outside world. The whole script can be found here, below you can find the explanation.

The very first section is dedicated to the variables being used in the script — it improves the readability significantly when all the variables used in the script are gathered in a single place.

Beyond the AMI and AZ which are self-descriptive there are two important variables defined: vpc_id and subnet_id . Since, as I’ve already mentioned, Jenkins server was set up manually these values are hardcoded and taken from the already existing pieces of infrastructure. The VPC and subnet allow to group net units logically. What’s important here, is that the subnet with the hardcoded ID can be accessed from the outside world through an internet gateway defined under the same VPC.

Next, in order, are instances for the slaves defined — here we have two of them. Beyond standard configuration (AZ, HDD and so on) there’re 3 interesting parts here:

user_data — used to install JDK on the instance — what’s interesting here that, despite the fact that you can log into newly-created instance doesn’t mean that user_data script has been finished. That is, it’s invoked asynchronously;

— used to install JDK on the instance — what’s interesting here that, despite the fact that you can log into newly-created instance doesn’t mean that script has been finished. That is, it’s invoked asynchronously; security_groups and subnet_id which are defined further in the script and will allow slaves to communicate with both jenkins and outside world.

First, security_groups . They both must be assigned to an already defined vpc_id , and define two rules:

one for incoming SSH traffic from the subnets gathered under the same VPC, this rule will allow an incoming traffic from the Jenkins master server

one for outgoing traffic from the instances itself, this rule allows the slaves to e.g. have access to linux repos with packages.

That’s easy.

Second, subnet_id . A subnet, is a logical unit that groups the newly-created instances. This is in on purpose, since we want to keep these instances hidden from the outside world.

To enable outgoing traffic we need to define a NAT gateway, through which, they will communicate with the external world. A NAT gateway needs a public IP, which is created using elastic IP. What’s important is that this NAT gateway isn’t defined in the same subnet as the slaves — this subnet doesn’t have access to the internet. Hence NAT gateway is defined in the same subnet as Jenkins master node.

Ok, now we have instances for slaves under a subnet, security groups for them and a NAT gateway along with a public IP assigned. To have access to the outside world there’s a one thing missing. Yes, a route table along with route and appropriate associations. This missing element matches the NAT gateway with our newly-created subnet and routes all the outgoing traffic through it. This way our slave instances can stay hidden from the outside world and have a one-way access to it.

Here’s how you can have publicly accessible Jenkins master server with two slaves that cannot be accessed from the outside. Below a simple diagram that illustrates our architecture.

Piotr Majcher — Frontend Developer

Formik external control with React Refs & Hooks

Formik is a great library that significantly facilitates working with forms in React. It makes all the tedious tasks, like implementing form validation or checking whether the form is dirty or not, really painless. If you haven’t used it yet, you should definitely check it out! However, life is not always ponies and rainbows and sometimes the requirements of your project don’t exactly align with the standard way of using a tool or a library — that’s exactly what happened to me recently with Formik. The standard way of implementing a form with this library is to pass your form together with form actions into the Formik component via render props. Formik passes a lot of handy stuff into the render function such as: all form handlers that you need to submit or reset the form, information about form errors or about which fields have been touched or whether the form is valid or dirty. Its usage is pretty straightforward, you bind all those props to the buttons and fields within the component that you are passing to the render function. But what if you want to read the form state or handle it form outside? It gets a little bit more tricky then… the case is not lost though, we can harness the power of React Refs and Hooks in order to achieve our goal. Let’s consider a simple example where we want to implement a form with two fields and submit or reset it using buttons placed outside of the Formik component (imagine that you have generic form controls on your page’s header that handle different forms that are dynamically loaded in the main area). We would also like to show the Reset button only when the form is dirty. Let’s have a look at the code below:

The FormikProps type (line 6) comes from the Formik library and describes the props that are passed to the render function. We take the dirty prop out of it and we want to be able to notify the parent component when it changes and this is why we pass the second prop to our FormComponent , which is the onDirtyChanged callback. Let’s use the React’s useEffect hook now to bind both together (line 11). There is nothing more interesting in the FormComponent , so let’s move to the SimpleForm , which is going to wrap our Formik and expose the form the outside world. It requires two props to be passed to it, onSubmit and onDirtyChanged callbacks — first will be called once the form is submitted and second is just passed down to the FormComponent . But wait … How are we going to submit the form if we have no actions inside of it? We are going to use the React.forwardRef function which lets us pass the ref to Formik to the parent component and thus grant the access to the submitForm() function. Have a look at the second snippet:

In order to get access to the Formik instance we are using a technique of passing a ref called callback refs , as it enables us to execute some actions once the ref is passed. Let’s have a look at line 14 — the setFormRef function will save the ref and perform the state initialisation, which in this case is setting the dirty state of the form. You might have noticed the use of Maybe — if you wonder what the heck it is, it’s a great library that makes it a lot easier to handle nulls and undefined in Typescript, go ahead and visit their github page and read an article of my colleague from SoftwareMill which explains why you should use it.

Coming back to the main subject of this article, we are all set and done already with handling the dirty state of the form and at line 44 we conditionally render the button for resetting the form. But how do we call the actions? Let’s have a look at submitForm and clearForm function at lines 21 and 27. What’s happening there is simply checking whether the ref to the Formik is there and calling the submitForm and resetForm functions. That’s it, nothing more! Once we click the submit button, the function from the instance of the Formik component will be called, which will cause the form to submit its values and eventually call the onSubmit that we provided.

Definitely, it is not the standard way that Formik should be used, and you should rather try to fit your actions inside of it. But if you ever come across a similar problem as I did, remember that React Refs and Hooks are there to help you!

Adam Smolarek — Senior Software Engineer

How to setup json casing in json4s and circe and a bit about monads philosophy

First, a pragmatic part about everyone’s favourite topic: JSON serialization :)

As you know, there are different casing options for JSON objects. In my opinion, the most popular are snake case (user_name), camel case (userName), and kebab case (user-name).

So how do you change them? For Circe, it is configured using implicit. You have to add in scope correct one. For snake case, it is like this:

For kebab case, it is like this:

For camel case you do not have to import anything, because it is the default.

There is also json4s which has a different interface for setting the same thing. And to be more precise, it is not setting the encoding on the encoder level but a single value level. That’s right there are methods on JValue level that you have to call if you would like to change the encoding after wrapped by implicit function jvalue2monadic .

For snake case, it is:

For kebab case, there is no support, so you have to write scala classes with field names like user-name to get kebab case. The dame goes for camel case which is default convention of naming fields in Scala classes.

Why know all of that? Sooner or later there will be a client that will ask you to encode JSON in a specific way.

Now, let’s talk about philosophy. Recently I finished listening to the audiobook “A History of Western Philosophy.”, a good book summarising philosophers and their point of view throughout the ages. Starting from ancient Greece and finishing on modern times. What caught my attention was one chapter about Gottfried Wilhelm Leibniz and his theory of windowless monads, yes those monads :) While the author of the summary was not a fan of the approach of describing the real world using this abstraction it was nonetheless interesting.

One warning — the book has ~40 hours duration, and this was only one chapter :)