Merge ~alexmurray/ubuntu-security-tools:cmd-qrt into ubuntu-security-tools:master
- Git
- lp:~alexmurray/ubuntu-security-tools
- cmd-qrt
- Merge into master
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) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Emilia Torino | Approve | ||
Eduardo Barretto | Approve | ||
Review via email: mp+376398@code.launchpad.net |
Commit message
Description of the change
Marc Deslauriers (mdeslaur) : | # |
Marc Deslauriers (mdeslaur) : | # |
Alex Murray (alexmurray) wrote : | # |
Emilia Torino (emitorino) wrote : | # |
This is great Alex! I added 2 inline comments with just refactoring suggestions + one question. Thanks!
Eduardo Barretto (ebarretto) wrote : | # |
Adding a comment to Emi's suggestion as discussed on IRC.
Eduardo Barretto (ebarretto) wrote : | # |
sorry for the spam, my comment didn't work, commenting again!
Alex Murray (alexmurray) wrote : | # |
emitorino + ebarretto - I just pushed https:/
Alex Murray (alexmurray) wrote : | # |
Whoops - accidentally left in too much old code - fixed in subsequent commit https:/
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?
Emilia Torino (emitorino) : | # |
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
1 | diff --git a/build-tools/umt b/build-tools/umt |
2 | index 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 |
276 | diff --git a/build-tools/umt-completion.bash b/build-tools/umt-completion.bash |
277 | index 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]}) ) |
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=d8b061e61a9 6bce2d22829c41f b4bf39302af080 which removes the revert part - and https:/ /git.launchpad. net/~alexmurray /ubuntu- security- tools/commit/ ?id=2ebdc31cb89 e21acd4f35d7881 6823fe13e00f84 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.