# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.

# Sets basic NixOS options for the generated ISO, and imports of all the other
# modules we're using.

{ config, lib, modulesPath, pkgs, ... }:
let
  cfg = config.iodriver;
in
{
  imports = [
    "${modulesPath}/installer/cd-dvd/iso-image.nix"
    ./vm.nix
  ];

  config = {
    networking.hostName = "iodriver";
    environment.systemPackages = with pkgs; [
      htop
      sysstat
      bottom
      tmux
      iotop
      fio
      xxd
    ];

    # Symlink our test disk to /dev/cobblestone.
    # Match a disk which, in the oxide control plane, has a name starting with
    # "iodrvtgt" The wildcard at the end lets us number them so we have unique
    # names. I don't know if there's a length limit on what gets put into this
    # field by propolis. I also don't know if there's limits on which characters
    # can be put in. so I'm using a conservatively-short prefix to match on. The
    # expanded form would be "iodriver-target", or something to that effect
    #
    # Matching this way means that we are not depending on disks getting
    # specific /dev/nvme<N> numbers during enumeration, and could be expanded
    services.udev.extraRules = ''
      KERNEL=="nvme*", ATTRS{subsysnqn}=="nqn.2014.08.org.nvmexpress:01de01deiodrvtgt*", SYMLINK+="cobblestone"
    '';


    # disk urandom initialization
    systemd.services.iodriverinit = {
      wantedBy = [ "multi-user.target" ];
      serviceConfig.Type = "oneshot";
      script = ''
        set -euxo pipefail
        # fill the disk before running any job, or indeed before asking for
        # any jobs to run

        # need to wait for the symlink to finish up being added by udev
        udevadm settle

        # /dev/cobblestone needs to exist after the settle. its absence means we
        # forgot to attach the target disk
        [[ -e /dev/cobblestone ]] || exit 1

        # this is expected to fail once dd hits the end of the device, since
        # we are not specifying a count. It'll fill the whole thing first, and
        # this way it works no matter the size of it
        dd if=/dev/urandom of=/dev/cobblestone bs=1M oflag=direct || true

        # we are ready :D
        echo 1 > /tmp/iodriver-ready.txt
      '';
    };


    # Allow login as root without a password.
    users.users.root.initialHashedPassword = "";

    # We've moved to ssh for VM control, so interactive getty on serial is fine
    # and probably good for debugging if something gets weird
    systemd.services."serial-getty@ttyS0".enable = true;

    # You can do this if you want to disable getty on serial later
    # systemd.services."serial-getty@ttyS0".enable = lib.mkForce false;

    # Automatically log in on gettys.
    services.getty.autologinUser = "root";

    # Instead of the NixOS version, write our version.
    services.getty.greetingLine = ''<<< iodriver ${pkgs.iodriver.rev} - \l >>>'';
    services.getty.helpLine = ''

      == iodriver console quick reference ==
      \e{blue}iodriver-run-all-jobs\e{reset}   Run all jobs in order
      \e{blue}iodriver-run-job-$NAME\e{reset}  Run a specific job
    '';

    # Let root log in to ssh without password. This is very insecure. Don't
    # deploy this to the internet :)
    services.openssh.enable = true;
    security.pam.services.sshd.allowNullPassword = true;
    services.openssh.settings.PermitRootLogin = "yes";
    services.openssh.settings.PasswordAuthentication = true;
    services.openssh.settings.PermitEmptyPasswords = "yes";
    services.openssh.settings.AllowUsers = [ "root" ];
    services.openssh.settings.KbdInteractiveAuthentication = false;

    # Name the output file `iodriver-${shortRev}.iso`.
    isoImage.isoBaseName = "iodriver-${pkgs.iodriver.shortRev}";

    # Heavy zstd compression does well, while giving us low decompression reqs
    # on the guest
    isoImage.squashfsCompression = "zstd -Xcompression-level 17";

    # Leave a few seconds for enabling kernel debug messages; but otherwise the
    # default of 10 seconds is a bit too long to be comfortable for automated
    # uses.
    boot.loader.timeout = lib.mkForce 3;
    boot.kernelParams = [
      # Display console messages to both the serial console and the VGA console.
      "copytoram"
      "console=ttyS0"
      "console=tty0"
    ];

    # These options are necessary for booting in Propolis.
    isoImage.makeEfiBootable = true;
    isoImage.makeUsbBootable = true;
    boot.initrd.availableKernelModules = [ "virtio_pci" "virtio_blk" ];

    # We do not intend to install NixOS from this ISO.
    system.disableInstallerTools = true;
    # This system has no state; squelch the warning.
    #
    # Doing this on a system with state is a bad idea; see:
    # https://nixos.org/manual/nixos/stable/options.html#opt-system.stateVersion
    system.stateVersion = config.system.nixos.version;
    # General other minimization
    documentation.doc.enable = false;
    documentation.info.enable = false;
    documentation.nixos.enable = false;
    programs.command-not-found.enable = false;
  };
}
