Category: Softwares

Software libraries or tools

  • Exploring Fractals with Animation

    Exploring Fractals with Animation

    Mandelbrot

    I was inspired recently to do the thing that every programmer does: Implement the Mandelbrot set.

    I’d been working with complex numbers and felt inspired to find a practical application to test my understanding. I opted to use Go since it’s a language I’m familiar with and it also has native support for complex numbers.

    I used it to not just create the set itself, like this

    But also to animate it, both to show the process of resolving, increasing the number of times you iterate a given value be more sure of it’s hyperbolicity.

    And also animations of zooming in within a specific region of the set, showing it’s infinite fractal depths.

    The iteration limit plays an important role in illustrating details, too much and some of the complexity at the edges is harder to see, too little and the details of those structures aren’t defined at all.

    Here, we see a region with an iteration limit of 200.

    And the same region again with an iteration limit of 4000.

    Much of what we’d decided was not hyperbolic within 200 iterations is now revealed to also be full of regions of hyperbolicity.

    I was happy with the code, so I published it. You can get the code here, but it’s not significantly different from the millions of other Mandelbrot set generators.

    Chaos Game

    The Chaos Game is a chaotic process, out of which complex order can emerge.

    To play the game, first you pick set of 3 or more anchor points on some plane. Then you pick some starting point and a proportion (e.g. a half). Now we’re going to iterate.

    At random, select one of the anchors and on the line between your current point and the anchor, move one proportion of the distance to it. Mark this location with a dot, this is your new location. This process is then repeated from the new location to a newly selected random anchor point.

    Over time, with iteration, patterns emerge from the chaos.

    While the choice of point locations is ultimately arbitrary, I opted pick a set of points all equally spaced from each other around some origin point by calculating roots of unity to give the most clear representation of the resulting forms. Given n points to pick, we pick a set of points such that xm=cos(2mπ/n) where m is the mth point of the n total points we wish to select, and similarly ym=sin(2mπ/n). e.g. the x for the 2nd root of 3 would be x2=cos(4π/3)=-1/2.

    Now for each frame of the animation we will iterate and plot some number of points. Then for the next frame, we will start with a blank slate but a slightly increased proportion.

    This gives us an animation where a fractal geometric shape looms out at us from the noise and then drifts into the distance.

    We see a Sierpinski Triangle here, with 3 points chosen.

    And a hexagon with 6.

    Any arbitrary number of points (3 or more, since 2 would produce a line and 1 a single point) can be chosen.

    An added bonus of writing your own tooling, is that you can make it do things that no existing tool is designed too (maybe for good reason!) and you can kind of go a bit crazy with it.

    I’ve published the code for this, and it is available here.

  • Mailu and Podman

    Mailu is a container based mail service software stack.

    At it’s core, it comprises of Postfix (SMTP), Dovecot (IMAP/POP) and Nginx (proxy) along with some optional extras. It’s all linked into a central container called admin which acts as a central authority. Officially it runs on Docker and Kubernetes (via Helm).

    I don’t currently use Docker or Kubernetes for my own services but I have been using Mailu to host some mail services. Instead, I use Podman which is more or less interchangable with Docker but has some differences. This means I’d been running Mailu with some custom setup steps and it was working quite happily.

    At various points, I documented my attempts at running it on Podman on a Github Issue. However, elsewhere on the Mailu issue tracker, something bad was happening. A user running on an unsupported setup raised a ticket and sniped at the developers, which results in the developers pushing this commit, which states it’s updating Roundcube but also includes the following:

    def test_unsupported():
        import codecs
        if os.path.isfile(codecs.decode('/.qbpxrerai', 'rot13')) or os.environ.get(codecs.decode('V_XABJ_ZL_FRGHC_QBRFAG_SVG_ERDHVERZRAGF_NAQ_JBAG_SVYR_VFFHRF_JVGUBHG_CNGPURF', 'rot13'), None):
            return
        print('Your system is not supported. Please start by reading the documentation and then http://www.catb.org/~esr/faqs/smart-questions.html')
        while True:
            time.sleep(5)

    For your convenience I’ll decode it for you, the rot13 encoded text is checking for the file /.dockerenv to check if it’s running in docker, and if it’s not it looks for the environment variable I_KNOW_MY_SETUP_DOESNT_FIT_REQUIREMENTS_AND_WONT_FILE_ISSUES_WITHOUT_PATCHES. If it’s not found, it prints an error (to stdout) and loops infinitely in a call to sleep().

    The intent is that unsupported setups need to set an environment variable to acknowledge that they’re not running a supported setup. However if you recall, I mentioned that one of their supported platforms was Kubernetes. Kubernetes may use containerd under the hood but it’s not going to create a .dockerenv file in the container. As such, some of their kubernetes users found that when they upgraded to 2.0.29 their admin containers silently hung. It was silent, because the error was printed rather than written like other log lines.

    This broke a few users setups (including mine) and left them confused as no error appeared to be reported. Infact, brining this to their attention led to them hurridly patching their Helm charts after initially denying it was an issue. However, while I disagree with their approach I do think they have a point. I decided since I was re-tooling my own setup anyway, to look at a better solution to running it on Podman. My previous attempts had always been just manually creating a 1:1 mapping from the services/volumes/networks in docker-compose.yml and making their equivalent in Pods and later Quadlets. These were specific to my own setup and could only really serve as a base for others to crib from.

    I started by finding a tool that would let me parse a docker-compose.yml file in Go, and I found the compose-spec.io library in golang which did exactly what I needed. I then used the Mailu docker-compose.yml template which is used to generate Mailu setups and create Go text templates to programmatically create a set of quadlet units to match an arbitrary docker-compose.yml for Mailu. After ironing out some issues, I’m currently using mail setup generated from it and I’ve tested initialising a few others with success.

    You can review my mailu-quadlet tool code here, there’s also a container image for it available. In terms of operation, it’s relatively simple. Just generate your setup on the Mailu site then download your provided docker-compose.yml and mailu.env to a directory on your host.

    wget https://[...]/docker-compose.yml
    wget https://[...]/mailu.env

    Now run the container, mounting the directory containing the docker-compose.yml and mailu.env file to /data inside the container (which it uses by default).

    podman run -v $(pwd):/data:z ghcr.io/cyberworm-uk/mailu-quadlet --uuid example

    It should now list a series of filenames ending in .container, .volume and .network which are the associated unit files for the resources it needs. You’ll notice they’re all prefixed with the --uuid you provided. If one isn’t provided a random one will be generated.

    The .env file will need to be copied to /etc/mailu as this is currently where the generated files expect to find it and these values are required for the services to operate correctly.

    sudo mkdir -p /etc/mailu
    sudo cp example.env /etc/mailu

    Next the unit files will need to be moved to where the Quadlet service expects to find them. Assuming they’re going to be rootful containers (the front container running Nginx requires binding to privileged ports) this would be /etc/containers/systemd/.

    sudo cp *.network *.container *.volume /etc/containers/systemd
    sudo systemctl daemon-reload
    # by default these will start on next boot
    # alternatively we can start them manually, e.g.
    for CONT in *.container; do sudo systemctl start ${CONT::-10}; done

    You should now see your Mailu containers running under podman.

    $ podman run ghcr.io/cyberworm-uk/mailu-quadlet:latest -help
    Usage of /cli:
      -compose string
            docker-compose.yml file for mailu (default "docker-compose.yml")
      -envfile string
            mailu.env file for mailu (default "mailu.env")
      -uuid string
            optional custom uuid to use for generated
    
    $ go doc github.com/cyberworm-uk/mailuquadlet.Mailu
    package mailuquadlet // import "github.com/cyberworm-uk/mailuquadlet"
    
    type Mailu struct {
            // Has unexported fields.
    }
    
    func NewMailu(compose, env string) *Mailu
    func (m *Mailu) Export()
    func (m *Mailu) Init(compose, env string)
    func (m *Mailu) Uuid(id string)