Skip to content

Implement autoupgrade #24

@crisidev

Description

@crisidev

We want to be able to autoupgrade fireguard (remember that the docker container version is synced with the running executable version).

To do this there are a bunch of things to do:

  • Figure out code to upgrade binary on the fly through double-fork, like in this aberration:
extern crate fork;
extern crate nix;

use nix::unistd::{fork, ForkResult};

use fork::{daemon, Fork};
use std::thread;
use std::time::Duration;
use nix::sys::signal;
use nix::unistd::Pid;
use std::env;
use std::fs::copy;
use std::os::unix::process::CommandExt;
use std::process::{self, Command};

struct Version {
    v: usize,
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let v = Version { v: 42 };
    if args.contains(&"--version".to_string()) {
        println!("{}", v.v);
        process::exit(0);
    }
    let old_pid = if args.contains(&"--old-pid".to_string()) {
        Some(args[2].clone())
    } else {
        None
    };
    let old_bin = if args.contains(&"--old-bin".to_string()) {
        Some(args[4].clone())
    } else {
        None
    };
    println!("Running catoblepa version {}", v.v);
    let version = Command::new("/tmp/catoblepa")
        .arg("--version")
        .stdout(process::Stdio::piped())
        .spawn()
        .unwrap()
        .wait_with_output()
        .unwrap()
        .stdout;
    let version = String::from_utf8_lossy(&version)
        .trim()
        .parse::<usize>()
        .unwrap();
    if version > v.v {
        println!("Running on old executable");
        println!("New catoblepa detected, version {}", version);
        if let Ok(Fork::Child) = daemon(true, true) {
            println!("I am inside the double fork");
            let result = Command::new("/tmp/catoblepa")
                .arg("--old-pid")
                .arg(&process::id().to_string())
                .arg("--old-bin")
                .arg("./target/debug/catoblepa")
                .spawn().unwrap().wait_with_output().unwrap().stdout;
            println!("Post daemonize: {}", String::from_utf8_lossy(&result));
        }
        println!("I am at the end of the old exec");
    } else {
        println!("Running new executable from /tmp/catoblepa");
        match old_pid {
            Some(pid) => {
                println!("We are killing the old blepa, pid: {}", pid);
                signal::kill(Pid::from_raw(pid.parse::<i32>().unwrap()), signal::SIGINT).unwrap();
            }
            None => {
                println!("We are NOT killing the old blepa");
            }
        }
        thread::sleep(Duration::from_secs(2));
        println!("Copying /tmp/catoblepa to {:?}", old_bin);
        let bytes = copy("/tmp/catoblepa", &old_bin.unwrap()).unwrap();
        println!("Copied {} new catoblepa", bytes);
    }
    process::exit(0);
}
  • Implement command upgrade with subcommands download, check, apply
  • Subcommand check: find latest release on github and validate the semver has changed curl -s https://api.github.com/repos/blackmesalab/fireguard/releases/latest |jq
  • Subcommand download: detect host triple and download latest release on github for the triple
  • Subcommand apply: Use the HORROR above and replace the binary after forking it twice wabbla bballabbab blu....
  • Automate apply using a background thread with a jitter. If we say that the upgrade SLA is 12 hours, we check and possibly upgrade after 12/2 hour (6) + a jitter between 1 minute and 6 hours.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions