# `CONSFIGURATOR.PROPERTY.INSTALLER`

## API reference

### Bootloaders

#### Generic function: `INSTALLER:INSTALL-BOOTLOADER-PROPSPEC`

`(installer:install-bootloader-propspec bootloader-type volume &key)`

Return a propspec expression which installs bootloader of type BOOTLOADER-TYPE to VOLUME. The propapp yielded by the propspec may be of type :POSIX or of type :LISP.

The property can call CONTAINER:CONTAINED-P with relevant factors to determine whether the host to which we are connected is the host the bootloader will boot. For example, (container:contained-p :efi-nvram) returns NIL when building disk images, and T when installing a host from a live environment. Bootloader installation might behave differently when certain factors are not contained, or error out. For examples, see GRUB:GRUB-INSTALLED and U-BOOT:INSTALLED-ROCKCHIP.

#### Generic function: `INSTALLER:INSTALL-BOOTLOADER-BINARIES-PROPSPEC`

`(installer:install-bootloader-binaries-propspec bootloader-type volume &key)`

Return a propspec expression evaluating to a :POSIX propapp which fetches/installs whatever binaries/packages need to be available to install BOOTLOADER-TYPE to VOLUME.

### Properties

#### Property: `INSTALLER:FILES-INSTALLED-TO-VOLUMES-FOR`

`(installer:files-installed-to-volumes-for options host volumes &key chroot leave-open mount-below)`

Where CHROOT contains the root filesystem of HOST and VOLUMES is a list of volumes, recursively open the volumes and rsync in the contents of CHROOT. Also update the fstab and crypttab, and try to install bootloader(s).

If CHROOT is NIL, bootstrap a root filesystem for HOST directly to VOLUMES. In that case, OPTIONS is passed on to CHROOT:OS-BOOTSTRAPPED-FOR, which see.

MOUNT-BELOW and LEAVE-OPEN are passed on to WITH-OPENED-VOLUMES, which see.

#### Property: `INSTALLER:BOOTLOADER-BINARIES-INSTALLED`

`(installer:bootloader-binaries-installed)`

Install whatever binaries/packages need to be available to install the host’s bootloaders to its volumes from within that host. For example, this might install a package providing /usr/sbin/grub-install, but it won’t execute it.

#### Property: `INSTALLER:BOOTLOADERS-INSTALLED`

`(installer:bootloaders-installed)`

Install the host’s bootloaders to its volumes. Intended to be attached to properties like INSTALLER:CLEANLY-INSTALLED-ONCE using a combinator like ON-CHANGE, or applied manually with DEPLOY-THESE.

### Live replacement of GNU/Linux distributions

#### Property: `INSTALLER:CLEANLY-INSTALLED-ONCE`

`(installer:cleanly-installed-once &optional options original-os)`

Replaces whatever operating system the host has with a clean installation of the OS that the host is meant to have, and reboot, once. This is intended for freshly launched machines in faraway datacentres, where your provider has installed some operating system image to get you started, but you’d like have a greater degree of control over the contents and configuration of the machine. For example, this can help you ensure that the operation of the host does not implicitly depend upon configuration present in the provider’s image but not captured by your consfig. This property’s approach can fail and leave the system unbootable, but it’s an time-efficient way to ensure that you’re starting from a truly clean slate for those cases in which it works.

OPTIONS will be passed on to CHROOT:OS-BOOTSTRAPPED-FOR, which see. ORIGINAL-OS, if supplied, is a propapp specifying the old OS, as you would apply to a host with that OS.

The internal property CHROOT::%OS-BOOTSTRAPPER-INSTALLED will attempt to install the OS bootstrapper (e.g. debootstrap(8) for Debian). If ORIGINAL-OS is supplied then installation will use a package manager property for that OS. Otherwise, CHROOT::%OS-BOOTSTRAPPER-INSTALLED will fall back to trying PACKAGE:INSTALLED. Alternatively, you can install the bootstrapper manually before running Consfigurator and not supply ORIGINAL-OS. This is useful for original OSs whose package managers Consfigurator doesn’t yet know how to drive. You might apply an OS-agnostic property before this one which manually downloads the bootstrapper and puts it on PATH.

The files from the old OS will be left in ‘/old-os’. Typically you will need to perform some additional configuration before rebooting to increase the likelihood that the system boots and is network-accessible. This might require copying information from ‘/old-os’ and/or the kernel’s state before the reboot. Some of this will need to be attached to the application of this property using ON-CHANGE, whereas other fixes can just be applied subsequent to this property. Here are two examples. If you already know the machine’s network configuration you might use

    (os:debian-stable "bullseye" :amd64)
    (installer:cleanly-installed-once ...)
    (network:static "ens3" "1.2.3.4" ...)
    (file:has-content "/etc/resolv.conf" ...)

