filesystem: "root"
mode: 0755
path: "/usr/local/sbin/set-valid-hostname.sh"
contents:
  inline: |
    #!/bin/bash
    # On some platforms the hostname may be too long (>63 chars).
    #  - On firstboot the hostname is set in the initramfs before NetworkManager
    #    And it may be truncated at 64 characters (too long)
    #  - On reboot affect nodes use 'localhost'.
    #
    # This script is a simple workaround for hostname woes, including
    #  - NOT a localhost name
    #  - NOT longer than 63 characters. Names will be truncated at the
    #    first dot, and then capped at 63 char (which ever is less).
    #  - Race conditions between truncated hostnames by the dhclient
    #    and NetworkManager.
    #
    # Finally, this script is invoked via:
    #  - /etc/NetworkManager/dispatcher.d/90-long-hostnames
    #  - on boot via node-valid-hostname.service

    export PATH="/usr/bin:/usr/local/bin:/sbin:/usr/local/sbin:/bin:${PATH}"
    log() { logger --tag "$(basename $0)" "${@}"; }

    # wait_localhost waits until the host gets a real hostname.
    # This will wait indefinately. node-valid-hostname.service will terminate
    # this after 5m.
    wait_localhost() {
        log "waiting for non-localhost hostname to be assigned"
        while [[ "$(< /proc/sys/kernel/hostname)" =~ (localhost|localhost.localdomain) ]];
        do
            sleep 1
        done
        log "node identified as $(</proc/sys/kernel/hostname)"
        exit 0
    }

    set_valid_hostname() {
        local host_name=${1}
        local type_arg="transient"

        # /etc/hostname is used for static hostnames and is authorative.
        # This will check to make sure that the static hostname is the
        # less than or equal to 63 characters in length.
        if [ -f /etc/hostname ] && [ "$(cat /etc/hostname | wc -m)" -gt 0 ]; then
            etc_name="$(< /etc/hostname)"
            type_arg="static"
            if [ "${etc_name}" != "${host_name}" ]; then
                log "/etc/hostname is set to ${etc_name} but does not match ${host_name}"
                log "using /etc/hostname as the authorative name"
                host_name="${etc_name}"
            fi
        fi

        # Only mutate the hostname if the length is longer than 63 characters. The
        # hostname will be the lesser of 63 characters after the first dot in the
        # FQDN.
        if [ "${#host_name}" -gt 63 ]; then
            alt_name=$(printf "${host_name}" | cut -f1 -d'.' | cut -c -63)
            log "${host_name} is longer than 63 characters, using trunacated hostname"
            host_name="${alt_name}"
        fi
        log "setting ${type_arg} hostname to ${host_name}"
        /bin/hostnamectl "--${type_arg}" set-hostname "${host_name}"
        exit 0
    }

    cli_run() {
        mode="${1:?mode must be the first argument}"; shift;
        case "${mode}" in
                wait_localhost) wait_localhost;;
            set_valid_hostname) hname="${1:?hostname is a required last argument}";
                                set_valid_hostname "${hname}";;
                            *) log "unknown mode ${mode}"; exit 1;;
        esac
    }

    # Allow the functions to be sourced. This can be run either as a
    # standalone command or in systemd or part of NetworkManager.
    if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
        cli_run ${@}
    fi

