| #!/usr/bin/env bash |
| set -e |
| |
| if ! command -v qemu-nbd &> /dev/null; then |
| echo >&2 'error: "qemu-nbd" not found!' |
| exit 1 |
| fi |
| |
| usage() { |
| echo "Convert disk image to docker image" |
| echo "" |
| echo "usage: $0 image-name disk-image-file [ base-image ]" |
| echo " ie: $0 cirros:0.3.3 cirros-0.3.3-x86_64-disk.img" |
| echo " $0 ubuntu:cloud ubuntu-14.04-server-cloudimg-amd64-disk1.img ubuntu:14.04" |
| } |
| |
| if [ "$#" -lt 2 ]; then |
| usage |
| exit 1 |
| fi |
| |
| CURDIR=$(pwd) |
| |
| image_name="${1%:*}" |
| image_tag="${1#*:}" |
| if [ "$image_tag" == "$1" ]; then |
| image_tag="latest" |
| fi |
| |
| disk_image_file="$2" |
| docker_base_image="$3" |
| |
| block_device=/dev/nbd0 |
| |
| builddir=$(mktemp -d) |
| |
| cleanup() { |
| umount "$builddir/disk_image" || true |
| umount "$builddir/workdir" || true |
| qemu-nbd -d $block_device &> /dev/null || true |
| rm -rf $builddir |
| } |
| trap cleanup EXIT |
| |
| # Mount disk image |
| modprobe nbd max_part=63 |
| qemu-nbd -rc ${block_device} -P 1 "$disk_image_file" |
| mkdir "$builddir/disk_image" |
| mount -o ro ${block_device} "$builddir/disk_image" |
| |
| mkdir "$builddir/workdir" |
| mkdir "$builddir/diff" |
| |
| base_image_mounts="" |
| |
| # Unpack base image |
| if [ -n "$docker_base_image" ]; then |
| mkdir -p "$builddir/base" |
| docker pull "$docker_base_image" |
| docker save "$docker_base_image" | tar -xC "$builddir/base" |
| |
| image_id=$(docker inspect -f "{{.Id}}" "$docker_base_image") |
| while [ -n "$image_id" ]; do |
| mkdir -p "$builddir/base/$image_id/layer" |
| tar -xf "$builddir/base/$image_id/layer.tar" -C "$builddir/base/$image_id/layer" |
| |
| base_image_mounts="${base_image_mounts}:$builddir/base/$image_id/layer=ro+wh" |
| image_id=$(docker inspect -f "{{.Parent}}" "$image_id") |
| done |
| fi |
| |
| # Mount work directory |
| mount -t aufs -o "br=$builddir/diff=rw${base_image_mounts},dio,xino=/dev/shm/aufs.xino" none "$builddir/workdir" |
| |
| # Update files |
| cd $builddir |
| LC_ALL=C diff -rq disk_image workdir \ |
| | sed -re "s|Only in workdir(.*?): |DEL \1/|g;s|Only in disk_image(.*?): |ADD \1/|g;s|Files disk_image/(.+) and workdir/(.+) differ|UPDATE /\1|g" \ |
| | while read action entry; do |
| case "$action" in |
| ADD|UPDATE) |
| cp -a "disk_image$entry" "workdir$entry" |
| ;; |
| DEL) |
| rm -rf "workdir$entry" |
| ;; |
| *) |
| echo "Error: unknown diff line: $action $entry" >&2 |
| ;; |
| esac |
| done |
| |
| # Pack new image |
| new_image_id="$(for i in $(seq 1 32); do printf "%02x" $(($RANDOM % 256)); done)" |
| mkdir -p $builddir/result/$new_image_id |
| cd diff |
| tar -cf $builddir/result/$new_image_id/layer.tar * |
| echo "1.0" > $builddir/result/$new_image_id/VERSION |
| cat > $builddir/result/$new_image_id/json <<-EOS |
| { "docker_version": "1.4.1" |
| , "id": "$new_image_id" |
| , "created": "$(date -u +%Y-%m-%dT%H:%M:%S.%NZ)" |
| EOS |
| |
| if [ -n "$docker_base_image" ]; then |
| image_id=$(docker inspect -f "{{.Id}}" "$docker_base_image") |
| echo ", \"parent\": \"$image_id\"" >> $builddir/result/$new_image_id/json |
| fi |
| |
| echo "}" >> $builddir/result/$new_image_id/json |
| |
| echo "{\"$image_name\":{\"$image_tag\":\"$new_image_id\"}}" > $builddir/result/repositories |
| |
| cd $builddir/result |
| |
| # mkdir -p $CURDIR/$image_name |
| # cp -r * $CURDIR/$image_name |
| tar -c * | docker load |