Merge ~alexmurray/ubuntu-security-tools:cmd-qrt into ubuntu-security-tools:master

Proposed by Alex Murray
Status: Merged
Merged at revision: aa982b0163d74e9bf82634768a2abf2b5796cbcf
Proposed branch: ~alexmurray/ubuntu-security-tools:cmd-qrt
Merge into: ubuntu-security-tools:master
Diff against target: 288 lines (+200/-15)
2 files modified
build-tools/umt (+199/-14)
build-tools/umt-completion.bash (+1/-1)
Reviewer Review Type Date Requested Status
Emilia Torino Approve
Eduardo Barretto Approve
Review via email: mp+376398@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Marc Deslauriers (mdeslaur) :
Revision history for this message
Marc Deslauriers (mdeslaur) :
Revision history for this message
Alex Murray (alexmurray) wrote :

Thanks for the review Marc - I have pushed extra commits to address your comments - see https://git.launchpad.net/~alexmurray/ubuntu-security-tools/commit/?id=d8b061e61a96bce2d22829c41fb4bf39302af080 which removes the revert part - and https://git.launchpad.net/~alexmurray/ubuntu-security-tools/commit/?id=2ebdc31cb89e21acd4f35d78816823fe13e00f84 which always adds -v when running tests so we always use verbose output. Perhaps in the future we might want a way to turn this off if the diffs get too messy with it but I think for now this an improvement.

Revision history for this message
Emilia Torino (emitorino) wrote :

This is great Alex! I added 2 inline comments with just refactoring suggestions + one question. Thanks!

Revision history for this message
Eduardo Barretto (ebarretto) wrote :

Adding a comment to Emi's suggestion as discussed on IRC.

Revision history for this message
Eduardo Barretto (ebarretto) wrote :

sorry for the spam, my comment didn't work, commenting again!

Revision history for this message
Alex Murray (alexmurray) wrote :

emitorino + ebarretto - I just pushed https://git.launchpad.net/~alexmurray/ubuntu-security-tools/commit/?id=67f072cbf84c8479e1ce67d85a732b3101f3f504 which should resolve your concerns.

Revision history for this message
Alex Murray (alexmurray) wrote :

Whoops - accidentally left in too much old code - fixed in subsequent commit https://git.launchpad.net/~alexmurray/ubuntu-security-tools/commit/?id=8b03bce

Revision history for this message
Eduardo Barretto (ebarretto) wrote :

LGTM

review: Approve
Revision history for this message
Steve Beattie (sbeattie) wrote :

I haven't looked at the implementation, so no real comment on that, but I did look briefly and tried to guess how it would be used; can we call the option 'test' rather than 'qrt', since the qrt tree is an implementation detail?

Revision history for this message
Emilia Torino (emitorino) :
review: Approve
Revision history for this message
Alex Murray (alexmurray) wrote :

> I haven't looked at the implementation, so no real comment on that, but I did
> look briefly and tried to guess how it would be used; can we call the option
> 'test' rather than 'qrt', since the qrt tree is an implementation detail?

