dallagi

dallagi

Excontainers - Throwaway containers for your tests

TL;DR: library to conveniently spawn throwaway docker containers for your integration/functional tests. Not stable yet. Written by an Elixir newbie, any feedback is super welcome!

Hello everyone :slight_smile:

I’ve been studying Elixir in the past few months, and to get some practice I decided to try and develop a library that I felt could be useful for someone.

I developed something similar to the Testcontainers java library for elixir.
The idea is to make it as convenient as possible to launch and use docker containers within your integration/functional tests.
No need for external docker-compose.yml files, your dependencies are nicely and declaratively defined within your own tests.

Repo: GitHub - dallagi/excontainers: Throwaway test containers for Elixir applications · GitHub
Hex.pm: excontainers | Hex

Installation, features and basic usage are documented in the project’s README.

Note: while the functionality is there and it is quite comprehensively tested, this library hasn’t seen any real world usage yet, so it is to be considered not stable yet.

I hope someone will find it useful! If you’d like any functionality that is not implemented yet you are welcome to open an issue.
As I mentioned I just started learning elixir, so if you have any feedback or suggestion on how to make the project better/more idiomatic, please let me know! Thanks a lot in advance :blush:

Most Liked

sasajuric

sasajuric

Author of Elixir In Action

This is great, and something I think is very much needed in the ecosystem!

I believe that this has potential to be used beyond just starting containers from test. For example, we could use this to start/link the accompanying containers when the system starts, or to run containerized one-off commands.

I’ve been meaning to develop something like this myself as the part of the ci library, but it looks like excontainers already solves many of the things I planned.

The usage of ryuk for container cleanup is nice, I didn’t even know about it! My plan was to start a separate OS process (ideally a hidden Erlang node), which would roughly do the same thing. I still plan on trying this out, because I think it could solve a subtle race condition that exists with ryuk.

I wonder why is Docker.Api marked with @moduledoc false? This is something that I think could be useful to the general population, and I think it’s possibly the most important part of this library. Having a simple docker API wrapper would allow people to build various custom abstractions & docker flows.

The synchronous logic based on polling in exec_and_wait likely won’t work for the scenarios I have in mind. I’d probably aim for a reactive approach, i.e. a GenServer that receives response chunks from the start request, and stops once the exec command has finished. That way the client could also handle stdout/stderr in realtime, e.g. by logging the output. I’m not sure if this can be done with tesla, but I think that it should be possible with mint. I’ll try this out myself, because I need this for the ci library, but I can’t commit on time.

In any case, great job, thank for making this!

10
Post #2
sasajuric

sasajuric

Author of Elixir In Action

It’s a general race condition, but very subtle. Consider the following sketch:

container_id = start_my_container()
Excontainers.ResourcesReaper.register({"id", container_id})

If the beam node is taken down after docker run is invoked, but before the container is registered, the container will not be reaped.

Admittedly such scenario is not very likely, but given time it’s bound to happen at some point, and it may in turn lead to bugs which are hard to track.

The only solution I can think of is to run an external program which would be both, the starter of docker containers, and the “garbage collector”. When we want to start the container we ask the program to do this for us (which would obviously be wrapped under some nice API). That way, even if the beam node is taken down in the middle of the container start, the external program can still clean up all the resources. My idea is to try implementing this in Elixir, rather than go. If this works, we could discuss the option of merging it to excontainers.

No need to change your priorities for the moment :slight_smile: My plan is to implement my own docker exec as a wrapper around OsCmd. This would give me all the features of OsCmd practically for free, most notably async & sync execution with reactive output capture and proper cleanup on early shutdown. As a consequence, such implementation would use CLI instead of the HTTP API, so I don’t think it makes sense to have it in excontainers.

dallagi

dallagi

You are right about the race condition, thanks for letting me know!

I’m not sure if this would fit your scenario, but perhaps a simpler solution could be to use something different than the container ID to register the container for reaping, so that we can set up the reaper before starting the container (Ryuk supports all kinds of filters – by name, label, etc.)
For example we could generate a unique identifier (e.g. a UUID) for the container, set the reaper to kill containers with label e.g. reaping_id=<my-id> and then start the container with the unique label.

My idea is to try implementing this in Elixir, rather than go. If this works, we could discuss the option of merging it to excontainers.

Sure! I’d be happy to make excontainers 100% elixir, even for containerized dependencies :smiley:

My plan is to implement my own docker exec as a wrapper around OsCmd […]

Yeah I agree both that excontainers would not be a good fit for your usecase, and that bringing CLI-based functionalities in excontainers would not make much sense.

I’ll let you know if I make substantial progress on that front :slight_smile: thanks again for your feedback!

Where Next?

Popular in Announcing Top

tmbb
I’ve published the first version of my Makeup library. It’s a syntax highlighter for Elixir in the spirit of Pygments, Currently it highl...
New
danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 29377 241
New
mischov
import Meeseeks.CSS html = HTTPoison.get!("https://news.ycombinator.com/").body for story &lt;- Meeseeks.all(html, css("tr.athing")) do...
New
seancribbs
Today I released a new dialyzer Mix task as the dialyzex package! At the time we started writing this task, the existing dialyzer integra...
New
riverrun
I’ve just released version 3 of Comeonin, a password hashing library. The following small changes have been made: changes to the NIF c...
New
Qqwy
Hello everyone, I wrote a small library today called MapDiff. It returns a map listing the (smallest amount of) changes to get from map...
New
bluzky
You may know https://ui.shadcn.com/, a UI component library for React. I really love it’s design style and components. I’ve built some co...
384 13736 119
New
achempion
Hi, I would like to tell about my initiative to further maintain and develop Waffle project which is the fork of Arc library. The progre...
New
mattludwigs
Grizzly is a library for working with Z-Wave devices. Z-Wave is a low-frequency radio protocol for controlling smart home devices on a me...
New
pkrawat1
Hey guyz We at @aviabird are working on a payment library in elixir/phoenix. We are targeting March 2018 to add 56 Gateways to it. Have...
New

Other popular topics Top

aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
chrismccord
Phoenix 1.4.0 released Phoenix 1.4 is out! This release ships with exciting new features, most notably with HTTP2 support, improved deve...
688 30877 112
New
johnnyicon
Hi all, I’ve just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I’m trying to use Postgres...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New
nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New
svb
Hi! Currently I want to submit a form by pressing the Enter key. However, since my input field is of type “textarea” this is just adds a...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

We're in Beta

About us Mission Statement