diff --git a/machines/louise/configuration.nix b/machines/louise/configuration.nix index c283db1f..48d9739b 100644 --- a/machines/louise/configuration.nix +++ b/machines/louise/configuration.nix @@ -8,8 +8,17 @@ ../modules/malobeo_user.nix ../modules/sshd.nix ../modules/minimal_tools.nix + ../modules/autoupdate.nix ]; + malobeo.autoUpdate = { + enable = true; + url = "https://hydra.dynamicdiscord.de"; + project = "malobeo"; + jobset = "infrastructure"; + cacheurl = "https://cache.dynamicdiscord.de"; + }; + boot.loader.systemd-boot.enable = true; hardware.sane.enable = true; #scanner support diff --git a/machines/modules/autoupdate.nix b/machines/modules/autoupdate.nix new file mode 100644 index 00000000..6b28ec00 --- /dev/null +++ b/machines/modules/autoupdate.nix @@ -0,0 +1,138 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.malobeo.autoUpdate; +in +{ + options.malobeo.autoUpdate = { + enable = mkOption { + description = '' + Enables a timer that periodically checks a hydra instance for the last build of the local system, and switches to it if it is different. + + Also enables periodical /nix/store GC. + ''; + type = types.bool; + default = false; + }; + + # https://hydra.dynamicdiscord.de/job/malobeo/infrastructure/louise/latest + url = mkOption { + description = lib.mdDoc '' + Url to your hydra instace. like https://hydra.example.de + ''; + type = types.str; + default = ""; + }; + + project = mkOption { + description = lib.mdDoc '' + Name of the project at the hydra instace + ''; + type = types.str; + default = ""; + }; + + jobset = mkOption { + description = lib.mdDoc '' + Name of the jobset at the hydra instace + ''; + type = types.str; + default = ""; + }; + + cacheurl = mkOption { + description = lib.mdDoc '' + Url of the binary cache + ''; + type = types.str; + default = cfg.url; + }; + }; + + config = { + # the presence of this .service file signifies that the system is + # autoupdate-enabled. it is checked to prevent autoupdating back + # to a system without autoupdate when deploying with autoupdate + # for the first time. + systemd.services.autoupdate = lib.mkIf config.malobeo.autoUpdate.enable { + wantedBy = [ "multi-user.target" ]; + path = with pkgs; [ nix nettools curl jq ]; + serviceConfig = { + Type = "oneshot"; + # switch-to-configuration may not return. HACK: cap running + # time so that the timer can be scheduled again. + TimeoutStartSec = "30min"; + }; + script = '' + OLD=$(readlink /run/current-system) + echo Current system: $(basename $OLD) + NEW=$(curl -sLH "Accept: application/json" ${cfg.url}/job/${cfg.project}/${cfg.jobset}/${config.networking.hostName}/latest | jq -er .buildoutputs.out.path) + if [ -z "$NEW" ] || [ "$NEW" = "null" ]; then + echo "Unable to obtain updated system" + exit 1 + fi + echo New system: $(basename $NEW) + + if [ "$OLD" != "$NEW" ]; then + echo "Fetching new system built by ${cfg.url}/job/${cfg.project}/${cfg.jobset}" + # this should fetch the new system from the binary cache + nix copy --from ${cfg.cacheurl} "$NEW" + if [ -e "$NEW/etc/systemd/system/autoupdate.timer" ]; then + echo "Switch to the new system..." + nix-env -p /nix/var/nix/profiles/system --set $NEW + "$NEW/bin/switch-to-configuration" switch + else + echo "New system is not yet autoupdate-enabled. Refusing to switch into a dead end." + fi + else + echo "No update required" + fi + ''; + # don't let the switch kill this service, aborting the switch + restartIfChanged = false; + unitConfig.X-StopOnRemoval = false; + # create timer + startAt = "hourly"; + }; + + nix = { + # Show a diff when activating a new system except for microvms which handle this seperately + #diffSystem = config.malobeo.deployment.server or "" == ""; + gc = lib.mkIf config.malobeo.autoUpdate.enable { + automatic = true; + randomizedDelaySec = "6h"; + options = "--delete-older-than 21d"; + }; + }; + + environment.systemPackages = [ ( + # Provide a manual updating script that fetches the latest + # updated+built system from Hydra + pkgs.writeScriptBin "update-from-hydra" '' + #! ${pkgs.runtimeShell} -e + + OLD=$(readlink /run/current-system) + echo Current system: $(basename $OLD) + NEW=$(curl -sLH "Accept: application/json" ${cfg.url}/job/${cfg.project}/${cfg.jobset}/${config.networking.hostName}/latest | ${pkgs.jq}/bin/jq -er .buildoutputs.out.path) + if [ -z "$NEW" ] || [ "$NEW" = "null" ]; then + echo "Unable to obtain updated system" + exit 1 + fi + echo New system: $(basename $NEW) + + if [ "$OLD" != "$NEW" ]; then + echo "Fetching new system built by ${cfg.url}/job/${cfg.project}/${cfg.jobset}" + # this should fetch the new system from the binary cache + nix copy --from ${cfg.cacheurl} "$NEW" + echo "Switch to the new system..." + nix-env -p /nix/var/nix/profiles/system --set $NEW + "$NEW/bin/switch-to-configuration" switch + else + echo "No update required" + fi + '' + ) ]; + }; +}