diff --git a/Dockerfile b/Dockerfile index 46525542d..abb148367 100644 --- a/Dockerfile +++ b/Dockerfile @@ -46,6 +46,11 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ vim \ && rm -rf /var/lib/apt/lists/* +RUN apt-get update && apt-get install -y --no-install-recommends \ + skopeo \ + jq \ + && rm -rf /var/lib/apt/lists/* + # Init entry COPY scripts/entry.sh /usr/sbin/ ENTRYPOINT ["/usr/sbin/entry.sh"] diff --git a/buildroot-external/package/hassio/builder/Dockerfile b/buildroot-external/package/hassio/builder/Dockerfile deleted file mode 100644 index 7a421327e..000000000 --- a/buildroot-external/package/hassio/builder/Dockerfile +++ /dev/null @@ -1,23 +0,0 @@ -FROM debian:buster - -# Set shell -SHELL ["/bin/bash", "-o", "pipefail", "-c"] - -# Docker -RUN apt-get update && apt-get install -y --no-install-recommends \ - apt-transport-https \ - ca-certificates \ - curl \ - jq \ - gpg-agent \ - gpg \ - dirmngr \ - software-properties-common \ - && curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add - \ - && add-apt-repository "deb https://download.docker.com/linux/debian $(lsb_release -cs) stable" \ - && apt-get update && apt-get install -y --no-install-recommends \ - docker-ce docker-ce-cli containerd.io \ - && rm -rf /var/lib/apt/lists/* - -COPY hostapp.sh /usr/bin/ -ENTRYPOINT ["/usr/bin/hostapp.sh"] diff --git a/buildroot-external/package/hassio/builder/hostapp.sh b/buildroot-external/package/hassio/builder/hostapp.sh deleted file mode 100755 index e5fd3854d..000000000 --- a/buildroot-external/package/hassio/builder/hostapp.sh +++ /dev/null @@ -1,104 +0,0 @@ -#!/bin/bash -set -e - -ARCH= -MACHINE= -DATA_IMG="/export/data.ext4" -VERSION_URL="https://version.home-assistant.io/stable.json" -APPARMOR_URL="https://version.home-assistant.io/apparmor.txt" - -# Parse -while [[ $# -gt 0 ]]; do - key=$1 - case $key in - --arch) - ARCH=$2 - shift - ;; - --machine) - MACHINE=$2 - shift - ;; - *) - exit 1 - ;; - esac - shift -done - -VERSION_JSON="$(curl -s ${VERSION_URL})" - -SUPERVISOR=$(echo "${VERSION_JSON}" | jq -e -r --arg arch "${ARCH}" '.images.supervisor | sub("{arch}"; $arch)') -DNS=$(echo "${VERSION_JSON}" | jq -e -r --arg arch "${ARCH}" '.images.dns | sub("{arch}"; $arch)') -AUDIO=$(echo "${VERSION_JSON}" | jq -e -r --arg arch "${ARCH}" '.images.audio | sub("{arch}"; $arch)') -CLI=$(echo "${VERSION_JSON}" | jq -e -r --arg arch "${ARCH}" '.images.cli | sub("{arch}"; $arch)') -MULTICAST=$(echo "${VERSION_JSON}" | jq -e -r --arg arch "${ARCH}" '.images.multicast | sub("{arch}"; $arch)') -OBSERVER=$(echo "${VERSION_JSON}" | jq -e -r --arg arch "${ARCH}" '.images.observer | sub("{arch}"; $arch)') -LANDINGPAGE=$(echo "${VERSION_JSON}" | jq -e -r --arg machine "${MACHINE}" '.images.core | sub("{machine}"; $machine)'):landingpage - -SUPERVISOR_VERSION=$(echo "${VERSION_JSON}" | jq -e -r '.supervisor') -DNS_VERSION=$(echo "${VERSION_JSON}" | jq -e -r '.dns') -CLI_VERSION=$(echo "${VERSION_JSON}" | jq -e -r '.cli') -AUDIO_VERSION=$(echo "${VERSION_JSON}" | jq -e -r '.audio') -MULTICAST_VERSION=$(echo "${VERSION_JSON}" | jq -e -r '.multicast') -OBSERVER_VERSION=$(echo "${VERSION_JSON}" | jq -e -r '.observer') - -# Make image -truncate --size="1G" "${DATA_IMG}" -mkfs.ext4 -L "hassos-data" -E lazy_itable_init=0,lazy_journal_init=0 ${DATA_IMG} - -# Setup local user -if [ "${BUILDER_UID:0}" -ne 0 ] && [ "${BUILDER_GID:0}" -ne 0 ]; then - groupadd -g "${BUILDER_GID}" builder - useradd -m -u "${BUILDER_UID}" -g "${BUILDER_GID}" -G docker builder - chown builder:builder ${DATA_IMG} -fi - -# Mount / init file structs -mkdir -p /mnt/data/ -mount -o loop ${DATA_IMG} /mnt/data -mkdir -p /mnt/data/docker - -# Run dockerd -dockerd -s overlay2 -g /mnt/data/docker & -DOCKER_PID=$! - -DOCKER_COUNT=0 -until docker info >/dev/null 2>&1; do - if [ ${DOCKER_COUNT} -gt 30 ]; then - exit 1 - fi - - sleep 1 - DOCKER_COUNT=$((DOCKER_COUNT + 1)) -done - -# Install supervisor -docker pull "${SUPERVISOR}:${SUPERVISOR_VERSION}" - -# Need match with the tag used by OS -docker tag "${SUPERVISOR}:${SUPERVISOR_VERSION}" "homeassistant/${ARCH}-hassio-supervisor:latest" - -# Install Plugins -docker pull "${CLI}:${CLI_VERSION}" -docker pull "${DNS}:${DNS_VERSION}" -docker pull "${AUDIO}:${AUDIO_VERSION}" -docker pull "${MULTICAST}:${MULTICAST_VERSION}" -docker pull "${OBSERVER}:${OBSERVER_VERSION}" - -# Install landing page -docker pull "${LANDINGPAGE}" - -# Setup AppArmor -mkdir -p "/mnt/data/supervisor/apparmor" -curl -sL -o "/mnt/data/supervisor/apparmor/hassio-supervisor" "${APPARMOR_URL}" - -# Finish -kill $DOCKER_PID && wait $DOCKER_PID - -# Unmount resource -if ! umount /mnt/data; then - umount -f /mnt/data || echo "umount force fails!" -fi - -exit 0 diff --git a/buildroot-external/package/hassio/create-data-partition.sh b/buildroot-external/package/hassio/create-data-partition.sh new file mode 100755 index 000000000..3d31e46b5 --- /dev/null +++ b/buildroot-external/package/hassio/create-data-partition.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +set -e + +build_dir=$1 +dst_dir=$2 + +data_img="${dst_dir}/data.ext4" + +# Make image +rm -f "${data_img}" +truncate --size="1280M" "${data_img}" +mkfs.ext4 -L "hassos-data" -E lazy_itable_init=0,lazy_journal_init=0 "${data_img}" + +# Mount / init file structs +mkdir -p "${build_dir}/data/" +sudo mount -o loop,discard "${data_img}" "${build_dir}/data/" + +# Use official Docker in Docker images +# Ideally we use the same version as Buildroot is using in case the +# overlayfs2 storage format changes +container=$(docker run --privileged -e DOCKER_TLS_CERTDIR="" \ + -v "${build_dir}/data/":/data \ + -v "${build_dir}/data/docker/":/var/lib/docker \ + -v "${build_dir}":/build \ + -d docker:20.10-dind --storage-driver overlay2) + +docker exec "${container}" sh /build/dind-import-containers.sh + +docker stop "${container}" + +# Unmount data image +sudo umount "${build_dir}/data/" diff --git a/buildroot-external/package/hassio/dind-import-containers.sh b/buildroot-external/package/hassio/dind-import-containers.sh new file mode 100755 index 000000000..8666173a8 --- /dev/null +++ b/buildroot-external/package/hassio/dind-import-containers.sh @@ -0,0 +1,19 @@ +#!/bin/sh +set -e + +APPARMOR_URL="https://version.home-assistant.io/apparmor.txt" + +# Install supervisor +for image in /build/images/*.tar; do + docker load --input "${image}" +done + +# Tag the Supervisor how the OS expects it to be tagged +supervisor=$(docker images --filter "label=io.hass.type=supervisor" --quiet) +arch=$(docker inspect --format '{{ index .Config.Labels "io.hass.arch" }}' "${supervisor}") +docker tag "${supervisor}" "homeassistant/${arch}-hassio-supervisor:latest" + +# Setup AppArmor +mkdir -p "/data/supervisor/apparmor" +wget -O "/data/supervisor/apparmor/hassio-supervisor" "${APPARMOR_URL}" + diff --git a/buildroot-external/package/hassio/fetch-container-image.sh b/buildroot-external/package/hassio/fetch-container-image.sh new file mode 100755 index 000000000..be6b099f0 --- /dev/null +++ b/buildroot-external/package/hassio/fetch-container-image.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +arch=$1 +machine=$2 +version_json=$3 +image_json_name=$4 +dl_dir=$5 +dst_dir=$6 + +set -e + +image_name=$(jq -e -r --arg image_json_name "${image_json_name}" \ + --arg arch "${arch}" --arg machine "${machine}" \ + '.images[$image_json_name] | sub("{arch}"; $arch) | sub("{machine}"; $machine)' \ + < "${version_json}") +image_tag=$(jq -e -r --arg image_json_name "${image_json_name}" \ + '.[$image_json_name]' < "${version_json}") +full_image_name="${image_name}:${image_tag}" + +image_digest=$(skopeo inspect "docker://${full_image_name}" | jq -r '.Digest') + +# Cleanup image name file name use +image_file_name="${full_image_name//[:\/]/_}@${image_digest//[:\/]/_}" +image_file_path="${dl_dir}/${image_file_name}.tar" +dst_image_file_path="${dst_dir}/${image_file_name}.tar" + +if [ -f "${image_file_path}" ] +then + echo "Skipping download of existing image: ${full_image_name} (digest ${image_digest})" + cp "${image_file_path}" "${dst_image_file_path}" + exit 0 +fi + +# Use digest here to avoid race conditions of any sort... +echo "Fetching image: ${full_image_name}" +skopeo copy "docker://${image_name}@${image_digest}" "docker-archive:${image_file_path}:${full_image_name}" + +cp "${image_file_path}" "${dst_image_file_path}" diff --git a/buildroot-external/package/hassio/hassio.mk b/buildroot-external/package/hassio/hassio.mk index c73d82e92..cf36ac9f6 100644 --- a/buildroot-external/package/hassio/hassio.mk +++ b/buildroot-external/package/hassio/hassio.mk @@ -9,18 +9,27 @@ HASSIO_LICENSE = Apache License 2.0 HASSIO_LICENSE_FILES = $(BR2_EXTERNAL_HASSOS_PATH)/../LICENSE HASSIO_SITE = $(BR2_EXTERNAL_HASSOS_PATH)/package/hassio HASSIO_SITE_METHOD = local +HASSIO_VERSION_URL = "https://version.home-assistant.io/stable.json" + +HASSIO_CONTAINER_IMAGES_ARCH = supervisor dns audio cli multicast observer core + +define HASSIO_CONFIGURE_CMDS + # Deploy only landing page for "core" by setting version to "landingpage" + curl -s $(HASSIO_VERSION_URL) | jq '.core = "landingpage"' > $(@D)/stable.json + + $(Q)mkdir -p $(@D)/images + $(Q)mkdir -p $(HASSIO_DL_DIR) + $(foreach image,$(HASSIO_CONTAINER_IMAGES_ARCH),\ + $(BR2_EXTERNAL_HASSOS_PATH)/package/hassio/fetch-container-image.sh \ + $(BR2_PACKAGE_HASSIO_ARCH) $(BR2_PACKAGE_HASSIO_MACHINE) $(@D)/stable.json $(image) "$(HASSIO_DL_DIR)" "$(@D)/images" + ) -define HASSIO_BUILD_CMDS - docker build --tag hassos-hostapps $(@D)/builder endef -define HASSIO_INSTALL_TARGET_CMDS - docker run --rm --privileged \ - -e BUILDER_UID="$(shell id -u)" -e BUILDER_GID="$(shell id -g)" \ - -v $(BINARIES_DIR):/export \ - hassos-hostapps \ - --arch $(BR2_PACKAGE_HASSIO_ARCH) \ - --machine $(BR2_PACKAGE_HASSIO_MACHINE) +HASSIO_INSTALL_IMAGES = YES + +define HASSIO_INSTALL_IMAGES_CMDS + $(BR2_EXTERNAL_HASSOS_PATH)/package/hassio/create-data-partition.sh "$(@D)" "$(BINARIES_DIR)" endef $(eval $(generic-package)) diff --git a/buildroot-external/scripts/hdd-image.sh b/buildroot-external/scripts/hdd-image.sh index 51079a0e0..2b77c0be4 100755 --- a/buildroot-external/scripts/hdd-image.sh +++ b/buildroot-external/scripts/hdd-image.sh @@ -15,7 +15,7 @@ BOOTSTATE_SIZE=8M SYSTEM_SIZE=256M KERNEL_SIZE=24M OVERLAY_SIZE=96M -DATA_SIZE=1G +DATA_SIZE=1280M function size2sectors() {