Merge ~smoser/cloud-init:bug/fix-ci-redhat into cloud-init:master

Proposed by Scott Moser on 2017-06-14
Status: Merged
Approved by: Scott Moser on 2017-06-14
Approved revision: c0e1a31e3c7abd5e193145e3b56b05dcd488d469
Merged at revision: 55a006afca73633c607c537dee62097e85011443
Proposed branch: ~smoser/cloud-init:bug/fix-ci-redhat
Merge into: cloud-init:master
Diff against target: 431 lines (+139/-118)
5 files modified
dev/null (+0/-49)
packages/pkg-deps.json (+1/-1)
tests/unittests/test_handler/test_handler_disk_setup.py (+16/-16)
tools/read-dependencies (+23/-9)
tools/run-centos (+99/-43)
Reviewer Review Type Date Requested Status
Server Team CI bot continuous-integration Approve on 2017-06-14
Chad Smith 2017-06-14 Approve on 2017-06-14
Joshua Powers (community) Approve on 2017-06-14
Review via email: mp+325679@code.launchpad.net

Commit Message

tools/run-centos: cleanups and move to using read-dependencies

These changes are all in an effort to get tools/run-centos using
read-dependencies rather than the 'setup-centos' script with a separate
set of dependencies listed.

 - tools/read-dependencies: support taking multiple --requirements options.
   This allows run-centos to get both test and build dependencies.
   Ultimately, I think it might be nicer for read-dependencies to take a
   list of "goals" (build, test, run or test-tox) rather than having the
   caller need to know to provide multiple --requirements.

 - packages/pkg-deps.json: drop the version on the sudo package.
   centos 6 has newer (1.8.6p3) version than listed, so its not a problem.

 - test_handler_disk_setup.py: a test case here was using assertLogs
   which is not present in the version of unittest2 that is available in
   centos 6 epel. We just adjust it to use with_logs = True.

 - tools/run-cents:
   - improve usage with example
   - add 'inside_as_cd' to provide the dir you want to cd first to.
   - avoid the intermediate tarball on disk in the container.
   - add 'prep' subcommand and use it to install pre-dependencies.
   - use read-dependencies.

To post a comment you must log in.
Scott Moser (smoser) wrote :

With this changeset applied i can:

for ver in 6 7; do
   ./tools/run-centos -vv $ver --unittest --rpm --srpm --artifact 2>&1 |
       tee out${ver}.log; done

Joshua Powers (powersj) wrote :

Only reviewed run-centos: clean up looks good and runs as expected locally. Thanks for taking the time to convert over to using read-dependencies.

review: Approve
Chad Smith (chad.smith) :
Chad Smith (chad.smith) :
Chad Smith (chad.smith) wrote :

Approve as is, but it'd be nice to check all requirements-files specified to read-dependencies.

review: Approve
Scott Moser (smoser) :
Scott Moser (smoser) wrote :

