Merge branch 'master' of https://github.com/zeha/grml-debootstrap
authorMichael Prokop <mika@grml.org>
Thu, 3 Jul 2014 10:16:34 +0000 (12:16 +0200)
committerMichael Prokop <mika@grml.org>
Thu, 3 Jul 2014 10:16:34 +0000 (12:16 +0200)
.gitignore [new file with mode: 0644]
Makefile
Vagrantfile [new file with mode: 0644]
packer/Makefile [new file with mode: 0644]
packer/README.md [new file with mode: 0644]
packer/debian64.bats [new file with mode: 0644]
packer/debian64_provision.sh [new file with mode: 0644]
packer/debian64_wheezy.json [new file with mode: 0644]
packer/fake-uname.c [new file with mode: 0644]
packer/fake-uname.so [new file with mode: 0755]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..74c16de
--- /dev/null
@@ -0,0 +1,3 @@
+*.box
+packer_cache
+*_output
index 364ebff..70cca32 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -24,3 +24,9 @@ online: all
 
 clean:
        rm -rf grml-debootstrap.8.html grml-debootstrap.8.xml grml-debootstrap.8 html-stamp man-stamp
+
+testrun:
+       cd ./packer && $(MAKE) compile && $(MAKE) packer
+
+vagrant:
+       vagrant up
diff --git a/Vagrantfile b/Vagrantfile
new file mode 100644 (file)
index 0000000..a14cc93
--- /dev/null
@@ -0,0 +1,122 @@
+# -*- mode: ruby -*-
+# vi: set ft=ruby :
+
+# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
+VAGRANTFILE_API_VERSION = "2"
+
+Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
+  # All Vagrant configuration is done here. The most common configuration
+  # options are documented and commented below. For a complete reference,
+  # please see the online documentation at vagrantup.com.
+
+  # Every Vagrant virtual environment requires a box to build off of.
+  config.vm.box = "packer/debian64_wheezy.box"
+
+  # Disable automatic box update checking. If you disable this, then
+  # boxes will only be checked for updates when the user runs
+  # `vagrant box outdated`. This is not recommended.
+  # config.vm.box_check_update = false
+
+  # Create a forwarded port mapping which allows access to a specific port
+  # within the machine from a port on the host machine. In the example below,
+  # accessing "localhost:8080" will access port 80 on the guest machine.
+  # config.vm.network "forwarded_port", guest: 80, host: 8080
+
+  # Create a private network, which allows host-only access to the machine
+  # using a specific IP.
+  # config.vm.network "private_network", ip: "192.168.33.10"
+
+  # Create a public network, which generally matched to bridged network.
+  # Bridged networks make the machine appear as another physical device on
+  # your network.
+  # config.vm.network "public_network"
+
+  # If true, then any SSH connections made will enable agent forwarding.
+  # Default value: false
+  # config.ssh.forward_agent = true
+
+  # Share an additional folder to the guest VM. The first argument is
+  # the path on the host to the actual folder. The second argument is
+  # the path on the guest to mount the folder. And the optional third
+  # argument is a set of non-required options.
+  # config.vm.synced_folder "../data", "/vagrant_data"
+
+  # Provider-specific configuration so you can fine-tune various
+  # backing providers for Vagrant. These expose provider-specific options.
+  # Example for VirtualBox:
+  #
+   config.vm.provider "virtualbox" do |vb|
+     # Don't boot with headless mode
+     vb.gui = true
+
+     # Use VBoxManage to customize the VM. For example to change memory:
+     # vb.customize ["modifyvm", :id, "--memory", "1024"]
+   end
+  #
+  # View the documentation for the provider you're using for more
+  # information on available options.
+
+  # Enable provisioning with CFEngine. CFEngine Community packages are
+  # automatically installed. For example, configure the host as a
+  # policy server and optionally a policy file to run:
+  #
+  # config.vm.provision "cfengine" do |cf|
+  #   cf.am_policy_hub = true
+  #   # cf.run_file = "motd.cf"
+  # end
+  #
+  # You can also configure and bootstrap a client to an existing
+  # policy server:
+  #
+  # config.vm.provision "cfengine" do |cf|
+  #   cf.policy_server_address = "10.0.2.15"
+  # end
+
+  # Enable provisioning with Puppet stand alone.  Puppet manifests
+  # are contained in a directory path relative to this Vagrantfile.
+  # You will need to create the manifests directory and a manifest in
+  # the file default.pp in the manifests_path directory.
+  #
+  # config.vm.provision "puppet" do |puppet|
+  #   puppet.manifests_path = "manifests"
+  #   puppet.manifest_file  = "site.pp"
+  # end
+
+  # Enable provisioning with chef solo, specifying a cookbooks path, roles
+  # path, and data_bags path (all relative to this Vagrantfile), and adding
+  # some recipes and/or roles.
+  #
+  # config.vm.provision "chef_solo" do |chef|
+  #   chef.cookbooks_path = "../my-recipes/cookbooks"
+  #   chef.roles_path = "../my-recipes/roles"
+  #   chef.data_bags_path = "../my-recipes/data_bags"
+  #   chef.add_recipe "mysql"
+  #   chef.add_role "web"
+  #
+  #   # You may also specify custom JSON attributes:
+  #   chef.json = { :mysql_password => "foo" }
+  # end
+
+  # Enable provisioning with chef server, specifying the chef server URL,
+  # and the path to the validation key (relative to this Vagrantfile).
+  #
+  # The Opscode Platform uses HTTPS. Substitute your organization for
+  # ORGNAME in the URL and validation key.
+  #
+  # If you have your own Chef Server, use the appropriate URL, which may be
+  # HTTP instead of HTTPS depending on your configuration. Also change the
+  # validation key to validation.pem.
+  #
+  # config.vm.provision "chef_client" do |chef|
+  #   chef.chef_server_url = "https://api.opscode.com/organizations/ORGNAME"
+  #   chef.validation_key_path = "ORGNAME-validator.pem"
+  # end
+  #
+  # If you're using the Opscode platform, your validator client is
+  # ORGNAME-validator, replacing ORGNAME with your organization name.
+  #
+  # If you have your own Chef Server, the default validation client name is
+  # chef-validator, unless you changed the configuration.
+  #
+  #   chef.validation_client_name = "ORGNAME-validator"
+end
diff --git a/packer/Makefile b/packer/Makefile
new file mode 100644 (file)
index 0000000..42785b0
--- /dev/null
@@ -0,0 +1,5 @@
+compile:
+       gcc -shared -fPIC -ldl fake-uname.c -o fake-uname.so
+
+packer:
+       packer build debian64_wheezy.json
diff --git a/packer/README.md b/packer/README.md
new file mode 100644 (file)
index 0000000..119a97f
--- /dev/null
@@ -0,0 +1,30 @@
+Creating Vagrant baseboxes and testing grml-debootstrap
+=======================================================
+
+Purpose
+-------
+
+This directory provides configurations and scripts to
+
+* test grml-debootstrap
+* generate base.box for Virtualbox usage with Vagrant
+
+Required software
+-----------------
+
+* [Packer](http://packer.io/) binary in $PATH
+* [Vagrant](http://vagrantup.com/)
+* [Virtualbox](https://www.virtualbox.org/)
+
+Usage instructions
+------------------
+
+To create a Debian base box for usage with Vagrant
+(and while at it run the grml-debootstrap tests):
+
+    % cd grml-debootstrap.git
+    % make testrun
+
+Start resulting Debian system via Vagrant:
+
+    % vagrant up
diff --git a/packer/debian64.bats b/packer/debian64.bats
new file mode 100644 (file)
index 0000000..a677168
--- /dev/null
@@ -0,0 +1,95 @@
+#!/usr/bin/env bats
+
+# config
+mountpath="/mnt"
+device="/dev/sda"
+disk="${device}1"
+
+setup() {
+  mountpoint "$mountpath" &>/dev/null || mount "$disk" "$mountpath"
+}
+
+teardown() {
+  mountpoint "$mountpath" &>/dev/null && umount "$mountpath"
+}
+
+# tests
+@test "debian_version exists" {
+  run cat "${mountpath}/etc/debian_version"
+  [ "$status" -eq 0 ]
+  [[ "$output" == [0-9].[0-9] ]]
+}
+
+@test "kernel exists" {
+  run ls "${mountpath}"/boot/vmlinuz-*
+  [ "$status" -eq 0 ]
+  [[ "$output" =~ ${mountpath}/boot/vmlinuz-* ]]
+}
+
+@test "initrd exists" {
+  run ls "${mountpath}"/boot/initrd.img-*
+  [ "$status" -eq 0 ]
+  [[ "$output" =~ ${mountpath}/boot/initrd.img-* ]]
+}
+
+@test "grub-pc installed" {
+  run chroot $mountpath dpkg-query --show --showformat='${Status}' grub-pc
+  [ "$status" -eq 0 ]
+  [[ "$output" == "install ok installed" ]]
+}
+
+@test "ext3/ext4 filesystem" {
+  fstype=$(blkid -o udev ${disk} | grep '^ID_FS_TYPE=')
+  run echo $fstype
+  [ "$status" -eq 0 ]
+  [[ $output =~ ID_FS_TYPE=ext[34] ]]
+}
+
+@test "partition table" {
+  table_info=$(parted -s ${device} 'unit s print' | grep -A1 '^Number.*Start.*End' | tail -1)
+  regex='1 2048s.*primary ext[34] boot'
+  run echo $table_info
+  echo "debug: table_info = $table_info"
+  echo "debug: output     = $output"
+  [[ $output =~ $regex ]]
+}
+
+@test "tune2fs mount count setting" {
+  mount_count=$(tune2fs -l "$disk" | grep "^Maximum mount count:")
+  run echo "$mount_count"
+  [[ "$output" == "Maximum mount count:      -1" ]]
+}
+
+@test "kernel entry in grub config" {
+  run grep "Debian GNU/Linux, with Linux" "${mountpath}/boot/grub/grub.cfg"
+  [ "$status" -eq 0 ]
+}
+
+@test "vim package is installed" {
+  run chroot "$mountpath" dpkg --list vim
+  [ "$status" -eq 0 ]
+}
+
+@test "home directory for user vagrant" {
+  run ls -d "$mountpath"/home/vagrant
+  [ "$status" -eq 0 ]
+}
+
+@test "home directory for user vagrant" {
+  run grep -q ssh-rsa "$mountpath"/home/vagrant/.ssh/authorized_keys
+  [ "$status" -eq 0 ]
+}
+
+@test "sudo setup for user vagrant" {
+  run grep -q '^vagrant ALL=(ALL) NOPASSWD: ALL' "${mountpath}/etc/sudoers.d/vagrant"
+  [ "$status" -eq 0 ]
+}
+
+@test "check for GRUB in MBR" {
+  regex='^00000180.*GRUB.*'
+  grub_string=$(dd if=${device} bs=512 count=1 2>/dev/null | hexdump -C | egrep "$regex")
+  run echo "$grub_string"
+  echo "debug: grub_string = $grub_string"
+  echo "debug: output      = $output"
+  [[ $output =~ $regex ]]
+}
diff --git a/packer/debian64_provision.sh b/packer/debian64_provision.sh
new file mode 100644 (file)
index 0000000..d0a49d3
--- /dev/null
@@ -0,0 +1,148 @@
+#!/bin/bash
+
+set -e
+
+[ -f /etc/grml_cd ] || { echo "File /etc/grml_cd doesn't exist, not executing script to avoid data loss." >&2 ; exit 1 ; }
+
+TARGET=/mnt
+
+# if we notice an error then do NOT immediately return but provide
+# user a chance to debug the VM
+bailout() {
+  echo "* Noticed problem during execution, sleeping for 9999 seconds to provide debugging option"
+  sleep 9999
+  echo "* Finally exiting with return code 1"
+  exit 1
+}
+trap bailout ERR
+
+echo "* Executing automated partition setup"
+cat > /tmp/partition_setup.txt << EOF
+disk_config sda disklabel:msdos bootable:1
+primary / 800M- ext4 rw
+EOF
+
+export LOGDIR='/tmp/setup-storage'
+mkdir -p $LOGDIR
+
+export disklist=$(/usr/lib/fai/fai-disk-info | sort)
+PATH=/usr/lib/fai:${PATH} setup-storage -f /tmp/partition_setup.txt -X
+
+echo "* Making sure we use latest grml-debootstrap version"
+apt-get update
+apt-get -y install grml-debootstrap
+
+# TODO - support testing the version provided by the ISO (without upgrading)
+if [ -r /tmp/grml-debootstrap ] ; then
+  echo "* Found /tmp/grml-debootstrap - considering for usage as main grml-debootstrap script"
+  GRML_DEBOOTSTRAP="bash /tmp/grml-debootstrap"
+else
+  GRML_DEBOOTSTRAP=grml-debootstrap
+fi
+
+echo "* Installing Debian"
+$GRML_DEBOOTSTRAP --hostname wheezy --release wheezy --target /dev/sda1 --grub /dev/sda --password grml --force 2>&1 | tee -a /tmp/grml-debootstrap.log
+
+echo "* Mounting target system"
+mount /dev/sda1 ${TARGET}
+
+echo "* Installing make + gcc packages for Virtualbox Guest Additions"
+chroot ${TARGET} apt-get -y install make gcc dkms
+
+echo "* Installing Virtualbox Guest Additions"
+isofile="${HOME}/VBoxGuestAdditions.iso"
+
+KERNELHEADERS=$(basename $(find $TARGET/usr/src/ -maxdepth 1 -name linux-headers\* ! -name \*common) | sort -u -r -V | head -1)
+if [ -z "$KERNELHEADERS" ] ; then
+  echo "Error: no kernel headers found for building the VirtualBox Guest Additions kernel module." >&2
+  exit 1
+fi
+
+KERNELVERSION=${KERNELHEADERS##linux-headers-}
+if [ -z "$KERNELVERSION" ] ; then
+  echo "Error: no kernel version could be identified." >&2
+  exit 1
+fi
+
+cp /tmp/fake-uname.so "${TARGET}/tmp/fake-uname.so"
+mkdir -p "${TARGET}/media/cdrom"
+mountpoint "${TARGET}/media/cdrom" >/dev/null && umount "${TARGET}/media/cdrom"
+mount -t iso9660 $isofile "${TARGET}/media/cdrom/"
+UTS_RELEASE=$KERNELVERSION LD_PRELOAD=/tmp/fake-uname.so grml-chroot "$TARGET" /media/cdrom/VBoxLinuxAdditions.run --nox11 || true
+tail -10 "${TARGET}/var/log/VBoxGuestAdditions.log"
+umount "${TARGET}/media/cdrom/"
+
+# work around regression in virtualbox-guest-additions-iso 4.3.10
+if [ -d ${TARGET}/opt/VBoxGuestAdditions-4.3.10 ] ; then
+  ln -s /opt/VBoxGuestAdditions-4.3.10/lib/VBoxGuestAdditions ${TARGET}/usr/lib/VBoxGuestAdditions
+fi
+
+echo "* Setting password for user root to 'vagrant'"
+echo root:vagrant | chroot ${TARGET} chpasswd
+
+echo "* Installing sudo package"
+chroot ${TARGET} apt-get -y install sudo
+
+echo "* Adding Vagrant user"
+chroot ${TARGET} useradd -d /home/vagrant -m -u 1000 vagrant
+
+echo "* Installing Vagrant ssh key"
+mkdir -m 0700 -p ${TARGET}/home/vagrant/.ssh
+echo "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key" >> ${TARGET}/home/vagrant/.ssh/authorized_keys
+chmod 0600 ${TARGET}/home/vagrant/.ssh/authorized_keys
+chroot ${TARGET} chown vagrant:vagrant /home/vagrant/.ssh /home/vagrant/.ssh/authorized_keys
+
+echo "* Setting up sudo configuration for user vagrant"
+echo "vagrant ALL=(ALL) NOPASSWD: ALL" > ${TARGET}/etc/sudoers.d/vagrant
+
+host="$(cat ${TARGET}/etc/hostname)"
+if ! grep -q "${host}$" "${TARGET}"/etc/hosts ; then
+  echo "* Setting up localhost entry for hostname $host in /etc/hosts"
+  cat >> "${TARGET}"/etc/hosts << EOF
+# Added by grml-debootstrap/provision to make sure host is resolvable for sudo:
+127.0.0.2 ${host}.local $host
+
+EOF
+fi
+
+echo "* Setting up stdin/tty workaround in /root/.profile"
+sed -i "s;^mesg n$;# modified via grml-debootstrap/provision script to work around stdin/tty issue:\ntty -s \&\& mesg n;g" "${TARGET}"/root/.profile
+
+if [ -f ${TARGET}/etc/ssh/sshd_config ] && ! grep -q '^UseDNS' ${TARGET}/etc/ssh/sshd_config ; then
+  echo "* Disabling UseDNS in sshd config"
+  echo "UseDNS no" >> ${TARGET}/etc/ssh/sshd_config
+fi
+
+echo "* Logging build information to /etc/grml_debootstrap.info"
+echo "System generated by grml-debootstrap/provision on $(date)" > ${TARGET}/etc/grml_debootstrap.info
+
+echo "* Cleaning up apt stuff"
+chroot ${TARGET} apt-get clean
+rm -f ${TARGET}/var/lib/apt/lists/*Packages \
+      ${TARGET}/var/lib/apt/lists/*Release \
+      ${TARGET}/var/lib/apt/lists/*Sources \
+      ${TARGET}/var/lib/apt/lists/*Index* \
+      ${TARGET}/var/lib/apt/lists/*Translation* \
+      ${TARGET}/var/lib/apt/lists/*.gpg \
+      ${TARGET}/var/cache/apt-show-versions/* \
+      ${TARGET}/var/cache/debconf/*.dat-old \
+      ${TARGET}/var/cache/apt/*.bin \
+      ${TARGET}/var/lib/aptitude/pkgstates.old
+
+echo "* Unmounting target system"
+umount ${TARGET}
+
+echo "* Checking for bats"
+if dpkg --list bats >/dev/null 2>&1 ; then
+  echo "* bats is already present, nothing to do."
+else
+  echo "* Installing bats"
+  apt-get update
+  apt-get -y install bats
+  # dpkg -i /tmp/bats*deb
+fi
+
+echo "* Running tests to verify grml-debootstrap system"
+bats /tmp/debian64.bats -t
+
+echo "* Finished execution of $0"
diff --git a/packer/debian64_wheezy.json b/packer/debian64_wheezy.json
new file mode 100644 (file)
index 0000000..80de5bd
--- /dev/null
@@ -0,0 +1,63 @@
+{
+  "variables": {
+    "grml_type": "grml64-full",
+    "grml_version": "2014.03",
+    "grml_iso_checksum": "1d3a0adfa3d395746e9e7f8748faec536f5c7b84"
+  },
+
+  "builders": [
+  {
+    "type": "virtualbox-iso",
+    "vm_name": "{{ user `grml_type` }}_{{ user `grml_version` }}",
+    "guest_os_type": "Debian_64",
+    "hard_drive_interface": "sata",
+    "iso_url": "http://download.grml.org/{{ user `grml_type` }}_{{ user `grml_version` }}.iso",
+    "iso_checksum": "{{ user `grml_iso_checksum` }}",
+    "iso_checksum_type": "sha1",
+    "ssh_wait_timeout": "20m",
+    "ssh_username": "root",
+    "ssh_password": "grml",
+    "output_directory": "{{ user `grml_type` }}_output",
+    "headless": false,
+    "boot_wait": "5s",
+    "boot_command": [
+      "<tab> ssh=grml <enter>"
+    ],
+    "shutdown_command": "shutdown -h now"
+  }
+  ],
+    "provisioners": [
+    {
+      "type": "file",
+      "source": "fake-uname.so",
+      "destination": "/tmp/fake-uname.so"
+    },
+    {
+      "type": "file",
+      "source": "debian64.bats",
+      "destination": "/tmp/debian64.bats"
+    },
+    {
+      "type": "file",
+      "source": "../grml-debootstrap",
+      "destination": "/tmp/grml-debootstrap"
+    },
+    {
+      "type": "shell",
+      "scripts": [
+        "debian64_provision.sh"
+        ]
+    }
+  ],
+    "post-processors": [
+    {
+      "type": "vagrant",
+      "compression_level": 5,
+      "override": {
+        "virtualbox": {
+          "output": "debian64_wheezy.box"
+        }
+      }
+    }
+  ]
+}
diff --git a/packer/fake-uname.c b/packer/fake-uname.c
new file mode 100644 (file)
index 0000000..7bbcb24
--- /dev/null
@@ -0,0 +1,44 @@
+#include <stdio.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/syslog.h>
+#include <sys/utsname.h>
+
+#ifndef UTS_RELEASE
+#define UTS_RELEASE "0.0.0"
+#endif
+
+#ifndef RTLD_NEXT
+#define RTLD_NEXT      ((void *) -1l)
+#endif
+
+typedef int (*uname_t) (struct utsname * buf);
+
+static void *get_libc_func(const char *funcname)
+{
+  void *func;
+  char *error;
+
+  func = dlsym(RTLD_NEXT, funcname);
+  if ((error = dlerror()) != NULL) {
+    fprintf(stderr, "Can't locate libc function `%s' error: %s", funcname, error);
+    _exit(EXIT_FAILURE);
+  }
+  return func;
+}
+
+int uname(struct utsname *buf)
+{
+  int ret;
+  char *env = NULL;
+  uname_t real_uname = (uname_t) get_libc_func("uname");
+
+  ret = real_uname((struct utsname *) buf);
+  strncpy(buf->release, ((env = getenv("UTS_RELEASE")) == NULL) ? UTS_RELEASE : env, 65);
+  return ret;
+}
diff --git a/packer/fake-uname.so b/packer/fake-uname.so
new file mode 100755 (executable)
index 0000000..1952279
Binary files /dev/null and b/packer/fake-uname.so differ