cloud-init-dev team mailing list archive
-
cloud-init-dev team
-
Mailing list archive
-
Message #04866
[Merge] ~smoser/cloud-init:feature/run-container into cloud-init:master
Scott Moser has proposed merging ~smoser/cloud-init:feature/run-container into cloud-init:master.
Commit message:
tools/run-container: replace tools/run-centos with more generic.
tools/run-container is like tools/run-centos, but currently supports
the following images from lxc-images
opensuse/42.3
centos/6
centos/7
ubuntu/16.04
debian/10
debian/sid
Also here is to make installation via zypper in tools/read-dependencies
not prompt user.
Requested reviews:
cloud-init commiters (cloud-init-dev)
For more details, see:
https://code.launchpad.net/~smoser/cloud-init/+git/cloud-init/+merge/345627
see commit message
--
Your team cloud-init commiters is requested to review the proposed merge of ~smoser/cloud-init:feature/run-container into cloud-init:master.
diff --git a/tools/read-dependencies b/tools/read-dependencies
index 421f470..b4656e6 100755
--- a/tools/read-dependencies
+++ b/tools/read-dependencies
@@ -51,6 +51,10 @@ MAYBE_RELIABLE_YUM_INSTALL = [
""",
'reliable-yum-install']
+ZYPPER_INSTALL = [
+ 'zypper', '--non-interactive', '--gpg-auto-import-keys', 'install',
+ '--auto-agree-with-licenses']
+
DRY_DISTRO_INSTALL_PKG_CMD = {
'centos': ['yum', 'install', '--assumeyes'],
'redhat': ['yum', 'install', '--assumeyes'],
@@ -61,8 +65,8 @@ DISTRO_INSTALL_PKG_CMD = {
'redhat': MAYBE_RELIABLE_YUM_INSTALL,
'debian': ['apt', 'install', '-y'],
'ubuntu': ['apt', 'install', '-y'],
- 'opensuse': ['zypper', 'install'],
- 'suse': ['zypper', 'install']
+ 'opensuse': ZYPPER_INSTALL,
+ 'suse': ZYPPER_INSTALL,
}
diff --git a/tools/run-centos b/tools/run-centos
index cb241ee..3d7ca0f 100755
--- a/tools/run-centos
+++ b/tools/run-centos
@@ -1,18 +1,18 @@
#!/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; }
+deprecated() {
+cat <<EOF
+ ================ DEPRECATED ================
+ | run-centos is deprecated. Please replace |
+ | your usage with tools/run-container |
+ | tools/run-container. |
+ ================ DEPRECATED ================
+EOF
+}
Usage() {
+ deprecated
cat <<EOF
Usage: ${0##*/} [ options ] version
@@ -34,319 +34,39 @@ Usage: ${0##*/} [ options ] version
Example:
* ${0##*/} --rpm --srpm --unittest 6
EOF
+ deprecated
+EOF
}
bad_Usage() { Usage 1>&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" dirty="$3"
- local changes="" top_d="" dname="cloud-init" pstat=""
- local gitdir="" commitish=""
- gitdir=$(git rev-parse --git-dir) || {
- errorrc "Failed to get git dir in $PWD";
- return
- }
- local t=${gitdir%/*}
- case "$t" in
- */worktrees)
- if [ -f "${t%worktrees}/config" ]; then
- gitdir="${t%worktrees}"
- fi
- esac
-
- # attempt to get branch name.
- commitish=$(git rev-parse --abbrev-ref HEAD) || {
- errorrc "Failed git rev-parse --abbrev-ref HEAD"
- return
- }
- if [ "$commitish" = "HEAD" ]; then
- # detached head
- commitish=$(git rev-parse HEAD) || {
- errorrc "failed git rev-parse HEAD"
- return
- }
- fi
-
- local local_changes=false
- if ! git diff --quiet "$commitish"; then
- # there are local changes not committed.
- local_changes=true
- if [ "$dirty" = "false" ]; then
- error "WARNING: You had uncommitted changes. Those changes will "
- error "be put into 'local-changes.diff' inside the container. "
- error "To test these changes you must pass --dirty."
- fi
- fi
-
- debug 1 "collecting ${gitdir} ($dname) into user $user in $name."
- tar -C "${gitdir}" -cpf - . |
- inside_as "$name" "$user" sh -ec '
- dname=$1
- commitish=$2
- rm -Rf "$dname"
- mkdir -p $dname/.git
- cd $dname/.git
- tar -xpf -
- cd ..
- git config core.bare false
- out=$(git checkout $commitish 2>&1) ||
- { echo "failed git checkout $commitish: $out" 1>&2; exit 1; }
- out=$(git checkout . 2>&1) ||
- { echo "failed git checkout .: $out" 1>&2; exit 1; }
- ' extract "$dname" "$commitish"
- [ "${PIPESTATUS[*]}" = "0 0" ] || {
- error "Failed to push tarball of '$gitdir' into $name" \
- " for user $user (dname=$dname)"
- return 1
- }
- echo "local_changes=$local_changes dirty=$dirty"
- if [ "$local_changes" = "true" ]; then
- git diff "$commitish" |
- inside_as "$name" "$user" sh -exc '
- cd "$1"
- if [ "$2" = "true" ]; then
- git apply
- else
- cat > local-changes.diff
- fi
- ' insert_changes "$dname" "$dirty"
- [ "${PIPESTATUS[*]}" = "0 0" ] || {
- error "Failed to apply local changes."
- return 1
- }
- fi
-
- 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
+main() {
+ if [ "$1" = "-h" -o "$1" == "--help" ]; then
+ Usage 1>&2;
+ exit 0;
fi
- error "Installing prep packages: ${needed}"
- set -- $needed
- local n max r
- n=0; max=10;
- bcmd="yum install --downloadonly --assumeyes --setopt=keepcache=1"
- while n=$(($n+1)); do
- error ":: running $bcmd $* [$n/$max]"
- $bcmd "$@"
- r=$?
- [ $r -eq 0 ] && break
- [ $n -ge $max ] && { error "gave up on $bcmd"; exit $r; }
- nap=$(($n*5))
- error ":: failed [$r] ($n/$max). sleeping $nap."
- sleep $nap
- done
- error ":: running yum install --cacheonly --assumeyes $*"
- yum install --cacheonly --assumeyes "$@"
-}
-
-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'";
+ local pt="" mydir=$(dirname "$0")
+ local run_container="$mydir/run-container"
+ if [ ! -x "$run_container" ]; then
+ bad_Usage "Could not find run-container."
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"
- inside "$name" sed -i s/enabled=1/enabled=0/ /etc/yum/pluginconf.d/fastestmirror.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,dirty,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=""
- local dirty=false
-
+
+ pt=( "$run_container" )
while [ $# -ne 0 ]; do
cur="${1:-}"; next="${2:-}";
case "$cur" in
- -a|--artifact) artifact=1;;
- --dirty) dirty=true;;
- -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;;
+ -r|--rpm) cur="--package";;
+ -s|--srpm) cur="--source-package";;
+ 6|7) cur="centos/$cur";;
esac
+ pt[${#pt[@]}]="$cur"
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" "$dirty" || {
- 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 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 cloudinit ||
- { 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
+ deprecated
+ exec "${pt[@]}"
}
-if [ "${1:-}" = "prep" ]; then
- shift
- prep "$@"
-else
- main "$@"
-fi
+main "$@"
+
# vi: ts=4 expandtab
diff --git a/tools/run-container b/tools/run-container
new file mode 100755
index 0000000..10f0c59
--- /dev/null
+++ b/tools/run-container
@@ -0,0 +1,552 @@
+#!/bin/bash
+# This file is part of cloud-init. See LICENSE file for license information.
+
+set -u
+
+VERBOSITY=0
+KEEP=false
+CONTAINER=""
+DEFAULT_WAIT_MAX=30
+
+error() { echo "$@" 1>&2; }
+fail() { [ $# -eq 0 ] || error "$@"; exit 1; }
+errorrc() { local r=$?; error "$@" "ret=$r"; return $r; }
+
+Usage() {
+ cat <<EOF
+Usage: ${0##*/} [ options ] [images:]image-ref
+
+ This utility can makes it easier to run tests, build rpm and source rpm
+ generation inside a LXC of the specified version of CentOS.
+
+ To see images available, run 'lxc image list images:'
+ Example input:
+ centos/7
+ opensuse/42.3
+ debian/10
+
+ options:
+ -a | --artifact keep build artifacts
+ --dirty apply local changes before running tests.
+ If not provided, a clean checkout of branch is tested.
+ Inside container, changes are in local-changes.diff.
+ -k | --keep keep container after tests
+ --pyexe V python version to use. Default=auto.
+ Should be name of an executable. ('python2' or 'python3')
+ -p | --package build a binary package (.deb or .rpm)
+ -s | --source-package build source package (debuild -S or srpm)
+ -u | --unittest run unit tests
+
+ Example:
+ * ${0##*/} --package --source-package --unittest centos/6
+EOF
+}
+
+bad_Usage() { Usage 1>&2; [ $# -eq 0 ] || error "$@"; return 1; }
+cleanup() {
+ if [ -n "$CONTAINER" -a "$KEEP" = "false" ]; then
+ delete_container "$CONTAINER"
+ fi
+}
+
+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" dirty="$3"
+ local changes="" top_d="" dname="cloud-init" pstat=""
+ local gitdir="" commitish=""
+ gitdir=$(git rev-parse --git-dir) || {
+ errorrc "Failed to get git dir in $PWD";
+ return
+ }
+ local t=${gitdir%/*}
+ case "$t" in
+ */worktrees)
+ if [ -f "${t%worktrees}/config" ]; then
+ gitdir="${t%worktrees}"
+ fi
+ esac
+
+ # attempt to get branch name.
+ commitish=$(git rev-parse --abbrev-ref HEAD) || {
+ errorrc "Failed git rev-parse --abbrev-ref HEAD"
+ return
+ }
+ if [ "$commitish" = "HEAD" ]; then
+ # detached head
+ commitish=$(git rev-parse HEAD) || {
+ errorrc "failed git rev-parse HEAD"
+ return
+ }
+ fi
+
+ local local_changes=false
+ if ! git diff --quiet "$commitish"; then
+ # there are local changes not committed.
+ local_changes=true
+ if [ "$dirty" = "false" ]; then
+ error "WARNING: You had uncommitted changes. Those changes will "
+ error "be put into 'local-changes.diff' inside the container. "
+ error "To test these changes you must pass --dirty."
+ fi
+ fi
+
+ debug 1 "collecting ${gitdir} ($dname) into user $user in $name."
+ tar -C "${gitdir}" -cpf - . |
+ inside_as "$name" "$user" sh -ec '
+ dname=$1
+ commitish=$2
+ rm -Rf "$dname"
+ mkdir -p $dname/.git
+ cd $dname/.git
+ tar -xpf -
+ cd ..
+ git config core.bare false
+ out=$(git checkout $commitish 2>&1) ||
+ { echo "failed git checkout $commitish: $out" 1>&2; exit 1; }
+ out=$(git checkout . 2>&1) ||
+ { echo "failed git checkout .: $out" 1>&2; exit 1; }
+ ' extract "$dname" "$commitish"
+ [ "${PIPESTATUS[*]}" = "0 0" ] || {
+ error "Failed to push tarball of '$gitdir' into $name" \
+ " for user $user (dname=$dname)"
+ return 1
+ }
+
+ echo "local_changes=$local_changes dirty=$dirty"
+ if [ "$local_changes" = "true" ]; then
+ git diff "$commitish" |
+ inside_as "$name" "$user" sh -exc '
+ cd "$1"
+ if [ "$2" = "true" ]; then
+ git apply
+ else
+ cat > local-changes.diff
+ fi
+ ' insert_changes "$dname" "$dirty"
+ [ "${PIPESTATUS[*]}" = "0 0" ] || {
+ error "Failed to apply local changes."
+ return 1
+ }
+ fi
+
+ return 0
+}
+
+get_os_info_in() {
+ # prep the container (install very basic dependencies)
+ [ -n "${OS_VERSION:-}" -a -n "${OS_NAME:-}" ] && return 0
+ data=$(run_self_inside "$name" os_info) ||
+ { errorrc "Failed to get os-info in container $name"; return; }
+ eval "$data" && [ -n "${OS_VERSION:-}" -a -n "${OS_NAME:-}" ] || return
+ debug 1 "determined $name is OS_VERSION=$OS_VERSION OS_NAME=$OS_NAME";
+}
+
+os_info() {
+ get_os_info || return
+ echo "OS_NAME=$OS_NAME"
+ echo "OS_VERSION=$OS_VERSION"
+}
+
+get_os_info() {
+ # run inside container, set OS_NAME, OS_VERSION
+ # example OS_NAME are centos, debian, opensuse
+ [ -n "${OS_NAME:-}" -a -n "${OS_VERSION:-}" ] && return 0
+ if [ -f /etc/os-release ]; then
+ local name="" os_version=""
+ OS_NAME=$(sh -c '. /etc/os-release; echo $ID')
+ OS_VERSION=$(sh -c '. /etc/os-release; echo $VERSION_ID')
+ if [ -z "$OS_VERSION" ]; then
+ local pname=""
+ pname=$(sh -c '. /etc/os-release; echo $PRETTY_NAME')
+ case "$pname" in
+ *buster*) OS_VERSION=10;;
+ *sid*) OS_VERSION="sid";;
+ esac
+ fi
+ elif [ -f /etc/centos-release ]; then
+ local line=""
+ read line < /etc/centos-release
+ case "$line" in
+ CentOS\ *\ 6.*) OS_VERSION="6"; OS_NAME="centos";;
+ esac
+ fi
+ [ -n "${OS_NAME:-}" -a -n "${OS_VERSION:-}" ] ||
+ { error "Unable to determine OS_NAME/OS_VERSION"; return 1; }
+}
+
+yum_install() {
+ local n=0 max=10 ret
+ bcmd="yum install --downloadonly --assumeyes --setopt=keepcache=1"
+ while n=$(($n+1)); do
+ error ":: running $bcmd $* [$n/$max]"
+ $bcmd "$@"
+ ret=$?
+ [ $ret -eq 0 ] && break
+ [ $n -ge $max ] && { error "gave up on $bcmd"; exit $ret; }
+ nap=$(($n*5))
+ error ":: failed [$ret] ($n/$max). sleeping $nap."
+ sleep $nap
+ done
+ error ":: running yum install --cacheonly --assumeyes $*"
+ yum install --cacheonly --assumeyes "$@"
+}
+
+zypper_install() {
+ local pkgs="$*"
+ set -- zypper --non-interactive --gpg-auto-import-keys install \
+ --auto-agree-with-licenses "$@"
+ debug 1 ":: installing $pkgs with zypper: $*"
+ "$@"
+}
+
+apt_install() {
+ apt-get update -q && apt-get install --no-install-recommends "$@"
+}
+
+install_packages() {
+ get_os_info || return
+ case "$OS_NAME" in
+ centos) yum_install "$@";;
+ opensuse) zypper_install "$@";;
+ debian|ubuntu) apt_install "$@";;
+ *) error "Do not know how to install packages on ${OS_NAME}";
+ return 1;;
+ esac
+}
+
+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=""
+ local pairs="tar:tar git:git"
+ local pyexe="$1"
+ case "$pyexe" in
+ python2) pairs="$pairs python2:python2";;
+ python3) pairs="$pairs python3:python3";;
+ esac
+ get_os_info
+
+ for pair in $pairs; do
+ pkg=${pair#*:}
+ cmd=${pair%%:*}
+ command -v $cmd >/dev/null 2>&1 || needed="${needed} $pkg"
+ done
+ if [ "$OS_NAME" = "centos" -a "$pyexe" = "python2" ]; 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}"
+ set -- $needed
+ install_packages "$@"
+}
+
+nose() {
+ local pyexe="$1" cmd=""
+ shift
+ get_os_info
+ if [ "$OS_NAME/$OS_VERSION" = "centos/6" ]; then
+ cmd="nosetests"
+ else
+ cmd="$pyexe -m nose"
+ fi
+ ${cmd} "$@"
+}
+
+is_done_cloudinit() {
+ [ -e "/run/cloud-init/result.json" ]
+ _RET=""
+}
+
+is_done_systemd() {
+ local s="" num="$1"
+ s=$(systemctl is-system-running 2>&1);
+ _RET="$? $s"
+ case "$s" in
+ initializing|starting) return 1;;
+ *[Ff]ailed*connect*bus*)
+ # warn if not the first run.
+ [ "$num" -lt 5 ] ||
+ error "Failed to connect to systemd bus [${_RET%% *}]";
+ return 1;;
+ esac
+ return 0
+}
+
+is_done_other() {
+ local out=""
+ out=$(getent hosts ubuntu.com 2>&1)
+ return
+}
+
+wait_inside() {
+ local name="$1" max="${2:-${DEFAULT_WAIT_MAX}}" debug=${3:-0}
+ local i=0 check="is_done_other";
+DEBUG=3
+ if [ -e /run/systemd ]; then
+ check=is_done_systemd
+ elif [ -x /usr/bin/cloud-init ]; then
+ check=is_done_cloudinit
+ fi
+ [ "$debug" != "0" ] && debug 1 "check=$check"
+ while ! $check $i && i=$(($i+1)); do
+ [ $i -ge $max ] && exit 1
+ [ "$debug" = "0" ] || echo -n .
+ sleep 1
+ done
+ if [ "$debug" != "0" ]; then
+ read up idle </proc/uptime
+ debug 1 "[$name ${i:+done after $i }up=$up${_RET:+ ${_RET}}]"
+ fi
+}
+
+wait_for_boot() {
+ local name="$1"
+ local out="" ret="" wtime=$DEFAULT_WAIT_MAX
+ get_os_info_in "$name"
+ [ "$OS_NAME" = "debian" ] && wtime=300 &&
+ debug 1 "on debian we wait for ${wtime}s"
+ debug 1 "waiting for boot of $name"
+ run_self_inside "$name" wait_inside "$name" "$wtime" "$VERBOSITY" ||
+ { errorrc "wait inside $name failed."; return; }
+
+ if [ ! -z "${http_proxy-}" ]; then
+ if [ "$OS_NAME" = "centos" ]; then
+ debug 1 "configuring proxy ${http_proxy}"
+ inside "$name" sh -c "echo proxy=$http_proxy >> /etc/yum.conf"
+ inside "$name" sed -i s/enabled=1/enabled=0/ \
+ /etc/yum/pluginconf.d/fastestmirror.conf
+ else
+ debug 1 "do not know how to configure proxy on $OS_NAME"
+ fi
+ fi
+}
+
+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
+ wait_for_boot "$name"
+}
+
+delete_container() {
+ debug 1 "removing container $1 [--keep to keep]"
+ lxc delete --force "$1"
+}
+
+run_self_inside() {
+ # run_self_inside(container, args)
+ local name="$1"
+ shift
+ inside "$name" bash -s "$@" <"$0"
+}
+
+run_self_inside_as_cd() {
+ local name="$1" user="$2" dir="$3"
+ shift 3
+ inside_as_cd "$name" "$user" "$dir" bash -s "$@" <"$0"
+}
+
+main() {
+ local short_opts="ahkrsuv"
+ local long_opts="artifact,dirty,help,keep,name:,pyexe:,package,source-package,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="" package="" source_package="" unittest="" name=""
+ local dirty=false pyexe="auto"
+
+ while [ $# -ne 0 ]; do
+ cur="${1:-}"; next="${2:-}";
+ case "$cur" in
+ -a|--artifact) artifact=1;;
+ --dirty) dirty=true;;
+ -h|--help) Usage ; exit 0;;
+ -k|--keep) KEEP=true;;
+ -n|--name) name="$next"; shift;;
+ --pyexe) pyexe=$next; shift;;
+ -p|--package) package=1;;
+ -s|--source-package) source_package=1;;
+ -u|--unittest) unittest=1;;
+ -v|--verbose) VERBOSITY=$((${VERBOSITY}+1));;
+ --) shift; break;;
+ esac
+ shift;
+ done
+
+ [ $# -eq 1 ] || { bad_Usage "ERROR: Must provide os/version!"; return; }
+ local img_ref_in="$1"
+ case "${img_ref_in}" in
+ *:*) :;;
+ *) img_ref="images:${img_ref_in}";;
+ esac
+ local ref="${img_ref##*:}"
+ # ref is something like centos/6 or opensuse/42, but could
+ # be anything that lxc launch will start.
+
+ # program starts here
+ local out="" user="ci-test" cdir="" home=""
+ home="/home/$user"
+ cdir="/home/$user/cloud-init"
+ if [ -z "$name" ]; then
+ if out=$(petname 2>&1); then
+ name="ci-${out}"
+ elif out=$(uuidgen -t 2>&1); then
+ name="ci-${uuid%%-*}"
+ else
+ error "Must provide name or have petname or uuidgen"
+ return 1
+ fi
+ fi
+
+ trap cleanup EXIT
+
+ start_container "$img_ref" "$name" ||
+ { errorrc "Failed to start container for $img_ref"; return; }
+
+ get_os_info_in "$name" ||
+ { errorrc "failed to get os_info in $name"; return; }
+
+ if [ "$pyexe" = "auto" ]; then
+ case "$OS_NAME/$OS_VERSION" in
+ centos/*|opensuse/*) pyexe=python2;;
+ *) pyexe=python3;;
+ esac
+ debug 1 "set pyexe=$pyexe for $OS_NAME/$OS_VERSION"
+ fi
+
+ # prep the container (install very basic dependencies)
+ run_self_inside "$name" prep "$pyexe" ||
+ { errorrc "Failed to prep container $name"; return; }
+
+ # add the user
+ inside "$name" useradd "$user" --create-home "--home-dir=$home" ||
+ { errorrc "Failed to add user '$user' in '$name'"; return 1; }
+
+ debug 1 "inserting cloud-init"
+ inject_cloud_init "$name" "$user" "$dirty" || {
+ errorrc "FAIL: injecting cloud-init into $name failed."
+ return
+ }
+
+ inside_as_cd "$name" root "$cdir" \
+ $pyexe ./tools/read-dependencies "--distro=${OS_NAME}" \
+ --test-distro || {
+ errorrc "FAIL: failed to install dependencies with read-dependencies"
+ return
+ }
+
+ local errors=0
+ inside_as_cd "$name" "$user" "$cdir" git status ||
+ { errorrc "git checkout failed."; errors=$(($errors+1)); }
+
+ if [ -n "$unittest" ]; then
+ debug 1 "running unit tests."
+ run_self_inside_as_cd "$name" "$user" "$cdir" nose "$pyexe" \
+ tests/unittests cloudinit/ ||
+ { errorrc "nosetests failed."; errors=$(($errors+1)); }
+ fi
+
+ local build_pkg="" build_srcpkg="" pkg_ext=""
+ case "$OS_NAME" in
+ debian|ubuntu)
+ build_pkg="./packages/bddeb -d"
+ build_srcpkg="./packages/bddeb -S -d"
+ pkg_ext=".deb";;
+ centos|opensuse)
+ build_pkg="./packages/brpm"
+ build_srcpkg="./packages/brpm --srpm"
+ pkg_ext=".rpm";;
+ esac
+ if [ -n "$source_package" ]; then
+ [ -n "$build_pkg" ] || {
+ error "Unknown package command for $OS_NAME"
+ return 1
+ }
+ debug 1 "building source package with $build_srcpkg."
+ inside_as_cd "$name" "$user" "$cdir" $pyexe $build_srcpkg ||
+ { errorrc "failed: $build_srcpkg"; errors=$(($errors+1)); }
+ fi
+
+ if [ -n "$package" ]; then
+ [ -n "$build_srcpkg" ] || {
+ error "Unknown build source command for $OS_NAME"
+ return 1
+ }
+ debug 1 "building binary package with $build_pkg."
+ inside_as_cd "$name" "$user" "$cdir" $pyexe $build_pkg ||
+ { errorrc "failed: $build_pkg"; errors=$(($errors+1)); }
+ fi
+
+ if [ -n "$artifact" ]; then
+ local art=""
+ for art in $(inside "$name" sh -c "echo $cdir/*.${pkg_ext}"); do
+ lxc file pull "$name/$art" .
+ debug 1 "wrote ./$art"
+ done
+ fi
+
+ if [ "$errors" != "0" ]; then
+ error "there were $errors errors."
+ return 1
+ fi
+ return 0
+}
+
+case "${1:-}" in
+ prep|os_info|wait_inside|nose) _n=$1; shift; "$_n" "$@";;
+ *) main "$@";;
+esac
+
+# vi: ts=4 expandtab
Follow ups