Approving per Chad and Josh.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/packages/pkg-deps.json b/packages/pkg-deps.json
2index 8b8f3c3..822d29d 100644
3--- a/packages/pkg-deps.json
4+++ b/packages/pkg-deps.json
5@@ -62,7 +62,7 @@
6 "procps",
7 "rsyslog",
8 "shadow-utils",
9- "sudo >= 1.7.2p2-3"
10+ "sudo"
11 ]
12 },
13 "suse" : {
14diff --git a/tests/unittests/test_handler/test_handler_disk_setup.py b/tests/unittests/test_handler/test_handler_disk_setup.py
15index 916a0d7..8a6d49e 100644
16--- a/tests/unittests/test_handler/test_handler_disk_setup.py
17+++ b/tests/unittests/test_handler/test_handler_disk_setup.py
18@@ -3,7 +3,7 @@
19 import random
20
21 from cloudinit.config import cc_disk_setup
22-from ..helpers import ExitStack, mock, TestCase
23+from ..helpers import CiTestCase, ExitStack, mock, TestCase
24
25
26 class TestIsDiskUsed(TestCase):
27@@ -174,32 +174,32 @@ class TestUpdateFsSetupDevices(TestCase):
28 return_value=('/dev/xdb1', False))
29 @mock.patch('cloudinit.config.cc_disk_setup.device_type', return_value=None)
30 @mock.patch('cloudinit.config.cc_disk_setup.util.subp', return_value=('', ''))
31-class TestMkfsCommandHandling(TestCase):
32+class TestMkfsCommandHandling(CiTestCase):
33+
34+ with_logs = True
35
36 def test_with_cmd(self, subp, *args):
37 """mkfs honors cmd and logs warnings when extra_opts or overwrite are
38 provided."""
39- with self.assertLogs(
40- 'cloudinit.config.cc_disk_setup') as logs:
41- cc_disk_setup.mkfs({
42- 'cmd': 'mkfs -t %(filesystem)s -L %(label)s %(device)s',
43- 'filesystem': 'ext4',
44- 'device': '/dev/xdb1',
45- 'label': 'with_cmd',
46- 'extra_opts': ['should', 'generate', 'warning'],
47- 'overwrite': 'should generate warning too'
48- })
49+ cc_disk_setup.mkfs({
50+ 'cmd': 'mkfs -t %(filesystem)s -L %(label)s %(device)s',
51+ 'filesystem': 'ext4',
52+ 'device': '/dev/xdb1',
53+ 'label': 'with_cmd',
54+ 'extra_opts': ['should', 'generate', 'warning'],
55+ 'overwrite': 'should generate warning too'
56+ })
57
58 self.assertIn(
59- 'WARNING:cloudinit.config.cc_disk_setup:fs_setup:extra_opts ' +
60+ 'extra_opts ' +
61 'ignored because cmd was specified: mkfs -t ext4 -L with_cmd ' +
62 '/dev/xdb1',
63- logs.output)
64+ self.logs.getvalue())
65 self.assertIn(
66- 'WARNING:cloudinit.config.cc_disk_setup:fs_setup:overwrite ' +
67+ 'overwrite ' +
68 'ignored because cmd was specified: mkfs -t ext4 -L with_cmd ' +
69 '/dev/xdb1',
70- logs.output)
71+ self.logs.getvalue())
72
73 subp.assert_called_once_with(
74 'mkfs -t ext4 -L with_cmd /dev/xdb1', shell=True)
75diff --git a/tools/read-dependencies b/tools/read-dependencies
76index 4ba2c1b..8a58534 100755
77--- a/tools/read-dependencies
78+++ b/tools/read-dependencies
79@@ -18,6 +18,7 @@ import re
80 import subprocess
81 import sys
82
83+DEFAULT_REQUIREMENTS = 'requirements.txt'
84
85 # Map the appropriate package dir needed for each distro choice
86 DISTRO_PKG_TYPE_MAP = {
87@@ -51,8 +52,9 @@ def get_parser():
88 """Return an argument parser for this command."""
89 parser = ArgumentParser(description=__doc__)
90 parser.add_argument(
91- '-r', '--requirements-file', type=str, dest='req_file',
92- default='requirements.txt', help='The pip-style requirements file')
93+ '-r', '--requirements-file', type=str, dest='req_files',
94+ action='append', default=None,
95+ help='pip-style requirements file [default=%s]' % DEFAULT_REQUIREMENTS)
96 parser.add_argument(
97 '-d', '--distro', type=str, choices=DISTRO_PKG_TYPE_MAP.keys(),
98 help='The name of the distro to generate package deps for.')
99@@ -144,12 +146,24 @@ def main(distro):
100 topd = os.path.realpath(os.environ.get('CLOUD_INIT_TOP_D'))
101 else:
102 topd = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
103- req_path = os.path.join(topd, args.req_file)
104- if not os.path.isfile(req_path):
105- sys.stderr.write("Unable to locate '%s' file that should "
106- "exist in cloud-init root directory." % req_path)
107- return 1
108- pip_pkg_names = parse_pip_requirements(req_path)
109+
110+ if args.req_files is None:
111+ args.req_files = [os.path.join(topd, DEFAULT_REQUIREMENTS)]
112+ if not os.path.isfile(args.req_files[0]):
113+ sys.stderr.write("Unable to locate '%s' file that should "
114+ "exist in cloud-init root directory." %
115+ args.req_files[0])
116+ sys.exit(1)
117+
118+ bad_files = [r for r in args.req_files if not os.path.isfile(r)]
119+ if bad_files:
120+ sys.stderr.write(
121+ "Unable to find requirements files: %s\n" % ','.join(bad_files))
122+ sys.exit(1)
123+
124+ pip_pkg_names = set()
125+ for req_path in args.req_files:
126+ pip_pkg_names.update(set(parse_pip_requirements(req_path)))
127 deps_from_json = get_package_deps_from_json(topd, args.distro)
128 renames = deps_from_json.get('renames', {})
129 translated_pip_names = translate_pip_to_system_pkg(
130@@ -174,7 +188,7 @@ def pkg_install(pkg_list, distro, dry_run=False):
131 """Install a list of packages using the DISTRO_INSTALL_PKG_CMD."""
132 print('Installing deps: {0}{1}'.format(
133 '(dryrun)' if dry_run else '', ' '.join(pkg_list)))
134- pkg_list.extend(EXTRA_SYSTEM_BASE_PKGS)
135+ pkg_list = list(pkg_list) + EXTRA_SYSTEM_BASE_PKGS
136 install_cmd = []
137 if dry_run:
138 install_cmd.append('echo')
139diff --git a/tools/run-centos b/tools/run-centos
140index de21d75..99ba6be 100755
141--- a/tools/run-centos
142+++ b/tools/run-centos
143@@ -14,17 +14,22 @@ errorrc() { local r=$?; error "$@" "ret=$r"; return $r; }
144
145 Usage() {
146 cat <<EOF
147-Usage: ${0##*/} [ options ] CentOS version
148+Usage: ${0##*/} [ options ] version
149
150 This utility can makes it easier to run tests, build rpm and source rpm
151 generation inside a LXC of the specified version of CentOS.
152
153+ version is major release number (6 or 7)
154+
155 options:
156 -a | --artifact keep .rpm artifacts
157 -k | --keep keep container after tests
158 -r | --rpm build .rpm
159 -s | --srpm build .src.rpm
160 -u | --unittest run unit tests
161+
162+ Example:
163+ * ${0##*/} --rpm --srpm --unittest 6
164 EOF
165 }
166
167@@ -48,6 +53,10 @@ inside_as() {
168 # executes cmd with args inside container as user in users home dir.
169 local name="$1" user="$2"
170 shift 2
171+ if [ "$user" = "root" ]; then
172+ inside "$name" "$@"
173+ return
174+ fi
175 local stuffed="" b64=""
176 stuffed=$(getopt --shell sh --options "" -- -- "$@")
177 stuffed=${stuffed# -- }
178@@ -56,6 +65,12 @@ inside_as() {
179 'cd; eval set -- "$(echo '$b64' | base64 --decode)" && exec "$@"'
180 }
181
182+inside_as_cd() {
183+ local name="$1" user="$2" dir="$3"
184+ shift 3
185+ inside_as "$name" "$user" sh -c 'cd "$0" && exec "$@"' "$dir" "$@"
186+}
187+
188 inside() {
189 local name="$1"
190 shift
191@@ -63,26 +78,52 @@ inside() {
192 }
193
194 inject_cloud_init(){
195- local name="$1"
196- tarball_name='cloud-init.tar.gz'
197- top_d=$(git rev-parse --show-toplevel) ||
198- fail "failed to get top level"
199- cd "$top_d" ||
200- fail "failed to cd to git top dir"
201- tar_folder=${PWD##*/}
202- cd ..
203- tar -czf "$TEMP_D/$tarball_name" "$tar_folder" ||
204- fail "failed: creating tarball_name"
205- cd "$tar_folder" ||
206- fail "failed: changing directory"
207-
208- user='centos'
209- tarball="/home/$user/$tarball_name"
210- inside "$name" useradd "$user"
211- lxc file push "$TEMP_D/$tarball_name" "$name/home/$user"/
212- inside "$name" chown "$user:$user" "$tarball"
213- inside_as "$name" "$user" tar -C "/home/$user" -xzf "$tarball" ||
214- fail "failed: extracting tarball"
215+ # take current cloud-init git dir and put it inside $name at
216+ # ~$user/cloud-init.
217+ local name="$1" user="$2" top_d="" dname="" pstat=""
218+ top_d=$(git rev-parse --show-toplevel) || {
219+ errorrc "Failed to get git top level in $PWD";
220+ return
221+ }
222+ dname=$(basename "${top_d}") || return
223+ debug 1 "collecting ${top_d} ($dname) into user $user in $name."
224+ tar -C "${top_d}/.." -cpf - "$dname" |
225+ inside_as "$name" "$user" sh -ec '
226+ dname=$1
227+ rm -Rf "$dname"
228+ tar -xpf -
229+ [ "$dname" = "cloud-init" ] || mv "$dname" cloud-init' \
230+ extract "$dname"
231+ [ "${PIPESTATUS[*]}" = "0 0" ] || {
232+ error "Failed to push tarball of '$top_d' into $name" \
233+ " for user $user (dname=$dname)"
234+ return 1
235+ }
236+ return 0
237+}
238+
239+prep() {
240+ # we need some very basic things not present in the container.
241+ # - git
242+ # - tar (CentOS 6 lxc container does not have it)
243+ # - python-argparse (or python3)
244+ local needed="" pair="" pkg="" cmd="" needed=""
245+ for pair in tar:tar git:git; do
246+ pkg=${pair#*:}
247+ cmd=${pair%%:*}
248+ command -v $cmd >/dev/null 2>&1 || needed="${needed} $pkg"
249+ done
250+ if ! command -v python3; then
251+ python -c "import argparse" >/dev/null 2>&1 ||
252+ needed="${needed} python-argparse"
253+ fi
254+ needed=${needed# }
255+ if [ -z "$needed" ]; then
256+ error "No prep packages needed"
257+ return 0
258+ fi
259+ error "Installing prep packages: ${needed}"
260+ yum install --assumeyes ${needed}
261 }
262
263 start_container() {
264@@ -121,8 +162,8 @@ delete_container() {
265 }
266
267 main() {
268- local short_opts="ahkrsuv:"
269- local long_opts="artifact,help,keep,rpm,srpm,unittest,verbose:"
270+ local short_opts="ahkrsuv"
271+ local long_opts="artifact,help,keep,rpm,srpm,unittest,verbose"
272 local getopt_out=""
273 getopt_out=$(getopt --name "${0##*/}" \
274 --options "${short_opts}" --long "${long_opts}" -- "$@") &&
275@@ -149,60 +190,70 @@ main() {
276
277 [ $# -eq 1 ] || { bad_Usage "ERROR: Must provide version!"; return; }
278 version="$1"
279+ case "$version" in
280+ 6|7) :;;
281+ *) error "Expected version of 6 or 7, not '$version'"; return;;
282+ esac
283
284 TEMP_D=$(mktemp -d "${TMPDIR:-/tmp}/${0##*/}.XXXXXX") ||
285 fail "failed to make tempdir"
286 trap cleanup EXIT
287
288 # program starts here
289- local uuid="" name=""
290+ local uuid="" name="" user="ci-test" cdir=""
291+ cdir="/home/$user/cloud-init"
292 uuid=$(uuidgen -t) || { error "no uuidgen"; return 1; }
293 name="cloud-init-centos-${uuid%%-*}"
294
295 start_container "images:centos/$version" "$name"
296- # CentOS 6 does not come with tar
297- if [ "$version" = "6" ]; then
298- inside "$name" yum install --assumeyes tar || {
299- errorrc "FAIL: yum install tar failed";
300- }
301- fi
302+
303+ # prep the container (install very basic dependencies)
304+ inside "$name" bash -s prep <"$0" ||
305+ { errorrc "Failed to prep container $name"; return; }
306+
307+ # add the user
308+ inside "$name" useradd "$user"
309
310 debug 1 "inserting cloud-init"
311- inject_cloud_init "$name" || {
312+ inject_cloud_init "$name" "$user" || {
313 errorrc "FAIL: injecting cloud-init into $name failed."
314 return
315 }
316
317- # install dependencies
318- debug 1 "installing dependencies"
319- inside "$name" /bin/sh <tools/setup-centos ||
320- fail "failed: setting up container $name"
321+ inside_as_cd "$name" root "$cdir" \
322+ ./tools/read-dependencies \
323+ --requirements-file=requirements.txt \
324+ --requirements-file=test-requirements.txt \
325+ --distro=centos --install || {
326+ errorrc "FAIL: failed to install dependencies with read-dependencies"
327+ return
328+ }
329
330- local errors=0 do_cd="cd $tar_folder"
331- inside_as "$name" "$user" sh -ec "$do_cd; git checkout .; git status" ||
332+ local errors=0
333+ inside_as_cd "$name" "$user" "$cdir" \
334+ sh -ec "git checkout .; git status" ||
335 { errorrc "git checkout failed."; errors=$(($errors+1)); }
336
337 if [ -n "$unittest" ]; then
338 debug 1 "running unit tests."
339- inside_as "$name" "$user" sh -ec "$do_cd; nosetests tests/unittests" ||
340+ inside_as_cd "$name" "$user" "$cdir" nosetests tests/unittests ||
341 { errorrc "nosetests failed."; errors=$(($errors+1)); }
342 fi
343
344 if [ -n "$srpm" ]; then
345 debug 1 "building srpm."
346- inside_as "$name" "$user" sh -ec "$do_cd; ./packages/brpm --srpm" ||
347+ inside_as_cd "$name" "$user" "$cdir" ./packages/brpm --srpm ||
348 { errorrc "brpm --srpm."; errors=$(($errors+1)); }
349 fi
350
351 if [ -n "$rpm" ]; then
352 debug 1 "building rpm."
353- inside_as "$name" "$user" sh -ec "$do_cd; ./packages/brpm" ||
354+ inside_as_cd "$name" "$user" "$cdir" ./packages/brpm ||
355 { errorrc "brpm failed."; errors=$(($errors+1)); }
356 fi
357
358 if [ -n "$artifact" ]; then
359- cmd="ls /home/$user/$tar_folder/*.rpm"
360- for built_rpm in $(lxc exec "$name" -- sh -c "$cmd"); do
361+ for built_rpm in $(inside "$name" sh -c "echo $cdir/*.rpm"); do
362 lxc file pull "$name/$built_rpm" .
363 done
364 fi
365@@ -214,5 +265,10 @@ main() {
366 return 0
367 }
368
369-main "$@"
370+if [ "$1" = "prep" ]; then
371+ shift
372+ prep "$@"
373+else
374+ main "$@"
375+fi
376 # vi: ts=4 expandtab
377diff --git a/tools/setup-centos b/tools/setup-centos
378deleted file mode 100755
379index bc5da8a..0000000
380--- a/tools/setup-centos
381+++ /dev/null
382@@ -1,49 +0,0 @@
383-#!/bin/sh
384-# This file is part of cloud-init. See LICENSE file for license information.
385-set -fux
386-export LANG=C
387-
388-packages="
389- file
390- git
391- pyserial
392- python-argparse
393- python-cheetah
394- python-configobj
395- python-devel
396- python-jinja2
397- python-jsonpatch
398- python-oauthlib
399- python-pip
400- python-prettytable
401- python-requests
402- python-six
403- PyYAML
404- rpm-build
405-"
406-
407-pips="
408- contextlib2
409- httpretty
410- mock
411- nose
412- pep8
413- unittest2
414-"
415-
416-error() { echo "$@" 1>&2; }
417-fail() { [ $# -eq 0 ] || error "$@"; exit 1; }
418-info() { echo "$@"; }
419-
420-pips=$(for p in $pips; do echo "$p"; done | sort -u)
421-packages=$(for p in $packages; do echo "$p"; done | sort -u)
422-
423-if ! rpm -q epel-release >/dev/null; then
424- yum install --assumeyes epel-release ||
425- fail "failed: yum install epel-release"
426-fi
427-yum install --assumeyes $packages ||
428- fail "failed: yum install" "$packages"
429-
430-pip install --upgrade $pips ||
431- fail "failed: pip install $pips"

Subscribers

People subscribed via source and target branches

to all changes: