``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

.. code-block:: none

  (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

.. code-block:: none

  (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:

.. code-block:: none

  (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:

.. code-block:: none

  (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:

.. code-block:: none

  '(: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:

.. code-block:: none

  (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.
