Skip to content

Instantly share code, notes, and snippets.

@morphis
Created April 12, 2017 11:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save morphis/abdc1e83ba0578e756073bd89fa128ed to your computer and use it in GitHub Desktop.
Save morphis/abdc1e83ba0578e756073bd89fa128ed to your computer and use it in GitHub Desktop.
Create snap image
#!/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