summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorKim Hagen <kim.sidney@gmail.com>2018-10-25 22:26:25 +0200
committerKim Hagen <kim.sidney@gmail.com>2018-10-25 22:26:25 +0200
commitb120f4f7a670674779a93f8c882c81f44a993888 (patch)
tree906d15f6520751b5e8fbeb49b680e673a5cc6aa3 /tools
parent838581d57c8765d3e487f58bc37ea103af39d26f (diff)
parent833adcdf6f85ec2305e62bea5a20f9363bf95507 (diff)
downloadvyos-cloud-init-b120f4f7a670674779a93f8c882c81f44a993888.tar.gz
vyos-cloud-init-b120f4f7a670674779a93f8c882c81f44a993888.zip
Merge tag 'ubuntu/18.4-0ubuntu1_16.04.2' into current
Conflicts: cloudinit/sources/DataSourceAzure.py config/cloud.cfg.tmpl integration-requirements.txt tools/read-version
Diffstat (limited to 'tools')
-rw-r--r--tools/Z99-cloud-locale-test.sh13
-rw-r--r--tools/Z99-cloudinit-warnings.sh8
-rwxr-xr-xtools/ds-identify135
-rwxr-xr-xtools/make-tarball15
-rwxr-xr-xtools/net-convert.py84
-rwxr-xr-xtools/read-dependencies8
-rwxr-xr-xtools/run-centos340
-rwxr-xr-xtools/run-container592
-rwxr-xr-xtools/tox-venv189
9 files changed, 921 insertions, 463 deletions
diff --git a/tools/Z99-cloud-locale-test.sh b/tools/Z99-cloud-locale-test.sh
index 4978d87e..9ee44bd2 100644
--- a/tools/Z99-cloud-locale-test.sh
+++ b/tools/Z99-cloud-locale-test.sh
@@ -11,8 +11,11 @@
# of how to fix them.
locale_warn() {
- local bad_names="" bad_lcs="" key="" val="" var="" vars="" bad_kv=""
- local w1 w2 w3 w4 remain
+ command -v local >/dev/null && local _local="local" ||
+ typeset _local="typeset"
+
+ $_local bad_names="" bad_lcs="" key="" val="" var="" vars="" bad_kv=""
+ $_local w1 w2 w3 w4 remain
# if shell is zsh, act like sh only for this function (-L).
# The behavior change will not permenently affect user's shell.
@@ -53,8 +56,8 @@ locale_warn() {
printf " This can affect your user experience significantly, including the\n"
printf " ability to manage packages. You may install the locales by running:\n\n"
- local bad invalid="" to_gen="" sfile="/usr/share/i18n/SUPPORTED"
- local pkgs=""
+ $_local bad invalid="" to_gen="" sfile="/usr/share/i18n/SUPPORTED"
+ $_local local pkgs=""
if [ -e "$sfile" ]; then
for bad in ${bad_lcs}; do
grep -q -i "${bad}" "$sfile" &&
@@ -67,7 +70,7 @@ locale_warn() {
fi
to_gen=${to_gen# }
- local pkgs=""
+ $_local pkgs=""
for bad in ${to_gen}; do
pkgs="${pkgs} language-pack-${bad%%_*}"
done
diff --git a/tools/Z99-cloudinit-warnings.sh b/tools/Z99-cloudinit-warnings.sh
index 1d413374..cb8b4638 100644
--- a/tools/Z99-cloudinit-warnings.sh
+++ b/tools/Z99-cloudinit-warnings.sh
@@ -4,9 +4,11 @@
# Purpose: show user warnings on login.
cloud_init_warnings() {
- local warning="" idir="/var/lib/cloud/instance" n=0
- local warndir="$idir/warnings"
- local ufile="$HOME/.cloud-warnings.skip" sfile="$warndir/.skip"
+ command -v local >/dev/null && local _local="local" ||
+ typeset _local="typeset"
+ $_local warning="" idir="/var/lib/cloud/instance" n=0
+ $_local warndir="$idir/warnings"
+ $_local ufile="$HOME/.cloud-warnings.skip" sfile="$warndir/.skip"
[ -d "$warndir" ] || return 0
[ ! -f "$ufile" ] || return 0
[ ! -f "$sfile" ] || return 0
diff --git a/tools/ds-identify b/tools/ds-identify
index 9a2db5c4..5afe5aa1 100755
--- a/tools/ds-identify
+++ b/tools/ds-identify
@@ -1,16 +1,25 @@
#!/bin/sh
+# shellcheck disable=2015,2039,2162,2166
#
# ds-identify is configured via /etc/cloud/ds-identify.cfg
-# or on the kernel command line. It takes primarily 2 inputs:
+# or on the kernel command line. It takes the following inputs:
+#
# datasource: can specify the datasource that should be used.
-# kernel command line option: ci.datasource=<dsname>
+# kernel command line option: ci.datasource=<dsname> or ci.ds=<dsname>
+# example line in /etc/cloud/ds-identify.cfg:
+# datasource: Ec2
#
# policy: a string that indicates how ds-identify should operate.
-# kernel command line option: ci.di.policy=<policy>
+#
# The format is:
# <mode>,found=value,maybe=value,notfound=value
# default setting is:
-# search,found=all,maybe=all,notfound=disable
+# search,found=all,maybe=all,notfound=disabled
+#
+# kernel command line option: ci.di.policy=<policy>
+# example line in /etc/cloud/ds-identify.cfg:
+# policy: search,found=all,maybe=none,notfound=disabled
+#
#
# Mode:
# disabled: disable cloud-init
@@ -115,7 +124,7 @@ DI_DSNAME=""
# be searched if there is no setting found in config.
DI_DSLIST_DEFAULT="MAAS ConfigDrive NoCloud AltCloud Azure Bigstep \
CloudSigma CloudStack DigitalOcean AliYun Ec2 GCE OpenNebula OpenStack \
-OVF SmartOS Scaleway Hetzner IBMCloud"
+OVF SmartOS Scaleway Hetzner IBMCloud Oracle"
DI_DSLIST=""
DI_MODE=""
DI_ON_FOUND=""
@@ -186,6 +195,16 @@ block_dev_with_label() {
return 0
}
+ensure_sane_path() {
+ local t
+ for t in /sbin /usr/sbin /bin /usr/bin; do
+ case ":$PATH:" in
+ *:$t:*|*:$t/:*) continue;;
+ esac
+ PATH="${PATH:+${PATH}:}$t"
+ done
+}
+
read_fs_info() {
cached "${DI_BLKID_OUTPUT}" && return 0
# do not rely on links in /dev/disk which might not be present yet.
@@ -210,7 +229,9 @@ read_fs_info() {
# 'set --' will collapse multiple consecutive entries in IFS for
# whitespace characters (\n, tab, " ") so we cannot rely on getting
# empty lines in "$@" below.
- IFS="$CR"; set -- $out; IFS="$oifs"
+
+ # shellcheck disable=2086
+ { IFS="$CR"; set -- $out; IFS="$oifs"; }
for line in "$@"; do
case "${line}" in
@@ -258,7 +279,7 @@ read_virt() {
is_container() {
case "${DI_VIRT}" in
- lxc|lxc-libvirt|systemd-nspawn|docker|rkt) return 0;;
+ container-other|lxc|lxc-libvirt|systemd-nspawn|docker|rkt) return 0;;
*) return 1;;
esac
}
@@ -310,6 +331,7 @@ read_dmi_product_serial() {
DI_DMI_PRODUCT_SERIAL="$_RET"
}
+# shellcheck disable=2034
read_uname_info() {
# run uname, and parse output.
# uname is tricky to parse as it outputs always in a given order
@@ -329,6 +351,7 @@ read_uname_info() {
return $ret
}
fi
+ # shellcheck disable=2086
set -- $out
DI_UNAME_KERNEL_NAME="$1"
DI_UNAME_NODENAME="$2"
@@ -356,7 +379,8 @@ parse_yaml_array() {
# the fix was to quote the open bracket (val=${val#"["}) (LP: #1689648)
val=${val#"["}
val=${val%"]"}
- IFS=","; set -- $val; IFS="$oifs"
+ # shellcheck disable=2086
+ { IFS=","; set -- $val; IFS="$oifs"; }
for tok in "$@"; do
trim "$tok"
unquote "$_RET"
@@ -392,7 +416,7 @@ read_datasource_list() {
fi
if [ -z "$dslist" ]; then
dslist=${DI_DSLIST_DEFAULT}
- debug 1 "no datasource_list found, using default:" $dslist
+ debug 1 "no datasource_list found, using default: $dslist"
fi
DI_DSLIST=$dslist
return 0
@@ -403,7 +427,8 @@ read_pid1_product_name() {
cached "${DI_PID_1_PRODUCT_NAME}" && return
[ -r "${PATH_PROC_1_ENVIRON}" ] || return
out=$(tr '\0' '\n' <"${PATH_PROC_1_ENVIRON}")
- IFS="$CR"; set -- $out; IFS="$oifs"
+ # shellcheck disable=2086
+ { IFS="$CR"; set -- $out; IFS="$oifs"; }
for tok in "$@"; do
key=${tok%%=*}
[ "$key" != "$tok" ] || continue
@@ -470,6 +495,7 @@ nocase_equal() {
[ "$1" = "$2" ] && return 0
local delim="-delim-"
+ # shellcheck disable=2018,2019
out=$(echo "$1${delim}$2" | tr A-Z a-z)
[ "${out#*${delim}}" = "${out%${delim}*}" ]
}
@@ -546,11 +572,13 @@ check_config() {
else
files="$*"
fi
- set +f; set -- $files; set -f;
+ # shellcheck disable=2086
+ { set +f; set -- $files; set -f; }
if [ "$1" = "$files" -a ! -f "$1" ]; then
return 1
fi
local fname="" line="" ret="" found=0 found_fn=""
+ # shellcheck disable=2094
for fname in "$@"; do
[ -f "$fname" ] || continue
while read line; do
@@ -600,7 +628,6 @@ dscheck_NoCloud() {
*\ ds=nocloud*) return ${DS_FOUND};;
esac
- is_ibm_cloud && return ${DS_NOT_FOUND}
for d in nocloud nocloud-net; do
check_seed_dir "$d" meta-data user-data && return ${DS_FOUND}
check_writable_seed_dir "$d" meta-data user-data && return ${DS_FOUND}
@@ -611,11 +638,12 @@ dscheck_NoCloud() {
return ${DS_NOT_FOUND}
}
+is_ds_enabled() {
+ local name="$1" pad=" ${DI_DSLIST} "
+ [ "${pad#* $name }" != "${pad}" ]
+}
+
check_configdrive_v2() {
- is_ibm_cloud && return ${DS_NOT_FOUND}
- if has_fs_with_label CONFIG-2 config-2; then
- return ${DS_FOUND}
- fi
# look in /config-drive <vlc>/seed/config_drive for a directory
# openstack/YYYY-MM-DD format with a file meta_data.json
local d=""
@@ -630,6 +658,15 @@ check_configdrive_v2() {
debug 1 "config drive seeded directory had only 'latest'"
return ${DS_FOUND}
fi
+
+ local ibm_enabled=false
+ is_ds_enabled "IBMCloud" && ibm_enabled=true
+ debug 1 "is_ds_enabled(IBMCloud) = $ibm_enabled."
+ [ "$ibm_enabled" = "true" ] && is_ibm_cloud && return ${DS_NOT_FOUND}
+
+ if has_fs_with_label CONFIG-2 config-2; then
+ return ${DS_FOUND}
+ fi
return ${DS_NOT_FOUND}
}
@@ -786,7 +823,7 @@ ec2_read_strict_setting() {
# 3. look for the key 'strict_id' (datasource/Ec2/strict_id)
# only in cloud.cfg or cloud.cfg.d/EC2.cfg (case insensitive)
local cfg="${PATH_ETC_CI_CFG}" cfg_d="${PATH_ETC_CI_CFG_D}"
- if check_config strict_id $cfg "$cfg_d/*[Ee][Cc]2*.cfg"; then
+ if check_config strict_id "$cfg" "$cfg_d/*[Ee][Cc]2*.cfg"; then
debug 2 "${_RET_fname} set strict_id to $_RET"
return 0
fi
@@ -971,12 +1008,14 @@ dscheck_SmartOS() {
# joyent cloud has two virt types: kvm and container
# on kvm, product name on joyent public cloud shows 'SmartDC HVM'
# on the container platform, uname's version has: BrandZ virtual linux
+ # for container, we also verify that the socketfile exists to protect
+ # against embedded containers (lxd running on brandz)
local smartdc_kver="BrandZ virtual linux"
+ local metadata_sockfile="${PATH_ROOT}/native/.zonecontrol/metadata.sock"
dmi_product_name_matches "SmartDC*" && return $DS_FOUND
- if [ "${DI_UNAME_KERNEL_VERSION}" = "${smartdc_kver}" ] &&
- [ "${DI_VIRT}" = "container-other" ]; then
- return ${DS_FOUND}
- fi
+ [ "${DI_UNAME_KERNEL_VERSION}" = "${smartdc_kver}" ] &&
+ [ -e "${metadata_sockfile}" ] &&
+ return ${DS_FOUND}
return ${DS_NOT_FOUND}
}
@@ -993,7 +1032,7 @@ dscheck_Scaleway() {
*\ scaleway\ *) return ${DS_FOUND};;
esac
- if [ -f ${PATH_ROOT}/var/run/scaleway ]; then
+ if [ -f "${PATH_ROOT}/var/run/scaleway" ]; then
return ${DS_FOUND}
fi
@@ -1005,8 +1044,32 @@ dscheck_Hetzner() {
return ${DS_NOT_FOUND}
}
+dscheck_Oracle() {
+ local asset_tag="OracleCloud.com"
+ dmi_chassis_asset_tag_matches "${asset_tag}" && return ${DS_FOUND}
+ return ${DS_NOT_FOUND}
+}
+
is_ibm_provisioning() {
- [ -f "${PATH_ROOT}/root/provisioningConfiguration.cfg" ]
+ local pcfg="${PATH_ROOT}/root/provisioningConfiguration.cfg"
+ local logf="${PATH_ROOT}/root/swinstall.log"
+ local is_prov=false msg="config '$pcfg' did not exist."
+ if [ -f "$pcfg" ]; then
+ msg="config '$pcfg' exists."
+ is_prov=true
+ if [ -f "$logf" ]; then
+ if [ "$logf" -nt "$PATH_PROC_1_ENVIRON" ]; then
+ msg="$msg log '$logf' from current boot."
+ else
+ is_prov=false
+ msg="$msg log '$logf' from previous boot."
+ fi
+ else
+ msg="$msg log '$logf' did not exist."
+ fi
+ fi
+ debug 2 "ibm_provisioning=$is_prov: $msg"
+ [ "$is_prov" = "true" ]
}
is_ibm_cloud() {
@@ -1130,6 +1193,7 @@ found() {
}
trim() {
+ # shellcheck disable=2048,2086
set -- $*
_RET="$*"
}
@@ -1150,7 +1214,7 @@ _read_config() {
# if no parameters are set, modifies _rc scoped environment vars.
# if keyname is provided, then returns found value of that key.
local keyname="${1:-_unset}"
- local line="" hash="#" ckey="" key="" val=""
+ local line="" hash="#" key="" val=""
while read line; do
line=${line%%${hash}*}
key="${line%%:*}"
@@ -1228,7 +1292,8 @@ parse_policy() {
local mode="" report="" found="" maybe="" notfound=""
local oifs="$IFS" tok="" val=""
- IFS=","; set -- $policy; IFS="$oifs"
+ # shellcheck disable=2086
+ { IFS=","; set -- $policy; IFS="$oifs"; }
for tok in "$@"; do
val=${tok#*=}
case "$tok" in
@@ -1295,15 +1360,15 @@ manual_clean_and_existing() {
}
read_uptime() {
- local up idle
+ local up _
_RET="${UNAVAILABLE}"
- [ -f "$PATH_PROC_UPTIME" ] &&
- read up idle < "$PATH_PROC_UPTIME" && _RET="$up"
+ [ -f "$PATH_PROC_UPTIME" ] && read up _ < "$PATH_PROC_UPTIME" &&
+ _RET="$up"
return
}
_main() {
- local dscheck="" ret_dis=1 ret_en=0
+ local dscheck_fn="" ret_dis=1 ret_en=0
read_uptime
debug 1 "[up ${_RET}s]" "ds-identify $*"
@@ -1338,8 +1403,9 @@ _main() {
return
fi
- # if there is only a single entry in $DI_DSLIST
+ # shellcheck disable=2086
set -- $DI_DSLIST
+ # if there is only a single entry in $DI_DSLIST
if [ $# -eq 1 ] || [ $# -eq 2 -a "$2" = "None" ] ; then
debug 1 "single entry in datasource_list ($DI_DSLIST) use that."
found "$@"
@@ -1372,6 +1438,7 @@ _main() {
done
debug 2 "found=${found# } maybe=${maybe# }"
+ # shellcheck disable=2086
set -- $found
if [ $# -ne 0 ]; then
if [ $# -eq 1 ]; then
@@ -1387,6 +1454,7 @@ _main() {
return
fi
+ # shellcheck disable=2086
set -- $maybe
if [ $# -ne 0 -a "${DI_ON_MAYBE}" != "none" ]; then
debug 1 "$# datasources returned maybe: $*"
@@ -1415,18 +1483,19 @@ _main() {
*) error "Unexpected result";;
esac
debug 1 "$msg"
- return $ret
+ return "$ret"
}
main() {
local ret=""
+ ensure_sane_path
[ -d "$PATH_RUN_CI" ] || mkdir -p "$PATH_RUN_CI"
if [ "${1:+$1}" != "--force" ] && [ -f "$PATH_RUN_CI_CFG" ] &&
[ -f "$PATH_RUN_DI_RESULT" ]; then
if read ret < "$PATH_RUN_DI_RESULT"; then
if [ "$ret" = "0" ] || [ "$ret" = "1" ]; then
debug 2 "used cached result $ret. pass --force to re-run."
- return $ret;
+ return "$ret";
fi
debug 1 "previous run returned unexpected '$ret'. Re-running."
else
@@ -1438,7 +1507,7 @@ main() {
echo "$ret" > "$PATH_RUN_DI_RESULT"
read_uptime
debug 1 "[up ${_RET}s]" "returning $ret"
- return $ret
+ return "$ret"
}
noop() {
diff --git a/tools/make-tarball b/tools/make-tarball
index 3197689f..8d540139 100755
--- a/tools/make-tarball
+++ b/tools/make-tarball
@@ -13,22 +13,28 @@ Usage: ${0##*/} [revision]
create a tarball of revision (default HEAD)
options:
- -o | --output FILE write to file
+ -h | --help print usage
+ -o | --output FILE write to file
+ --orig-tarball Write file cloud-init_<version>.orig.tar.gz
+ --long Use git describe --long for versioning
EOF
}
short_opts="ho:v"
-long_opts="help,output:,long,verbose"
+long_opts="help,output:,orig-tarball,long"
getopt_out=$(getopt --name "${0##*/}" \
--options "${short_opts}" --long "${long_opts}" -- "$@") &&
eval set -- "${getopt_out}" || { Usage 1>&2; exit 1; }
long_opt=""
+orig_opt=""
while [ $# -ne 0 ]; do
cur=$1; next=$2
case "$cur" in
+ -h|--help) Usage; exit 0;;
-o|--output) output=$next; shift;;
--long) long_opt="--long";;
+ --orig-tarball) orig_opt=".orig";;
--) shift; break;;
esac
shift;
@@ -39,7 +45,10 @@ version=$(git describe --abbrev=8 "--match=[0-9]*" ${long_opt} $rev)
archive_base="cloud-init-$version"
if [ -z "$output" ]; then
- output="$archive_base.tar.gz"
+ if [ ! -z "$orig_opt" ]; then
+ archive_base="cloud-init_$version"
+ fi
+ output="$archive_base$orig_opt.tar.gz"
fi
# when building an archiving from HEAD, ensure that there aren't any
diff --git a/tools/net-convert.py b/tools/net-convert.py
deleted file mode 100755
index 68559cbf..00000000
--- a/tools/net-convert.py
+++ /dev/null
@@ -1,84 +0,0 @@
-#!/usr/bin/python3
-# This file is part of cloud-init. See LICENSE file for license information.
-
-import argparse
-import json
-import os
-import yaml
-
-from cloudinit.sources.helpers import openstack
-
-from cloudinit.net import eni
-from cloudinit.net import netplan
-from cloudinit.net import network_state
-from cloudinit.net import sysconfig
-
-
-def main():
- parser = argparse.ArgumentParser()
- parser.add_argument("--network-data", "-p", type=open,
- metavar="PATH", required=True)
- parser.add_argument("--kind", "-k",
- choices=['eni', 'network_data.json', 'yaml'],
- required=True)
- parser.add_argument("-d", "--directory",
- metavar="PATH",
- help="directory to place output in",
- required=True)
- parser.add_argument("-m", "--mac",
- metavar="name,mac",
- action='append',
- help="interface name to mac mapping")
- parser.add_argument("--output-kind", "-ok",
- choices=['eni', 'netplan', 'sysconfig'],
- required=True)
- args = parser.parse_args()
-
- if not os.path.isdir(args.directory):
- os.makedirs(args.directory)
-
- if args.mac:
- known_macs = {}
- for item in args.mac:
- iface_name, iface_mac = item.split(",", 1)
- known_macs[iface_mac] = iface_name
- else:
- known_macs = None
-
- net_data = args.network_data.read()
- if args.kind == "eni":
- pre_ns = eni.convert_eni_data(net_data)
- ns = network_state.parse_net_config_data(pre_ns)
- elif args.kind == "yaml":
- pre_ns = yaml.load(net_data)
- if 'network' in pre_ns:
- pre_ns = pre_ns.get('network')
- print("Input YAML")
- print(yaml.dump(pre_ns, default_flow_style=False, indent=4))
- ns = network_state.parse_net_config_data(pre_ns)
- else:
- pre_ns = openstack.convert_net_json(
- json.loads(net_data), known_macs=known_macs)
- ns = network_state.parse_net_config_data(pre_ns)
-
- if not ns:
- raise RuntimeError("No valid network_state object created from"
- "input data")
-
- print("\nInternal State")
- print(yaml.dump(ns, default_flow_style=False, indent=4))
- if args.output_kind == "eni":
- r_cls = eni.Renderer
- elif args.output_kind == "netplan":
- r_cls = netplan.Renderer
- else:
- r_cls = sysconfig.Renderer
-
- r = r_cls()
- r.render_network_state(network_state=ns, target=args.directory)
-
-
-if __name__ == '__main__':
- main()
-
-# vi: ts=4 expandtab
diff --git a/tools/read-dependencies b/tools/read-dependencies
index 421f470a..b4656e69 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 cb241ee5..4506b20d 100755
--- a/tools/run-centos
+++ b/tools/run-centos
@@ -1,18 +1,17 @@
#!/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 . |
+ ================ DEPRECATED ================
+EOF
+}
Usage() {
+ deprecated
cat <<EOF
Usage: ${0##*/} [ options ] version
@@ -34,319 +33,40 @@ 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";;
+ -a|--artifact) cur="--artifacts=.";;
+ 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 00000000..6dedb757
--- /dev/null
+++ b/tools/run-container
@@ -0,0 +1,592 @@
+#!/bin/bash
+# This file is part of cloud-init. See LICENSE file for license information.
+#
+# shellcheck disable=2015,2016,2039,2162,2166
+
+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 | --artifacts DIR copy build artifacts out to DIR.
+ by default artifacts are not copied out.
+ --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" ]; then
+ if [ "$KEEP" = "true" ]; then
+ error "not deleting container '$CONTAINER' due to --keep"
+ else
+ delete_container "$CONTAINER"
+ fi
+ 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 dname="cloud-init" 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_NAME/$OS_VERSION"
+}
+
+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
+ 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"
+ get_os_info
+ local py2pkg="python2" py3pkg="python3"
+ case "$OS_NAME" in
+ opensuse)
+ py2pkg="python-base"
+ py3pkg="python3-base";;
+ esac
+
+ case "$pyexe" in
+ python2) pairs="$pairs python2:$py2pkg";;
+ python3) pairs="$pairs python3:$py3pkg";;
+ esac
+
+ 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}"
+ # shellcheck disable=SC2086
+ 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";
+ 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 _ </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="a:hknpsuv"
+ local long_opts="artifacts:,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 package=false srcpackage=false unittest="" name=""
+ local dirty=false pyexe="auto" artifact_d="."
+
+ while [ $# -ne 0 ]; do
+ cur="${1:-}"; next="${2:-}";
+ case "$cur" in
+ -a|--artifacts) artifact_d="$next";;
+ --dirty) dirty=true;;
+ -h|--help) Usage ; exit 0;;
+ -k|--keep) KEEP=true;;
+ -n|--name) name="$next"; shift;;
+ --pyexe) pyexe=$next; shift;;
+ -p|--package) package=true;;
+ -s|--source-package) srcpackage=true;;
+ -u|--unittest) unittest=1;;
+ -v|--verbose) VERBOSITY=$((VERBOSITY+1));;
+ --) shift; break;;
+ esac
+ shift;
+ done
+
+ [ $# -eq 1 ] || { bad_Usage "Expected 1 arg, got $# ($*)"; return; }
+ local img_ref_in="$1"
+ case "${img_ref_in}" in
+ *:*) img_ref="${img_ref_in}";;
+ *) img_ref="images:${img_ref_in}";;
+ esac
+
+ # program starts here
+ local out="" user="ci-test" cdir="" home=""
+ home="/home/$user"
+ cdir="$home/cloud-init"
+ if [ -z "$name" ]; then
+ if out=$(petname 2>&1); then
+ name="ci-${out}"
+ elif out=$(uuidgen -t 2>&1); then
+ name="ci-${out%%-*}"
+ 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=( )
+ inside_as_cd "$name" "$user" "$cdir" git status || {
+ errorrc "git checkout failed."
+ errors[${#errors[@]}]="git checkout";
+ }
+
+ 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[@]}]="nosetests"
+ }
+ fi
+
+ local build_pkg="" build_srcpkg="" pkg_ext="" distflag=""
+ case "$OS_NAME" in
+ centos) distflag="--distro=redhat";;
+ opensuse) distflag="--distro=suse";;
+ esac
+
+ 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 $distflag"
+ build_srcpkg="./packages/brpm $distflag --srpm"
+ pkg_ext=".rpm";;
+ esac
+ if [ "$srcpackage" = "true" ]; then
+ [ -n "$build_srcpkg" ] || {
+ error "Unknown package command for $OS_NAME"
+ return 1
+ }
+ debug 1 "building source package with $build_srcpkg."
+ # shellcheck disable=SC2086
+ inside_as_cd "$name" "$user" "$cdir" $pyexe $build_srcpkg || {
+ errorrc "failed: $build_srcpkg";
+ errors[${#errors[@]}]="source package"
+ }
+ fi
+
+ if [ "$package" = "true" ]; then
+ [ -n "$build_pkg" ] || {
+ error "Unknown build source command for $OS_NAME"
+ return 1
+ }
+ debug 1 "building binary package with $build_pkg."
+ # shellcheck disable=SC2086
+ inside_as_cd "$name" "$user" "$cdir" $pyexe $build_pkg || {
+ errorrc "failed: $build_pkg";
+ errors[${#errors[@]}]="binary package"
+ }
+ fi
+
+ if [ -n "$artifact_d" ] &&
+ [ "$package" = "true" -o "$srcpackage" = "true" ]; then
+ local art=""
+ artifact_d="${artifact_d%/}/"
+ [ -d "${artifact_d}" ] || mkdir -p "$artifact_d" || {
+ errorrc "failed to create artifact dir '$artifact_d'"
+ return
+ }
+
+ for art in $(inside "$name" sh -c "echo $cdir/*${pkg_ext}"); do
+ lxc file pull "$name/$art" "$artifact_d" || {
+ errorrc "Failed to pull '$name/$art' to ${artifact_d}"
+ errors[${#errors[@]}]="artifact copy: $art"
+ }
+ debug 1 "wrote ${artifact_d}${art##*/}"
+ done
+ fi
+
+ if [ "${#errors[@]}" != "0" ]; then
+ local e=""
+ error "there were ${#errors[@]} errors."
+ for e in "${errors[@]}"; do
+ error " $e"
+ done
+ return 1
+ fi
+ return 0
+}
+
+case "${1:-}" in
+ prep|os_info|wait_inside|nose) _n=$1; shift; "$_n" "$@";;
+ *) main "$@";;
+esac
+
+# vi: ts=4 expandtab
diff --git a/tools/tox-venv b/tools/tox-venv
index 76ed5076..a5d21625 100755
--- a/tools/tox-venv
+++ b/tools/tox-venv
@@ -1,42 +1,185 @@
#!/bin/sh
+# https://gist.github.com/smoser/2d4100a6a5d230ca937f
+CR='
+'
error() { echo "$@" 1>&2; }
fail() { [ $# -eq 0 ] || error "$@"; exit 1; }
+get_env_dirs() {
+ # read 'tox --showconfig'. return list of
+ # envname:dir
+ local key="" equal="" val="" curenv="" out=""
+ while read key equal val; do
+ case "$key" in
+ "[testenv:"*)
+ curenv=${key#*:};
+ curenv=${curenv%%"]"*};
+ continue;;
+ esac
+ if [ "${key#*=}" != "$key" ]; then
+ # older tox shows key=value or key= value
+ # newer tox shows: key = value
+ key=${key%%=*}
+ val=${equal}
+ fi
+ [ "$key" = "envdir" ] || continue
+ out="${out:+${out}${CR}}${curenv}:$val"
+ done
+ echo "$out"
+}
+
+load_config() {
+ local tox_ini="$1" out="" envs=""
+ if [ "$tox_ini" = "${CACHED_ENVS_INI}" ]; then
+ _RET="$CACHED_ENVS"
+ return
+ fi
+ out=$(tox -c "$tox_ini" --showconfig) || return 1
+ envs=$(echo "$out" | get_env_dirs) || return 1
+ CACHED_ENVS="$envs"
+ CACHED_ENVS_INI="$tox_ini"
+ _RET="$envs"
+}
+
+list_environments() {
+ local tox_ini="$1" prefix=" " out="" envs="" oifs="$IFS"
+ load_config "$tox_ini" || return 1
+ envs="${_RET}"
+ IFS="$CR"
+ for d in ${envs}; do
+ env=${d%%:*}
+ dir=${d#*:}
+ [ -f "$dir/bin/activate" ] && s="*" || s=""
+ echo "${prefix}$env$s";
+ done
+ IFS="$oifs"
+}
+
+get_command() {
+ local tox_ini="$1" env="$2" out=""
+ shift 2
+ out=$(
+ sed -e ':x; /\\$/ { N; s/\\\n[ ]*//; tx };' "${tox_ini}" |
+ gawk '
+ $1 ~ /^\[testenv.*\]/ {
+ name=$1;
+ sub("\\[", "", name); sub(".*:", "", name);
+ sub("].*", "", name);
+ curenv=name; };
+ $1 == "basepython" && (name == "testenv" || name == n) { python=$3 }
+ $1 == "commands" && (name == "testenv" || name == n) {
+ sub("commands = ", ""); cmd = $0; };
+ END {
+ sub("{envpython}", python, cmd);
+ sub("{toxinidir}", toxinidir, cmd);
+ if (inargs == "") replacement = "\\1"
+ else replacement = inargs
+ cmd = gensub(/{posargs:?([^}]*)}/, replacement, "global", cmd)
+ print(cmd);
+ }' n="$env" toxinidir="$(dirname $tox_ini)" inargs="$*")
+ if [ -z "$out" ]; then
+ error "Failed to find command for $env in $tox_ini"
+ return 1
+ fi
+ echo "$out"
+}
+
+get_env_dir() {
+ local tox_ini="$1" env="$2" oifs="$IFS" t="" d="" envs=""
+ if [ "${TOX_VENV_SHORTCUT:-1}" != "0" ]; then
+ local stox_d="${tox_ini%/*}/.tox/${env}"
+ if [ -e "${stox_d}/bin/activate" ]; then
+ _RET="${stox_d}"
+ return
+ fi
+ fi
+ load_config "$tox_ini" && envs="$_RET" || return 1
+ IFS="$CR"
+ for t in $envs; do
+ [ "$env" = "${t%%:*}" ] && d="${t#*:}" && break
+ done
+ IFS=${oifs}
+ [ -n "$d" ] || return 1
+ _RET="$d"
+}
+
Usage() {
- cat <<EOF
-Usage: ${0##*/} tox-environment [command [args]]
+ local tox_ini="$1"
+ cat <<EOF
+Usage: ${0##*/} [--no-create] tox-environment [command [args]]
run command with provided arguments in the provided tox environment
- command defaults to \${SHELL:-/bin/sh}.
+ command defaults to 'cmd' (see below).
+
+ run with '--list' to show available environments
- invoke with '--list' to show available environments
+ if 'command' above is literal 'cmd' or '-', then the 'command' will
+ be read from tox.ini. This allows you to do:
+ tox-venv py27 - tests/some/sub/dir
+ and have the 'command' read correctly and have that execute:
+ python -m nose tests/some/sub/dir
EOF
-}
-list_toxes() {
- local td="$1" pre="$2" d=""
- ( cd "$tox_d" &&
- for d in *; do [ -f "$d/bin/activate" ] && echo "${pre}$d"; done)
+
+ if [ -f "$tox_ini" ]; then
+ local oini=${tox_ini}
+ [ "${tox_ini}" -ef "$PWD/tox.ini" ] && oini="./tox.ini"
+ echo
+ echo "environments in $oini"
+ list_environments "$tox_ini"
+ fi
}
-[ $# -eq 0 ] && { Usage 1>&2; exit 1; }
-[ "$1" = "-h" -o "$1" = "--help" ] && { Usage; exit 0; }
+if [ -f tox.ini ]; then
+ tox_ini="$PWD/tox.ini"
+else
+ tox_ini="${0%/*}/../tox.ini"
+fi
-env="$1"
-shift
-tox_d="${0%/*}/../.tox"
-activate="$tox_d/$env/bin/activate"
+[ $# -eq 0 ] && { Usage "$tox_ini" 1>&2; exit 1; }
+[ "$1" = "-h" -o "$1" = "--help" ] && { Usage "$tox_ini"; exit 0; }
+[ -f "$tox_ini" ] || fail "$tox_ini: did not find tox.ini"
-[ -d "$tox_d" ] || fail "$tox_d: not a dir. maybe run 'tox'?"
+if [ "$1" = "-l" -o "$1" = "--list" ]; then
+ list_environments "$tox_ini"
+ exit
+fi
-[ "$env" = "-l" -o "$env" = "--list" ] && { list_toxes ; exit ; }
+nocreate="false"
+if [ "$1" = "--no-create" ]; then
+ nocreate="true"
+ shift
+fi
-if [ ! -f "$activate" ]; then
- error "$env: not a valid tox environment?"
- error "try one of:"
- list_toxes "$tox_d" " "
- fail
+env="$1"
+shift
+[ "$1" = "--" ] && shift
+get_env_dir "$tox_ini" "$env" && activate="$_RET/bin/activate" || activate=""
+
+if [ -z "$activate" -o ! -f "$activate" ]; then
+ if $nocreate; then
+ fail "tox env '$env' did not exist, and no-create specified"
+ elif [ -n "$activate" ]; then
+ error "attempting to create $env:"
+ error " tox -c $tox_ini --recreate --notest -e $env"
+ tox -c "$tox_ini" --recreate --notest -e "$env" ||
+ fail "failed creation of env $env"
+ else
+ error "$env: not a valid tox environment?"
+ error "found tox_ini=$tox_ini"
+ error "try one of:"
+ list_environments "$tox_ini" 1>&2
+ fail
+ fi
fi
. "$activate"
-[ "$#" -gt 0 ] || set -- ${SHELL:-/bin/bash}
+[ $# -eq 0 ] && set -- cmd
+if [ "$1" = "cmd" -o "$1" = "-" ]; then
+ shift
+ out=$(get_command "$tox_ini" "$env" "$@") || exit
+ eval set -- "$out"
+fi
+echo "inside tox:$env running: $*" 1>&2
debian_chroot="tox:$env" exec "$@"
+
+# vi: ts=4 expandtab