[gnome-ostree/wip/qa] qa: Some work on script to generate disk images as non-root



commit efeb0d982ca5d237552a2ca2b62dca47a9f9ee58
Author: Colin Walters <walters verbum org>
Date:   Sat Jan 5 08:11:25 2013 -0500

    qa: Some work on script to generate disk images as non-root
    
    Using guestfish

 Makefile-ostbuild.am                      |    6 +-
 src/ostbuild/js/builtins/qa_make_disk.js  |  168 +++++++++++++++++++++++++++++
 src/ostbuild/js/main.js                   |    3 +-
 src/ostbuild/ostbuild-qa-qemu-pull-deploy |   81 ++++++++++++++
 4 files changed, 256 insertions(+), 2 deletions(-)
---
diff --git a/Makefile-ostbuild.am b/Makefile-ostbuild.am
index 7a685c4..b6d3bdc 100644
--- a/Makefile-ostbuild.am
+++ b/Makefile-ostbuild.am
@@ -27,7 +27,10 @@ ostbuild: src/ostbuild/ostbuild.in Makefile
 EXTRA_DIST += ostbuild/ostbuild.in
 
 if BUILDSYSTEM
-bin_SCRIPTS += ostbuild $(srcdir)/src/ostbuild/ostbuild-qemu-pull-deploy
+bin_SCRIPTS += ostbuild \
+	$(srcdir)/src/ostbuild/ostbuild-qemu-pull-deploy \
+	$(srcdir)/src/ostbuild/ostbuild-qa-qemu-pull-deploy \
+	$(NULL)
 
 utils_SCRIPTS = \
 	src/ostbuild/ostree-build-compile-one \
@@ -57,6 +60,7 @@ jsostbuiltins_DATA= \
 	src/ostbuild/js/builtins/build.js \
 	src/ostbuild/js/builtins/checkout.js \
 	src/ostbuild/js/builtins/git_mirror.js \
+	src/ostbuild/js/builtins/qa_make_disk.js \
 	src/ostbuild/js/builtins/prefix.js \
 	src/ostbuild/js/builtins/resolve.js \
 	$(NULL)