whereas if you don’t have that information, you would want something like

    (os:debian-stable "bullseye" :amd64)
    (on-change (installer:cleanly-installed-once ...)
      (file:is-copy-of "/etc/resolv.conf" "/old-os/etc/resolv.conf"))
    (network:preserve-static-once)

You will probably need to install a kernel, bootloader, sshd etc. in the list of properties subsequent to this one. A more complete example, using the combinator INSTALLER:WITH-CLEANLY-INSTALLED-ONCE, which see:

    (os:debian-stable "bullseye" :amd64)
    (disk:has-volumes
     (physical-disk
      :device-file #P"/dev/sda"
      :boots-with '(grub:grub :target "x86_64-efi")))
    (installer:with-cleanly-installed-once
        (nil '(os:debian-stable "buster" :amd64))

      :post-install
      ;; Clear out the old OS's EFI system partition contents.
      (file:directory-does-not-exist "/boot/efi/EFI")

      (apt:installed "linux-image-amd64")
      (installer:bootloaders-installed)

      (fstab:has-entries-for-volumes
       (disk:volumes
         (mounted-ext4-filesystem :mount-point #P"/")
         (partition
          (mounted-fat32-filesystem :mount-point #P"/boot/efi/"))))

      (file:is-copy-of "/etc/resolv.conf" "/old-os/etc/resolv.conf")
      (mount:unmounted-below-and-removed "/old-os")

      :always
      (network:static ...)
      (sshd:installed)
      (swap:has-swap-file "2G"))

Here are some other propapps you might want in the :POST-INSTALL list:

    (bootloaders-installed)
    (fstab:has-entries-for-volumes
     (disk:volumes
       (mounted-ext4-filesystem :mount-point #P"/")
       (partition (mounted-fat32-filesystem :mount-point #P"/boot/efi/"))))
    (file:is-copy-of "/root/.ssh/authorized_keys"
                     "/old-os/root/.ssh/authorized_keys")
    (mount:unmounted-below-and-removed "/old-os")

If the system is not freshly provisioned, you couldn’t easily recover from the system becoming unbootable, or you have physical access to the machine, it is probably better to use Consfigurator to build a disk image, or boot into a live system and use Consfigurator to install to the host’s usual storage.

#### Macro: `INSTALLER:WITH-CLEANLY-INSTALLED-ONCE`

`(installer:with-cleanly-installed-once &optional &body propapps)`

Apply INSTALLER:CLEANLY-INSTALLED-ONCE, passing along OPTIONS and ORIGINAL-OS, and attach to that application, using other property combinators, the application of PROPAPPS.

PROPAPPS is a concatenation of three lists of propapps delimited by keywords:

    '(:post-install
      (propapp1) (propapp2) ...

      :always
      (propapp3) (propapp4) ...

      :post-first-reboot
      (propapp5) (propapp6) ...)

Any of the keywords and their propapps may be absent, but the three lists must appear in this order. The :POST-INSTALL propapps are applied only if this deployment performed the clean reinstallation, right after that. The :ALWAYS propapps are applied next, whether or not this deployment performed the clean reinstallation. Finally, the :POST-FIRST-REBOOT propapps are applied, only if this deployment did not perform the clean reinstallation.

This mechanism handles common usages of INSTALLER:CLEANLY-INSTALLED-ONCE. For example:

    (installer:with-cleanly-installed-once (...)
      :post-install
      (installer:bootloaders-installed)
      (file:is-copy-of "/etc/resolv.conf" "/old-os/etc/resolv.conf")
      (mount:unmounted-below-and-removed "/old-os")

      :always
      (apt:installed "openssh-server")
      (ssh:authorized-keys ...)
      (network:static "enp1s0" ...)

      :post-first-reboot
      (my-cool-web-service)
      (apache:https-vhost ...))

Properties that should be applied only once, or that rely on accessing files from /old-os, are applied under :POST-INSTALL. Networking and shell access are established before the first reboot, so we don’t lock ourselves out. However, as these properties are part of the usual definition of the host, they go under :ALWAYS, not :POST-INSTALL, so that Consfigurator checks they are still applied each deployment. Finally, we defer setting up the host’s sites and services until after the first reboot, in case there are any problems setting those up when it’s still the old OS’s kernel that’s running.