Universal Control Plane

Docker Entreprise Edition


The Project

Docker Universal Control Plane (UCP) is the entreprise grade cluster management tool from Docker. It lets you install it on premises or in a private cloud. It's aim is to enable entreprises to migrate towards using container based applications but also to manage them and monitor them through a single interface. I've been working with the UCP team since 2017 and participated to the major releases of UCP 2.2, 3.0, and 3.1. My role mainly focused on the user interface so for the time I've worked on this project I was frontend engineer.

The video above showcases a detail view of one Kubernetes workload (here a Pod). The challenge when implementing such screen in the product is to have a 1:1 mapping with what the original command line interface displays for a similar action. Building accessible and scalable React components that can display the various data structures of a Kubernetes workload, or even a Swarm workload, in a cohesive manner is definitely a priority as a frontend engineer on UCP.

The creation wizard of "Grants" is one example of a user flow I had to focus on with both a product manager and a designer. Building a form that can guide the users through the creation of a grant (a UCP resource that will give certain rights for a given user or team against a set of resource) was a challenge as it requires the user to choose between multiple elements such as a user, a team, a role, and a resource set in a certain order. The resulting components and forms with the transition we can see above is an example of UI engineering, UX and product work I've got to take part on a daily basis as part of the UCP team.

This is perhaps one of my favorite feature I got to work on. Displaying large sets of data in tables is central in UCP. I worked here on adding an extra small UI/UX feature to allow the user to resize the columns of the tables and have them stick to their size through navigation by using Redux.

Technical details

For this project I focused exclusively on the frontend with the following technology:

  • React
  • Redux
  • Flow
  • Jest
  • Nightwatch
  • Emotion

Personal Growth

UCP was the first entreprise software I've worked on as I've only focused on SaaS before. It was a real change compared to the work I had done before since we only had 2 releases per year, and our user target was big corporations which meant that there was little to no margin of error when it came to releasing new features. This is how I've got to grow my skills as a frontend engineer. For the first time I had to mainly focus on testing not as a nice to have but as a necessity. I got to write tests for the UCP UI at every level:

  • Unit tests: we used Jest for our unit tests which is perhaps among my favorite tool. Unit testing was essentially done at the component level, for reducers and for utilities functions at the beginning. I've seen the benefits of proper unit testing in previous projects but UCP is perhaps the first project where I learned how to write proper tests (not testing implementation details, judge whether test is valuable or not, ...)

  • Integration tests: this is the part that I've felt was the most beneficial in terms of testing for this project. Using some pre-existing tools and re-implementing some mocked versions of some of the building blocks of our UI helped to efficiently write advanced tests with mocked data to check specific states or test the flow of some specific scenarios. Among these tests I wrote some tooling for my team to have a more advanced "mockedStore" as well as a full mock of the UCP UI sdks to test actions creators and Redux aware views of our app. It was fun and made testing a fun task. You can see a somehow similar implementation of this utility here: https://gist.github.com/MaximeHeckel/e640030e305c5ae9d38f133e1c032631. By adding and extra middleware that pushes every action executed in an array, we were able to:

    • fully test complex Redux actions and how they influence the state of the app.
    • test entire user flows like filling a form and checking if the submit API call contained the right payload and triggered the correct Redux action against the correct API endpoint.

As a result, every reducers, actions creators and form in the UI had some sort of test that would guarantee them to work under the right circumstances.

  • E2E tests: this was perhaps the most challenging part of the testing stack I worked with. Indeed, the toughest part was to ensure that the test we've written would always act in the same way and avoid any random failures which at the beginning felt like an impossible task given the complexity of the UCP infrastructure, but in the end, we made sure that every essential part of the app was fully covered by e2e tests. E2E testing was based on Nightwatch and Selenium.

https://testingjavascript.com taught me a lot of what I know about testing and enabled me to help my team to efficiently test UCP. I highly recommend this course to anyone who is eager to learn more about testing.

I also learned how to appreciate good tooling while working on this project. Both static typing with ESLint and type checking with Flow helped me to ensure consistency in the codebase, fix tech debt and avoid mistakes to happen before they hit production. Prettier is also among these tools that was tremendously helpful.

As stated in the integration testing part, I really enjoy building nice tooling, but what I enjoyed perhaps the most here was to build very efficient and useful components. The sub-component (or compound components) pattern (see below) was perhaps one of my favorite component patterns.

The tools and method used on this project inspired me to write about them on medium:

Challenges

Big Application

This is the first large scale project I got to work on. UCP contains more than 500 views, forms and lists and more than a 100 different React components. Ensuring performance and scalability for all these views and components was definitely a priority and one of the biggest challenge. I think the following best describes the challenges UI engineers face on a daily basis with big projects:

In a tiny app, we can hardcode a lot of special cases to account for the above problems. But apps tend to grow. We want to be able to reuse, fork, and join parts of our code, and work on it collectively. We want to define clear boundaries between the pieces familiar to different people, and avoid making often-changing logic too rigid. How do we create abstractions that hide implementation details of a particular UI part? How do we avoid re-introducing the same problems that we just solved as our app grows?

Quote from The Elements of UI Engineering

Entreprise Software release cycle

There are only 2 big releases per year. Having good automation and test coverage was key to make sure no issues were pushed to productions. If any error made it to the final release, the customer had to wait for an entire month to get a patch.

Tech Debt

Given its size, it was easy for this UI project to cary a lot of tech debt. Addressing it was even mote complicated due to the bi-annual release cycle, there was little to no margin for error when replacing or updating code.

Managing 2 different set of APIs and constructs

UCP supports both Swarm and Kubernetes. These 2 orchestrators have different APIs, but the frontend engineering team had to provide the same experience for both of them.


© 2019 Maxime Heckel. Made in SF.