a muffin with purple glowing regions where a 3d vornoi function using chebychev distance exceeds some threshold

metamuffin's personal website


infra - my homebrew package and configuration manager

The last 8 months I was configuring my server with my own little package manager infra before just recently replacing it again a few days ago for reasons I will mention later. This had multiple reasons; primarily that I want to version control all configuration of all my servers (the VPS and a Raspi) from one source-controlled repository.

infra good

My "package manager" was actually more like a library where one would write code to specify how the machines were set up. This is similar to how Nix or Guix do it. My implementation however was written in Rust and initially hacked together in a few hours. Let's look at an example:

// You would pass credentials to the machine here
let m = Machine::new(...); 

// Load a minimal gnix config without any hosts.
let mut gnix = Gnix::load(include_str!("gnix_base.yaml"))

m.install(Fastbangs {
    // Here gnix automatically allocates a new port for internal proxying.
    bind_addr: gnix.new_host("search.metamuffin.org"),
    data_dir: "/var/local/fastbangs".into(),
    ..Default::default()
}.service());

m.eu1.install(MetamuffinWebsite { 
    bind_addr: eu1_gnix.new_host("metamuffin.org"),
}.service()); // `service` transforms the mere software configuration to a systemd service that uses it. 

m.install(gnix);

Infra would for this example automatically download, compile, install, configure and start/enable required services. It was a total solution. The current state of each machine (i.e. installed packages) was tracked locally. Whenever the deployment specification changes, a diff is calculated and applied incrementally. Applying an installation is very general term in infra, it could mean placing some configs somewhere but also starting and stopping systemd services or whatever.

Procedurally generating the server configuration had more advantages than not struggling with port numbers, I could also create fully meshed Wireguard VPNs with ease. Something that is considerably more pain if done manually. The following excerpt creates such a network, automatically assigns IP addresses and even writes hostnames to /etc/hosts.

let mut hosts = HostsFile::default();
let mut wg_myservers_subnet = IpSubnet([10, 123, 0, 0].into(), 16);
let wg_myservers = WireguardMesh::new("myservers", 16);
server1.install(wg_myservers.add_peer(
    hosts.register(wg_myservers_subnet.next(), "server1"),
    "server1.example.org:12345".into(),
    "SECRET VALUE GOES HERE"
));
server2.install(wg_myservers.add_peer(
    hosts.register(wg_myservers_subnet.next(), "server2"),
    "server2.example.org:12345".into(),
    "SECRET VALUE GOES HERE"
));
server3.install(wg_myservers.add_peer(
    hosts.register(wg_myservers_subnet.next(), "server2"),
    "server3.example.org:12345".into(),
    "SECRET VALUE GOES HERE"
))
for s in [server1, server2, server3] {
    s.install(hosts);
}

infra bad

Like mentioned the state was tracked locally, which meant I was restricted to a single machine for maintaining my server.

Also, and this is the worst part, infra would manage the server by connecting via SSH and executing random commands through a simple shell wrapper. This process will not take any feedback about the success about what it did. Obviously all commands are executed intelligently but if the connection broke or something else destabilized the system, there was no way to fix it trivially.

Another problem was transparency. Although infra allows exporting an overview of your deployment as a directed graph, it still didn't suffice for a good understanding about what was configured where. Sometimes I would install two packages that wrote to the same configuration - something that infra does not worry about - and get unexpected results.

It could also be considered a minor problem, that some configuration files just don't prettier when written with a Rust DSL.

infra useless?

infra is flawed. It just doesn't work if you have anything serious to maintain. However the basic concept of generating configuration from code is quite nice and were somewhat elegant to use sometimes.

In the last week I replaced infra in my deployments with a different system that I will describe in the next article. Going forward though I am thinking of taking the best from infra and turning it into a static configuration and script generator to be used in the new system.

That's that. If you have any interesting feedback or thoughts on this topic, please reach out.

Article written by metamuffin, text licenced under CC BY-ND 4.0, non-trivial code blocks under GPL-3.0-only except where indicated otherwise



Advertisement by a first-party
Advertisement by a third-party