#!/bin/bash # This file is part of cloud-init. See LICENSE file for license information. set -u VERBOSITY=0 TEMP_D="" KEEP=false CONTAINER="" error() { echo "$@" 1>&2; } fail() { [ $# -eq 0 ] || error "$@"; exit 1; } errorrc() { local r=$?; error "$@" "ret=$r"; return $r; } Usage() { cat <&2; [ $# -eq 0 ] || error "$@"; return 1; } cleanup() { if [ -n "$CONTAINER" -a "$KEEP" = "false" ]; then delete_container "$CONTAINER" fi [ -z "${TEMP_D}" -o ! -d "${TEMP_D}" ] || rm -Rf "${TEMP_D}" } debug() { local level=${1}; shift; [ "${level}" -gt "${VERBOSITY}" ] && return error "${@}" } inside_as() { # inside_as(container_name, user, cmd[, args]) # executes cmd with args inside container as user in users home dir. local name="$1" user="$2" shift 2 if [ "$user" = "root" ]; then inside "$name" "$@" return fi local stuffed="" b64="" stuffed=$(getopt --shell sh --options "" -- -- "$@") stuffed=${stuffed# -- } b64=$(printf "%s\n" "$stuffed" | base64 --wrap=0) inside "$name" su "$user" -c \ 'cd; eval set -- "$(echo '$b64' | base64 --decode)" && exec "$@"' } inside_as_cd() { local name="$1" user="$2" dir="$3" shift 3 inside_as "$name" "$user" sh -c 'cd "$0" && exec "$@"' "$dir" "$@" } inside() { local name="$1" shift lxc exec "$name" -- "$@" } inject_cloud_init(){ # take current cloud-init git dir and put it inside $name at # ~$user/cloud-init. local name="$1" user="$2" top_d="" dname="" pstat="" top_d=$(git rev-parse --show-toplevel) || { errorrc "Failed to get git top level in $PWD"; return } dname=$(basename "${top_d}") || return debug 1 "collecting ${top_d} ($dname) into user $user in $name." tar -C "${top_d}/.." -cpf - "$dname" | inside_as "$name" "$user" sh -ec ' dname=$1 rm -Rf "$dname" tar -xpf - [ "$dname" = "cloud-init" ] || mv "$dname" cloud-init' \ extract "$dname" [ "${PIPESTATUS[*]}" = "0 0" ] || { error "Failed to push tarball of '$top_d' into $name" \ " for user $user (dname=$dname)" return 1 } return 0 } prep() { # we need some very basic things not present in the container. # - git # - tar (CentOS 6 lxc container does not have it) # - python-argparse (or python3) local needed="" pair="" pkg="" cmd="" needed="" for pair in tar:tar git:git; do pkg=${pair#*:} cmd=${pair%%:*} command -v $cmd >/dev/null 2>&1 || needed="${needed} $pkg" done if ! command -v python3; then python -c "import argparse" >/dev/null 2>&1 || needed="${needed} python-argparse" fi needed=${needed# } if [ -z "$needed" ]; then error "No prep packages needed" return 0 fi error "Installing prep packages: ${needed}" yum install --assumeyes ${needed} } start_container() { local src="$1" name="$2" debug 1 "starting container $name from '$src'" lxc launch "$src" "$name" || { errorrc "Failed to start container '$name' from '$src'"; return } CONTAINER=$name local out="" ret="" debug 1 "waiting for networking" out=$(inside "$name" sh -c ' i=0 while [ $i -lt 60 ]; do getent hosts mirrorlist.centos.org && exit 0 sleep 2 done' 2>&1) ret=$? if [ $ret -ne 0 ]; then error "Waiting for network in container '$name' failed. [$ret]" error "$out" return $ret fi if [ ! -z "${http_proxy-}" ]; then debug 1 "configuring proxy ${http_proxy}" inside "$name" sh -c "echo proxy=$http_proxy >> /etc/yum.conf" fi } delete_container() { debug 1 "removing container $1 [--keep to keep]" lxc delete --force "$1" } main() { local short_opts="ahkrsuv" local long_opts="artifact,help,keep,rpm,srpm,unittest,verbose" local getopt_out="" getopt_out=$(getopt --name "${0##*/}" \ --options "${short_opts}" --long "${long_opts}" -- "$@") && eval set -- "${getopt_out}" || { bad_Usage; return; } local cur="" next="" local artifact="" keep="" rpm="" srpm="" unittest="" version="" while [ $# -ne 0 ]; do cur="${1:-}"; next="${2:-}"; case "$cur" in -a|--artifact) artifact=1;; -h|--help) Usage ; exit 0;; -k|--keep) KEEP=true;; -r|--rpm) rpm=1;; -s|--srpm) srpm=1;; -u|--unittest) unittest=1;; -v|--verbose) VERBOSITY=$((${VERBOSITY}+1));; --) shift; break;; esac shift; done [ $# -eq 1 ] || { bad_Usage "ERROR: Must provide version!"; return; } version="$1" case "$version" in 6|7) :;; *) error "Expected version of 6 or 7, not '$version'"; return;; esac TEMP_D=$(mktemp -d "${TMPDIR:-/tmp}/${0##*/}.XXXXXX") || fail "failed to make tempdir" trap cleanup EXIT # program starts here local uuid="" name="" user="ci-test" cdir="" cdir="/home/$user/cloud-init" uuid=$(uuidgen -t) || { error "no uuidgen"; return 1; } name="cloud-init-centos-${uuid%%-*}" start_container "images:centos/$version" "$name" # prep the container (install very basic dependencies) inside "$name" bash -s prep <"$0" || { errorrc "Failed to prep container $name"; return; } # add the user inside "$name" useradd "$user" debug 1 "inserting cloud-init" inject_cloud_init "$name" "$user" || { errorrc "FAIL: injecting cloud-init into $name failed." return } inside_as_cd "$name" root "$cdir" \ ./tools/read-dependencies --distro=centos --test-distro || { errorrc "FAIL: failed to install dependencies with read-dependencies" return } local errors=0 inside_as_cd "$name" "$user" "$cdir" \ sh -ec "git checkout .; git status" || { errorrc "git checkout failed."; errors=$(($errors+1)); } if [ -n "$unittest" ]; then debug 1 "running unit tests." inside_as_cd "$name" "$user" "$cdir" nosetests tests/unittests || { errorrc "nosetests failed."; errors=$(($errors+1)); } fi if [ -n "$srpm" ]; then debug 1 "building srpm." inside_as_cd "$name" "$user" "$cdir" ./packages/brpm --srpm || { errorrc "brpm --srpm."; errors=$(($errors+1)); } fi if [ -n "$rpm" ]; then debug 1 "building rpm." inside_as_cd "$name" "$user" "$cdir" ./packages/brpm || { errorrc "brpm failed."; errors=$(($errors+1)); } fi if [ -n "$artifact" ]; then for built_rpm in $(inside "$name" sh -c "echo $cdir/*.rpm"); do lxc file pull "$name/$built_rpm" . done fi if [ "$errors" != "0" ]; then error "there were $errors errors." return 1 fi return 0 } if [ "${1:-}" = "prep" ]; then shift prep "$@" else main "$@" fi # vi: ts=4 expandtab