diff options
-rw-r--r-- | imgpkg.morph | 12 | ||||
-rwxr-xr-x | imgpkg.write | 142 | ||||
-rw-r--r-- | imgpkg/common.sh.in | 71 | ||||
-rw-r--r-- | imgpkg/disk-install.sh.in | 47 | ||||
-rw-r--r-- | imgpkg/make-disk-image.sh.in | 33 |
5 files changed, 305 insertions, 0 deletions
diff --git a/imgpkg.morph b/imgpkg.morph new file mode 100644 index 00000000..a7018459 --- /dev/null +++ b/imgpkg.morph @@ -0,0 +1,12 @@ +name: imgpkg +kind: cluster +description: | + Packaged system and script for installing it, for deferred instansiation. +systems: +- morph: base-system-x86_32-generic + deploy: + imgpkg: + type: imgpkg + location: imgpkg.tar + BOOTLOADER_BLOBS: "/usr/share/syslinux/mbr.bin" + INCLUDE_SCRIPTS: "imgpkg/make-disk-image.sh.in:imgpkg/disk-install.sh.in:imgpkg/common.sh.in" diff --git a/imgpkg.write b/imgpkg.write new file mode 100755 index 00000000..c911c89b --- /dev/null +++ b/imgpkg.write @@ -0,0 +1,142 @@ +#!/bin/sh +# Copyright (C) 2014 Codethink Limited +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# =*= License: GPL-2 =*= +# +# +# This is a write extension for making a package that can be used to +# install the produced system. Ideally we'd instead have Baserock +# everywhere to do the deployment, but we need to support this workflow +# until that is possible. +# +# It gets configured with a script templates that get filled in with the +# paths to the rootfs tarball as @@ROOTFS_TAR_PATH@@, script directory as +# @@SCRIPT_DIR@@ and copied boot-files as @@IMAGE_DIR@@. +# +# Scripts to include are specified as a relative path to your morphologies +# repository, separated by : characters in the INCLUDE_SCRIPTS variable. +# If another separator is convenient, it can be specified in the +# INCLUDE_SCRIPTS_SEPARATOR variable, which has the same semantics as the +# shell IFS variable. +# +# Other files needed to create the disk image, such as bootloader image +# files can be specified in BOOTLOADER_BLOBS, relative to the root of the +# rootfs, separated with similar rules to INCLUDE_SCRIPTS. +# +# The name of the rootfs tarball, the scripts subdirectory and the +# bootloader blobs subdirectory can be changed with the ROOTFS_TAR, +# SCRIPT_SUBDIR and IMAGE_SUBDIR variables, but the default should be +# sufficient. +# +# The compression of the rootfs tarball, and the package tarball can be +# specified by specifying a shell command that reads the uncompressed +# tarball from stdin and writes to stdout in the ROOTFS_COMPRESS and +# OUTPUT_COMPRESS variables. + +set -eu + +die(){ + echo "$@" >&2 + exit 1 +} + +warn(){ + echo "$@" >&2 +} + +info(){ + echo "$@" >&2 +} + +shellescape(){ + echo "'$(echo "$1" | sed -e "s/'/'\\''/g")'" +} + +sedescape(){ + # Escape the passed in string so it can be safely interpolated into + # a sed expression as a literal value. + echo "$1" | sed -e 's/[\/&]/\\&/g' +} + +ROOTDIR="$1" +OUTPUT_TAR="$2" +td="$(mktemp -d)" +IMAGE_SUBDIR="${IMAGE_SUBDIR-image_files}" +SCRIPT_SUBDIR="${SCRIPT_SUBDIR-tools}" +ROOTFS_TAR="${ROOTFS_TAR-rootfs.tar}" + +install_script(){ + local source_file="$1" + local output_dir="$2" + local target_file="$output_dir/$SCRIPT_SUBDIR/$(basename "$source_file" .in)" + local find_script_dir='"$(readlink -f "$(dirname "$0")")"' + local image_dir="$find_script_dir/../$(shellescape "$IMAGE_SUBDIR")" + local rootfs_tar_path="$image_dir/$(shellescape "$ROOTFS_TAR")" + sed -e "s/@@SCRIPT_DIR@@/$(sedescape "$find_script_dir")/g" \ + -e "s/@@IMAGE_DIR@@/$(sedescape "$image_dir")/g" \ + -e "s/@@ROOTFS_TAR_PATH@@/$(sedescape "$rootfs_tar_path")/g" \ + "$source_file" \ + | install -D -m 755 /proc/self/fd/0 "$target_file" +} + +install_scripts(){ + local output_dir="$1" + ( + IFS="${INCLUDE_SCRIPTS_SEPATATOR-:}" + for script in $INCLUDE_SCRIPTS; do + local script_path="$(pwd)/$script" + if [ ! -e "$script_path" ]; then + warn Script "$script" not found, ignoring + continue + fi + install_script "$script" "$output_dir" + done + ) +} + +install_bootloader_blobs(){ + local output_dir="$1" + local image_dir="$output_dir/$IMAGE_SUBDIR" + ( + IFS="${BOOTLOADER_BLOBS_SEPATATOR-:}" + for blob in $BOOTLOADER_BLOBS; do + local blob_path="$ROOTDIR/$blob" + if [ ! -e "$blob_path" ]; then + warn Bootloader blob "$blob" not found, ignoring + continue + fi + install -D -m644 "$blob_path" "$image_dir/$(basename "$blob_path")" + done + ) +} + +output_dir="$(basename "$OUTPUT_TAR")" +for ext in .xz .bz2 .gzip .gz .tgz .tar; do + output_dir="${output_dir%$ext}" +done + +info Installing scripts +install_scripts "$td/$output_dir" + +info Installing bootloader blobs +install_bootloader_blobs "$td/$output_dir" + +info Writing rootfs tar to "$IMAGE_SUBDIR/$ROOTFS_TAR" +tar -C "$ROOTDIR" -c . \ +| sh -c "${ROOTFS_COMPRESS-cat}" >"$td/$output_dir/$IMAGE_SUBDIR/$ROOTFS_TAR" + +info Writing image package tar to "$OUTPUT_TAR" +tar -C "$td" -c "$output_dir" | sh -c "${OUTPUT_COMPRESS-cat}" >"$OUTPUT_TAR" diff --git a/imgpkg/common.sh.in b/imgpkg/common.sh.in new file mode 100644 index 00000000..08473a6b --- /dev/null +++ b/imgpkg/common.sh.in @@ -0,0 +1,71 @@ +#!/bin/false + +status(){ + echo "$@" +} + +info(){ + echo "$@" >&2 +} + +warn(){ + echo "$@" >&2 +} + +extract_rootfs(){ + tar -C "$1" -xf @@ROOTFS_TAR_PATH@@ . +} + +make_disk_image(){ + truncate --size "$1" "$2" +} + +format_disk(){ + local disk="$1" + mkfs.ext4 -F -L rootfs "$disk" +} + +install_fs_config(){ + local mountpoint="$1" + local rootdisk="${2-/dev/vda}" + cat >>"$mountpoint/etc/fstab" <<EOF +$rootdisk / ext4 rw,errors=remount-ro 0 0 +EOF + install -D -m 644 /proc/self/fd/0 "$mountpoint/boot/extlinux.conf" <<EOF +DEFAULT baserock +LABEL baserock +SAY Booting Baserock +LINUX /boot/vmlinuz +APPEND root=$rootdisk +EOF +} + +install_bootloader(){ + local disk="$1" + local mountpoint="$2" + dd if=@@IMAGE_DIR@@/mbr.bin conv=notrunc bs=440 count=1 of="$disk" + extlinux --install "$mountpoint/boot" +} + +loop_file(){ + losetup --show --find "$1" +} +unloop_file(){ + #losetup --detach "$1" + # unlooping handled by umount -d, for busybox compatibility + true +} + +temp_mount(){ + local mp="$(mktemp -d)" + if ! mount "$@" "$mp"; then + rmdir "$mp" + return 1 + fi + echo "$mp" +} +untemp_mount(){ + # Unmount and detach in one step for busybox compatibility + umount -d "$1" + rmdir "$1" +} diff --git a/imgpkg/disk-install.sh.in b/imgpkg/disk-install.sh.in new file mode 100644 index 00000000..60b3f465 --- /dev/null +++ b/imgpkg/disk-install.sh.in @@ -0,0 +1,47 @@ +#!/bin/sh + +set -eu + +usage(){ + cat <<EOF +usage: $0 DISK [TARGET_DISK] + +DISK: Where the disk appears on your development machine +TARGET_DISK: What the disk will appear as on the target machine +EOF +} + +. @@SCRIPT_DIR@@/common.sh + +if [ "$#" -lt 1 -o "$#" -gt 2 ]; then + usage + exit 1 +fi + +DISK="$1" +TARGET_DISK="${1-/dev/sda}" + +status Formatting "$DISK" as ext4 +format_disk "$DISK" +( + info Mounting "$DISK" + MP="$(temp_mount -t ext4 "$DISK")" + info Mounted "$DISK" to "$MP" + set +e + ( + set -e + info Copying rootfs onto disk + extract_rootfs "$MP" + info Configuring disk paths + install_fs_config "$MP" "$TARGET_DISK" + info Installing bootloader + install_bootloader "$DISK" "$MP" + ) + ret="$?" + if [ "$ret" != 0 ]; then + warn Filling rootfs failed with "$ret" + fi + info Unmounting "$DISK" from "$MP" and removing "$MP" + untemp_mount "$MP" + exit "$ret" +) diff --git a/imgpkg/make-disk-image.sh.in b/imgpkg/make-disk-image.sh.in new file mode 100644 index 00000000..29a0e13a --- /dev/null +++ b/imgpkg/make-disk-image.sh.in @@ -0,0 +1,33 @@ +#!/bin/sh + +usage(){ + cat <<EOF +usage: $0 FILENAME SIZE [TARGET_DISK] + +FILENAME: Location to write the disk image to +SIZE: Size to create the disk image with +TARGET_DISK: What the disk will appear as on the target machine +EOF +} + +. @@SCRIPT_DIR@@/common.sh + +if [ "$#" -lt 2 -o "$#" -gt 3 ]; then + usage + exit 1 +fi + +DISK_IMAGE="$1" +DISK_SIZE="$2" +TARGET_DISK="${3-/dev/vda}" + +make_disk_image "$DISK_SIZE" "$DISK_IMAGE" + +( + LOOP="$(loop_file "$DISK_IMAGE")" + set +e + @@SCRIPT_DIR@@/disk-install.sh "$DISK_IMAGE" "$TARGET_DISK" + ret="$?" + unloop_file "$LOOP" + exit "$ret" +) |