Well I want to add autopkgtest support as well and so was thinking it was easier to add both as separate sub-commands, otherwise if there was a single 'test' command this would have to have multiple mutually incompatible options depending on whether it ran qrt or autopkgtest etc - so to keep it simpler, I figure it is better to have separate qrt and autopkgtest commands.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/build-tools/umt b/build-tools/umt
2index 5aea099..c8939bc 100755
3--- a/build-tools/umt
4+++ b/build-tools/umt
5@@ -36,6 +36,7 @@ binary_dest = '../binary'
6 reports_dest = '../reports'
7 previous_dest = '../previous'
8 coverity_dest = '../coverity'
9+tests_dest = '../tests'
10
11 # Per-package overrides for the sbuild resolver
12 # <sbuild resolver> = sbuild_dep_resolver_overrides[<srcpkg>][<ubuntu release>]
13@@ -1364,19 +1365,11 @@ Acquire::Languages "none";
14 recursive_rm(tempdir)
15
16
17-def cmd_repo():
18+def copy_to_repo(opt, details, quiet=False):
19 '''Copy all built packages into local repository'''
20- parser = umt_optparse("usage: %prog repo [options]")
21-
22- parser.add_option("-r", "--release", dest="release", default=False,
23- help="specify repository to copy to")
24- parser.add_option("--purge", dest="purge", default=False, action='store_true',
25- help="purge repository of all packages before copying")
26-
27- (opt, args) = parser.parse_args()
28 repo_base = ust['package_tools_repo_base']
29- details = parse_package_details(release = opt.release, skip_sanity=True)
30- print_details(details)
31+ if not quiet:
32+ print_details(details)
33
34 repo_dest = os.path.join(repo_base, details['release'])
35
36@@ -1404,7 +1397,8 @@ def cmd_repo():
37 err("Could not find 'update_repo' (is '$UST' set?). Aborting.")
38 sys.exit(1)
39
40- print("Repository: %s" % (repo_dest))
41+ if not quiet:
42+ print("Repository: %s" % (repo_dest))
43
44 # Copy the source files, ignoring stuff we don't want
45 files = os.listdir(source_dest)
46@@ -1412,7 +1406,8 @@ def cmd_repo():
47 (root, ext) = os.path.splitext(f)
48 if ext in [ ".build", ".upload", ".debdiff" ]:
49 continue
50- print("Copying '%s'..." % (f))
51+ if not quiet:
52+ print("Copying '%s'..." % (f))
53 shutil.copy(os.path.join(source_dest, f), repo_dest)
54
55 # Copy the files, as we may have built multiple architectures and the
56@@ -1420,7 +1415,8 @@ def cmd_repo():
57 files = os.listdir(binary_dest)
58 for f in files:
59 if f.endswith("deb"):
60- print("Copying '%s'..." % (f))
61+ if not quiet:
62+ print("Copying '%s'..." % (f))
63 shutil.copy(os.path.join(binary_dest, f), repo_dest)
64
65 (rc, report) = runcmd([update_repo, details['release']], stdin=sys.stdin, stdout=None)
66@@ -1428,6 +1424,22 @@ def cmd_repo():
67 err("failure running '%s':\n%s" % (update_repo, report))
68 sys.exit(1)
69
70+
71+def add_repo_arguments(parser):
72+ '''Add the command-line arguments used by repo'''
73+
74+ parser.add_option("-r", "--release", dest="release", default=False,
75+ help="specify repository to copy to")
76+ parser.add_option("--purge", dest="purge", default=False, action='store_true',
77+ help="purge repository of all packages before copying")
78+
79+def cmd_repo():
80+ '''Copy all built packages into local repository'''
81+ parser = umt_optparse("usage: %prog repo [options]")
82+ add_repo_arguments(parser)
83+ (opt, args) = parser.parse_args()
84+ details = parse_package_details(release = opt.release, skip_sanity=True)
85+ copy_to_repo(opt, details)
86 success("SUCCESS")
87
88 def cmd_upload():
89@@ -1556,6 +1568,178 @@ def cmd_search():
90 print("%s: %s, Pocket: %s, Component: %s" % (release,
91 pkg_list[release][0], pkg_list[release][1], pkg_list[release][2]))
92
93+def cmd_qrt():
94+ '''Run QRT tests for the package in the current directory of unpacked source'''
95+ parser = umt_optparse("usage: %prog qrt [options]")
96+ add_repo_arguments(parser)
97+ parser.add_option("-s", "--sudo", dest="sudo", default=False, action='store_true',
98+ help="Whether to run the QRT tests via sudo as a regular user")
99+ parser.add_option("-u", "--user", dest="user", default=os.getlogin(), metavar='USER',
100+ help="The username to run the QRT tests as (default: current user)")
101+ parser.add_option("-q", "--qrt-path", dest="qrt_path", default=os.getenv("QRT", ""), metavar='QRT_PATH',
102+ help="Path to the local copy of the qa-regression-tools (QRT) repo")
103+ parser.add_option("--no-update", dest="no_update", default=False, action='store_true',
104+ help="Don't update UVT VM before initial test run")
105+ parser.add_option("--no-snapshot", dest="no_snapshot", default=False, action='store_true',
106+ help="Don't revert or update snapshot of UVT VM on update")
107+ parser.add_option("-n", "--dry-run", dest="dry_run", default=False, action='store_true',
108+ help="Don't actually execute tests, instead print what would be run")
109+ parser.add_option("-p", "--uvt-prefix", dest="uvt_prefix", default="sec",
110+ help="Prefix to use for UVT VM names (default: sec)")
111+ parser.add_option("-v", "--uvt-vm", dest="uvt_vm", default=None,
112+ help="VM use for UVT tests (default: use {uvt-prefix}-release-{arch})")
113+ parser.add_option("--debug", default=False, action='store_true',
114+ help="Report additional debug details")
115+ parser.add_option("-f", "--force", dest="force", default=False, action='store_true',
116+ help="force deletion of ../tests before running")
117+ (opt, args) = parser.parse_args()
118+
119+ validate_toplevel()
120+ details = parse_package_details(skip_sanity=True)
121+ run_qrt_tests(opt, details)
122+ # also alert if there appear to be other bits in QRT worth looking at
123+ for d in ['build_testing', 'notes_testing']:
124+ testing_dir = os.path.join(opt.qrt_path, d, details["package"])
125+ try:
126+ files = os.listdir(testing_dir)
127+ except:
128+ files = []
129+ finally:
130+ if len(files) > 0:
131+ warn("QRT also contains the following relevant files for " + details["package"] + ":")
132+ for f in files:
133+ warn(" " + os.path.join(d, details["package"], f))
134+ warn("Please ensure you consult these")
135+
136+
137+def runcmdopt(cmd, opt, okrc=[0]):
138+ output = ""
139+ if opt.debug:
140+ print("[" + " ".join(cmd) + "]")
141+ if not opt.dry_run:
142+ (rc, output) = runcmd(cmd)
143+ if rc not in okrc:
144+ raise Exception("Failed to execute command: " + " ".join(cmd) + ": " + output)
145+ if opt.debug:
146+ print(output)
147+ return output
148+
149+
150+def run_qrt_tests(opt, details):
151+ qrt_test = os.path.join(opt.qrt_path, "scripts", "test-" + details["package"] + ".py")
152+ if not os.path.exists(qrt_test):
153+ print("No QRT test found for " + details["package"] + ": expected to find in " + qrt_test)
154+ return
155+ qrt_tests_dest = os.path.join(tests_dest, "qrt")
156+ prepare_dir(qrt_tests_dest, opt.force)
157+ user = opt.user
158+ sudo = ""
159+ if opt.sudo:
160+ sudo = 'echo ubuntu | sudo -S '
161+ print("Running QRT test " + qrt_test + " for " + details["package"] + " as " + user + (" via sudo" if opt.sudo else ""))
162+ if opt.uvt_vm is not None:
163+ vm = opt.uvt_vm
164+ else:
165+ # TODO suport different arch's
166+ vm = opt.uvt_prefix + "-" + details["release"] + "-amd64"
167+ # find path to uvt binary - first see if UQT is defined, otherwise try
168+ # in relative location to the current script
169+ uqt = os.getenv("UQT")
170+ if uqt != None and os.path.exists(os.path.join(uqt, "vm-tools", "uvt")):
171+ uvt = os.path.join(uqt, "vm-tools", "uvt")
172+ elif os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))),
173+ "ubuntu-qa-tools", "vm-tools", "uvt"):
174+ uvt = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))),
175+ "ubuntu-qa-tools", "vm-tools", "uvt")
176+ else:
177+ err("Unable to find uvt - please define QRT as the path to ubuntu-qa-tools repo")
178+ # catch any exception which occurred during execution
179+ try:
180+ if not opt.no_update:
181+ # ensure vm is stopped first - unless no_snapshot we can stop
182+ # it by force
183+ cmd = [uvt, "stop", vm]
184+ if not opt.no_snapshot:
185+ cmd.append("-f")
186+ print("Ensuring uvt VM " + vm + " is stopped...")
187+ runcmdopt(cmd, opt)
188+ print("Updating uvt VM " + vm + "...")
189+ cmd = [uvt, "update", "-f", vm]
190+ if opt.no_snapshot:
191+ cmd.append("--nosnapshot")
192+ runcmdopt(cmd, opt)
193+ print("Launching uvt VM " + vm + "...")
194+ # run headless since we don't use the GUI and wait for ssh
195+ # availability
196+ runcmdopt([uvt, "start", "-v", "-w", vm], opt)
197+ print("Packaging QRT test...")
198+ runcmdopt([os.path.join(opt.qrt_path, "scripts", "make-test-tarball"),
199+ qrt_test], opt)
200+ print("Deploying QRT test...")
201+ runcmdopt(["/usr/bin/scp",
202+ "/tmp/qrt-test-" + details["package"] + ".tar.gz",
203+ user + "@" + vm + ":"], opt)
204+ print("Extracting QRT test...")
205+ runcmdopt(["/usr/bin/ssh", "-T", user + "@" + vm,
206+ "tar -xvf qrt-test-" + details["package"] + ".tar.gz" ],
207+ opt)
208+ print("Installing packages for QRT test...")
209+ # always install packages via sudo
210+ runcmdopt(["/usr/bin/ssh", "-T", user + "@" + vm,
211+ "cd ./qrt-test-" + details["package"] + "; " +
212+ "echo ubuntu | sudo -S ./install-packages ./test-" + details["package"] + ".py"],
213+ opt)
214+ print("Executing QRT baseline test...")
215+ report = runcmdopt(["/usr/bin/ssh", "-T", user + "@" + vm,
216+ "cd ./qrt-test-" + details["package"] + ";" +
217+ sudo + " ./test-" + details["package"] + ".py -v"],
218+ opt)
219+ # save the report so we can compare it later
220+ path = os.path.join(qrt_tests_dest, "qrt-test-" + details["package"] + '-orig.txt')
221+ with open(path,"w+") as handle:
222+ handle.write(report)
223+ handle.flush()
224+
225+ print("Copying packages to umt repo...")
226+ if not opt.dry_run:
227+ copy_to_repo(opt, details, quiet=not opt.debug)
228+
229+ print("Enabling repo for uvt VM...")
230+ runcmdopt([uvt, "repo", "-e", vm], opt)
231+
232+ print("Upgrading packages in uvt VM...")
233+ runcmdopt(["/usr/bin/ssh", "-T", "root@" + vm,
234+ "apt-get dist-upgrade -y --force-yes"], opt)
235+
236+ print("Re-executing QRT test...")
237+ report = runcmdopt(["/usr/bin/ssh", "-T", user + "@" + vm,
238+ "cd ./qrt-test-" + details["package"] + ";" +
239+ sudo + " ./test-" + details["package"] + ".py -v"], opt)
240+ # save the report so we can compare it later
241+ path = os.path.join(qrt_tests_dest, "qrt-test-" + details["package"] + '.txt')
242+ with open(path,"w+") as handle:
243+ handle.write(report)
244+ handle.flush()
245+
246+ print("Generating diff of QRT tests output...")
247+ report = runcmdopt(["/usr/bin/diff", "-u",
248+ os.path.join(qrt_tests_dest, "qrt-test-" + details["package"] + '-orig.txt'),
249+ os.path.join(qrt_tests_dest, "qrt-test-" + details["package"] + '.txt')],
250+ # diff returns 1 if different and 0 if same - so
251+ # both are valid ok return codes
252+ opt, [0, 1])
253+ # save the diff
254+ path = os.path.join(qrt_tests_dest, "qrt-test-" + details["package"] + '.diff')
255+ with open(path,"w+") as handle:
256+ handle.write(report)
257+ handle.flush()
258+ print("QRT test run diff in " + path)
259+
260+ except Exception as e:
261+ err(str(e))
262+ print("Stopping uvt VM " + vm + "...")
263+ runcmdopt([uvt, "stop", vm], opt)
264+
265 #
266 # Misc functions
267 #
268@@ -3273,6 +3457,7 @@ commands = {
269 'compare-bin' : cmd_compare_bin,
270 'repo' : cmd_repo,
271 'upload' : cmd_upload,
272+ 'qrt' : cmd_qrt,
273 'open' : cmd_open,
274 'read' : cmd_read,
275 'help' : cmd_help
276diff --git a/build-tools/umt-completion.bash b/build-tools/umt-completion.bash
277index 7935679..bd73861 100644
278--- a/build-tools/umt-completion.bash
279+++ b/build-tools/umt-completion.bash
280@@ -12,7 +12,7 @@ _umt_complete()
281 COMPREPLY=( $( compgen -W "$( grep Package: /var/lib/apt/lists/*Sources | awk '{print $2}' | sort | uniq )" "$cur") )
282 }
283
284- umt_commands="search download changelog source binary build build-orig compare-log compare-bin sign check upload"
285+ umt_commands="search download changelog source binary build build-orig compare-log compare-bin sign check upload qrt"
286
287 if [ $COMP_CWORD -eq 1 ]; then
288 COMPREPLY=( $(compgen -W "$umt_commands" -- ${COMP_WORDS[COMP_CWORD]}) )

Subscribers

People subscribed via source and target branches