diff --git a/src/ostbuild/js/builtins/qa_make_disk.js b/src/ostbuild/js/builtins/qa_make_disk.js
new file mode 100644
index 0000000..0827093
--- /dev/null
+++ b/src/ostbuild/js/builtins/qa_make_disk.js
@@ -0,0 +1,168 @@
+// -*- indent-tabs-mode: nil; tab-width: 2; -*-
+// Copyright (C) 2013 Colin Walters <walters verbum org>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library 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
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+// Boston, MA 02111-1307, USA.
+
+const GLib = imports.gi.GLib;
+const Gio = imports.gi.Gio;
+const Lang = imports.lang;
+const Format = imports.format;
+
+const GSystem = imports.gi.GSystem;
+
+const Task = imports.task;
+const ProcUtil = imports.procutil;
+
+const loop = GLib.MainLoop.new(null, true);
+
+function runProcWithInputSyncGetLines(argv, cancellable, input) {
+    let mainContext = new GLib.MainContext();
+    let mainLoop = new GLib.MainLoop(mainContext, true);
+    let context = new GSystem.SubprocessContext({ argv: argv });
+    context.set_stdout_disposition(GSystem.SubprocessStreamDisposition.PIPE);
+    context.set_stdin_disposition(GSystem.SubprocessStreamDisposition.PIPE);
+    let proc = new GSystem.Subprocess(context);
+    proc.init(cancellable);
+    let stdinPipe = proc.get_stdin_pipe();
+    let memStream = Gio.MemoryInputStream.new_from_bytes(new GLib.Bytes(input));
+    let asyncOps = 3;
+    function asyncOpComplete() {
+        asyncOps--;
+        if (asyncOps == 0)
+            mainLoop.quit();
+    }
+    function onSpliceComplete(stdinPipe, result) {
+        try {
+            let bytesWritten = stdinPipe.splice_finish(result);
+        } finally {
+            asyncOpComplete();
+        }
+    }
+    let closeBoth = Gio.OutputStreamSpliceFlags.CLOSE_SOURCE | Gio.OutputStreamSpliceFlags.CLOSE_TARGET;
+    stdinPipe.splice(memStream, closeBoth, cancellable, onSpliceComplete);
+
+    let procException = null;
+    function onProcExited(proc, result) {
+        try {
+            let [success, statusText] = ProcUtil.asyncWaitCheckFinish(proc, result);
+            if (!success)
+                procException = statusText;
+        } finally {
+            asyncOpComplete();
+        }
+    }
+    proc.wait(cancellable, onProcExited);
+    
+    let stdoutPipe = proc.get_stdout_pipe();
+    let stdoutData = Gio.DataInputStream.new(stdoutPipe);
+    let lines = [];
+    function onReadLine(datastream, result) {
+        try {
+            let line = stdoutData.read_line_finish_utf8(result);
+            if (line == null)
+                asyncOpComplete();
+            else {
+                lines.push(line);
+                stdoutData.read_line_async(GLib.PRIORITY_DEFAULT, cancellable, onReadLine);
+            }
+        } catch (e) {
+            asyncOpComplete();
+            throw e;
+        }
+    }
+    stdoutData.read_line_async(GLib.PRIORITY_DEFAULT, cancellable, onReadLine);
+
+    mainLoop.run();
+
+    return lines;
+}
+
+const QaMakeDisk = new Lang.Class({
+    Name: 'QaMakeDisk',
+
+    execute: function(argv) {
+        let cancellable = null;
+        let parser = new ArgParse.ArgumentParser("Generate a disk image");
+        parser.addArgument('diskpath');
+        
+        let args = parser.parse(argv);
+
+        let path = Gio.File.new_for_path(args.diskpath);
+        if (path.query_exists(null))
+            throw new Error("" + path.get_path() + " exists");
+
+        let tmppath = path.get_parent().get_child(path.get_name() + '.tmp');
+        let sizeMb = 8 * 1024;
+        let bootsizeMb = 200;
+        let swapsizeMb = 64;
+
+        let guestfishProcess;
+        
+        ProcUtil.runSync(['qemu-img', 'create', tmppath, '' + sizeMb + 'M'], cancellable);
+        let lines = runProcWithInputSyncGetLines(['guestfish', '-a', tmppath], 
+                                                 'launch\n\
+part-init /dev/vda mbr\n\
+blockdev-getsize64 /dev/vda\n\
+blockdev-getss /dev/vda\n'
+                                                 
+                                                 cancellable);
+        if (lines.length != 2)
+            throw new Error("guestfish returned unexpected output lines (" + lines.length + ", expected 2");
+        let diskBytesize = parseInt(lines[0]);
+        let diskSectorsize = parseInt(lines[1]);
+        GSystem.
+    rm devicesize.tmp
+    read disk_bytesize < disk_bytesize.tmp
+    rm disk_bytesize.tmp
+    read disk_sectorsize < disk_sectorsize.tmp
+    rm disk_sectorsize.tmp
+    echo "bytesize: ${disk_bytesize} sectorsize: ${disk_sectorsize}"
+    bootsize_sectors=$(($bootsize_mb * 1024 / ${disk_sectorsize} * 1024))
+    swapsize_sectors=$(($swapsize_mb * 1024 / ${disk_sectorsize} * 1024))
+    rootsize_sectors=$(($disk_bytesize / $disk_sectorsize - $bootsize_sectors - $swapsize_sectors - 64))
+    boot_offset=64
+    swap_offset=$(($boot_offset + $bootsize_sectors))
+    root_offset=$(($swap_offset + $swapsize_sectors))
+    end_offset=$(($root_offset + $rootsize_sectors))
+    echo "parts: ${bootsize_sectors} ${swapsize_sectors} ${rootsize_sectors}"
+    echo "offsets: ${swap_offset} ${root_offset} ${end_offset}"
+
+    cat > partconfig.tmp <<EOF
+launch
+part-add /dev/vda p ${boot_offset} $((${swap_offset} - 1))
+part-add /dev/vda p ${swap_offset} $((${root_offset} - 1))
+part-add /dev/vda p ${root_offset} $((${end_offset} - 1))
+mkfs ext4 /dev/vda1
+mkswap /dev/vda2
+mkfs ext4 /dev/vda3
+mount /dev/vda3 /
+mkdir /boot
+EOF
+    cat partconfig.tmp
+    guestfish -a ${tmppath} < partconfig.tmp
+    rm partconfig.tmp
+    mv ${tmppath} ${diskpath}
+    }
+});
+
+function main(argv) {
+    let ecode = 1;
+    var app = new QaMakeDisk();
+    GLib.idle_add(GLib.PRIORITY_DEFAULT,
+                  function() { try { app.execute(argv); ecode = 0; } finally { loop.quit(); }; return false; });
+    loop.run();
+    return ecode;
+}
diff --git a/src/ostbuild/js/main.js b/src/ostbuild/js/main.js
index 55ed988..9a71fe7 100755
--- a/src/ostbuild/js/main.js
+++ b/src/ostbuild/js/main.js
@@ -22,7 +22,8 @@ const BUILTINS = {'autobuilder': "Run resolve and build",
                   'prefix': "Display or modify \"prefix\" (build target)",
                   'git-mirror': "Update internal git mirror for one or more components",
                   'resolve': "Expand git revisions in source to exact targets",
-                  'build': "Build multiple components and generate trees"};
+                  'build': "Build multiple components and generate trees",
+                  'qa-make-disk': "Generate a bare disk image"};
 
 function usage(ecode) {
     print("Builtins:");
diff --git a/src/ostbuild/ostbuild-qa-qemu-pull-deploy b/src/ostbuild/ostbuild-qa-qemu-pull-deploy
new file mode 100644
index 0000000..04ad625
--- /dev/null
+++ b/src/ostbuild/ostbuild-qa-qemu-pull-deploy
@@ -0,0 +1,81 @@
+#!/bin/bash
+# Copyright (C) 2012 Colin Walters <walters verbum org>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+set -e
+set -x
+
+# See gsystem-file-utils.c
+export LIBGSYSTEM_ENABLE_GUESTFS_FUSE_WORKAROUND=1
+
+diskpath=$1
+shift
+srcrepo=$1
+shift
+osname=$1
+shift
+target="$1"
+
+workdir=$(pwd)
+mntdir=${workdir}/mnt
+ostreedir=${mntdir}/ostree
+
+test -n "$osname" || (echo 1>&2 "usage: $0: DISKPATH SRCREPO OSNAME TARGET"; exit 1)
+
+# This crap is from man guestmount
+cleanup() {
+  # Save the PID of guestmount *before* calling fusermount.
+  pid="$(cat ${workdir}/guestmount.pid)"
+  timeout=10
+  count=$timeout
+  
+  if test -n "$pid"; then
+    fusermount -u ${mntdir}
+  
+    while kill -0 "$pid" 2>/dev/null && [ $count -gt 0 ]; do
+      sleep 1
+      ((count--))
+    done
+    if [ $count -eq 0 ]; then
+        echo "$0: wait for guestmount to exit failed after $timeout seconds"
+        exit 1
+    fi
+  fi
+}
+
+die() {
+  echo "$@" 1>&2
+  cleanup
+  exit 1
+}
+
+mkdir -p ${mntdir}
+if ! test -f ${diskpath}; then
+fi
+
+guestmount --rw -o allow_root --pid-file ${workdir}/guestmount.pid -a ${diskpath} -m /dev/sda3 -m /dev/sda1:/boot ${mntdir}
+
+if ! test -d ${ostreedir}; then
+    ostree admin init-fs ${mntdir} || die "init-fs"
+fi
+
+ostree admin --ostree-dir=${ostreedir} os-init ${osname} || die "os-init"
+ostree --repo=${ostreedir}/repo pull-local ${srcrepo} ${target} || die "pull-local"
+ostree admin --ostree-dir=${ostreedir} --boot-dir=${mntdir}/boot deploy ${osname} ${target} || die "deploy"
+ostree admin --ostree-dir=${ostreedir} prune ${osname} || die "prune"
+
+cleanup



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]