| #!/bin/bash | |
| # | |
| # Copyright (C) 2016 Canonical Ltd | |
| # | |
| # This program is free software: you can redistribute it and/or modify | |
| # it under the terms of the GNU General Public License version 3 as | |
| # published by the Free Software Foundation. | |
| # | |
| # 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, see <http://www.gnu.org/licenses/>. | |
| set -ex | |
| workdir=$(mktemp -d) | |
| output= | |
| kernel= | |
| gadget= | |
| model_assertion= | |
| extra_snaps= | |
| extra_devmode_snaps= | |
| extra_assertions= | |
| arch= | |
| channel=stable | |
| show_help() { | |
| echo "Usage: $0 [OPTIONS]" | |
| echo | |
| echo "optional arguments:" | |
| echo " --help: Show this help message and exit" | |
| echo " --channel: Select another channel to use for build the image (default: $channel)" | |
| echo " --kernel-snap: Kernel snap to use for building the boot image" | |
| echo " --gadget-snap: Device gadget snap to use" | |
| echo " --model-assertion: Model assertion file to use" | |
| echo " --extra-snap: Additional snap to add to the created image. Value is directly passed to prepare-image" | |
| echo " --extra-assertion: Additional assertion file to include in the image" | |
| echo " --output: Target path and file name for the boot image to create" | |
| echo " --arch: Target device architecture" | |
| } | |
| while [ -n "$1" ]; do | |
| case "$1" in | |
| --help) | |
| show_help | |
| exit | |
| ;; | |
| --channel=*) | |
| channel=${1#*=} | |
| shift | |
| ;; | |
| --kernel-snap=*) | |
| kernel=${1#*=} | |
| shift | |
| ;; | |
| --gadget-snap=*) | |
| gadget=${1#*=} | |
| shift | |
| ;; | |
| --output=*) | |
| output=${1#*=} | |
| shift | |
| ;; | |
| --model-assertion=*) | |
| model_assertion=${1#*=} | |
| shift | |
| ;; | |
| --extra-snap=*) | |
| snap=${1#*=} | |
| extra_snaps="$extra_snaps --extra-snaps $snap" | |
| shift | |
| ;; | |
| --extra-devmode-snap=*) | |
| snap=${1#*=} | |
| extra_devmode_snaps="$extra_devmode_snaps $snap" | |
| shift | |
| ;; | |
| --extra-assertion=*) | |
| assert=${1#*=} | |
| extra_assertions="$extra_assertions $assert" | |
| shift | |
| ;; | |
| --arch=*) | |
| arch=${1#*=} | |
| shift | |
| ;; | |
| *) | |
| echo "Unknown command: $1" | |
| exit 1 | |
| ;; | |
| esac | |
| done | |
| if [ -z "$kernel" ] || [ -z "$gadget" ] || [ -z "$model_assertion" ] ; then | |
| echo "ERROR: Please provide a valid kernel and gadget snap and a model assertion" | |
| exit 1 | |
| fi | |
| channel=stable | |
| image_size=2G | |
| image_name=ubuntu-core-mako | |
| image_fs_label=writable | |
| cp $kernel $workdir/mako-kernel.snap | |
| cp $gadget $workdir/mako.snap | |
| mkdir $workdir/rootfs | |
| mkdir $workdir/rootfs/boot | |
| snap prepare-image \ | |
| --channel $channel \ | |
| --extra-snaps $workdir/mako.snap \ | |
| --extra-snaps $workdir/mako-kernel.snap \ | |
| $extra_snaps \ | |
| $model_assertion \ | |
| $workdir/rootfs | |
| sudo chown -R root:root $workdir/rootfs | |
| mkdir $workdir/writable | |
| mkdir $workdir/writable/system-data | |
| cp -ra $workdir/rootfs/image/* $workdir/writable/system-data/ | |
| # Migrate all systemd units from core snap into the writable area. This | |
| # would be normally done on firstboot by the initramfs but we can't rely | |
| # on that because we are adding another file in there and that will | |
| # prevent the initramfs from transitioning any files. | |
| core_snap=$(find $workdir/writable/system-data/var/lib/snapd/snaps -name "core_*.snap") | |
| tmp_core=`mktemp -d` | |
| sudo mount -o loop $core_snap $tmp_core | |
| mkdir -p $workdir/writable/system-data/etc/systemd | |
| cp -rav $tmp_core/etc/systemd/* \ | |
| $workdir/writable/system-data/etc/systemd/ | |
| sudo umount $tmp_core | |
| rm -rf $tmp_core | |
| # system-user assertion which gives us our ubuntu:ubuntu user we use to | |
| # log into the system | |
| if [ -n "$extra_assertions" ]; then | |
| mkdir -p $workdir/writable/system-data/var/lib/snapd/seed/assertions | |
| for assert in $extra_assertions ; do | |
| cp $assert $workdir/writable/system-data/var/lib/snapd/seed/assertions/ | |
| done | |
| fi | |
| # Use NetworkManager as our default network management solution | |
| mkdir -p $workdir/writable/system-data/etc/netplan | |
| cat << EOF > $workdir/writable/system-data/etc/netplan/00-default-nm-renderer.yaml | |
| network: | |
| renderer: NetworkManager | |
| EOF | |
| # Disable console-conf for the first boot | |
| mkdir -p $workdir/writable/system-data/var/lib/console-conf/ | |
| touch $workdir/writable/system-data/var/lib/console-conf/complete | |
| # Create systemd service which is running on firstboot and sets up | |
| # various things for us. | |
| mkdir -p $workdir/writable/system-data/etc/systemd/system | |
| cat << 'EOF' > $workdir/writable/system-data/etc/systemd/system/devmode-firstboot.service | |
| [Unit] | |
| Description=Run devmode firstboot setup | |
| After=snapd.service snapd.socket | |
| [Service] | |
| Type=oneshot | |
| ExecStart=/bin/bash -c "/bin/bash /writable/system-data/var/lib/devmode-firstboot/run.sh > /dev/console" | |
| RemainAfterExit=yes | |
| EOF | |
| mkdir -p $workdir/writable/system-data/etc/systemd/system/multi-user.target.wants | |
| ln -sf /etc/systemd/system/devmode-firstboot.service \ | |
| $workdir/writable/system-data/etc/systemd/system/multi-user.target.wants/devmode-firstboot.service | |
| mkdir $workdir/writable/system-data/var/lib/devmode-firstboot | |
| cat << 'EOF' > $workdir/writable/system-data/var/lib/devmode-firstboot/run.sh | |
| #!/bin/bash | |
| set -ex | |
| # Don't start again if we're already done | |
| if [ -e /writable/system-data/var/lib/devmode-firstboot/complete ] ; then | |
| exit 0 | |
| fi | |
| echo "Start devmode-firstboot $(date -Iseconds --utc)" | |
| if [ "$(snap managed)" = "true" ]; then | |
| echo "System already managed, exiting" | |
| exit 0 | |
| fi | |
| # no changes at all | |
| while ! snap changes ; do | |
| echo "No changes yet, waiting" | |
| sleep 1 | |
| done | |
| while snap changes | grep -qE '(Do|Doing) .*Initialize system state' ; do | |
| echo "Initialize system state is in progress, waiting" | |
| sleep 1 | |
| done | |
| if [ -n "$(snap known system-user)" ]; then | |
| echo "Trying to create known user" | |
| snap create-user --known --sudoer | |
| fi | |
| # Enable console-conf again | |
| rm /writable/system-data/var/lib/console-conf/complete | |
| # Mark us done | |
| touch /writable/system-data/var/lib/devmode-firstboot/complete | |
| # Reboot the system as its now prepared for the user | |
| reboot | |
| EOF | |
| chmod +x $workdir/writable/system-data/var/lib/devmode-firstboot/run.sh | |
| if [ -n "$extra_devmode_snaps" ]; then | |
| mkdir -p $workdir/writable/system-data/var/lib/snapd/seed/snaps/ | |
| for snap in $extra_devmode_snaps ; do | |
| snap_name= | |
| if [ -e $snap ]; then | |
| cp $snap $workdir/writable/system-data/var/lib/snapd/seed/snaps/ | |
| snap_name=`basename $snap .snap` | |
| else | |
| snap_name=`echo $snap | cut -d':' -f 1` | |
| if [ -z "$snap_name" ]; then | |
| echo "ERROR: no valid snap name provided '$snap'" | |
| exit 1 | |
| fi | |
| snap_channel=`echo $snap | cut -d':' -f 2` | |
| if [ -z "$snap_channel" ]; then | |
| snap_channel=edge | |
| fi | |
| snap_url=`curl -H "X-Ubuntu-Series: 16" -H "X-Ubuntu-Architecture: $arch" https://search.apps.ubuntu.com/api/v1/snaps/details/$snap_name?channel=$snap_channel | jq .download_url` | |
| wget $snap_url -O $workdir/writable/system-data/snapd/seed/snaps/$snap_name.snap | |
| fi | |
| name=`snap info $snap | grep name | awk '{print $2}'` | |
| cat <<EOF >> $workdir/writable/system-data/var/lib/snapd/seed/seed.yaml | |
| - name: $name | |
| unasserted: true | |
| devmode: true | |
| file: $snap_name.snap | |
| EOF | |
| done | |
| fi | |
| (cd $workdir/writable ; tar cf $workdir/writable.tar *) | |
| tar --numeric-owner --exclude=dev/ -tvvf $workdir/writable.tar \ | |
| --directory $workdir > $workdir/writable.content | |
| make_ext4fs -u / -U $workdir/writable.content -l $image_size -s \ | |
| -L $image_fs_label $workdir/$image_name.img $workdir/writable | |
| cp $workdir/$image_name.img $output | |
| sudo rm -rf $workdir | |
| 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment