Skip to content

Commit

Permalink
Initial commit of imagetools.
Browse files Browse the repository at this point in the history
These tools are used for creating an initial seed zone as a baseline, based
on files from a smartos-live build.  This seed zone can then be built upon
for more general purpose images.
  • Loading branch information
Jonathan Perkin committed Aug 9, 2013
1 parent 295a3d2 commit eaaa56a
Show file tree
Hide file tree
Showing 10 changed files with 677 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
@@ -0,0 +1 @@
output/*
55 changes: 54 additions & 1 deletion README.md
@@ -1,4 +1,57 @@
imagetools
==========

Tools for creating SmartOS images
Tools for creating SmartOS images.

### Creating seed images

Seed images are the absolute baseline of a SmartOS image. They are created
from files produced by a smartos-live build, containing the original `/etc`,
`/var` and SMF manifest database.

First, you need to perform a smartos-live build, as described in the wiki
page:

http://wiki.smartos.org/display/DOC/Building+SmartOS+on+SmartOS

Then from the global zone:

$ ./create-seed /path/to/smartos-live seed-image

This will create a `zones/seed-image` file system containing the seed files.

To create a SNGL seed, you will then need to extract a bootstrap tarball over
the top to populate enough of `/usr` for the zone to boot. I tend to use our
pbulk pkgsrc bootstrap for this, for example:

$ ./create-seed /path/to/smartos-live seed-sngl
$ gtar -zxf /path/to/bootstrap-2013Q2-sngl.tar.gz -C /zones/seed-sngl/root/

Once you have a seed file system ready, use `create-image` to turn it into a
provisionable image and manifest.

$ ./create-image seed-1.0 seed-image
Enter your username: jperkin
Enter your user UUID (leave empty to create one): d9d9dd4b-3011-4a45-bffe-ddc9ba4be2d2
Enter description: Seed Image

This will use `/zones/seed-image`, snapshot it, and create a file system image
along with a manifest file ready for importing with `imgadm`. Give it your
user credentials (or just make some up) and a useful description when
prompted.

Once complete it will output an `imgadm` command you can use, e.g.

Done. Now run this to install the image:

imgadm install -m ./output/seed-1.0.json -f ./output/seed-1.0.zfs.gz

You can then use the UUID as input for the next phase.

### Creating base images

Base images are comprised of a baseline seed image, plus an applied `basesh`
image. This overlays the basic set of pkgsrc packages, standard users, and
some config customisation applicable to each base image.

TODO..
59 changes: 59 additions & 0 deletions create-image
@@ -0,0 +1,59 @@
#!/bin/bash
#
# Create an image and manifest out of a shut down zone. The zone
# should have been prepared with sm-prepare-image.
#

if [ $# -ne 2 ]; then
echo "usage: $0 <name> <zone>" >&2
exit 2
else
name=$1; shift
zone=$1; shift
fi

set -e

read -p 'Enter your username: ' creator_name
read -p 'Enter your user UUID (leave empty to create one): ' creator_uuid

if [ -z "${creator_uuid}" ]; then
creator_uuid=$(uuid)
fi

read -p 'Enter description: ' description

if [ -z "${description}" ]; then
echo "WARNING: No description entered. Edit ${outdir}/${name}.json"
echo "when this script finishes."
fi

outdir="$(dirname $0)/output"
echo "Taking snapshot ..."
zfs snapshot zones/${zone}@snap$$
echo "Sending snapshot to ${outdir}/${name}.zfs.gz ..."
zfs send zones/${zone}@snap$$ | gzip -9 >${outdir}/${name}.zfs.gz
zfs destroy zones/${zone}@snap$$

echo "Creating ${outdir}/${name}.json ..."
sha1=$(digest -a sha1 ${outdir}/${name}.zfs.gz)
size=$(ls -l ${outdir}/${name}.zfs.gz | awk '{print $5}')
uuid=$(uuid)
datestamp=$(date '+%FT%RZ')

cat $(dirname $0)/imgjson.in \
| sed -e "s/@NAME@/${name%-*}/g" \
-e "s/@VERSION@/${name##*-}/g" \
-e "s/@CREATOR_NAME@/${creator_name}/g" \
-e "s/@CREATOR_UUID@/${creator_uuid}/g" \
-e "s/@DATESTAMP@/${datestamp}/g" \
-e "s/@DESCRIPTION@/${description}/g" \
-e "s/@SHA1@/${sha1}/g" \
-e "s/@SIZE@/${size}/g" \
-e "s/@UUID@/${uuid}/g" \
>${outdir}/${name}.json

echo "Done. Now run this to install the image:"
echo ""
echo " imgadm install -m ${outdir}/${name}.json -f ${outdir}/${name}.zfs.gz"
echo ""
128 changes: 128 additions & 0 deletions create-seed
@@ -0,0 +1,128 @@
#!/bin/bash
#
# This is a script to create an initial 'seed' image based on pristine
# sources. User images will be built on top of this one.
#
# Two arguments are required:
#
# * a path to a built smartos-live repository
# * the name of the seed image to create
#


if [ $# -ne 2 ]; then
echo "usage: $0 <smartos-live> <seedname>" >&2
exit 2
else
smartoslive=$1; shift
protoname=$1; shift
fi

imagetoolsdir="$(dirname $0)"

set -ex

zfs destroy zones/${protoname} || true
zfs create zones/${protoname}

#
# Requisite directories and symlinks
#
for dir in /home /root /var/ssh
do
mkdir -p /zones/${protoname}/root${dir}
done
mkdir -p -m 1777 /zones/${protoname}/root/tmp
ln -s ./usr/bin /zones/${protoname}/root/bin

#
# Copy in proto directories
#
for dir in etc var
do
rsync -av ${smartoslive}/proto/${dir} /zones/${protoname}/root/
done

#
# Apply overlays
#
rsync -av ${smartoslive}/overlay/generic/etc/ /zones/${protoname}/root/etc/

#
# Apply manual changes.
#
# - remove legacy rc2.d scripts
# - remove users which may clash with pkgsrc, and sort.
#
rm -f /zones/${protoname}/root/etc/rc2.d/S*
ed /zones/${protoname}/root/etc/passwd <<EOF
/^lp/d
/^gdm/d
/^mysql/d
/^openldap/d
/^webservd/d
/^postgres/d
w
q
EOF
ed /zones/${protoname}/root/etc/shadow <<EOF
/^lp/d
/^gdm/d
/^mysql/d
/^openldap/d
/^webservd/d
/^postgres/d
w
q
EOF
ed /zones/${protoname}/root/etc/group <<EOF
/^lp/d
/^gdm/d
/^mysql/d
/^openldap/d
/^webservd/d
/^postgres/d
w
q
EOF
for file in group passwd
do
sort -t: -k3,3n /zones/${protoname}/root/etc/${file} >/tmp/${file} \
&& mv /tmp/${file} /zones/${protoname}/root/etc/${file}
done
ed /zones/${protoname}/root/etc/user_attr >/dev/null <<EOF
/^lp/d
/^admin/d
w
q
EOF

#
# Generate SVC repository. Some manifests are missing, apply them manually.
#
${imagetoolsdir}/create-smf-repo ${smartoslive} ${imagetoolsdir}/include/manifests /tmp/repository.db
#svcdir="${smartoslive}/projects/illumos/usr/src/cmd/svc"
#for mf in ${smartoslive}/proto/lib/svc/manifest/system/boot-archive.xml \
# ${smartoslive}/overlay/generic/lib/svc/manifest/system/sysidtool.xml \
#for mf in ${imagetoolsdir}/include/mdata.xml
#do
# env SVCCFG_REPOSITORY=/tmp/repository.db \
# SVCCFG_CONFIGD_PATH=${svcdir}/configd/svc.configd-native \
# ${svcdir}/svccfg/svccfg-native import ${mf}
#done
#
# Import our fake mdata.xml to satisfy SDC 6.5 and 7.0
#
#env SVCCFG_REPOSITORY=/tmp/repository.db \
# SVCCFG_CONFIGD_PATH=${smartoslive}/projects/illumos/usr/src/cmd/svc/configd/svc.configd-native \
# ${smartoslive}/projects/illumos/usr/src/cmd/svc/svccfg/svccfg-native import ${imagetoolsdir}/include/mdata.xml
cp /tmp/repository.db /zones/${protoname}/root/etc/svc/repository.db

#
# Do just enough zoneinit stuff to get a zone to boot, this will be overwritten
# later when we do a proper smtools install.
#
mkdir -p /zones/${protoname}/root/var/zoneinit
cp ${imagetoolsdir}/include/zoneinit.json /zones/${protoname}/root/var/zoneinit
cp ${imagetoolsdir}/include/S99final /zones/${protoname}/root/etc/rc2.d/S99final
chmod 0755 /zones/${protoname}/root/etc/rc2.d/S99final
138 changes: 138 additions & 0 deletions create-smf-repo
@@ -0,0 +1,138 @@
#!/bin/bash
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
# Copyright (c) 2012 Joyent, Inc. All rights reserved.
#

#
# This tool builds a small SMF seed repository for a given brand based on its
# manifests file which is used by our zones service.
#

#
# Changes for this 'imagetools' version:
#
# * Use a configurable location to the 'smartos-live' build area
# * Copy in manifests from multiple locations, and show where they came from.
#

if [ $# -ne 3 ]; then
echo "usage: $0 <smartoslive> <manifests> <output>" >&2
exit 2
else
SMARTOS_LIVE=$1; shift
MANIFEST_LIST=$1; shift
OUTPUT=$1; shift
fi

SVCCFG="${SMARTOS_LIVE}/projects/illumos/usr/src/cmd/svc/svccfg/svccfg-native"
CONFIGD="${SMARTOS_LIVE}/projects/illumos/usr/src/cmd/svc/configd/svc.configd-native"
#MANIFEST_LIST="${SMARTOS_LIVE}/overlay/generic/usr/lib/brand/joyent/manifests"
MANIFESTS_LOCAL="$(dirname $0)/manifest"
MANIFESTS_GENERIC="${SMARTOS_LIVE}/overlay/generic/lib/svc/manifest"
MANIFESTS_PROTO="${SMARTOS_LIVE}/proto/lib/svc/manifest"
MANIFESTS_SMARTOS="${SMARTOS_LIVE}/overlay/smartos/lib/svc/manifest"
TMPFILE="/tmp/$(basename $0).$$"

function fail
{
local msg="$*"
[[ -z "$msg" ]] && msg="failed"
echo "$msg" >&2
exit 1
}

#
# XXX We really want to control this in a different way. We'd really rather not
# have to hardcode machinations on the XML format of a manifest, because really,
# that's just bad. On the flip side, we can't use libscf, but we can change the
# file format that we use, which is probably what makes sense in the long run.
# e.g. because a manifest can deliver more than one instance, we'd need to
# explicitly call out which instances delivered by the manifest we'd like to
# enable and disable.
#
function import_manifest
{
local svc

if [ -f "$MANIFESTS_LOCAL/$1" ]; then
svc="$MANIFESTS_LOCAL/$1"
echo -n "local: "
elif [ -f "$MANIFESTS_GENERIC/$1" ]; then
svc="$MANIFESTS_GENERIC/$1"
echo -n "generic: "
elif [ -f "$MANIFESTS_PROTO/$1" ]; then
svc="$MANIFESTS_PROTO/$1"
echo -n "proto: "
elif [ -f "$MANIFESTS_SMARTOS/$1" ]; then
svc="$MANIFESTS_SMARTOS/$1"
echo -n "smartos: "
else
fail "cannot find $1"
fi
on=""
[[ ! -f "$svc" ]] && fail "cannot find $svc"
nawk -v status=$2 < $svc > $TMPFILE '{
if (($1 == "<instance" &&
$2 == "name=\047default\047") ||
$1 == "<create_default_instance") {
if (status == "enabled")
n=sub("\047false\047", "\047true\047")
else
n=sub("\047true\047", "\047false\047")
}
print $0
}'
[[ $? -eq 0 ]] || fail "failed to nawk manifest"
$SVCCFG import $TMPFILE || fail "failed to import $svc"
rm -f $TMPFILE
}

function build_database
{
local input output
input=$1
output=$2
export SVCCFG_REPOSITORY=$output
export SVCCFG_CONFIGD_PATH=$CONFIGD
rm -f $SVCCFG_REPOSITORY

[[ -f $input ]] || fail "can't read manifest input file: $input"
while read service enabled import; do
[[ -z "$service" ]] && continue
[[ "$service" =~ ^\# ]] && continue
[[ "$import" == "noimport" ]] && continue
import_manifest $service $enabled

echo $service $enabled
done < $input
}

#
# We need to walk through a few different sources of brands and work through it.
# For now we only care about doing this for the Joyent minimal zone. Some day we
# can extend this to everything.
#
build_database ${MANIFEST_LIST} ${OUTPUT}
chmod 444 ${OUTPUT}
chown root:root ${OUTPUT}
echo "Generated ${OUTPUT}"

0 comments on commit eaaa56a

Please sign in to comment.