Merge lp:~ubuntu-server/curtin/bug-1574113-derived-repositories into lp:~curtin-dev/curtin/trunk
- bug-1574113-derived-repositories
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 410 |
Proposed branch: | lp:~ubuntu-server/curtin/bug-1574113-derived-repositories |
Merge into: | lp:~curtin-dev/curtin/trunk |
Diff against target: |
4136 lines (+3606/-197) 25 files modified
curtin/__init__.py (+2/-0) curtin/commands/apt_config.py (+658/-0) curtin/commands/curthooks.py (+50/-168) curtin/commands/main.py (+1/-1) curtin/gpg.py (+74/-0) curtin/util.py (+161/-17) doc/devel/README-vmtest.txt (+59/-4) doc/topics/apt_source.rst (+158/-0) examples/apt-source.yaml (+267/-0) examples/tests/apt_config_command.yaml (+85/-0) examples/tests/apt_source_custom.yaml (+97/-0) examples/tests/apt_source_modify.yaml (+92/-0) examples/tests/apt_source_modify_arches.yaml (+102/-0) examples/tests/apt_source_modify_disable_suite.yaml (+92/-0) examples/tests/apt_source_preserve.yaml (+98/-0) examples/tests/apt_source_search.yaml (+97/-0) examples/tests/test_old_apt_features.yaml (+10/-0) examples/tests/test_old_apt_features_ports.yaml (+10/-0) tests/unittests/test_apt_custom_sources_list.py (+170/-0) tests/unittests/test_apt_source.py (+929/-0) tests/unittests/test_util.py (+2/-2) tests/vmtests/__init__.py (+19/-5) tests/vmtests/test_apt_config_cmd.py (+55/-0) tests/vmtests/test_apt_source.py (+238/-0) tests/vmtests/test_old_apt_features.py (+80/-0) |
To merge this branch: | bzr merge lp:~ubuntu-server/curtin/bug-1574113-derived-repositories |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Scott Moser (community) | Approve | ||
Server Team CI bot | continuous-integration | Approve | |
Review via email: mp+301362@code.launchpad.net |
Commit message
Adding apt features to curtin.
This allows to define derived repositories by being able to control various apt configurations, proxies, mirrors, ppa's, sources.list content and pgp keys trusted by apt.
See examples/
Along the actual features a huge list of related unit- and vm-tests is added.
Description of the change
Adding apt_source features for the install stage of curtin.
This is inherited from cloud-init, but simplified (as we don't have to care about some features and old compatibility).
And then added plenty of unit- and vm-tests for the functionality.
See the current spec discussion at https:/
Christian Ehrhardt (paelzer) wrote : | # |
Server Team CI bot (server-team-bot) wrote : | # |
FAILED: Continuous integration, rev:623
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
- 624. By Christian Ehrhardt
-
fixup codeing style to pass checks
Running: pep8 curtin/ tests/
curtin/commands/ apt_config. py:399: 80: E501 line too long (80 > 79 characters)
tests/unittests/test_apt_ source. py:52:1: E302 expected 2 blank lines, found 1
tests/unittests/test_apt_ source. py:59:1: W293 blank line contains whitespace
tests/unittests/test_apt_ source. py:438: 5: E303 too many blank lines (2)
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:624
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
Scott Moser (smoser) wrote : | # |
Reviewed on phone (probably not a good habit). See inline comments.
- 625. By Christian Ehrhardt
-
no fqdn can be quite common in early environemnts, silence the LOG entry a bit
- 626. By Christian Ehrhardt
-
fixup most of the dns vmtest and disable it until fully fixed
- 627. By Christian Ehrhardt
-
make get_default_mirrors take arch instead of target
- 628. By Christian Ehrhardt
-
make lsb_release not use caching if target is provided
Eventually with target provided the release could even "change", so never
use cache when target is given.
If any one could implement a per-target cache, but even that might not be
100% true at all times. - 629. By Christian Ehrhardt
-
move target out of update_mirror_info as it was only used for arch
- 630. By Christian Ehrhardt
-
drop search_dns feature
This is following smosers guidance.
In a discussion it became clear that this doesn't make much sense in a curtin
environment at all.
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:630
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
- 631. By Christian Ehrhardt
-
rework usage of target and chroot
- reduce passing of target where only arch is needed
- unify the adding of chroot (from various sources) into subpBased on a nice diff by smoser but fixed some remaining issues with in_chroot.
- 632. By Scott Moser
-
merge from trunk. address removal of RunInChroot
- 633. By Christian Ehrhardt
-
APT: rework examples and documentation
Now that we have settled on the format ensure that the examples are on par.
Also rework and simplify the examples for readability.
Drop the filename key which is working (for transparency and compatibility),
but not meant to be documented as this might change. - 634. By Christian Ehrhardt
-
merge trunk to ver 409
- 635. By Christian Ehrhardt
-
document Test calss variables
I wanted to "just" document boot_cloudconf, but found that none of the vmtest
base class variables we usually use is documented so far.
So I added some short description of all of these along the furtunately already
existing doc of the environment variables.
Christian Ehrhardt (paelzer) wrote : | # |
make check:
Ran 234 tests in 2.612s
Ran 234 tests in 2.224s
tox
py27: commands succeeded
py3: commands succeeded
py3-flake8: commands succeeded
py3-pylint: commands succeeded
py27-pylint: commands succeeded
trusty-check: commands succeeded
congratulations :)
vmtests:
Ran 727 tests in 1803.671s
All tests passed, pushed repo again after merging trunk and finalizing some doc polishing as discussed on #curtin.
Scott Moser (smoser) : | # |
Preview Diff
1 | === modified file 'curtin/__init__.py' |
2 | --- curtin/__init__.py 2015-10-26 17:35:29 +0000 |
3 | +++ curtin/__init__.py 2016-08-02 09:15:49 +0000 |
4 | @@ -33,6 +33,8 @@ |
5 | 'SUBCOMMAND_SYSTEM_INSTALL', |
6 | # subcommand 'system-upgrade' is present |
7 | 'SUBCOMMAND_SYSTEM_UPGRADE', |
8 | + # supports new format of apt configuration |
9 | + 'APT_CONFIG_V1', |
10 | ] |
11 | |
12 | # vi: ts=4 expandtab syntax=python |
13 | |
14 | === added file 'curtin/commands/apt_config.py' |
15 | --- curtin/commands/apt_config.py 1970-01-01 00:00:00 +0000 |
16 | +++ curtin/commands/apt_config.py 2016-08-02 09:15:49 +0000 |
17 | @@ -0,0 +1,658 @@ |
18 | +# Copyright (C) 2016 Canonical Ltd. |
19 | +# |
20 | +# Author: Christian Ehrhardt <christian.ehrhardt@canonical.com> |
21 | +# |
22 | +# Curtin is free software: you can redistribute it and/or modify it under |
23 | +# the terms of the GNU Affero General Public License as published by the |
24 | +# Free Software Foundation, either version 3 of the License, or (at your |
25 | +# option) any later version. |
26 | +# |
27 | +# Curtin is distributed in the hope that it will be useful, but WITHOUT ANY |
28 | +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
29 | +# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for |
30 | +# more details. |
31 | +# |
32 | +# You should have received a copy of the GNU Affero General Public License |
33 | +# along with Curtin. If not, see <http://www.gnu.org/licenses/>. |
34 | +""" |
35 | +apt.py |
36 | +Handle the setup of apt related tasks like proxies, mirrors, repositories. |
37 | +""" |
38 | + |
39 | +import argparse |
40 | +import glob |
41 | +import os |
42 | +import re |
43 | +import sys |
44 | +import yaml |
45 | + |
46 | +from curtin.log import LOG |
47 | +from curtin import (config, util, gpg) |
48 | + |
49 | +from . import populate_one_subcmd |
50 | + |
51 | +# this will match 'XXX:YYY' (ie, 'cloud-archive:foo' or 'ppa:bar') |
52 | +ADD_APT_REPO_MATCH = r"^[\w-]+:\w" |
53 | + |
54 | +# place where apt stores cached repository data |
55 | +APT_LISTS = "/var/lib/apt/lists" |
56 | + |
57 | +# Files to store proxy information |
58 | +APT_CONFIG_FN = "/etc/apt/apt.conf.d/94curtin-config" |
59 | +APT_PROXY_FN = "/etc/apt/apt.conf.d/90curtin-aptproxy" |
60 | + |
61 | +# Default keyserver to use |
62 | +DEFAULT_KEYSERVER = "keyserver.ubuntu.com" |
63 | + |
64 | +# Default archive mirrors |
65 | +PRIMARY_ARCH_MIRRORS = {"PRIMARY": "http://archive.ubuntu.com/ubuntu/", |
66 | + "SECURITY": "http://security.ubuntu.com/ubuntu/"} |
67 | +PORTS_MIRRORS = {"PRIMARY": "http://ports.ubuntu.com/ubuntu-ports", |
68 | + "SECURITY": "http://ports.ubuntu.com/ubuntu-ports"} |
69 | +PRIMARY_ARCHES = ['amd64', 'i386'] |
70 | +PORTS_ARCHES = ['s390x', 'arm64', 'armhf', 'powerpc', 'ppc64el'] |
71 | + |
72 | + |
73 | +def get_default_mirrors(arch=None): |
74 | + """returns the default mirrors for the target. These depend on the |
75 | + architecture, for more see: |
76 | + https://wiki.ubuntu.com/UbuntuDevelopment/PackageArchive#Ports""" |
77 | + if arch is None: |
78 | + arch = util.get_architecture() |
79 | + if arch in PRIMARY_ARCHES: |
80 | + return PRIMARY_ARCH_MIRRORS |
81 | + if arch in PORTS_ARCHES: |
82 | + return PORTS_MIRRORS |
83 | + raise ValueError("No default mirror known for arch %s" % arch) |
84 | + |
85 | + |
86 | +def handle_apt(cfg, target=None): |
87 | + """ handle_apt |
88 | + process the config for apt_config. This can be called from |
89 | + curthooks if a global apt config was provided or via the "apt" |
90 | + standalone command. |
91 | + """ |
92 | + release = util.lsb_release(target=target)['codename'] |
93 | + arch = util.get_architecture(target) |
94 | + mirrors = find_apt_mirror_info(cfg, arch) |
95 | + LOG.debug("Apt Mirror info: %s", mirrors) |
96 | + |
97 | + apply_debconf_selections(cfg, target) |
98 | + |
99 | + if not config.value_as_boolean(cfg.get('preserve_sources_list', |
100 | + True)): |
101 | + generate_sources_list(cfg, release, mirrors, target) |
102 | + rename_apt_lists(mirrors, target) |
103 | + |
104 | + try: |
105 | + apply_apt_proxy_config(cfg, target + APT_PROXY_FN, |
106 | + target + APT_CONFIG_FN) |
107 | + except (IOError, OSError): |
108 | + LOG.exception("Failed to apply proxy or apt config info:") |
109 | + |
110 | + # Process 'apt_source -> sources {dict}' |
111 | + if 'sources' in cfg: |
112 | + params = mirrors |
113 | + params['RELEASE'] = release |
114 | + params['MIRROR'] = mirrors["MIRROR"] |
115 | + |
116 | + matcher = None |
117 | + matchcfg = cfg.get('add_apt_repo_match', ADD_APT_REPO_MATCH) |
118 | + if matchcfg: |
119 | + matcher = re.compile(matchcfg).search |
120 | + |
121 | + add_apt_sources(cfg['sources'], target, |
122 | + template_params=params, aa_repo_match=matcher) |
123 | + |
124 | + |
125 | +def apply_debconf_selections(cfg, target=None): |
126 | + """apply_debconf_selections - push content to debconf""" |
127 | + # debconf_selections: |
128 | + # set1: | |
129 | + # cloud-init cloud-init/datasources multiselect MAAS |
130 | + # set2: pkg pkg/value string bar |
131 | + selsets = cfg.get('debconf_selections') |
132 | + if not selsets: |
133 | + LOG.debug("debconf_selections was not set in config") |
134 | + return |
135 | + |
136 | + # for each entry in selections, chroot and apply them. |
137 | + # keep a running total of packages we've seen. |
138 | + pkgs_cfgd = set() |
139 | + |
140 | + for key, content in selsets.items(): |
141 | + LOG.debug("setting for %s, %s", key, content) |
142 | + util.subp(['debconf-set-selections'], |
143 | + data=content.encode(), target=target) |
144 | + for line in content.splitlines(): |
145 | + if line.startswith("#"): |
146 | + continue |
147 | + pkg = re.sub(r"[:\s].*", "", line) |
148 | + pkgs_cfgd.add(pkg) |
149 | + |
150 | + pkgs_installed = util.get_installed_packages(target) |
151 | + |
152 | + LOG.debug("pkgs_cfgd: %s", pkgs_cfgd) |
153 | + LOG.debug("pkgs_installed: %s", pkgs_installed) |
154 | + need_reconfig = pkgs_cfgd.intersection(pkgs_installed) |
155 | + |
156 | + if len(need_reconfig) == 0: |
157 | + LOG.debug("no need for reconfig") |
158 | + return |
159 | + |
160 | + # For any packages that are already installed, but have preseed data |
161 | + # we populate the debconf database, but the filesystem configuration |
162 | + # would be preferred on a subsequent dpkg-reconfigure. |
163 | + # so, what we have to do is "know" information about certain packages |
164 | + # to unconfigure them. |
165 | + unhandled = [] |
166 | + to_config = [] |
167 | + for pkg in need_reconfig: |
168 | + if pkg in CONFIG_CLEANERS: |
169 | + LOG.debug("unconfiguring %s", pkg) |
170 | + CONFIG_CLEANERS[pkg](target) |
171 | + to_config.append(pkg) |
172 | + else: |
173 | + unhandled.append(pkg) |
174 | + |
175 | + if len(unhandled): |
176 | + LOG.warn("The following packages were installed and preseeded, " |
177 | + "but cannot be unconfigured: %s", unhandled) |
178 | + |
179 | + if len(to_config): |
180 | + util.subp(['dpkg-reconfigure' '--frontend=noninteractive'] + |
181 | + list(to_config), data=None, target=target) |
182 | + |
183 | + |
184 | +def clean_cloud_init(target): |
185 | + """clean out any local cloud-init config""" |
186 | + flist = glob.glob( |
187 | + util.target_path(target, "/etc/cloud/cloud.cfg.d/*dpkg*")) |
188 | + |
189 | + LOG.debug("cleaning cloud-init config from: %s", flist) |
190 | + for dpkg_cfg in flist: |
191 | + os.unlink(dpkg_cfg) |
192 | + |
193 | + |
194 | +def mirrorurl_to_apt_fileprefix(mirror): |
195 | + """ mirrorurl_to_apt_fileprefix |
196 | + Convert a mirror url to the file prefix used by apt on disk to |
197 | + store cache information for that mirror. |
198 | + To do so do: |
199 | + - take off ???:// |
200 | + - drop tailing / |
201 | + - convert in string / to _ |
202 | + """ |
203 | + string = mirror |
204 | + if string.endswith("/"): |
205 | + string = string[0:-1] |
206 | + pos = string.find("://") |
207 | + if pos >= 0: |
208 | + string = string[pos + 3:] |
209 | + string = string.replace("/", "_") |
210 | + return string |
211 | + |
212 | + |
213 | +def rename_apt_lists(new_mirrors, target=None): |
214 | + """rename_apt_lists - rename apt lists to preserve old cache data""" |
215 | + default_mirrors = get_default_mirrors(util.get_architecture(target)) |
216 | + |
217 | + pre = util.target_path(target, APT_LISTS) |
218 | + for (name, omirror) in default_mirrors.items(): |
219 | + nmirror = new_mirrors.get(name) |
220 | + if not nmirror: |
221 | + continue |
222 | + |
223 | + oprefix = pre + os.path.sep + mirrorurl_to_apt_fileprefix(omirror) |
224 | + nprefix = pre + os.path.sep + mirrorurl_to_apt_fileprefix(nmirror) |
225 | + if oprefix == nprefix: |
226 | + continue |
227 | + olen = len(oprefix) |
228 | + for filename in glob.glob("%s_*" % oprefix): |
229 | + newname = "%s%s" % (nprefix, filename[olen:]) |
230 | + LOG.debug("Renaming apt list %s to %s", filename, newname) |
231 | + try: |
232 | + os.rename(filename, newname) |
233 | + except OSError: |
234 | + # since this is a best effort task, warn with but don't fail |
235 | + LOG.warn("Failed to rename apt list:", exc_info=True) |
236 | + |
237 | + |
238 | +def mirror_to_placeholder(tmpl, mirror, placeholder): |
239 | + """ mirror_to_placeholder |
240 | + replace the specified mirror in a template with a placeholder string |
241 | + Checks for existance of the expected mirror and warns if not found |
242 | + """ |
243 | + if mirror not in tmpl: |
244 | + LOG.warn("Expected mirror '%s' not found in: %s", mirror, tmpl) |
245 | + return tmpl.replace(mirror, placeholder) |
246 | + |
247 | + |
248 | +def map_known_suites(suite): |
249 | + """there are a few default names which will be auto-extended. |
250 | + This comes at the inability to use those names literally as suites, |
251 | + but on the other hand increases readability of the cfg quite a lot""" |
252 | + mapping = {'updates': '$RELEASE-updates', |
253 | + 'backports': '$RELEASE-backports', |
254 | + 'security': '$RELEASE-security', |
255 | + 'proposed': '$RELEASE-proposed', |
256 | + 'release': '$RELEASE'} |
257 | + try: |
258 | + retsuite = mapping[suite] |
259 | + except KeyError: |
260 | + retsuite = suite |
261 | + return retsuite |
262 | + |
263 | + |
264 | +def disable_suites(disabled, src, release): |
265 | + """reads the config for suites to be disabled and removes those |
266 | + from the template""" |
267 | + if not disabled: |
268 | + return src |
269 | + |
270 | + retsrc = src |
271 | + for suite in disabled: |
272 | + suite = map_known_suites(suite) |
273 | + releasesuite = util.render_string(suite, {'RELEASE': release}) |
274 | + LOG.debug("Disabling suite %s as %s", suite, releasesuite) |
275 | + |
276 | + newsrc = "" |
277 | + for line in retsrc.splitlines(True): |
278 | + if line.startswith("#"): |
279 | + newsrc += line |
280 | + continue |
281 | + |
282 | + # sources.list allow options in cols[1] which can have spaces |
283 | + # so the actual suite can be [2] or later |
284 | + cols = line.split() |
285 | + pcol = 2 |
286 | + if cols[1].startswith("["): |
287 | + for col in cols[1:]: |
288 | + pcol += 1 |
289 | + if col.endswith("]"): |
290 | + break |
291 | + |
292 | + if cols[pcol] == releasesuite: |
293 | + line = '# suite disabled by curtin: %s' % line |
294 | + newsrc += line |
295 | + retsrc = newsrc |
296 | + |
297 | + return retsrc |
298 | + |
299 | + |
300 | +def generate_sources_list(cfg, release, mirrors, target=None): |
301 | + """ generate_sources_list |
302 | + create a source.list file based on a custom or default template |
303 | + by replacing mirrors and release in the template |
304 | + """ |
305 | + default_mirrors = get_default_mirrors(util.get_architecture(target)) |
306 | + aptsrc = "/etc/apt/sources.list" |
307 | + params = {'RELEASE': release} |
308 | + for k in mirrors: |
309 | + params[k] = mirrors[k] |
310 | + |
311 | + tmpl = cfg.get('sources_list', None) |
312 | + if tmpl is None: |
313 | + LOG.info("No custom template provided, fall back to modify" |
314 | + "mirrors in %s on the target system", aptsrc) |
315 | + tmpl = util.load_file(util.target_path(target, aptsrc)) |
316 | + # Strategy if no custom template was provided: |
317 | + # - Only replacing mirrors |
318 | + # - no reason to replace "release" as it is from target anyway |
319 | + # - The less we depend upon, the more stable this is against changes |
320 | + # - warn if expected original content wasn't found |
321 | + tmpl = mirror_to_placeholder(tmpl, default_mirrors['PRIMARY'], |
322 | + "$MIRROR") |
323 | + tmpl = mirror_to_placeholder(tmpl, default_mirrors['SECURITY'], |
324 | + "$SECURITY") |
325 | + |
326 | + orig = util.target_path(target, aptsrc) |
327 | + if os.path.exists(orig): |
328 | + os.rename(orig, orig + ".curtin.old") |
329 | + |
330 | + rendered = util.render_string(tmpl, params) |
331 | + disabled = disable_suites(cfg.get('disable_suites'), rendered, release) |
332 | + util.write_file(util.target_path(target, aptsrc), disabled, mode=0o644) |
333 | + |
334 | + # protect the just generated sources.list from cloud-init |
335 | + cloudfile = "/etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg" |
336 | + # this has to work with older cloud-init as well, so use old key |
337 | + cloudconf = yaml.dump({'apt_preserve_sources_list': True}, indent=1) |
338 | + try: |
339 | + util.write_file(util.target_path(target, cloudfile), |
340 | + cloudconf, mode=0o644) |
341 | + except IOError: |
342 | + LOG.exception("Failed to protect source.list from cloud-init in (%s)", |
343 | + util.target_path(target, cloudfile)) |
344 | + raise |
345 | + |
346 | + |
347 | +def add_apt_key_raw(key, target=None): |
348 | + """ |
349 | + actual adding of a key as defined in key argument |
350 | + to the system |
351 | + """ |
352 | + LOG.debug("Adding key:\n'%s'", key) |
353 | + try: |
354 | + util.subp(['apt-key', 'add', '-'], data=key.encode(), target=target) |
355 | + except util.ProcessExecutionError: |
356 | + LOG.exception("failed to add apt GPG Key to apt keyring") |
357 | + raise |
358 | + |
359 | + |
360 | +def add_apt_key(ent, target=None): |
361 | + """ |
362 | + Add key to the system as defined in ent (if any). |
363 | + Supports raw keys or keyid's |
364 | + The latter will as a first step fetched to get the raw key |
365 | + """ |
366 | + if 'keyid' in ent and 'key' not in ent: |
367 | + keyserver = DEFAULT_KEYSERVER |
368 | + if 'keyserver' in ent: |
369 | + keyserver = ent['keyserver'] |
370 | + |
371 | + ent['key'] = gpg.getkeybyid(ent['keyid'], keyserver) |
372 | + |
373 | + if 'key' in ent: |
374 | + add_apt_key_raw(ent['key'], target) |
375 | + |
376 | + |
377 | +def add_apt_sources(srcdict, target=None, template_params=None, |
378 | + aa_repo_match=None): |
379 | + """ |
380 | + add entries in /etc/apt/sources.list.d for each abbreviated |
381 | + sources.list entry in 'srcdict'. When rendering template, also |
382 | + include the values in dictionary searchList |
383 | + """ |
384 | + if template_params is None: |
385 | + template_params = {} |
386 | + |
387 | + if aa_repo_match is None: |
388 | + raise ValueError('did not get a valid repo matcher') |
389 | + |
390 | + if not isinstance(srcdict, dict): |
391 | + raise TypeError('unknown apt format: %s' % (srcdict)) |
392 | + |
393 | + for filename in srcdict: |
394 | + ent = srcdict[filename] |
395 | + if 'filename' not in ent: |
396 | + ent['filename'] = filename |
397 | + |
398 | + add_apt_key(ent, target) |
399 | + |
400 | + if 'source' not in ent: |
401 | + continue |
402 | + source = ent['source'] |
403 | + source = util.render_string(source, template_params) |
404 | + |
405 | + if not ent['filename'].startswith("/"): |
406 | + ent['filename'] = os.path.join("/etc/apt/sources.list.d/", |
407 | + ent['filename']) |
408 | + if not ent['filename'].endswith(".list"): |
409 | + ent['filename'] += ".list" |
410 | + |
411 | + if aa_repo_match(source): |
412 | + try: |
413 | + with util.ChrootableTarget( |
414 | + target, sys_resolvconf=True) as in_chroot: |
415 | + in_chroot.subp(["add-apt-repository", source]) |
416 | + except util.ProcessExecutionError: |
417 | + LOG.exception("add-apt-repository failed.") |
418 | + raise |
419 | + continue |
420 | + |
421 | + sourcefn = util.target_path(target, ent['filename']) |
422 | + try: |
423 | + contents = "%s\n" % (source) |
424 | + util.write_file(sourcefn, contents, omode="a") |
425 | + except IOError as detail: |
426 | + LOG.exception("failed write to file %s: %s", sourcefn, detail) |
427 | + raise |
428 | + |
429 | + util.apt_update(target=target, force=True, |
430 | + comment="apt-source changed config") |
431 | + |
432 | + return |
433 | + |
434 | + |
435 | +def search_for_mirror(candidates): |
436 | + """ |
437 | + Search through a list of mirror urls for one that works |
438 | + This needs to return quickly. |
439 | + """ |
440 | + if candidates is None: |
441 | + return None |
442 | + |
443 | + LOG.debug("search for mirror in candidates: '%s'", candidates) |
444 | + for cand in candidates: |
445 | + try: |
446 | + if util.is_resolvable_url(cand): |
447 | + LOG.debug("found working mirror: '%s'", cand) |
448 | + return cand |
449 | + except Exception: |
450 | + pass |
451 | + return None |
452 | + |
453 | + |
454 | +def update_mirror_info(pmirror, smirror, arch): |
455 | + """sets security mirror to primary if not defined. |
456 | + returns defaults if no mirrors are defined""" |
457 | + if pmirror is not None: |
458 | + if smirror is None: |
459 | + smirror = pmirror |
460 | + return {'PRIMARY': pmirror, |
461 | + 'SECURITY': smirror} |
462 | + return get_default_mirrors(arch) |
463 | + |
464 | + |
465 | +def get_arch_mirrorconfig(cfg, mirrortype, arch): |
466 | + """out of a list of potential mirror configurations select |
467 | + and return the one matching the architecture (or default)""" |
468 | + # select the mirror specification (if-any) |
469 | + mirror_cfg_list = cfg.get(mirrortype, None) |
470 | + if mirror_cfg_list is None: |
471 | + return None |
472 | + |
473 | + # select the specification matching the target arch |
474 | + default = None |
475 | + for mirror_cfg_elem in mirror_cfg_list: |
476 | + arches = mirror_cfg_elem.get("arches") |
477 | + if arch in arches: |
478 | + return mirror_cfg_elem |
479 | + if "default" in arches: |
480 | + default = mirror_cfg_elem |
481 | + return default |
482 | + |
483 | + |
484 | +def get_mirror(cfg, mirrortype, arch): |
485 | + """pass the three potential stages of mirror specification |
486 | + returns None is neither of them found anything otherwise the first |
487 | + hit is returned""" |
488 | + mcfg = get_arch_mirrorconfig(cfg, mirrortype, arch) |
489 | + if mcfg is None: |
490 | + return None |
491 | + |
492 | + # directly specified |
493 | + mirror = mcfg.get("uri", None) |
494 | + |
495 | + # fallback to search if specified |
496 | + if mirror is None: |
497 | + # list of mirrors to try to resolve |
498 | + mirror = search_for_mirror(mcfg.get("search", None)) |
499 | + |
500 | + return mirror |
501 | + |
502 | + |
503 | +def find_apt_mirror_info(cfg, arch=None): |
504 | + """find_apt_mirror_info |
505 | + find an apt_mirror given the cfg provided. |
506 | + It can check for separate config of primary and security mirrors |
507 | + If only primary is given security is assumed to be equal to primary |
508 | + If the generic apt_mirror is given that is defining for both |
509 | + """ |
510 | + |
511 | + if arch is None: |
512 | + arch = util.get_architecture() |
513 | + LOG.debug("got arch for mirror selection: %s", arch) |
514 | + pmirror = get_mirror(cfg, "primary", arch) |
515 | + LOG.debug("got primary mirror: %s", pmirror) |
516 | + smirror = get_mirror(cfg, "security", arch) |
517 | + LOG.debug("got security mirror: %s", smirror) |
518 | + |
519 | + # Note: curtin has no cloud-datasource fallback |
520 | + |
521 | + mirror_info = update_mirror_info(pmirror, smirror, arch) |
522 | + |
523 | + # less complex replacements use only MIRROR, derive from primary |
524 | + mirror_info["MIRROR"] = mirror_info["PRIMARY"] |
525 | + |
526 | + return mirror_info |
527 | + |
528 | + |
529 | +def apply_apt_proxy_config(cfg, proxy_fname, config_fname): |
530 | + """apply_apt_proxy_config |
531 | + Applies any apt*proxy config from if specified |
532 | + """ |
533 | + # Set up any apt proxy |
534 | + cfgs = (('proxy', 'Acquire::http::Proxy "%s";'), |
535 | + ('http_proxy', 'Acquire::http::Proxy "%s";'), |
536 | + ('ftp_proxy', 'Acquire::ftp::Proxy "%s";'), |
537 | + ('https_proxy', 'Acquire::https::Proxy "%s";')) |
538 | + |
539 | + proxies = [fmt % cfg.get(name) for (name, fmt) in cfgs if cfg.get(name)] |
540 | + if len(proxies): |
541 | + LOG.debug("write apt proxy info to %s", proxy_fname) |
542 | + util.write_file(proxy_fname, '\n'.join(proxies) + '\n') |
543 | + elif os.path.isfile(proxy_fname): |
544 | + util.del_file(proxy_fname) |
545 | + LOG.debug("no apt proxy configured, removed %s", proxy_fname) |
546 | + |
547 | + if cfg.get('conf', None): |
548 | + LOG.debug("write apt config info to %s", config_fname) |
549 | + util.write_file(config_fname, cfg.get('conf')) |
550 | + elif os.path.isfile(config_fname): |
551 | + util.del_file(config_fname) |
552 | + LOG.debug("no apt config configured, removed %s", config_fname) |
553 | + |
554 | + |
555 | +def apt_command(args): |
556 | + """ Main entry point for curtin apt-config standalone command |
557 | + This does not read the global config as handled by curthooks, but |
558 | + instead one can specify a different "target" and a new cfg via --config |
559 | + """ |
560 | + cfg = config.load_command_config(args, {}) |
561 | + |
562 | + if args.target is not None: |
563 | + target = args.target |
564 | + else: |
565 | + state = util.load_command_environment() |
566 | + target = state['target'] |
567 | + |
568 | + if target is None: |
569 | + sys.stderr.write("Unable to find target. " |
570 | + "Use --target or set TARGET_MOUNT_POINT\n") |
571 | + sys.exit(2) |
572 | + |
573 | + apt_cfg = cfg.get("apt") |
574 | + # if no apt config section is available, do nothing |
575 | + if apt_cfg is not None: |
576 | + LOG.debug("Handling apt to target %s with config %s", |
577 | + target, apt_cfg) |
578 | + try: |
579 | + with util.ChrootableTarget(target, sys_resolvconf=True): |
580 | + handle_apt(apt_cfg, target) |
581 | + except (RuntimeError, TypeError, ValueError, IOError): |
582 | + LOG.exception("Failed to configure apt features '%s'", apt_cfg) |
583 | + sys.exit(1) |
584 | + else: |
585 | + LOG.info("No apt config provided, skipping") |
586 | + |
587 | + sys.exit(0) |
588 | + |
589 | + |
590 | +def translate_old_apt_features(cfg): |
591 | + """translate the few old apt related features into the new config format""" |
592 | + predef_apt_cfg = cfg.get("apt") |
593 | + if predef_apt_cfg is None: |
594 | + cfg['apt'] = {} |
595 | + predef_apt_cfg = cfg.get("apt") |
596 | + |
597 | + if cfg.get('apt_proxy') is not None: |
598 | + if predef_apt_cfg.get('proxy') is not None: |
599 | + msg = ("Error in apt_proxy configuration: " |
600 | + "old and new format of apt features " |
601 | + "are mutually exclusive") |
602 | + LOG.error(msg) |
603 | + raise ValueError(msg) |
604 | + |
605 | + cfg['apt']['proxy'] = cfg.get('apt_proxy') |
606 | + LOG.debug("Transferred %s into new format: %s", cfg.get('apt_proxy'), |
607 | + cfg.get('apte')) |
608 | + del cfg['apt_proxy'] |
609 | + |
610 | + if cfg.get('apt_mirrors') is not None: |
611 | + if predef_apt_cfg.get('mirrors') is not None: |
612 | + msg = ("Error in apt_mirror configuration: " |
613 | + "old and new format of apt features " |
614 | + "are mutually exclusive") |
615 | + LOG.error(msg) |
616 | + raise ValueError(msg) |
617 | + |
618 | + old = cfg.get('apt_mirrors') |
619 | + cfg['apt']['primary'] = [{"arches": ["default"], |
620 | + "uri": old.get('ubuntu_archive')}] |
621 | + cfg['apt']['security'] = [{"arches": ["default"], |
622 | + "uri": old.get('ubuntu_security')}] |
623 | + LOG.debug("Transferred %s into new format: %s", cfg.get('apt_mirror'), |
624 | + cfg.get('apt')) |
625 | + del cfg['apt_mirrors'] |
626 | + # to work this also needs to disable the default protection |
627 | + psl = predef_apt_cfg.get('preserve_sources_list') |
628 | + if psl is not None: |
629 | + if config.value_as_boolean(psl) is True: |
630 | + msg = ("Error in apt_mirror configuration: " |
631 | + "apt_mirrors and preserve_sources_list: True " |
632 | + "are mutually exclusive") |
633 | + LOG.error(msg) |
634 | + raise ValueError(msg) |
635 | + cfg['apt']['preserve_sources_list'] = False |
636 | + |
637 | + if cfg.get('debconf_selections') is not None: |
638 | + if predef_apt_cfg.get('debconf_selections') is not None: |
639 | + msg = ("Error in debconf_selections configuration: " |
640 | + "old and new format of apt features " |
641 | + "are mutually exclusive") |
642 | + LOG.error(msg) |
643 | + raise ValueError(msg) |
644 | + |
645 | + selsets = cfg.get('debconf_selections') |
646 | + cfg['apt']['debconf_selections'] = selsets |
647 | + LOG.info("Transferred %s into new format: %s", |
648 | + cfg.get('debconf_selections'), |
649 | + cfg.get('apt')) |
650 | + del cfg['debconf_selections'] |
651 | + |
652 | + return cfg |
653 | + |
654 | + |
655 | +CMD_ARGUMENTS = ( |
656 | + ((('-c', '--config'), |
657 | + {'help': 'read configuration from cfg', 'action': util.MergedCmdAppend, |
658 | + 'metavar': 'FILE', 'type': argparse.FileType("rb"), |
659 | + 'dest': 'cfgopts', 'default': []}), |
660 | + (('-t', '--target'), |
661 | + {'help': 'chroot to target. default is env[TARGET_MOUNT_POINT]', |
662 | + 'action': 'store', 'metavar': 'TARGET', |
663 | + 'default': os.environ.get('TARGET_MOUNT_POINT')}),) |
664 | +) |
665 | + |
666 | + |
667 | +def POPULATE_SUBCMD(parser): |
668 | + """Populate subcommand option parsing for apt-config""" |
669 | + populate_one_subcmd(parser, CMD_ARGUMENTS, apt_command) |
670 | + |
671 | +CONFIG_CLEANERS = { |
672 | + 'cloud-init': clean_cloud_init, |
673 | +} |
674 | + |
675 | +# vi: ts=4 expandtab syntax=python |
676 | |
677 | === modified file 'curtin/commands/curthooks.py' |
678 | --- curtin/commands/curthooks.py 2016-07-28 20:11:26 +0000 |
679 | +++ curtin/commands/curthooks.py 2016-08-02 09:15:49 +0000 |
680 | @@ -16,10 +16,8 @@ |
681 | # along with Curtin. If not, see <http://www.gnu.org/licenses/>. |
682 | |
683 | import copy |
684 | -import glob |
685 | import os |
686 | import platform |
687 | -import re |
688 | import sys |
689 | import shutil |
690 | import textwrap |
691 | @@ -32,6 +30,7 @@ |
692 | from curtin import util |
693 | from curtin import net |
694 | from curtin.reporter import events |
695 | +from curtin.commands import apt_config |
696 | |
697 | from . import populate_one_subcmd |
698 | |
699 | @@ -90,45 +89,15 @@ |
700 | info.get('perms', "0644"))) |
701 | |
702 | |
703 | -def apt_config(cfg, target): |
704 | - # cfg['apt_proxy'] |
705 | - |
706 | - proxy_cfg_path = os.path.sep.join( |
707 | - [target, '/etc/apt/apt.conf.d/90curtin-aptproxy']) |
708 | - if cfg.get('apt_proxy'): |
709 | - util.write_file( |
710 | - proxy_cfg_path, |
711 | - content='Acquire::HTTP::Proxy "%s";\n' % cfg['apt_proxy']) |
712 | +def do_apt_config(cfg, target): |
713 | + cfg = apt_config.translate_old_apt_features(cfg) |
714 | + apt_cfg = cfg.get("apt") |
715 | + if apt_cfg is not None: |
716 | + LOG.info("curthooks handling apt to target %s with config %s", |
717 | + target, apt_cfg) |
718 | + apt_config.handle_apt(apt_cfg, target) |
719 | else: |
720 | - if os.path.isfile(proxy_cfg_path): |
721 | - os.unlink(proxy_cfg_path) |
722 | - |
723 | - # cfg['apt_mirrors'] |
724 | - # apt_mirrors: |
725 | - # ubuntu_archive: http://local.archive/ubuntu |
726 | - # ubuntu_security: http://local.archive/ubuntu |
727 | - sources_list = os.path.sep.join([target, '/etc/apt/sources.list']) |
728 | - if (isinstance(cfg.get('apt_mirrors'), dict) and |
729 | - os.path.isfile(sources_list)): |
730 | - repls = [ |
731 | - ('ubuntu_archive', r'http://\S*[.]*archive.ubuntu.com/\S*'), |
732 | - ('ubuntu_security', r'http://security.ubuntu.com/\S*'), |
733 | - ] |
734 | - content = None |
735 | - for name, regex in repls: |
736 | - mirror = cfg['apt_mirrors'].get(name) |
737 | - if not mirror: |
738 | - continue |
739 | - |
740 | - if content is None: |
741 | - with open(sources_list) as fp: |
742 | - content = fp.read() |
743 | - util.write_file(sources_list + ".dist", content) |
744 | - |
745 | - content = re.sub(regex, mirror + " ", content) |
746 | - |
747 | - if content is not None: |
748 | - util.write_file(sources_list, content) |
749 | + LOG.info("No apt config provided, skipping") |
750 | |
751 | |
752 | def disable_overlayroot(cfg, target): |
753 | @@ -140,15 +109,6 @@ |
754 | shutil.move(local_conf, local_conf + ".old") |
755 | |
756 | |
757 | -def clean_cloud_init(target): |
758 | - flist = glob.glob( |
759 | - os.path.sep.join([target, "/etc/cloud/cloud.cfg.d/*dpkg*"])) |
760 | - |
761 | - LOG.debug("cleaning cloud-init config from: %s" % flist) |
762 | - for dpkg_cfg in flist: |
763 | - os.unlink(dpkg_cfg) |
764 | - |
765 | - |
766 | def _maybe_remove_legacy_eth0(target, |
767 | path="/etc/network/interfaces.d/eth0.cfg"): |
768 | """Ubuntu cloud images previously included a 'eth0.cfg' that had |
769 | @@ -250,118 +210,45 @@ |
770 | mapping = copy.deepcopy(KERNEL_MAPPING) |
771 | config.merge_config(mapping, kernel_cfg.get('mapping', {})) |
772 | |
773 | - with util.ChrootableTarget(target) as in_chroot: |
774 | - |
775 | - if kernel_package: |
776 | - util.install_packages([kernel_package], target=target) |
777 | - return |
778 | - |
779 | - # uname[2] is kernel name (ie: 3.16.0-7-generic) |
780 | - # version gets X.Y.Z, flavor gets anything after second '-'. |
781 | - kernel = os.uname()[2] |
782 | - codename, err = in_chroot.subp( |
783 | - ['lsb_release', '--codename', '--short'], capture=True) |
784 | - codename = codename.strip() |
785 | - version, abi, flavor = kernel.split('-', 2) |
786 | - |
787 | - try: |
788 | - map_suffix = mapping[codename][version] |
789 | - except KeyError: |
790 | - LOG.warn("Couldn't detect kernel package to install for %s." |
791 | - % kernel) |
792 | - if kernel_fallback is not None: |
793 | - util.install_packages([kernel_fallback], target=target) |
794 | - return |
795 | - |
796 | - package = "linux-{flavor}{map_suffix}".format( |
797 | - flavor=flavor, map_suffix=map_suffix) |
798 | - |
799 | - if util.has_pkg_available(package, target): |
800 | - if util.has_pkg_installed(package, target): |
801 | - LOG.debug("Kernel package '%s' already installed", package) |
802 | - else: |
803 | - LOG.debug("installing kernel package '%s'", package) |
804 | - util.install_packages([package], target=target) |
805 | - else: |
806 | - if kernel_fallback is not None: |
807 | - LOG.info("Kernel package '%s' not available. " |
808 | - "Installing fallback package '%s'.", |
809 | - package, kernel_fallback) |
810 | - util.install_packages([kernel_fallback], target=target) |
811 | - else: |
812 | - LOG.warn("Kernel package '%s' not available and no fallback." |
813 | - " System may not boot.", package) |
814 | - |
815 | - |
816 | -def apply_debconf_selections(cfg, target): |
817 | - # debconf_selections: |
818 | - # set1: | |
819 | - # cloud-init cloud-init/datasources multiselect MAAS |
820 | - # set2: pkg pkg/value string bar |
821 | - selsets = cfg.get('debconf_selections') |
822 | - if not selsets: |
823 | - LOG.debug("debconf_selections was not set in config") |
824 | - return |
825 | - |
826 | - # for each entry in selections, chroot and apply them. |
827 | - # keep a running total of packages we've seen. |
828 | - pkgs_cfgd = set() |
829 | - for key, content in selsets.items(): |
830 | - LOG.debug("setting for %s, %s" % (key, content)) |
831 | - util.subp(['debconf-set-selections'], data=content.encode(), |
832 | - target=target) |
833 | - for line in content.splitlines(): |
834 | - if line.startswith("#"): |
835 | - continue |
836 | - pkg = re.sub(r"[:\s].*", "", line) |
837 | - pkgs_cfgd.add(pkg) |
838 | - |
839 | - pkgs_installed = get_installed_packages(target) |
840 | - |
841 | - LOG.debug("pkgs_cfgd: %s" % pkgs_cfgd) |
842 | - LOG.debug("pkgs_installed: %s" % pkgs_installed) |
843 | - need_reconfig = pkgs_cfgd.intersection(pkgs_installed) |
844 | - |
845 | - if len(need_reconfig) == 0: |
846 | - LOG.debug("no need for reconfig") |
847 | - return |
848 | - |
849 | - # For any packages that are already installed, but have preseed data |
850 | - # we populate the debconf database, but the filesystem configuration |
851 | - # would be preferred on a subsequent dpkg-reconfigure. |
852 | - # so, what we have to do is "know" information about certain packages |
853 | - # to unconfigure them. |
854 | - unhandled = [] |
855 | - to_config = [] |
856 | - for pkg in need_reconfig: |
857 | - if pkg in CONFIG_CLEANERS: |
858 | - LOG.debug("unconfiguring %s" % pkg) |
859 | - CONFIG_CLEANERS[pkg](target) |
860 | - to_config.append(pkg) |
861 | - else: |
862 | - unhandled.append(pkg) |
863 | - |
864 | - if len(unhandled): |
865 | - LOG.warn("The following packages were installed and preseeded, " |
866 | - "but cannot be unconfigured: %s", unhandled) |
867 | - |
868 | - util.subp(['dpkg-reconfigure', '--frontend=noninteractive'] + |
869 | - list(to_config), data=None, target=target) |
870 | - |
871 | - |
872 | -def get_installed_packages(target=None): |
873 | - (out, _) = util.subp(['dpkg-query', '--list'], target=target, capture=True) |
874 | - |
875 | - pkgs_inst = set() |
876 | - for line in out.splitlines(): |
877 | - try: |
878 | - (state, pkg, other) = line.split(None, 2) |
879 | - except ValueError: |
880 | - continue |
881 | - if state.startswith("hi") or state.startswith("ii"): |
882 | - pkgs_inst.add(re.sub(":.*", "", pkg)) |
883 | - |
884 | - return pkgs_inst |
885 | + if kernel_package: |
886 | + util.install_packages([kernel_package], target=target) |
887 | + return |
888 | + |
889 | + # uname[2] is kernel name (ie: 3.16.0-7-generic) |
890 | + # version gets X.Y.Z, flavor gets anything after second '-'. |
891 | + kernel = os.uname()[2] |
892 | + codename, _ = util.subp(['lsb_release', '--codename', '--short'], |
893 | + capture=True, target=target) |
894 | + codename = codename.strip() |
895 | + version, abi, flavor = kernel.split('-', 2) |
896 | + |
897 | + try: |
898 | + map_suffix = mapping[codename][version] |
899 | + except KeyError: |
900 | + LOG.warn("Couldn't detect kernel package to install for %s." |
901 | + % kernel) |
902 | + if kernel_fallback is not None: |
903 | + util.install_packages([kernel_fallback], target=target) |
904 | + return |
905 | + |
906 | + package = "linux-{flavor}{map_suffix}".format( |
907 | + flavor=flavor, map_suffix=map_suffix) |
908 | + |
909 | + if util.has_pkg_available(package, target): |
910 | + if util.has_pkg_installed(package, target): |
911 | + LOG.debug("Kernel package '%s' already installed", package) |
912 | + else: |
913 | + LOG.debug("installing kernel package '%s'", package) |
914 | + util.install_packages([package], target=target) |
915 | + else: |
916 | + if kernel_fallback is not None: |
917 | + LOG.info("Kernel package '%s' not available. " |
918 | + "Installing fallback package '%s'.", |
919 | + package, kernel_fallback) |
920 | + util.install_packages([kernel_fallback], target=target) |
921 | + else: |
922 | + LOG.warn("Kernel package '%s' not available and no fallback." |
923 | + " System may not boot.", package) |
924 | |
925 | |
926 | def setup_grub(cfg, target): |
927 | @@ -731,7 +618,7 @@ |
928 | } |
929 | |
930 | needed_packages = [] |
931 | - installed_packages = get_installed_packages(target) |
932 | + installed_packages = util.get_installed_packages(target) |
933 | for cust_cfg, pkg_reqs in custom_configs.items(): |
934 | if cust_cfg not in cfg: |
935 | continue |
936 | @@ -811,7 +698,7 @@ |
937 | name=stack_prefix, reporting_enabled=True, level="INFO", |
938 | description="writing config files and configuring apt"): |
939 | write_files(cfg, target) |
940 | - apt_config(cfg, target) |
941 | + do_apt_config(cfg, target) |
942 | disable_overlayroot(cfg, target) |
943 | |
944 | # packages may be needed prior to installing kernel |
945 | @@ -834,7 +721,6 @@ |
946 | setup_zipl(cfg, target) |
947 | install_kernel(cfg, target) |
948 | run_zipl(cfg, target) |
949 | - apply_debconf_selections(cfg, target) |
950 | |
951 | restore_dist_interfaces(cfg, target) |
952 | |
953 | @@ -897,8 +783,4 @@ |
954 | populate_one_subcmd(parser, CMD_ARGUMENTS, curthooks) |
955 | |
956 | |
957 | -CONFIG_CLEANERS = { |
958 | - 'cloud-init': clean_cloud_init, |
959 | -} |
960 | - |
961 | # vi: ts=4 expandtab syntax=python |
962 | |
963 | === modified file 'curtin/commands/main.py' |
964 | --- curtin/commands/main.py 2016-04-04 20:12:01 +0000 |
965 | +++ curtin/commands/main.py 2016-08-02 09:15:49 +0000 |
966 | @@ -27,7 +27,7 @@ |
967 | |
968 | SUB_COMMAND_MODULES = [ |
969 | 'apply_net', 'block-meta', 'block-wipe', 'curthooks', 'extract', |
970 | - 'hook', 'in-target', 'install', 'mkfs', 'net-meta', |
971 | + 'hook', 'in-target', 'install', 'mkfs', 'net-meta', 'apt-config', |
972 | 'pack', 'swap', 'system-install', 'system-upgrade'] |
973 | |
974 | |
975 | |
976 | === added file 'curtin/gpg.py' |
977 | --- curtin/gpg.py 1970-01-01 00:00:00 +0000 |
978 | +++ curtin/gpg.py 2016-08-02 09:15:49 +0000 |
979 | @@ -0,0 +1,74 @@ |
980 | +# Copyright (C) 2016 Canonical Ltd. |
981 | +# |
982 | +# Author: Scott Moser <scott.moser@canonical.com> |
983 | +# Christian Ehrhardt <christian.ehrhardt@canonical.com> |
984 | +# |
985 | +# Curtin is free software: you can redistribute it and/or modify it under |
986 | +# the terms of the GNU Affero General Public License as published by the |
987 | +# Free Software Foundation, either version 3 of the License, or (at your |
988 | +# option) any later version. |
989 | +# |
990 | +# Curtin is distributed in the hope that it will be useful, but WITHOUT ANY |
991 | +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
992 | +# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for |
993 | +# more details. |
994 | +# |
995 | +# You should have received a copy of the GNU Affero General Public License |
996 | +# along with Curtin. If not, see <http://www.gnu.org/licenses/>. |
997 | +""" gpg.py |
998 | +gpg related utilities to get raw keys data by their id |
999 | +""" |
1000 | + |
1001 | +from curtin import util |
1002 | + |
1003 | +from .log import LOG |
1004 | + |
1005 | + |
1006 | +def export_armour(key): |
1007 | + """Export gpg key, armoured key gets returned""" |
1008 | + try: |
1009 | + (armour, _) = util.subp(["gpg", "--export", "--armour", key], |
1010 | + capture=True) |
1011 | + except util.ProcessExecutionError as error: |
1012 | + # debug, since it happens for any key not on the system initially |
1013 | + LOG.debug('Failed to export armoured key "%s": %s', key, error) |
1014 | + armour = None |
1015 | + return armour |
1016 | + |
1017 | + |
1018 | +def recv_key(key, keyserver): |
1019 | + """Receive gpg key from the specified keyserver""" |
1020 | + LOG.debug('Receive gpg key "%s"', key) |
1021 | + try: |
1022 | + util.subp(["gpg", "--keyserver", keyserver, "--recv", key], |
1023 | + capture=True) |
1024 | + except util.ProcessExecutionError as error: |
1025 | + raise ValueError(('Failed to import key "%s" ' |
1026 | + 'from server "%s" - error %s') % |
1027 | + (key, keyserver, error)) |
1028 | + |
1029 | + |
1030 | +def delete_key(key): |
1031 | + """Delete the specified key from the local gpg ring""" |
1032 | + try: |
1033 | + util.subp(["gpg", "--batch", "--yes", "--delete-keys", key], |
1034 | + capture=True) |
1035 | + except util.ProcessExecutionError as error: |
1036 | + LOG.warn('Failed delete key "%s": %s', key, error) |
1037 | + |
1038 | + |
1039 | +def getkeybyid(keyid, keyserver='keyserver.ubuntu.com'): |
1040 | + """get gpg keyid from keyserver""" |
1041 | + armour = export_armour(keyid) |
1042 | + if not armour: |
1043 | + try: |
1044 | + recv_key(keyid, keyserver=keyserver) |
1045 | + armour = export_armour(keyid) |
1046 | + except ValueError: |
1047 | + LOG.exception('Failed to obtain gpg key %s', keyid) |
1048 | + raise |
1049 | + finally: |
1050 | + # delete just imported key to leave environment as it was before |
1051 | + delete_key(keyid) |
1052 | + |
1053 | + return armour |
1054 | |
1055 | === modified file 'curtin/util.py' |
1056 | --- curtin/util.py 2016-07-28 20:03:42 +0000 |
1057 | +++ curtin/util.py 2016-08-02 09:15:49 +0000 |
1058 | @@ -16,18 +16,30 @@ |
1059 | # along with Curtin. If not, see <http://www.gnu.org/licenses/>. |
1060 | |
1061 | import argparse |
1062 | +import collections |
1063 | import errno |
1064 | import glob |
1065 | import json |
1066 | import os |
1067 | import platform |
1068 | +import re |
1069 | import shutil |
1070 | +import socket |
1071 | import subprocess |
1072 | import stat |
1073 | import sys |
1074 | import tempfile |
1075 | import time |
1076 | |
1077 | +# avoid the dependency to python3-six as used in cloud-init |
1078 | +try: |
1079 | + from urlparse import urlparse |
1080 | +except ImportError: |
1081 | + # python3 |
1082 | + # avoid triggering pylint, https://github.com/PyCQA/pylint/issues/769 |
1083 | + # pylint:disable=import-error,no-name-in-module |
1084 | + from urllib.parse import urlparse |
1085 | + |
1086 | try: |
1087 | string_types = (basestring,) |
1088 | except NameError: |
1089 | @@ -40,6 +52,11 @@ |
1090 | |
1091 | _LSB_RELEASE = {} |
1092 | |
1093 | +_DNS_REDIRECT_IP = None |
1094 | + |
1095 | +# matcher used in template rendering functions |
1096 | +BASIC_MATCHER = re.compile(r'\$\{([A-Za-z0-9_.]+)\}|\$([A-Za-z0-9_.]+)') |
1097 | + |
1098 | |
1099 | def _subp(args, data=None, rcs=None, env=None, capture=False, shell=False, |
1100 | logstring=False, decode="replace", target=None): |
1101 | @@ -487,6 +504,21 @@ |
1102 | return False |
1103 | |
1104 | |
1105 | +def get_installed_packages(target=None): |
1106 | + (out, _) = subp(['dpkg-query', '--list'], target=target, capture=True) |
1107 | + |
1108 | + pkgs_inst = set() |
1109 | + for line in out.splitlines(): |
1110 | + try: |
1111 | + (state, pkg, other) = line.split(None, 2) |
1112 | + except ValueError: |
1113 | + continue |
1114 | + if state.startswith("hi") or state.startswith("ii"): |
1115 | + pkgs_inst.add(re.sub(":.*", "", pkg)) |
1116 | + |
1117 | + return pkgs_inst |
1118 | + |
1119 | + |
1120 | def has_pkg_installed(pkg, target=None): |
1121 | try: |
1122 | out, _ = subp(['dpkg-query', '--show', '--showformat', |
1123 | @@ -839,27 +871,37 @@ |
1124 | return (isinstance(exc, IOError) and exc.errno == errno.ENOENT) |
1125 | |
1126 | |
1127 | -def lsb_release(): |
1128 | +def _lsb_release(target=None): |
1129 | fmap = {'Codename': 'codename', 'Description': 'description', |
1130 | 'Distributor ID': 'id', 'Release': 'release'} |
1131 | + |
1132 | + data = {} |
1133 | + try: |
1134 | + out, _ = subp(['lsb_release', '--all'], capture=True, target=target) |
1135 | + for line in out.splitlines(): |
1136 | + fname, _, val = line.partition(":") |
1137 | + if fname in fmap: |
1138 | + data[fmap[fname]] = val.strip() |
1139 | + missing = [k for k in fmap.values() if k not in data] |
1140 | + if len(missing): |
1141 | + LOG.warn("Missing fields in lsb_release --all output: %s", |
1142 | + ','.join(missing)) |
1143 | + |
1144 | + except ProcessExecutionError as err: |
1145 | + LOG.warn("Unable to get lsb_release --all: %s", err) |
1146 | + data = {v: "UNAVAILABLE" for v in fmap.values()} |
1147 | + |
1148 | + return data |
1149 | + |
1150 | + |
1151 | +def lsb_release(target=None): |
1152 | + if target_path(target) != "/": |
1153 | + # do not use or update cache if target is provided |
1154 | + return _lsb_release(target) |
1155 | + |
1156 | global _LSB_RELEASE |
1157 | if not _LSB_RELEASE: |
1158 | - data = {} |
1159 | - try: |
1160 | - out, err = subp(['lsb_release', '--all'], capture=True) |
1161 | - for line in out.splitlines(): |
1162 | - fname, tok, val = line.partition(":") |
1163 | - if fname in fmap: |
1164 | - data[fmap[fname]] = val.strip() |
1165 | - missing = [k for k in fmap.values() if k not in data] |
1166 | - if len(missing): |
1167 | - LOG.warn("Missing fields in lsb_release --all output: %s", |
1168 | - ','.join(missing)) |
1169 | - |
1170 | - except ProcessExecutionError as e: |
1171 | - LOG.warn("Unable to get lsb_release --all: %s", e) |
1172 | - data = {v: "UNAVAILABLE" for v in fmap.values()} |
1173 | - |
1174 | + data = _lsb_release() |
1175 | _LSB_RELEASE.update(data) |
1176 | return _LSB_RELEASE |
1177 | |
1178 | @@ -889,6 +931,108 @@ |
1179 | return platform2arch.get(platform.machine(), platform.machine()) |
1180 | |
1181 | |
1182 | +def basic_template_render(content, params): |
1183 | + """This does simple replacement of bash variable like templates. |
1184 | + |
1185 | + It identifies patterns like ${a} or $a and can also identify patterns like |
1186 | + ${a.b} or $a.b which will look for a key 'b' in the dictionary rooted |
1187 | + by key 'a'. |
1188 | + """ |
1189 | + |
1190 | + def replacer(match): |
1191 | + """ replacer |
1192 | + replacer used in regex match to replace content |
1193 | + """ |
1194 | + # Only 1 of the 2 groups will actually have a valid entry. |
1195 | + name = match.group(1) |
1196 | + if name is None: |
1197 | + name = match.group(2) |
1198 | + if name is None: |
1199 | + raise RuntimeError("Match encountered but no valid group present") |
1200 | + path = collections.deque(name.split(".")) |
1201 | + selected_params = params |
1202 | + while len(path) > 1: |
1203 | + key = path.popleft() |
1204 | + if not isinstance(selected_params, dict): |
1205 | + raise TypeError("Can not traverse into" |
1206 | + " non-dictionary '%s' of type %s while" |
1207 | + " looking for subkey '%s'" |
1208 | + % (selected_params, |
1209 | + selected_params.__class__.__name__, |
1210 | + key)) |
1211 | + selected_params = selected_params[key] |
1212 | + key = path.popleft() |
1213 | + if not isinstance(selected_params, dict): |
1214 | + raise TypeError("Can not extract key '%s' from non-dictionary" |
1215 | + " '%s' of type %s" |
1216 | + % (key, selected_params, |
1217 | + selected_params.__class__.__name__)) |
1218 | + return str(selected_params[key]) |
1219 | + |
1220 | + return BASIC_MATCHER.sub(replacer, content) |
1221 | + |
1222 | + |
1223 | +def render_string(content, params): |
1224 | + """ render_string |
1225 | + render a string following replacement rules as defined in |
1226 | + basic_template_render returning the string |
1227 | + """ |
1228 | + if not params: |
1229 | + params = {} |
1230 | + return basic_template_render(content, params) |
1231 | + |
1232 | + |
1233 | +def is_resolvable(name): |
1234 | + """determine if a url is resolvable, return a boolean |
1235 | + This also attempts to be resilent against dns redirection. |
1236 | + |
1237 | + Note, that normal nsswitch resolution is used here. So in order |
1238 | + to avoid any utilization of 'search' entries in /etc/resolv.conf |
1239 | + we have to append '.'. |
1240 | + |
1241 | + The top level 'invalid' domain is invalid per RFC. And example.com |
1242 | + should also not exist. The random entry will be resolved inside |
1243 | + the search list. |
1244 | + """ |
1245 | + global _DNS_REDIRECT_IP |
1246 | + if _DNS_REDIRECT_IP is None: |
1247 | + badips = set() |
1248 | + badnames = ("does-not-exist.example.com.", "example.invalid.") |
1249 | + badresults = {} |
1250 | + for iname in badnames: |
1251 | + try: |
1252 | + result = socket.getaddrinfo(iname, None, 0, 0, |
1253 | + socket.SOCK_STREAM, |
1254 | + socket.AI_CANONNAME) |
1255 | + badresults[iname] = [] |
1256 | + for (_, _, _, cname, sockaddr) in result: |
1257 | + badresults[iname].append("%s: %s" % (cname, sockaddr[0])) |
1258 | + badips.add(sockaddr[0]) |
1259 | + except (socket.gaierror, socket.error): |
1260 | + pass |
1261 | + _DNS_REDIRECT_IP = badips |
1262 | + if badresults: |
1263 | + LOG.debug("detected dns redirection: %s", badresults) |
1264 | + |
1265 | + try: |
1266 | + result = socket.getaddrinfo(name, None) |
1267 | + # check first result's sockaddr field |
1268 | + addr = result[0][4][0] |
1269 | + if addr in _DNS_REDIRECT_IP: |
1270 | + LOG.debug("dns %s in _DNS_REDIRECT_IP", name) |
1271 | + return False |
1272 | + LOG.debug("dns %s resolved to '%s'", name, result) |
1273 | + return True |
1274 | + except (socket.gaierror, socket.error): |
1275 | + LOG.debug("dns %s failed to resolve", name) |
1276 | + return False |
1277 | + |
1278 | + |
1279 | +def is_resolvable_url(url): |
1280 | + """determine if this url is resolvable (existing or ip).""" |
1281 | + return is_resolvable(urlparse(url).hostname) |
1282 | + |
1283 | + |
1284 | def target_path(target, path=None): |
1285 | # return 'path' inside target, accepting target as None |
1286 | if target in (None, ""): |
1287 | |
1288 | === modified file 'doc/devel/README-vmtest.txt' |
1289 | --- doc/devel/README-vmtest.txt 2016-07-18 16:27:49 +0000 |
1290 | +++ doc/devel/README-vmtest.txt 2016-08-02 09:15:49 +0000 |
1291 | @@ -90,13 +90,13 @@ |
1292 | The tests themselves don't actually have to run as root, but the |
1293 | test setup does. |
1294 | * the 'tools' directory must be in your path. |
1295 | - * test will set apt_proxy in the guests to the value of |
1296 | - 'apt_proxy' environment variable. If that is not set it will |
1297 | + * test will set apt: { proxy } in the guests to the value of |
1298 | + 'apt_proxy' environment variable. If that is not set it will |
1299 | look at the host's apt config and read 'Acquire::HTTP::Proxy' |
1300 | |
1301 | == Environment Variables == |
1302 | Some environment variables affect the running of vmtest |
1303 | - * apt_proxy: |
1304 | + * apt_proxy: |
1305 | test will set apt_proxy in the guests to the value of 'apt_proxy'. |
1306 | If that is not set it will look at the host's apt config and read |
1307 | 'Acquire::HTTP::Proxy' |
1308 | @@ -146,7 +146,7 @@ |
1309 | |
1310 | * CURTIN_VMTEST_INSTALL_TIMEOUT: default 3000 |
1311 | timeout before giving up on installation. |
1312 | - |
1313 | + |
1314 | * CURTIN_VMTEST_PARALLEL: default '' |
1315 | only supported through ./tools/jenkins-runner . |
1316 | -1 : then run one per core. |
1317 | @@ -164,3 +164,58 @@ |
1318 | Environment 'boolean' values: |
1319 | For boolean environment variables the value is considered True |
1320 | if it is any value other than case insensitive 'false', '' or "0" |
1321 | + |
1322 | + |
1323 | +== Test Class Variables == |
1324 | +The base VMBaseClass defines several variables that help creating a new test |
1325 | +easily. Among those the common ones are: |
1326 | + |
1327 | +Generic: |
1328 | +- arch_skip |
1329 | + If a test is not supported on an architecture it can list the arch in this |
1330 | + variable to auto-skip the test if executed on that arch. |
1331 | +- conf_file |
1332 | + The configuration that will be processed by this vmtest. |
1333 | +- extra_kern_args |
1334 | + Extra arguments to the guest kernel on boot. |
1335 | + |
1336 | +Data Collection: |
1337 | +- collect_scripts |
1338 | + The commands run when booting into the installed environment to collect the |
1339 | + data for the test to verify a proper execution. |
1340 | +- boot_cloudconf |
1341 | + Extra cloud-init config content for the install phase. |
1342 | + This allows to gather content of the install phase if needed for test |
1343 | + verification. |
1344 | + |
1345 | + |
1346 | +Disk Setup: |
1347 | +- disk_block_size = 512 |
1348 | +- disk_driver = 'virtio-blk' |
1349 | + Used to set up the disks for the virtual environment used by the vmtest. |
1350 | + Will set the values used in extra_disks if not specified there. |
1351 | +- extra_disks |
1352 | + A list of extra disks to be created for the testcase. The definition is |
1353 | + dpath:size:driver:block_size |
1354 | +- multipath = False |
1355 | +- multipath_num_paths = 2 |
1356 | + Details for the (virtual) multipath setup |
1357 | +- nvme_disks |
1358 | + a shortcut to provide extra disks with the nvme driver |
1359 | + |
1360 | +Timeouts: |
1361 | +- boot_timeout |
1362 | +- install_timeout |
1363 | + Usually set via CURTIN_VMTEST_BOOT_TIMEOUT/CURTIN_VMTEST_INSTALL_TIMEOUT |
1364 | + (see above) environment var, but can be overwritten by a testcase if needed. |
1365 | + |
1366 | +Checks: |
1367 | +- disk_to_check |
1368 | + A disk name that is verified to be existing after the installation. |
1369 | +- fstab_expected |
1370 | + |
1371 | +Release: |
1372 | +- release = None |
1373 | +- krel = None |
1374 | + Those two define the distribution and kernel release to be tested and are |
1375 | + usually set by importing and inheriting from tests/vmtests/releases.py |
1376 | |
1377 | === added file 'doc/topics/apt_source.rst' |
1378 | --- doc/topics/apt_source.rst 1970-01-01 00:00:00 +0000 |
1379 | +++ doc/topics/apt_source.rst 2016-08-02 09:15:49 +0000 |
1380 | @@ -0,0 +1,158 @@ |
1381 | +========== |
1382 | +APT Source |
1383 | +========== |
1384 | + |
1385 | +This part of curtin is meant to allow influencing the apt behaviour and configuration. |
1386 | + |
1387 | +By default - if no apt config is provided - it does nothing. That keeps behavior compatible on upgrades. |
1388 | + |
1389 | +The feature has an optional target argument which - by default - is used to modify the environment that curtin currently installs (@TARGET_MOUNT_POINT). |
1390 | + |
1391 | +Features |
1392 | +-------- |
1393 | + |
1394 | +* Add PGP keys to the APT trusted keyring |
1395 | + |
1396 | + - add via short keyid |
1397 | + |
1398 | + - add via long key fingerprint |
1399 | + |
1400 | + - specify a custom keyserver to pull from |
1401 | + |
1402 | + - add raw keys (which makes you independent of keyservers) |
1403 | + |
1404 | +* Influence global apt configuration |
1405 | + |
1406 | + - adding ppa's |
1407 | + |
1408 | + - replacing mirror, security mirror and release in sources.list |
1409 | + |
1410 | + - able to provide a fully custom template for sources.list |
1411 | + |
1412 | + - add arbitrary apt.conf settings |
1413 | + |
1414 | + - provide debconf configurations |
1415 | + |
1416 | + - disabling suites (=pockets) |
1417 | + |
1418 | + - per architecture mirror definition |
1419 | + |
1420 | + |
1421 | +Configuration |
1422 | +------------- |
1423 | + |
1424 | +The general configuration of the apt feature is under an element called ``apt``. |
1425 | + |
1426 | +This can have various "global" subelements as listed in the examples below. |
1427 | +The file ``apt-source.yaml`` holds more examples. |
1428 | + |
1429 | +These global configurations are valid throughput all of the apt feature. |
1430 | +So for exmaple a global specification of a ``primary`` mirror will apply to all rendered sources entries. |
1431 | + |
1432 | +Then there is a section ``sources`` which can hold any number of source subelements itself. |
1433 | +The key is the filename and will be prepended by /etc/apt/sources.list.d/ if it doesn't start with a ``/``. |
1434 | +There are certain cases - where no content is written into a source.list file where the filename will be ignored - yet it can still be used as index for merging. |
1435 | + |
1436 | +The values inside the entries consist of the following optional entries:: |
1437 | +* ``source``: a sources.list entry (some variable replacements apply) |
1438 | + |
1439 | +* ``keyid``: providing a key to import via shortid or fingerprint |
1440 | + |
1441 | +* ``key``: providing a raw PGP key |
1442 | + |
1443 | +* ``keyserver``: specify an alternate keyserver to pull keys from that were specified by keyid |
1444 | + |
1445 | +The section "sources" is is a dictionary (unlike most block/net configs which are lists). This format allows merging between multiple input files than a list like:: |
1446 | + sources: |
1447 | + s1: {'key': 'key1', 'source': 'source1'} |
1448 | + |
1449 | + sources: |
1450 | + s2: {'key': 'key2'} |
1451 | + s1: {'keyserver': 'foo'} |
1452 | + |
1453 | + This would be merged into |
1454 | + s1: {'key': 'key1', 'source': 'source1', keyserver: 'foo'} |
1455 | + s2: {'key': 'key2'} |
1456 | + |
1457 | +Here is just one of the most common examples for this feature: install with curtin in an isolated environment (derived repository): |
1458 | + |
1459 | +For that we need to: |
1460 | +* insert the PGP key of the local repository to be trusted |
1461 | + |
1462 | + - since you are locked down you can't pull from keyserver.ubuntu.com |
1463 | + |
1464 | + - if you have an internal keyserver you could pull from there, but let us assume you don't even have that; so you have to provide the raw key |
1465 | + |
1466 | + - in the example I'll use the key of the "Ubuntu CD Image Automatic Signing Key" which makes no sense as it is in the trusted keyring anyway, but it is a good example. (Also the key is shortened to stay readable) |
1467 | + |
1468 | +:: |
1469 | + |
1470 | + -----BEGIN PGP PUBLIC KEY BLOCK----- |
1471 | + Version: GnuPG v1 |
1472 | + mQGiBEFEnz8RBAC7LstGsKD7McXZgd58oN68KquARLBl6rjA2vdhwl77KkPPOr3O |
1473 | + RwIbDAAKCRBAl26vQ30FtdxYAJsFjU+xbex7gevyGQ2/mhqidES4MwCggqQyo+w1 |
1474 | + Twx6DKLF+3rF5nf1F3Q= |
1475 | + =PBAe |
1476 | + -----END PGP PUBLIC KEY BLOCK----- |
1477 | + |
1478 | +* replace the mirrors used to some mirrors available inside the isolated environment for apt to pull repository data from. |
1479 | + |
1480 | + - lets consider we have a local mirror at ``mymirror.local`` but otherwise following the usual paths |
1481 | + |
1482 | + - make an example with a partial mirror that doesn't mirror the backports suite, so backports have to be disabled |
1483 | + |
1484 | +That would be specified as |
1485 | +:: |
1486 | + |
1487 | + apt: |
1488 | + primary: |
1489 | + - arches [default] |
1490 | + uri: http://mymirror.local/ubuntu/ |
1491 | + disable_suites: [backports] |
1492 | + sources: |
1493 | + localrepokey: |
1494 | + key: | # full key as block |
1495 | + -----BEGIN PGP PUBLIC KEY BLOCK----- |
1496 | + Version: GnuPG v1 |
1497 | + |
1498 | + mQGiBEFEnz8RBAC7LstGsKD7McXZgd58oN68KquARLBl6rjA2vdhwl77KkPPOr3O |
1499 | + RwIbDAAKCRBAl26vQ30FtdxYAJsFjU+xbex7gevyGQ2/mhqidES4MwCggqQyo+w1 |
1500 | + Twx6DKLF+3rF5nf1F3Q= |
1501 | + =PBAe |
1502 | + -----END PGP PUBLIC KEY BLOCK----- |
1503 | + |
1504 | +The file examples/apt-source.yaml holds various further examples that can be configured with this feature. |
1505 | + |
1506 | + |
1507 | +Common snippets |
1508 | +--------------- |
1509 | +This is a collection of additional ideas people can use the feature for customizing their to-be-installed system. |
1510 | + |
1511 | +* enable proposed on installing |
1512 | + apt: |
1513 | + sources: |
1514 | + proposed.list: deb $MIRROR $RELEASE-proposed main restricted universe multiverse |
1515 | + |
1516 | +* Make debug symbols available |
1517 | + apt: |
1518 | + sources: |
1519 | + ddebs.list: | |
1520 | + deb http://ddebs.ubuntu.com $RELEASE main restricted universe multiverse |
1521 | + deb http://ddebs.ubuntu.com $RELEASE-updates main restricted universe multiverse |
1522 | + deb http://ddebs.ubuntu.com $RELEASE-security main restricted universe multiverse |
1523 | + deb http://ddebs.ubuntu.com $RELEASE-proposed main restricted universe multiverse |
1524 | + |
1525 | + |
1526 | +Timing |
1527 | +------ |
1528 | +The feature is implemented at the stage of curthooks_commands, which runs just after curtin has extracted the image to the target. |
1529 | +Additionally it can be ran as standalong command "curtin -v --config <yourconfigfile> apt-config". |
1530 | + |
1531 | +This will pick up the target from the environment variable that is set by curtin, if you want to use it to a different target or outside of usual curtin handling you can add ``--target <path>`` to it to overwrite the target path. |
1532 | +This target should have at least a minimal system with apt, apt-add-repository and dpkg being installed for the functionality to work. |
1533 | + |
1534 | + |
1535 | +Dependencies |
1536 | +------------ |
1537 | +Cloud-init might need to resolve dependencies and install packages in the ephemeral environment to run curtin. |
1538 | +Therefore it is recommended to not only provide an apt configuration to curtin for the target, but also one to the install environment via cloud-init. |
1539 | |
1540 | === added file 'examples/apt-source.yaml' |
1541 | --- examples/apt-source.yaml 1970-01-01 00:00:00 +0000 |
1542 | +++ examples/apt-source.yaml 2016-08-02 09:15:49 +0000 |
1543 | @@ -0,0 +1,267 @@ |
1544 | +# YAML example of an apt config. |
1545 | +apt: |
1546 | + # The apt config consists of two major "areas". |
1547 | + # |
1548 | + # On one hand there is the global configuration for the apt feature. |
1549 | + # |
1550 | + # On one hand (down in this file) there is the source dictionary which allows |
1551 | + # to define various entries to be considered by apt. |
1552 | + |
1553 | + ############################################################################## |
1554 | + # Section 1: global apt configuration |
1555 | + # |
1556 | + # The following examples number the top keys to ease identification in |
1557 | + # discussions. |
1558 | + |
1559 | + # 1.1 preserve_sources_list |
1560 | + # |
1561 | + # Preserves the existing /etc/apt/sources.list |
1562 | + # Default: True - do not overwrite sources_list. If staying at true |
1563 | + # then any "mirrors" configuration will have no effect. |
1564 | + # Set to False to affect sources.list with the configuration. Without setting |
1565 | + # this to false only "extra" source specifications will be written into |
1566 | + # /etc/apt/sources.list.d/* |
1567 | + preserve_sources_list: false |
1568 | + |
1569 | + # 1.2 disable_suites |
1570 | + # |
1571 | + # This is an empty list by default, so nothing is disabled. |
1572 | + # |
1573 | + # If given, those suites are removed from sources.list after all other |
1574 | + # modifications have been made. |
1575 | + # Suites are even disabled if no other modification was made, |
1576 | + # but not if is preserve_sources_list is active. |
1577 | + # There is a special alias “$RELEASE” as in the sources that will be replace |
1578 | + # by the matching release. |
1579 | + # |
1580 | + # To ease configuration and improve readability the following common ubuntu |
1581 | + # suites will be automatically mapped to their full definition. |
1582 | + # updates => $RELEASE-updates |
1583 | + # backports => $RELEASE-backports |
1584 | + # security => $RELEASE-security |
1585 | + # proposed => $RELEASE-proposed |
1586 | + # release => $RELEASE |
1587 | + # |
1588 | + # There is no harm in specifying a suite to be disabled that is not found in |
1589 | + # the source.list file (just a no-op then) |
1590 | + # |
1591 | + # Note: Lines don’t get deleted, but disabled by being converted to a comment. |
1592 | + # The following example disables all usual defaults except $RELEASE-security. |
1593 | + # On top it disables a custom suite called "mysuite" |
1594 | + disable_suites: [$RELEASE-updates, backports, $RELEASE, mysuite] |
1595 | + |
1596 | + # 1.3 primary/security |
1597 | + # |
1598 | + # define a custom (e.g. localized) mirror that will be used in sources.list |
1599 | + # and any custom sources entries for deb / deb-src lines. |
1600 | + # |
1601 | + # One can set primary and security mirror to different uri's |
1602 | + # the child elements to the keys primary and secondary are equivalent |
1603 | + primary: |
1604 | + # arches is list of architectures the following config applies to |
1605 | + # the special keyword "default" applies to any architecture not explicitly |
1606 | + # listed. |
1607 | + - arches: [amd64, i386, default] |
1608 | + # uri is just defining the target as-is |
1609 | + uri: http://us.archive.ubuntu.com/ubuntu |
1610 | + # |
1611 | + # via search one can define lists that are |
1612 | + # tried one by one. The first with a working DNS resolution (or if it is an |
1613 | + # IP) will be picked. That way one can keep one configuration for multiple |
1614 | + # subenvironments that select the working one. |
1615 | + search: |
1616 | + - http://cool.but-sometimes-unreachable.com/ubuntu |
1617 | + - http://us.archive.ubuntu.com/ubuntu |
1618 | + # |
1619 | + # If multiple of a category are given |
1620 | + # 1. uri |
1621 | + # 2. search |
1622 | + # the first defining a valid mirror wins (in the order as defined here, |
1623 | + # not the order as listed in the config). |
1624 | + # |
1625 | + - arches: [s390x, arm64] |
1626 | + # as above, allowing to have one config for different per arch mirrors |
1627 | + # security is optional, if not defined it is set to the same value as primary |
1628 | + security: |
1629 | + uri: http://security.ubuntu.com/ubuntu |
1630 | + [...] |
1631 | + |
1632 | + # if no mirrors are specified at all, or all lookups fail it will use: |
1633 | + # primary: http://archive.ubuntu.com/ubuntu |
1634 | + # security: http://security.ubuntu.com/ubuntu |
1635 | + |
1636 | + # 1.4 sources_list |
1637 | + # |
1638 | + # Provide a custom template for rendering sources.list |
1639 | + # without one provided curtin will try to modify the sources.list it finds |
1640 | + # in the target at /etc/apt/sources.list. |
1641 | + # Within these sources.list templates you can use the following replacement |
1642 | + # variables (all have sane Ubuntu defaults, but mirrors can be overwritten |
1643 | + # as needed (see above)): |
1644 | + # => $RELEASE, $MIRROR, $PRIMARY, $SECURITY |
1645 | + sources_list: | # written by curtin custom template |
1646 | + deb $MIRROR $RELEASE main restricted |
1647 | + deb-src $MIRROR $RELEASE main restricted |
1648 | + deb $PRIMARY $RELEASE universe restricted |
1649 | + deb $SECURITY $RELEASE-security multiverse |
1650 | + |
1651 | + # 1.5 conf |
1652 | + # |
1653 | + # Any apt config string that will be made available to apt |
1654 | + # see the APT.CONF(5) man page for details what can be specified |
1655 | + conf: | # APT config |
1656 | + APT { |
1657 | + Get { |
1658 | + Assume-Yes "true"; |
1659 | + Fix-Broken "true"; |
1660 | + }; |
1661 | + }; |
1662 | + |
1663 | + # 1.6 (http_|ftp_|https_)proxy |
1664 | + # |
1665 | + # Proxies are the most common apt.conf option, so that for simplified use |
1666 | + # there is a shortcut for those. Those get automatically translated into the |
1667 | + # correct Acquire::*::Proxy statements. |
1668 | + # |
1669 | + # note: proxy actually being a short synonym to http_proxy |
1670 | + proxy: http://[[user][:pass]@]host[:port]/ |
1671 | + http_proxy: http://[[user][:pass]@]host[:port]/ |
1672 | + ftp_proxy: ftp://[[user][:pass]@]host[:port]/ |
1673 | + https_proxy: https://[[user][:pass]@]host[:port]/ |
1674 | + |
1675 | + # 1.7 add_apt_repo_match |
1676 | + # |
1677 | + # 'source' entries in apt-sources that match this python regex |
1678 | + # expression will be passed to add-apt-repository |
1679 | + # The following example is also the builtin default if nothing is specified |
1680 | + add_apt_repo_match: '^[\w-]+:\w' |
1681 | + |
1682 | + |
1683 | + ############################################################################## |
1684 | + # Section 2: source list entries |
1685 | + # |
1686 | + # This is a dictionary (unlike most block/net which are lists) |
1687 | + # |
1688 | + # The key of each source entry is the filename and will be prepended by |
1689 | + # /etc/apt/sources.list.d/ if it doesn't start with a '/'. |
1690 | + # If it doesn't end with .list it will be appended so that apt picks up it's |
1691 | + # configuration. |
1692 | + # |
1693 | + # Whenever there is no content to be written into such a file, the key is |
1694 | + # not used as filename - yet it can still be used as index for merging |
1695 | + # configuration. |
1696 | + # |
1697 | + # The values inside the entries consost of the following optional entries: |
1698 | + # 'source': a sources.list entry (some variable replacements apply) |
1699 | + # 'keyid': providing a key to import via shortid or fingerprint |
1700 | + # 'key': providing a raw PGP key |
1701 | + # 'keyserver': specify an alternate keyserver to pull keys from that |
1702 | + # were specified by keyid |
1703 | + |
1704 | + # This allows merging between multiple input files than a list like: |
1705 | + # cloud-config1 |
1706 | + # sources: |
1707 | + # s1: {'key': 'key1', 'source': 'source1'} |
1708 | + # cloud-config2 |
1709 | + # sources: |
1710 | + # s2: {'key': 'key2'} |
1711 | + # s1: {'keyserver': 'foo'} |
1712 | + # This would be merged to |
1713 | + # sources: |
1714 | + # s1: |
1715 | + # keyserver: foo |
1716 | + # key: key1 |
1717 | + # source: source1 |
1718 | + # s2: |
1719 | + # key: key2 |
1720 | + # |
1721 | + # The following examples number the subfeatures per sources entry to ease |
1722 | + # identification in discussions. |
1723 | + |
1724 | + |
1725 | + sources: |
1726 | + curtin-dev-ppa.list: |
1727 | + # 2.1 source |
1728 | + # |
1729 | + # Creates a file in /etc/apt/sources.list.d/ for the sources list entry |
1730 | + # based on the key: "/etc/apt/sources.list.d/curtin-dev-ppa.list" |
1731 | + source: "deb http://ppa.launchpad.net/curtin-dev/test-archive/ubuntu xenial main" |
1732 | + |
1733 | + # 2.2 keyid |
1734 | + # |
1735 | + # Importing a gpg key for a given key id. Used keyserver defaults to |
1736 | + # keyserver.ubuntu.com |
1737 | + keyid: F430BBA5 # GPG key ID published on a key server |
1738 | + |
1739 | + ignored1: |
1740 | + # 2.3 PPA shortcut |
1741 | + # |
1742 | + # Setup correct apt sources.list line and Auto-Import the signing key |
1743 | + # from LP |
1744 | + # |
1745 | + # See https://help.launchpad.net/Packaging/PPA for more information |
1746 | + # this requires 'add-apt-repository'. This will create a file in |
1747 | + # /etc/apt/sources.list.d automatically, therefore the key here is |
1748 | + # ignored as filename in those cases. |
1749 | + source: "ppa:curtin-dev/test-archive" # Quote the string |
1750 | + |
1751 | + my-repo2.list: |
1752 | + # 2.4 replacement variables |
1753 | + # |
1754 | + # sources can use $MIRROR, $PRIMARY, $SECURITY and $RELEASE replacement |
1755 | + # variables. |
1756 | + # They will be replaced with the default or specified mirrors and the |
1757 | + # running release. |
1758 | + # The entry below would be possibly turned into: |
1759 | + # source: deb http://archive.ubuntu.com/ubuntu xenial multiverse |
1760 | + source: deb $MIRROR $RELEASE multiverse |
1761 | + |
1762 | + my-repo3.list: |
1763 | + # this would have the same end effect as 'ppa:curtin-dev/test-archive' |
1764 | + source: "deb http://ppa.launchpad.net/curtin-dev/test-archive/ubuntu xenial main" |
1765 | + keyid: F430BBA5 # GPG key ID published on the key server |
1766 | + filename: curtin-dev-ppa.list |
1767 | + |
1768 | + ignored2: |
1769 | + # 2.5 key only |
1770 | + # |
1771 | + # this would only import the key without adding a ppa or other source spec |
1772 | + # since this doesn't generate a source.list file the filename key is ignored |
1773 | + keyid: F430BBA5 # GPG key ID published on a key server |
1774 | + |
1775 | + ignored3: |
1776 | + # 2.6 key id alternatives |
1777 | + # |
1778 | + # Keyid's can also be specified via their long fingerprints |
1779 | + keyid: B59D 5F15 97A5 04B7 E230 6DCA 0620 BBCF 0368 3F77 |
1780 | + |
1781 | + ignored4: |
1782 | + # 2.7 alternative keyservers |
1783 | + # |
1784 | + # One can also specify alternative keyservers to fetch keys from. |
1785 | + keyid: B59D 5F15 97A5 04B7 E230 6DCA 0620 BBCF 0368 3F77 |
1786 | + keyserver: pgp.mit.edu |
1787 | + |
1788 | + |
1789 | + my-repo4.list: |
1790 | + # 2.8 raw key |
1791 | + # |
1792 | + # The apt signing key can also be specified by providing a pgp public key |
1793 | + # block. Providing the PGP key this way is the most robust method for |
1794 | + # specifying a key, as it removes dependency on a remote key server. |
1795 | + # |
1796 | + # As with keyid's this can be specified with or without some actual source |
1797 | + # content. |
1798 | + key: | # The value needs to start with -----BEGIN PGP PUBLIC KEY BLOCK----- |
1799 | + -----BEGIN PGP PUBLIC KEY BLOCK----- |
1800 | + Version: SKS 1.0.10 |
1801 | + |
1802 | + mI0ESpA3UQEEALdZKVIMq0j6qWAXAyxSlF63SvPVIgxHPb9Nk0DZUixn+akqytxG4zKCONz6 |
1803 | + qLjoBBfHnynyVLfT4ihg9an1PqxRnTO+JKQxl8NgKGz6Pon569GtAOdWNKw15XKinJTDLjnj |
1804 | + 9y96ljJqRcpV9t/WsIcdJPcKFR5voHTEoABE2aEXABEBAAG0GUxhdW5jaHBhZCBQUEEgZm9y |
1805 | + IEFsZXN0aWOItgQTAQIAIAUCSpA3UQIbAwYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJEA7H |
1806 | + 5Qi+CcVxWZ8D/1MyYvfj3FJPZUm2Yo1zZsQ657vHI9+pPouqflWOayRR9jbiyUFIn0VdQBrP |
1807 | + t0FwvnOFArUovUWoKAEdqR8hPy3M3APUZjl5K4cMZR/xaMQeQRZ5CHpS4DBKURKAHC0ltS5o |
1808 | + uBJKQOZm5iltJp15cgyIkBkGe8Mx18VFyVglAZey |
1809 | + =Y2oI |
1810 | + -----END PGP PUBLIC KEY BLOCK----- |
1811 | |
1812 | === added file 'examples/tests/apt_config_command.yaml' |
1813 | --- examples/tests/apt_config_command.yaml 1970-01-01 00:00:00 +0000 |
1814 | +++ examples/tests/apt_config_command.yaml 2016-08-02 09:15:49 +0000 |
1815 | @@ -0,0 +1,85 @@ |
1816 | +# This pushes curtin through a automatic installation |
1817 | +# where no storage configuration is necessary. |
1818 | +# exercising the standalong curtin apt-config command |
1819 | +-placeholder_simple_install: unused |
1820 | +bucket: |
1821 | + - &run_with_stdin | |
1822 | + #!/bin/sh |
1823 | + input="$1" |
1824 | + shift |
1825 | + printf "%s\n" "$input" | "$@" |
1826 | + |
1827 | + - &run_apt_config_file | |
1828 | + #!/bin/sh |
1829 | + # take the first argument, write it to a tmp file and execute |
1830 | + # curtin apt --config=<tmpfile> "$@" |
1831 | + set -e |
1832 | + config="$1" |
1833 | + shift |
1834 | + TEMP_D=$(mktemp -d "${TMPDIR:-/tmp}/${0##*/}.XXXXXX") |
1835 | + trap cleanup EXIT |
1836 | + cleanup() { [ -z "${TEMP_D}" || rm -Rf "${TEMP_D}"; } |
1837 | + cfg_file="${TEMP_D}/curtin-apt.conf" |
1838 | + printf "%s\n" "$config" > "${cfg_file}" |
1839 | + curtin apt-config "--config=${cfg_file}" "$@" |
1840 | + |
1841 | + - &apt_config_ppa | |
1842 | + # this is just a large string |
1843 | + apt: |
1844 | + sources: |
1845 | + ignored: |
1846 | + source: "ppa:curtin-dev/test-archive" |
1847 | + curtin-test1.list: |
1848 | + source: "deb $MIRROR $RELEASE-proposed main" |
1849 | + |
1850 | + - &apt_config_source | |
1851 | + apt: |
1852 | + preserve_sources_list: false |
1853 | + sources: |
1854 | + ignored: |
1855 | + source: "ppa:curtin-dev/test-archive" |
1856 | + # apt-add-repositroy adds the key anyway, but lets pass that code |
1857 | + # path of adding once more in this scope |
1858 | + key: | |
1859 | + -----BEGIN PGP PUBLIC KEY BLOCK----- |
1860 | + Version: GnuPG v1 |
1861 | + |
1862 | + mQINBFazYtEBEADXrW53tDOvwcnHwchLapTKK89+wBWR2qQKXx5Mymtjkrb688Fs |
1863 | + ciXcCsvClnNGJ9bEhrJTucyb7WF0KcDVQcvOd0C4HOSEAc0DANBu1Mdp/tmCWuiW |
1864 | + 1TbbhomyHAcHNdbuSZeMDh5xi9M3DYPVq72PwYwjrE4lotVxHeX5nYEH304U+5nJ |
1865 | + tBNpVon91k3ItymQ6Jii+9gVoQ7ujiH1/Gw4/J/1/5zQ3C1mOjq68vLunz5iw1Kn |
1866 | + 7TMVyID6qwq2UFEgudpseLfFZcb/p7KgI0m3S/OViwzSc44m63ggTPMmbeHW51xA |
1867 | + 1rpUChSU+cm0cJ4tNtAcYHRYRltWAo/3J1OzB6Ut5P7vIC5r+QcCyyMbku9NjYaw |
1868 | + dWX4DDKqW3is3qJ/7EeOKPL4N8wuKwuWUC7s2wqsIZL8EmsvR+ZOnTJ3bHZFvsLg |
1869 | + p/OKqmhxMGYXiXOWDOEJ+vwboPxrvhD90JZl8weNGPnpla+EkxRDBSpEb31Vgt5X |
1870 | + AIoxE7XxwfuXS3MGMA7fSqkGPGHfSLYQFFk+CAIeTUV+ypKW94hIxXKgqRxa7dxz |
1871 | + Ymqs+wgIGaWJCnx7z1Kpd3HD9iTAYjyWyhlQ/Tjt43kwUBdALhTL0vYUTGQyTgKt |
1872 | + tAriVf5bqHb6Hj5PS5YZQ/+YoCUI2OTrAWWNyH9rIEZGsFc30oJFPHj3fQARAQAB |
1873 | + tCNMYXVuY2hwYWQgUFBBIGZvciBjdXJ0aW4gZGV2ZWxvcGVyc4kCOAQTAQIAIgUC |
1874 | + VrNi0QIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQVf58jAFlAT4TGg// |
1875 | + SV7vWmkJqr5TSlT9JqCBfmtFjgudxTGG8XM2zwnta+m/3YVOMo0ZjyGL4fUKjCmN |
1876 | + eh6eYihwpRtfdawziaEOydDxNfdjwscV4Qcy7FjHX+DQnNzQyzK+WgWRJwNWloCw |
1877 | + skg2tF+EDRajalTRjHJAn+5zAilXVn71T/hhOCxkF0PBiH9s/e7pW/KcgBEC1MYV |
1878 | + Fs0fLST8SYhsIxttVRWuRkJDrtEY1zeVhkvk+PN6UuCY6/gyRSQ1rhhBF3ePqiba |
1879 | + CmLiUjnJMEm1OJOkuD33IMNPKQi99TZhr8y3AGCcrmAQtJsYLvVDPcsOsjGQHXP4 |
1880 | + 2qQXK+jE/AAUycCQ6tgrAqCcUNQiClP8xUPkZOiDNvVMiPvIj/s79ShkoRaWLMb7 |
1881 | + n9jyDOhs3L7dtmKQwHWq9qJ56fzx1L0/jxSanzm+ZJ/Q7t6E/GFxY1RsAk7xtI1C |
1882 | + SzSmrGKmtlbWlOyqqQb6zhULIJpaXvh/GaYyo0xI3rA+QvPDt/fgUJEBiSidwabW |
1883 | + Q8JU9iI5HXQxbVq1gSdy/z31fue5JuZSqjnjCjgho/UrXa4i1RPtqsY3FoTk7Hmo |
1884 | + C1z2cJc8HQI8JnEX/4qJXvPMRM2JsMD9DqvgsUJG5M9Qchy8cymYY+xeiBVYzJI+ |
1885 | + WHCq6LHqnVxYZ+RM858lSsD6wetN44vguIjL3qJJ+wU= |
1886 | + curtin-test1.list: |
1887 | + source: "deb $MIRROR $RELEASE-proposed main" |
1888 | + |
1889 | +# into ephemeral environment |
1890 | +early_commands: |
1891 | + 00_add_archive: [sh, -c, *run_with_stdin, "curtin-apt", |
1892 | + *apt_config_ppa, curtin, apt-config, --config=-, --target=/] |
1893 | + # tests itself by installing a packet only available in that ppa |
1894 | + 00_install_package: [apt-get, install, --assume-yes, smello] |
1895 | + |
1896 | +# into target environment |
1897 | +late_commands: |
1898 | + 00_add_archive: [sh, -c, *run_apt_config_file, "curtin-apt-file", |
1899 | + *apt_config_source] |
1900 | + 00_install_package: [curtin, in-target, --, apt-get, install, --assume-yes, smello] |
1901 | |
1902 | === added file 'examples/tests/apt_source_custom.yaml' |
1903 | --- examples/tests/apt_source_custom.yaml 1970-01-01 00:00:00 +0000 |
1904 | +++ examples/tests/apt_source_custom.yaml 2016-08-02 09:15:49 +0000 |
1905 | @@ -0,0 +1,97 @@ |
1906 | +showtrace: true |
1907 | +apt: |
1908 | + preserve_sources_list: false |
1909 | + primary: |
1910 | + - arches: [default] |
1911 | + uri: http://us.archive.ubuntu.com/ubuntu |
1912 | + security: |
1913 | + - arches: [default] |
1914 | + uri: http://security.ubuntu.com/ubuntu |
1915 | + sources_list: | # written by curtin custom template |
1916 | + deb $MIRROR $RELEASE main restricted |
1917 | + deb-src $MIRROR $RELEASE main restricted |
1918 | + deb $PRIMARY $RELEASE universe restricted |
1919 | + deb $SECURITY $RELEASE-security multiverse |
1920 | + # nice line to check in test |
1921 | + conf: | # APT config |
1922 | + ACQUIRE { |
1923 | + Retries "3"; |
1924 | + }; |
1925 | + sources: |
1926 | + curtin-dev-ppa.list: |
1927 | + source: "deb http://ppa.launchpad.net/curtin-dev/test-archive/ubuntu xenial main" |
1928 | + keyid: F430BBA5 |
1929 | + ignored1: |
1930 | + source: "ppa:curtin-dev/test-archive" |
1931 | + my-repo2.list: |
1932 | + source: deb $MIRROR $RELEASE multiverse |
1933 | + ignored3: |
1934 | + keyid: 0E72 9061 0D2F 6DC4 D65E A921 9A31 4EC5 F470 A0AC |
1935 | + my-repo4.list: |
1936 | + source: deb http://ppa.launchpad.net/curtin-dev/test-archive/ubuntu xenial main |
1937 | + key: | |
1938 | + -----BEGIN PGP PUBLIC KEY BLOCK----- |
1939 | + Version: GnuPG v1 |
1940 | + |
1941 | + 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 |
2000 | + 7oh88hTkC1w= |
2001 | + =UNSw |
2002 | + -----END PGP PUBLIC KEY BLOCK----- |
2003 | |
2004 | === added file 'examples/tests/apt_source_modify.yaml' |
2005 | --- examples/tests/apt_source_modify.yaml 1970-01-01 00:00:00 +0000 |
2006 | +++ examples/tests/apt_source_modify.yaml 2016-08-02 09:15:49 +0000 |
2007 | @@ -0,0 +1,92 @@ |
2008 | +showtrace: true |
2009 | +apt: |
2010 | + preserve_sources_list: false |
2011 | + primary: |
2012 | + - arches: [default] |
2013 | + uri: http://us.archive.ubuntu.com/ubuntu |
2014 | + security: |
2015 | + - arches: [default] |
2016 | + uri: http://security.ubuntu.com/ubuntu |
2017 | + conf: | # APT config |
2018 | + ACQUIRE { |
2019 | + Retries "3"; |
2020 | + }; |
2021 | + sources: |
2022 | + curtin-dev-ppa.list: |
2023 | + source: "deb http://ppa.launchpad.net/curtin-dev/test-archive/ubuntu xenial main" |
2024 | + keyid: F430BBA5 |
2025 | + ignored1: |
2026 | + source: "ppa:curtin-dev/test-archive" |
2027 | + # intentionally dropped the .list here, has to be added by the code |
2028 | + my-repo2: |
2029 | + source: deb $MIRROR $RELEASE multiverse |
2030 | + ignored3: |
2031 | + keyid: 0E72 9061 0D2F 6DC4 D65E A921 9A31 4EC5 F470 A0AC |
2032 | + my-repo4.list: |
2033 | + source: deb http://ppa.launchpad.net/curtin-dev/test-archive/ubuntu xenial main |
2034 | + key: | |
2035 | + -----BEGIN PGP PUBLIC KEY BLOCK----- |
2036 | + Version: GnuPG v1 |
2037 | + |
2038 | + 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 |
2097 | + 7oh88hTkC1w= |
2098 | + =UNSw |
2099 | + -----END PGP PUBLIC KEY BLOCK----- |
2100 | |
2101 | === added file 'examples/tests/apt_source_modify_arches.yaml' |
2102 | --- examples/tests/apt_source_modify_arches.yaml 1970-01-01 00:00:00 +0000 |
2103 | +++ examples/tests/apt_source_modify_arches.yaml 2016-08-02 09:15:49 +0000 |
2104 | @@ -0,0 +1,102 @@ |
2105 | +showtrace: true |
2106 | +apt: |
2107 | + preserve_sources_list: false |
2108 | + primary: |
2109 | + # we don't know on which arch this will run, so we can't put the "right" |
2110 | + # config in an arch, but we can provide various confusing alternatives |
2111 | + # and orders and it has to pick default out of them |
2112 | + - arches: [x86_2048, x86_4096, x86_8192, amd18.5, "foobar"] |
2113 | + uri: http://notthis.com/ubuntu |
2114 | + - arches: ["*"] |
2115 | + uri: http://notthis.com/ubuntu |
2116 | + - arches: [default] |
2117 | + uri: http://us.archive.ubuntu.com/ubuntu |
2118 | + - arches: [] |
2119 | + uri: http://notthis.com/ubuntu |
2120 | + security: |
2121 | + - arches: [default] |
2122 | + uri: http://security.ubuntu.com/ubuntu |
2123 | + - arches: ["supersecurearchthatdoesnexist"] |
2124 | + uri: http://notthat.com/ubuntu |
2125 | + conf: | # APT config |
2126 | + ACQUIRE { |
2127 | + Retries "3"; |
2128 | + }; |
2129 | + sources: |
2130 | + curtin-dev-ppa.list: |
2131 | + source: "deb http://ppa.launchpad.net/curtin-dev/test-archive/ubuntu xenial main" |
2132 | + keyid: F430BBA5 |
2133 | + ignored1: |
2134 | + source: "ppa:curtin-dev/test-archive" |
2135 | + my-repo2.list: |
2136 | + source: deb $MIRROR $RELEASE multiverse |
2137 | + ignored3: |
2138 | + keyid: 0E72 9061 0D2F 6DC4 D65E A921 9A31 4EC5 F470 A0AC |
2139 | + my-repo4.list: |
2140 | + source: deb http://ppa.launchpad.net/curtin-dev/test-archive/ubuntu xenial main |
2141 | + key: | |
2142 | + -----BEGIN PGP PUBLIC KEY BLOCK----- |
2143 | + Version: GnuPG v1 |
2144 | + |
2145 | + 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 |
2204 | + 7oh88hTkC1w= |
2205 | + =UNSw |
2206 | + -----END PGP PUBLIC KEY BLOCK----- |
2207 | |
2208 | === added file 'examples/tests/apt_source_modify_disable_suite.yaml' |
2209 | --- examples/tests/apt_source_modify_disable_suite.yaml 1970-01-01 00:00:00 +0000 |
2210 | +++ examples/tests/apt_source_modify_disable_suite.yaml 2016-08-02 09:15:49 +0000 |
2211 | @@ -0,0 +1,92 @@ |
2212 | +showtrace: true |
2213 | +apt: |
2214 | + preserve_sources_list: false |
2215 | + primary: |
2216 | + - arches: [default] |
2217 | + uri: http://us.archive.ubuntu.com/ubuntu |
2218 | + security: |
2219 | + - arches: [default] |
2220 | + uri: http://security.ubuntu.com/ubuntu |
2221 | + disable_suites: [$RELEASE-updates] |
2222 | + conf: | # APT config |
2223 | + ACQUIRE { |
2224 | + Retries "3"; |
2225 | + }; |
2226 | + sources: |
2227 | + curtin-dev-ppa.list: |
2228 | + source: "deb http://ppa.launchpad.net/curtin-dev/test-archive/ubuntu xenial main" |
2229 | + keyid: F430BBA5 |
2230 | + ignored1: |
2231 | + source: "ppa:curtin-dev/test-archive" |
2232 | + my-repo2.list: |
2233 | + source: deb $MIRROR $RELEASE multiverse |
2234 | + ignored3: |
2235 | + keyid: 0E72 9061 0D2F 6DC4 D65E A921 9A31 4EC5 F470 A0AC |
2236 | + my-repo4.list: |
2237 | + source: deb http://ppa.launchpad.net/curtin-dev/test-archive/ubuntu xenial main |
2238 | + key: | |
2239 | + -----BEGIN PGP PUBLIC KEY BLOCK----- |
2240 | + Version: GnuPG v1 |
2241 | + |
2242 | + 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 |
2301 | + 7oh88hTkC1w= |
2302 | + =UNSw |
2303 | + -----END PGP PUBLIC KEY BLOCK----- |
2304 | |
2305 | === added file 'examples/tests/apt_source_preserve.yaml' |
2306 | --- examples/tests/apt_source_preserve.yaml 1970-01-01 00:00:00 +0000 |
2307 | +++ examples/tests/apt_source_preserve.yaml 2016-08-02 09:15:49 +0000 |
2308 | @@ -0,0 +1,98 @@ |
2309 | +showtrace: true |
2310 | +apt: |
2311 | + # this is like the other apt_source test but with preserve true |
2312 | + # this is the default now preserve_sources_list: true |
2313 | + primary: |
2314 | + - arches: [default] |
2315 | + uri: http://us.archive.ubuntu.com/ubuntu |
2316 | + security: |
2317 | + - arches: [default] |
2318 | + uri: http://security.ubuntu.com/ubuntu |
2319 | + sources_list: | # written by curtin custom template |
2320 | + deb $MIRROR $RELEASE main restricted |
2321 | + deb-src $MIRROR $RELEASE main restricted |
2322 | + deb $PRIMARY $RELEASE universe restricted |
2323 | + deb $SECURITY $RELEASE-security multiverse |
2324 | + # nice line to check in test |
2325 | + conf: | # APT config |
2326 | + ACQUIRE { |
2327 | + Retries "3"; |
2328 | + }; |
2329 | + sources: |
2330 | + curtin-dev-ppa.list: |
2331 | + source: "deb http://ppa.launchpad.net/curtin-dev/test-archive/ubuntu xenial main" |
2332 | + keyid: F430BBA5 |
2333 | + ignored1: |
2334 | + source: "ppa:curtin-dev/test-archive" |
2335 | + my-repo2.list: |
2336 | + source: deb $MIRROR $RELEASE multiverse |
2337 | + ignored3: |
2338 | + keyid: 0E72 9061 0D2F 6DC4 D65E A921 9A31 4EC5 F470 A0AC |
2339 | + my-repo4.list: |
2340 | + source: deb http://ppa.launchpad.net/curtin-dev/test-archive/ubuntu xenial main |
2341 | + key: | |
2342 | + -----BEGIN PGP PUBLIC KEY BLOCK----- |
2343 | + Version: GnuPG v1 |
2344 | + |
2345 | + mQINBFXJ3NcBEAC85PMdaKdItkdjCT1vRJrdwNqj4lN5mu6z4dDVfeZlmozRDBGb |
2346 | + ENSOWCiYz3meANO7bKthQQCqAETSBV72rrDCqFZUpXeyG3zCN98Z/UdJ8zpQD9uw |
2347 | + mq2CaAqWMk6ty+PkHQ4gtIc390lGfRbHNoZ5HaWJNVOK7FCB2hBmnTZW7AViYiYa |
2348 | + YswOjYxaCkwQ/DsMOPD7S5OjwbLucs2YGjkBm7YF1nnXNzyt+BwieKQW/sQ2+ga1 |
2349 | + mkgLW1BTQN3+JreBpeHy/yrRdK4dOZZUar4WPZitZzOW2eNpaaf6hKNA14LB/96a |
2350 | + tEguK8VazoqSQGvNV/R3PjIYmurVP3/Z9bEVgOKhMCflgwKCYgx+tBUypN3zFWv9 |
2351 | + pgVq3iHx1MFCvoP9FsNB7I6jzOxlQP4z25BzR3ympx/QexkFw5CBFXhdrU+qNVBl |
2352 | + SSnz69aLEjCRXqBOnQEr0irs/e/35+yLJdEuw89vSwWwrzbV5r1Y7uxinEGWSydT |
2353 | + qddj97uKOWeMmnp20Be4+nhDDW/BMiTFI4Y3bYeDTrftbWMaSEmtSTw5HHxtAFtg |
2354 | + X9Hyx0Q3eN1w3gRZgIdm0xYTe7bNTofFRdfXzB/9wtNIcaW10+IlODShFHPCnh+d |
2355 | + i56a8LCdZcXiiLfCIhEcnqmM37BVvhjIQKSyOU1eMEgX148aVEz36OVuMwARAQAB |
2356 | + tCdDaHJpc3RpYW4gRWhyaGFyZHQgPGNwYWVsemVyQGdtYWlsLmNvbT6JAjgEEwEC |
2357 | + ACIFAlXJ3NcCGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJELo+KTOCgLJC |
2358 | + BugP/ir0ES3wCzvHMnkz2UlXt9FR4KqY0L9uFmwu9VYpmfAploEVIOi2HcuxpcRp |
2359 | + hgoQlUtkz3lRhUeZzCxuB1ljM2JKTJiezP1tFTTGCbVYhPyA0LmUiHDWylG7FzPb |
2360 | + TX96HY/G0jf+m4CfR8q3HNHjeDi4VeA2ppBxdHcVE5I7HihDgRPJd+CvCa3nYdAb |
2361 | + nXDKlQZz5aZc7AgrRVamr4mshkzWuwNNCwOt3AIgHDkU/HzA5xlXfwHxOoP6scWH |
2362 | + /T7vFsd/vOikBphGseWPgKm6w1zyQ5Dk/wjRL8UeSJZW+Rh4PuBMbxg01lAZpPTq |
2363 | + tu/bePeNty3g5bhwO6oHMpWhprn3dO37R680qo6UnBPzICeuBUnSYgpPnsQC9maz |
2364 | + FEjiBtMsXSanU5vww7TpxY1JHjk5KFcmKx4sBeablznsm+GuVaDFN8R4eDjrM14r |
2365 | + SOzA9cV0bSQr4dMqA9fZFSx6qLTacIeMfptybW3zaDX/pJOeBBWRAtoAfZIFbBnu |
2366 | + /ZxDDgiQtZzpVK4UkYk5rjjtV/CPVXx64AnTHi35YfUn14KkE+k3odHdvPfBiv9+ |
2367 | + NxfkTuV/koOgpD3+lTIYXyVHS9gwvhfRD/YfdrnVGl7bRZe68j7bfWDuQuSqIhSA |
2368 | + jpeJslJCawnqv6fVB6buj6jjcgHIxqCVn99chaPFSblEIPfXtDVDaHJpc3RpYW4g |
2369 | + RWhyaGFyZHQgPGNocmlzdGlhbi5laHJoYXJkdEBjYW5vbmljYWwuY29tPokCOAQT |
2370 | + AQIAIgUCVsbUOgIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQuj4pM4KA |
2371 | + skJNPg/7BF/iXHHdSBigWKXCCvQz58uInoc/R4beIegxRCMq7wkYEey4B7Fd35zY |
2372 | + zv9CBOTV3hZePMCg9jxl4ki2kSsrZSCIEJw4L/aXDtJtx3HT18uTW0QKoU3nK/ro |
2373 | + OtthVqBqmiSEi40UUU+5MGrUjwLSm+PjaaSapjK/lddf0KbXBB78/BtR/XT0gxWM |
2374 | + +o68Oei9Nj1S3h6UndJwNAQ1xaDWmU2T7CRJet3F+cXZd3aDuS2axOTSTZbraSq7 |
2375 | + zdl1xUiKtzXZIp8X1ewne+dzkewZuWj7DOwOBEFK26UhxCjKd5mUr7jpWQ4ampFX |
2376 | + 6xfd/MK8SJFY+iHOBKyzq9po40tE23dqWuaHB+T3MxOgQ9JHCo9x22XNvEuKZW/V |
2377 | + 4WaoGHVkR+jtWNC8Qv/xCMHL3CEvAklKJR68WDhozwUYTgNt5vCoJOviMlbhDSwf |
2378 | + 0zVXpQwMR//4c0QSA0+BPpIEPDnx5vTIHBVXHy4bBBHU2Vi87QIDS0AtiBpNcspN |
2379 | + 6AG0ktuldkE/pqfSTJ2A9HpHZyU+8boagRS5/z102Pjtmf/mzUkcHmfRb9o0DE15 |
2380 | + X5fqpA3lYyx9eHIAgH4eaB1+G20Ez/EY5hr8IMS2nNBSem491UW6DXDYRu6eBLrR |
2381 | + sRmtrJ6DlTZFRFlqVZ47bce/SbeM/xljvRkBxWG6RtDRsTyNVI65Ag0EVcnc1wEQ |
2382 | + ANzk9W058tSHqf05UEtJGrN0K8DLriCvPd7QdFA8yVIZM3WD+m0AMBGXjd8BT5c2 |
2383 | + lt0GmhB8klonHZvPiVLTRTLcSsc3NBopr1HL1bWsgOczwWiXSrc62oGAHUOQT/bv |
2384 | + vS6KIkZgez+qtCo/DCOGJrADaoJBiBCLSsZgowpzazZZDPUF7rAsfcryVCFvftK0 |
2385 | + wAe1OdvUG77NHrMrE1oX3zh82hTqR5azBre6Y81lNwxxug/Xl/RHjNhEOYohcsLS |
2386 | + /xl0m2X831fHzcGGpoISRgrfel+M4RoC7KsLrwVhrF8koCD/ZQlevfLpuRl5LNpO |
2387 | + s1ZtEi8ZvLliih+H+BOlBD0zUc3zZrrks/NCpm1eZba0Z6L48r4TIHW08SGlHx7o |
2388 | + SrXgkq3mtoM8C4uDiLwjav5KxiF7n68s/9LF82aAr7YjNXd+xYZNjsmmFlYj9CGI |
2389 | + lL4jVt4v4EtTONa6pbtCNv5ezOLDZ6BBcQ36xdkrWzdpjQjL2mnh3sqIAGIPu7tH |
2390 | + N8euQ5L1zIvIjVqYlR1eJssp96QYPWYxF7TosfML4BUhCP631IWfuD9X/K2LzDmv |
2391 | + B2gVZo9fbhSC+P7GYVG+tV4VLAMbspAxRXXL69+j98aeV5g59f8OFQPbGpKE/SAY |
2392 | + eIXtq8DD+PYUXXq3VUI2brVLv42LBVdSJpKNKG3decIBABEBAAGJAh8EGAECAAkF |
2393 | + AlXJ3NcCGwwACgkQuj4pM4KAskKzeg/9FxXJLV3eWwY4nn2VhwYTHnHtSUpi8usk |
2394 | + RzIa3Mcj6OEVjU2LZaT3UQF8h6dLM9y+CemcwyjMqm1RQ5+ogfrItby1AaBXwCvm |
2395 | + XCUGw2zFOAnyzSHHoDFj27sllFxDmfSiBY5KP8M+/ywHKZDkRb6EjzMPx5oKFeGW |
2396 | + Hmqaj5FDmTeWChSIHd1ZxobashFauOZDbS/ijRRMsVGFulU2Nb/4QJK73g3orfhY |
2397 | + 5mq1TMkQ5Kcbqh4OmYYYayLtJQcpa6ZVopaRhAJFe30P83zW9pM5LQDpP9JIyY+S |
2398 | + DjasEY4ekYtw6oCKAjpqlwaaNDjl27OkJ7R7laFKy4grZ2TSB/2KTjn/Ea3CH/pA |
2399 | + SrpVis1LvC90XytbBnsEKYXU55H943wmBc6oj+itQhx4WyIiv+UgtHI/DbnYbUru |
2400 | + 71wpfapqGBXYfu/zAra8PITngOFuizeYu+idemu55ANO3keJPKr3ZBUSBBpNFauT |
2401 | + VUUCSnrLt+kpSLopYESiNdsPW/aQTFgFvA4BkBJTIMQsQZXicuXUePYlg5xFzXOv |
2402 | + XgiqkjRA9xBI5JAIUgLRk3ulVFt2bIsTG9XgtGyphEs86Q0MOIMo0WbZGtAYDrZO |
2403 | + DITbm2KzVLGVLn/ZJiW11RSHPNiwgg66/puKdFWrSogYYDJdDEUJtLIhypZ+ORxe |
2404 | + 7oh88hTkC1w= |
2405 | + =UNSw |
2406 | + -----END PGP PUBLIC KEY BLOCK----- |
2407 | |
2408 | === added file 'examples/tests/apt_source_search.yaml' |
2409 | --- examples/tests/apt_source_search.yaml 1970-01-01 00:00:00 +0000 |
2410 | +++ examples/tests/apt_source_search.yaml 2016-08-02 09:15:49 +0000 |
2411 | @@ -0,0 +1,97 @@ |
2412 | +showtrace: true |
2413 | +apt: |
2414 | + preserve_sources_list: false |
2415 | + primary: |
2416 | + - arches: [default] |
2417 | + search: |
2418 | + - http://does.not.exist/ubuntu |
2419 | + - http://does.also.not.exist/ubuntu |
2420 | + - http://us.archive.ubuntu.com/ubuntu |
2421 | + security: |
2422 | + - arches: [default] |
2423 | + search: |
2424 | + - http://does.not.exist/ubuntu |
2425 | + - http://does.also.not.exist/ubuntu |
2426 | + - http://security.ubuntu.com/ubuntu |
2427 | + conf: | # APT config |
2428 | + ACQUIRE { |
2429 | + Retries "3"; |
2430 | + }; |
2431 | + sources: |
2432 | + curtin-dev-ppa.list: |
2433 | + source: "deb http://ppa.launchpad.net/curtin-dev/test-archive/ubuntu xenial main" |
2434 | + keyid: F430BBA5 |
2435 | + ignored1: |
2436 | + source: "ppa:curtin-dev/test-archive" |
2437 | + my-repo2.list: |
2438 | + source: deb $MIRROR $RELEASE multiverse |
2439 | + ignored3: |
2440 | + keyid: 0E72 9061 0D2F 6DC4 D65E A921 9A31 4EC5 F470 A0AC |
2441 | + my-repo4.list: |
2442 | + source: deb http://ppa.launchpad.net/curtin-dev/test-archive/ubuntu xenial main |
2443 | + key: | |
2444 | + -----BEGIN PGP PUBLIC KEY BLOCK----- |
2445 | + Version: GnuPG v1 |
2446 | + |
2447 | + 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 |
2506 | + 7oh88hTkC1w= |
2507 | + =UNSw |
2508 | + -----END PGP PUBLIC KEY BLOCK----- |
2509 | |
2510 | === added file 'examples/tests/test_old_apt_features.yaml' |
2511 | --- examples/tests/test_old_apt_features.yaml 1970-01-01 00:00:00 +0000 |
2512 | +++ examples/tests/test_old_apt_features.yaml 2016-08-02 09:15:49 +0000 |
2513 | @@ -0,0 +1,10 @@ |
2514 | +showtrace: true |
2515 | +# apt_proxy gets configured by tools/launch and tests/vmtests/__init__.py |
2516 | +apt_mirrors: |
2517 | +# we need a mirror that works (even in CI) but isn't the default |
2518 | + ubuntu_archive: http://us.archive.ubuntu.com/ubuntu |
2519 | + ubuntu_security: http://archive.ubuntu.com/ubuntu |
2520 | +# set some key that surely is available to a non-default value |
2521 | +debconf_selections: |
2522 | + set1: | |
2523 | + debconf debconf/priority select low |
2524 | |
2525 | === added file 'examples/tests/test_old_apt_features_ports.yaml' |
2526 | --- examples/tests/test_old_apt_features_ports.yaml 1970-01-01 00:00:00 +0000 |
2527 | +++ examples/tests/test_old_apt_features_ports.yaml 2016-08-02 09:15:49 +0000 |
2528 | @@ -0,0 +1,10 @@ |
2529 | +showtrace: true |
2530 | +# apt_proxy gets configured by tools/launch and tests/vmtests/__init__.py |
2531 | +apt_mirrors: |
2532 | +# For ports there is no non-default alternative we could use |
2533 | + ubuntu_archive: http://ports.ubuntu.com/ubuntu-ports |
2534 | + ubuntu_security: http://ports.ubuntu.com/ubuntu-ports |
2535 | +# set some key that surely is available to a non-default value |
2536 | +debconf_selections: |
2537 | + set1: | |
2538 | + debconf debconf/priority select low |
2539 | |
2540 | === added file 'tests/unittests/test_apt_custom_sources_list.py' |
2541 | --- tests/unittests/test_apt_custom_sources_list.py 1970-01-01 00:00:00 +0000 |
2542 | +++ tests/unittests/test_apt_custom_sources_list.py 2016-08-02 09:15:49 +0000 |
2543 | @@ -0,0 +1,170 @@ |
2544 | +""" test_apt_custom_sources_list |
2545 | +Test templating of custom sources list |
2546 | +""" |
2547 | +import logging |
2548 | +import os |
2549 | +import shutil |
2550 | +import tempfile |
2551 | + |
2552 | +from unittest import TestCase |
2553 | + |
2554 | +import yaml |
2555 | +import mock |
2556 | +from mock import call |
2557 | + |
2558 | +from curtin import util |
2559 | +from curtin.commands import apt_config |
2560 | + |
2561 | +LOG = logging.getLogger(__name__) |
2562 | + |
2563 | +TARGET = "/" |
2564 | + |
2565 | +# Input and expected output for the custom template |
2566 | +YAML_TEXT_CUSTOM_SL = """ |
2567 | +preserve_sources_list: false |
2568 | +primary: |
2569 | + - arches: [default] |
2570 | + uri: http://test.ubuntu.com/ubuntu/ |
2571 | +security: |
2572 | + - arches: [default] |
2573 | + uri: http://testsec.ubuntu.com/ubuntu/ |
2574 | +sources_list: | |
2575 | + |
2576 | + ## Note, this file is written by curtin at install time. It should not end |
2577 | + ## up on the installed system itself. |
2578 | + # |
2579 | + # See http://help.ubuntu.com/community/UpgradeNotes for how to upgrade to |
2580 | + # newer versions of the distribution. |
2581 | + deb $MIRROR $RELEASE main restricted |
2582 | + deb-src $MIRROR $RELEASE main restricted |
2583 | + deb $PRIMARY $RELEASE universe restricted |
2584 | + deb $SECURITY $RELEASE-security multiverse |
2585 | + # FIND_SOMETHING_SPECIAL |
2586 | +""" |
2587 | + |
2588 | +EXPECTED_CONVERTED_CONTENT = """ |
2589 | +## Note, this file is written by curtin at install time. It should not end |
2590 | +## up on the installed system itself. |
2591 | +# |
2592 | +# See http://help.ubuntu.com/community/UpgradeNotes for how to upgrade to |
2593 | +# newer versions of the distribution. |
2594 | +deb http://test.ubuntu.com/ubuntu/ fakerel main restricted |
2595 | +deb-src http://test.ubuntu.com/ubuntu/ fakerel main restricted |
2596 | +deb http://test.ubuntu.com/ubuntu/ fakerel universe restricted |
2597 | +deb http://testsec.ubuntu.com/ubuntu/ fakerel-security multiverse |
2598 | +# FIND_SOMETHING_SPECIAL |
2599 | +""" |
2600 | + |
2601 | +# mocked to be independent to the unittest system |
2602 | +MOCKED_APT_SRC_LIST = """ |
2603 | +deb http://test.ubuntu.com/ubuntu/ notouched main restricted |
2604 | +deb-src http://test.ubuntu.com/ubuntu/ notouched main restricted |
2605 | +deb http://test.ubuntu.com/ubuntu/ notouched-updates main restricted |
2606 | +deb http://testsec.ubuntu.com/ubuntu/ notouched-security main restricted |
2607 | +""" |
2608 | + |
2609 | +EXPECTED_BASE_CONTENT = (""" |
2610 | +deb http://test.ubuntu.com/ubuntu/ notouched main restricted |
2611 | +deb-src http://test.ubuntu.com/ubuntu/ notouched main restricted |
2612 | +deb http://test.ubuntu.com/ubuntu/ notouched-updates main restricted |
2613 | +deb http://testsec.ubuntu.com/ubuntu/ notouched-security main restricted |
2614 | +""") |
2615 | + |
2616 | +EXPECTED_MIRROR_CONTENT = (""" |
2617 | +deb http://test.ubuntu.com/ubuntu/ notouched main restricted |
2618 | +deb-src http://test.ubuntu.com/ubuntu/ notouched main restricted |
2619 | +deb http://test.ubuntu.com/ubuntu/ notouched-updates main restricted |
2620 | +deb http://test.ubuntu.com/ubuntu/ notouched-security main restricted |
2621 | +""") |
2622 | + |
2623 | +EXPECTED_PRIMSEC_CONTENT = (""" |
2624 | +deb http://test.ubuntu.com/ubuntu/ notouched main restricted |
2625 | +deb-src http://test.ubuntu.com/ubuntu/ notouched main restricted |
2626 | +deb http://test.ubuntu.com/ubuntu/ notouched-updates main restricted |
2627 | +deb http://testsec.ubuntu.com/ubuntu/ notouched-security main restricted |
2628 | +""") |
2629 | + |
2630 | + |
2631 | +class TestAptSourceConfigSourceList(TestCase): |
2632 | + """TestAptSourceConfigSourceList - Class to test sources list rendering""" |
2633 | + def setUp(self): |
2634 | + super(TestAptSourceConfigSourceList, self).setUp() |
2635 | + self.new_root = tempfile.mkdtemp() |
2636 | + self.addCleanup(shutil.rmtree, self.new_root) |
2637 | + # self.patchUtils(self.new_root) |
2638 | + |
2639 | + @staticmethod |
2640 | + def _apt_source_list(cfg, expected): |
2641 | + "_apt_source_list - Test rendering from template (generic)" |
2642 | + |
2643 | + arch = util.get_architecture() |
2644 | + # would fail inside the unittest context |
2645 | + with mock.patch.object(util, 'get_architecture', |
2646 | + return_value=arch) as mockga: |
2647 | + with mock.patch.object(util, 'write_file') as mockwrite: |
2648 | + # keep it side effect free and avoid permission errors |
2649 | + with mock.patch.object(os, 'rename'): |
2650 | + # make test independent to executing system |
2651 | + with mock.patch.object(util, 'load_file', |
2652 | + return_value=MOCKED_APT_SRC_LIST): |
2653 | + with mock.patch.object(util, 'lsb_release', |
2654 | + return_value={'codename': |
2655 | + 'fakerel'}): |
2656 | + apt_config.handle_apt(cfg, TARGET) |
2657 | + |
2658 | + mockga.assert_called_with("/") |
2659 | + |
2660 | + cloudfile = '/etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg' |
2661 | + cloudconf = yaml.dump({'apt_preserve_sources_list': True}, indent=1) |
2662 | + calls = [call(util.target_path(TARGET, '/etc/apt/sources.list'), |
2663 | + expected, |
2664 | + mode=0o644), |
2665 | + call(util.target_path(TARGET, cloudfile), |
2666 | + cloudconf, |
2667 | + mode=0o644)] |
2668 | + mockwrite.assert_has_calls(calls) |
2669 | + |
2670 | + def test_apt_source_list(self): |
2671 | + """test_apt_source_list - Test with neither custom sources nor parms""" |
2672 | + cfg = {'preserve_sources_list': False} |
2673 | + |
2674 | + self._apt_source_list(cfg, EXPECTED_BASE_CONTENT) |
2675 | + |
2676 | + def test_apt_source_list_psm(self): |
2677 | + """test_apt_source_list_psm - Test specifying prim+sec mirrors""" |
2678 | + cfg = {'preserve_sources_list': False, |
2679 | + 'primary': [{'arches': ["default"], |
2680 | + 'uri': 'http://test.ubuntu.com/ubuntu/'}], |
2681 | + 'security': [{'arches': ["default"], |
2682 | + 'uri': 'http://testsec.ubuntu.com/ubuntu/'}]} |
2683 | + |
2684 | + self._apt_source_list(cfg, EXPECTED_PRIMSEC_CONTENT) |
2685 | + |
2686 | + @staticmethod |
2687 | + def test_apt_srcl_custom(): |
2688 | + """test_apt_srcl_custom - Test rendering a custom source template""" |
2689 | + cfg = yaml.safe_load(YAML_TEXT_CUSTOM_SL) |
2690 | + |
2691 | + arch = util.get_architecture() |
2692 | + # would fail inside the unittest context |
2693 | + with mock.patch.object(util, 'get_architecture', |
2694 | + return_value=arch) as mockga: |
2695 | + with mock.patch.object(util, 'write_file') as mockwrite: |
2696 | + # keep it side effect free and avoid permission errors |
2697 | + with mock.patch.object(os, 'rename'): |
2698 | + with mock.patch.object(util, 'lsb_release', |
2699 | + return_value={'codename': |
2700 | + 'fakerel'}): |
2701 | + apt_config.handle_apt(cfg, TARGET) |
2702 | + |
2703 | + mockga.assert_called_with("/") |
2704 | + cloudfile = '/etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg' |
2705 | + cloudconf = yaml.dump({'apt_preserve_sources_list': True}, indent=1) |
2706 | + calls = [call(util.target_path(TARGET, '/etc/apt/sources.list'), |
2707 | + EXPECTED_CONVERTED_CONTENT, mode=0o644), |
2708 | + call(util.target_path(TARGET, cloudfile), cloudconf, |
2709 | + mode=0o644)] |
2710 | + mockwrite.assert_has_calls(calls) |
2711 | + |
2712 | + |
2713 | +# vi: ts=4 expandtab |
2714 | |
2715 | === added file 'tests/unittests/test_apt_source.py' |
2716 | --- tests/unittests/test_apt_source.py 1970-01-01 00:00:00 +0000 |
2717 | +++ tests/unittests/test_apt_source.py 2016-08-02 09:15:49 +0000 |
2718 | @@ -0,0 +1,929 @@ |
2719 | +""" test_apt_source |
2720 | +Testing various config variations of the apt_source custom config |
2721 | +""" |
2722 | +import glob |
2723 | +import os |
2724 | +import re |
2725 | +import shutil |
2726 | +import socket |
2727 | +import tempfile |
2728 | + |
2729 | +from unittest import TestCase |
2730 | + |
2731 | +import mock |
2732 | +from mock import call |
2733 | + |
2734 | +from curtin import util |
2735 | +from curtin import gpg |
2736 | +from curtin.commands import apt_config |
2737 | + |
2738 | + |
2739 | +EXPECTEDKEY = u"""-----BEGIN PGP PUBLIC KEY BLOCK----- |
2740 | +Version: GnuPG v1 |
2741 | + |
2742 | +mI0ESuZLUgEEAKkqq3idtFP7g9hzOu1a8+v8ImawQN4TrvlygfScMU1TIS1eC7UQ |
2743 | +NUA8Qqgr9iUaGnejb0VciqftLrU9D6WYHSKz+EITefgdyJ6SoQxjoJdsCpJ7o9Jy |
2744 | +8PQnpRttiFm4qHu6BVnKnBNxw/z3ST9YMqW5kbMQpfxbGe+obRox59NpABEBAAG0 |
2745 | +HUxhdW5jaHBhZCBQUEEgZm9yIFNjb3R0IE1vc2VyiLYEEwECACAFAkrmS1ICGwMG |
2746 | +CwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRAGILvPA2g/d3aEA/9tVjc10HOZwV29 |
2747 | +OatVuTeERjjrIbxflO586GLA8cp0C9RQCwgod/R+cKYdQcHjbqVcP0HqxveLg0RZ |
2748 | +FJpWLmWKamwkABErwQLGlM/Hwhjfade8VvEQutH5/0JgKHmzRsoqfR+LMO6OS+Sm |
2749 | +S0ORP6HXET3+jC8BMG4tBWCTK/XEZw== |
2750 | +=ACB2 |
2751 | +-----END PGP PUBLIC KEY BLOCK-----""" |
2752 | + |
2753 | +ADD_APT_REPO_MATCH = r"^[\w-]+:\w" |
2754 | + |
2755 | +TARGET = "/" |
2756 | + |
2757 | + |
2758 | +def load_tfile(filename): |
2759 | + """ load_tfile |
2760 | + load file and return content after decoding |
2761 | + """ |
2762 | + try: |
2763 | + content = util.load_file(filename, mode="r") |
2764 | + except Exception as error: |
2765 | + print('failed to load file content for test: %s' % error) |
2766 | + raise |
2767 | + |
2768 | + return content |
2769 | + |
2770 | + |
2771 | +class PseudoChrootableTarget(util.ChrootableTarget): |
2772 | + # no-ops the mounting and modifying that ChrootableTarget does |
2773 | + def __enter__(self): |
2774 | + return self |
2775 | + |
2776 | + def __exit__(self, exc_type, exc_value, traceback): |
2777 | + return |
2778 | + |
2779 | +ChrootableTargetStr = "curtin.commands.apt_config.util.ChrootableTarget" |
2780 | + |
2781 | + |
2782 | +class TestAptSourceConfig(TestCase): |
2783 | + """ TestAptSourceConfig |
2784 | + Main Class to test apt configs |
2785 | + """ |
2786 | + def setUp(self): |
2787 | + super(TestAptSourceConfig, self).setUp() |
2788 | + self.tmp = tempfile.mkdtemp() |
2789 | + self.addCleanup(shutil.rmtree, self.tmp) |
2790 | + self.aptlistfile = os.path.join(self.tmp, "single-deb.list") |
2791 | + self.aptlistfile2 = os.path.join(self.tmp, "single-deb2.list") |
2792 | + self.aptlistfile3 = os.path.join(self.tmp, "single-deb3.list") |
2793 | + self.join = os.path.join |
2794 | + self.matcher = re.compile(ADD_APT_REPO_MATCH).search |
2795 | + |
2796 | + @staticmethod |
2797 | + def _add_apt_sources(*args, **kwargs): |
2798 | + with mock.patch.object(util, 'apt_update'): |
2799 | + apt_config.add_apt_sources(*args, **kwargs) |
2800 | + |
2801 | + @staticmethod |
2802 | + def _get_default_params(): |
2803 | + """ get_default_params |
2804 | + Get the most basic default mrror and release info to be used in tests |
2805 | + """ |
2806 | + params = {} |
2807 | + params['RELEASE'] = util.lsb_release()['codename'] |
2808 | + arch = util.get_architecture() |
2809 | + params['MIRROR'] = apt_config.get_default_mirrors(arch)["PRIMARY"] |
2810 | + return params |
2811 | + |
2812 | + def _myjoin(self, *args, **kwargs): |
2813 | + """ _myjoin - redir into writable tmpdir""" |
2814 | + if (args[0] == "/etc/apt/sources.list.d/" and |
2815 | + args[1] == "cloud_config_sources.list" and |
2816 | + len(args) == 2): |
2817 | + return self.join(self.tmp, args[0].lstrip("/"), args[1]) |
2818 | + else: |
2819 | + return self.join(*args, **kwargs) |
2820 | + |
2821 | + def _apt_src_basic(self, filename, cfg): |
2822 | + """ _apt_src_basic |
2823 | + Test Fix deb source string, has to overwrite mirror conf in params |
2824 | + """ |
2825 | + params = self._get_default_params() |
2826 | + |
2827 | + self._add_apt_sources(cfg, TARGET, template_params=params, |
2828 | + aa_repo_match=self.matcher) |
2829 | + |
2830 | + self.assertTrue(os.path.isfile(filename)) |
2831 | + |
2832 | + contents = load_tfile(filename) |
2833 | + self.assertTrue(re.search(r"%s %s %s %s\n" % |
2834 | + ("deb", "http://test.ubuntu.com/ubuntu", |
2835 | + "karmic-backports", |
2836 | + "main universe multiverse restricted"), |
2837 | + contents, flags=re.IGNORECASE)) |
2838 | + |
2839 | + def test_apt_src_basic(self): |
2840 | + """test_apt_src_basic - Test fix deb source string""" |
2841 | + cfg = {self.aptlistfile: {'source': |
2842 | + ('deb http://test.ubuntu.com/ubuntu' |
2843 | + ' karmic-backports' |
2844 | + ' main universe multiverse restricted')}} |
2845 | + self._apt_src_basic(self.aptlistfile, cfg) |
2846 | + |
2847 | + def test_apt_src_basic_tri(self): |
2848 | + """test_apt_src_basic_tri - Test multiple fix deb source strings""" |
2849 | + cfg = {self.aptlistfile: {'source': |
2850 | + ('deb http://test.ubuntu.com/ubuntu' |
2851 | + ' karmic-backports' |
2852 | + ' main universe multiverse restricted')}, |
2853 | + self.aptlistfile2: {'source': |
2854 | + ('deb http://test.ubuntu.com/ubuntu' |
2855 | + ' precise-backports' |
2856 | + ' main universe multiverse restricted')}, |
2857 | + self.aptlistfile3: {'source': |
2858 | + ('deb http://test.ubuntu.com/ubuntu' |
2859 | + ' lucid-backports' |
2860 | + ' main universe multiverse restricted')}} |
2861 | + self._apt_src_basic(self.aptlistfile, cfg) |
2862 | + |
2863 | + # extra verify on two extra files of this test |
2864 | + contents = load_tfile(self.aptlistfile2) |
2865 | + self.assertTrue(re.search(r"%s %s %s %s\n" % |
2866 | + ("deb", "http://test.ubuntu.com/ubuntu", |
2867 | + "precise-backports", |
2868 | + "main universe multiverse restricted"), |
2869 | + contents, flags=re.IGNORECASE)) |
2870 | + contents = load_tfile(self.aptlistfile3) |
2871 | + self.assertTrue(re.search(r"%s %s %s %s\n" % |
2872 | + ("deb", "http://test.ubuntu.com/ubuntu", |
2873 | + "lucid-backports", |
2874 | + "main universe multiverse restricted"), |
2875 | + contents, flags=re.IGNORECASE)) |
2876 | + |
2877 | + def _apt_src_replacement(self, filename, cfg): |
2878 | + """ apt_src_replace |
2879 | + Test Autoreplacement of MIRROR and RELEASE in source specs |
2880 | + """ |
2881 | + params = self._get_default_params() |
2882 | + self._add_apt_sources(cfg, TARGET, template_params=params, |
2883 | + aa_repo_match=self.matcher) |
2884 | + |
2885 | + self.assertTrue(os.path.isfile(filename)) |
2886 | + |
2887 | + contents = load_tfile(filename) |
2888 | + self.assertTrue(re.search(r"%s %s %s %s\n" % |
2889 | + ("deb", params['MIRROR'], params['RELEASE'], |
2890 | + "multiverse"), |
2891 | + contents, flags=re.IGNORECASE)) |
2892 | + |
2893 | + def test_apt_src_replace(self): |
2894 | + """test_apt_src_replace - Test Autoreplacement of MIRROR and RELEASE""" |
2895 | + cfg = {self.aptlistfile: {'source': 'deb $MIRROR $RELEASE multiverse'}} |
2896 | + self._apt_src_replacement(self.aptlistfile, cfg) |
2897 | + |
2898 | + def test_apt_src_replace_fn(self): |
2899 | + """test_apt_src_replace_fn - Test filename being overwritten in dict""" |
2900 | + cfg = {'ignored': {'source': 'deb $MIRROR $RELEASE multiverse', |
2901 | + 'filename': self.aptlistfile}} |
2902 | + # second file should overwrite the dict key |
2903 | + self._apt_src_replacement(self.aptlistfile, cfg) |
2904 | + |
2905 | + def _apt_src_replace_tri(self, cfg): |
2906 | + """ _apt_src_replace_tri |
2907 | + Test three autoreplacements of MIRROR and RELEASE in source specs with |
2908 | + generic part |
2909 | + """ |
2910 | + self._apt_src_replacement(self.aptlistfile, cfg) |
2911 | + |
2912 | + # extra verify on two extra files of this test |
2913 | + params = self._get_default_params() |
2914 | + contents = load_tfile(self.aptlistfile2) |
2915 | + self.assertTrue(re.search(r"%s %s %s %s\n" % |
2916 | + ("deb", params['MIRROR'], params['RELEASE'], |
2917 | + "main"), |
2918 | + contents, flags=re.IGNORECASE)) |
2919 | + contents = load_tfile(self.aptlistfile3) |
2920 | + self.assertTrue(re.search(r"%s %s %s %s\n" % |
2921 | + ("deb", params['MIRROR'], params['RELEASE'], |
2922 | + "universe"), |
2923 | + contents, flags=re.IGNORECASE)) |
2924 | + |
2925 | + def test_apt_src_replace_tri(self): |
2926 | + """test_apt_src_replace_tri - Test multiple replacements/overwrites""" |
2927 | + cfg = {self.aptlistfile: {'source': 'deb $MIRROR $RELEASE multiverse'}, |
2928 | + 'notused': {'source': 'deb $MIRROR $RELEASE main', |
2929 | + 'filename': self.aptlistfile2}, |
2930 | + self.aptlistfile3: {'source': 'deb $MIRROR $RELEASE universe'}} |
2931 | + self._apt_src_replace_tri(cfg) |
2932 | + |
2933 | + def _apt_src_keyid(self, filename, cfg, keynum): |
2934 | + """ _apt_src_keyid |
2935 | + Test specification of a source + keyid |
2936 | + """ |
2937 | + params = self._get_default_params() |
2938 | + |
2939 | + with mock.patch("curtin.util.subp", |
2940 | + return_value=('fakekey 1234', '')) as mockobj: |
2941 | + self._add_apt_sources(cfg, TARGET, template_params=params, |
2942 | + aa_repo_match=self.matcher) |
2943 | + |
2944 | + # check if it added the right ammount of keys |
2945 | + calls = [] |
2946 | + for _ in range(keynum): |
2947 | + calls.append(call(['apt-key', 'add', '-'], data=b'fakekey 1234', |
2948 | + target=TARGET)) |
2949 | + mockobj.assert_has_calls(calls, any_order=True) |
2950 | + |
2951 | + self.assertTrue(os.path.isfile(filename)) |
2952 | + |
2953 | + contents = load_tfile(filename) |
2954 | + self.assertTrue(re.search(r"%s %s %s %s\n" % |
2955 | + ("deb", |
2956 | + ('http://ppa.launchpad.net/smoser/' |
2957 | + 'cloud-init-test/ubuntu'), |
2958 | + "xenial", "main"), |
2959 | + contents, flags=re.IGNORECASE)) |
2960 | + |
2961 | + @mock.patch(ChrootableTargetStr, new=PseudoChrootableTarget) |
2962 | + def test_apt_src_keyid(self): |
2963 | + """test_apt_src_keyid - Test source + keyid with filename being set""" |
2964 | + cfg = {self.aptlistfile: {'source': ('deb ' |
2965 | + 'http://ppa.launchpad.net/' |
2966 | + 'smoser/cloud-init-test/ubuntu' |
2967 | + ' xenial main'), |
2968 | + 'keyid': "03683F77"}} |
2969 | + self._apt_src_keyid(self.aptlistfile, cfg, 1) |
2970 | + |
2971 | + @mock.patch(ChrootableTargetStr, new=PseudoChrootableTarget) |
2972 | + def test_apt_src_keyid_tri(self): |
2973 | + """test_apt_src_keyid_tri - Test multiple src+keyid+filen overwrites""" |
2974 | + cfg = {self.aptlistfile: {'source': ('deb ' |
2975 | + 'http://ppa.launchpad.net/' |
2976 | + 'smoser/cloud-init-test/ubuntu' |
2977 | + ' xenial main'), |
2978 | + 'keyid': "03683F77"}, |
2979 | + 'ignored': {'source': ('deb ' |
2980 | + 'http://ppa.launchpad.net/' |
2981 | + 'smoser/cloud-init-test/ubuntu' |
2982 | + ' xenial universe'), |
2983 | + 'keyid': "03683F77", |
2984 | + 'filename': self.aptlistfile2}, |
2985 | + self.aptlistfile3: {'source': ('deb ' |
2986 | + 'http://ppa.launchpad.net/' |
2987 | + 'smoser/cloud-init-test/ubuntu' |
2988 | + ' xenial multiverse'), |
2989 | + 'keyid': "03683F77"}} |
2990 | + |
2991 | + self._apt_src_keyid(self.aptlistfile, cfg, 3) |
2992 | + contents = load_tfile(self.aptlistfile2) |
2993 | + self.assertTrue(re.search(r"%s %s %s %s\n" % |
2994 | + ("deb", |
2995 | + ('http://ppa.launchpad.net/smoser/' |
2996 | + 'cloud-init-test/ubuntu'), |
2997 | + "xenial", "universe"), |
2998 | + contents, flags=re.IGNORECASE)) |
2999 | + contents = load_tfile(self.aptlistfile3) |
3000 | + self.assertTrue(re.search(r"%s %s %s %s\n" % |
3001 | + ("deb", |
3002 | + ('http://ppa.launchpad.net/smoser/' |
3003 | + 'cloud-init-test/ubuntu'), |
3004 | + "xenial", "multiverse"), |
3005 | + contents, flags=re.IGNORECASE)) |
3006 | + |
3007 | + @mock.patch(ChrootableTargetStr, new=PseudoChrootableTarget) |
3008 | + def test_apt_src_key(self): |
3009 | + """test_apt_src_key - Test source + key""" |
3010 | + params = self._get_default_params() |
3011 | + cfg = {self.aptlistfile: {'source': ('deb ' |
3012 | + 'http://ppa.launchpad.net/' |
3013 | + 'smoser/cloud-init-test/ubuntu' |
3014 | + ' xenial main'), |
3015 | + 'key': "fakekey 4321"}} |
3016 | + |
3017 | + with mock.patch.object(util, 'subp') as mockobj: |
3018 | + self._add_apt_sources(cfg, TARGET, template_params=params, |
3019 | + aa_repo_match=self.matcher) |
3020 | + |
3021 | + mockobj.assert_any_call(['apt-key', 'add', '-'], data=b'fakekey 4321', |
3022 | + target=TARGET) |
3023 | + |
3024 | + self.assertTrue(os.path.isfile(self.aptlistfile)) |
3025 | + |
3026 | + contents = load_tfile(self.aptlistfile) |
3027 | + self.assertTrue(re.search(r"%s %s %s %s\n" % |
3028 | + ("deb", |
3029 | + ('http://ppa.launchpad.net/smoser/' |
3030 | + 'cloud-init-test/ubuntu'), |
3031 | + "xenial", "main"), |
3032 | + contents, flags=re.IGNORECASE)) |
3033 | + |
3034 | + @mock.patch(ChrootableTargetStr, new=PseudoChrootableTarget) |
3035 | + def test_apt_src_keyonly(self): |
3036 | + """test_apt_src_keyonly - Test key without source""" |
3037 | + params = self._get_default_params() |
3038 | + cfg = {self.aptlistfile: {'key': "fakekey 4242"}} |
3039 | + |
3040 | + with mock.patch.object(util, 'subp') as mockobj: |
3041 | + self._add_apt_sources(cfg, TARGET, template_params=params, |
3042 | + aa_repo_match=self.matcher) |
3043 | + |
3044 | + mockobj.assert_any_call(['apt-key', 'add', '-'], data=b'fakekey 4242', |
3045 | + target=TARGET) |
3046 | + |
3047 | + # filename should be ignored on key only |
3048 | + self.assertFalse(os.path.isfile(self.aptlistfile)) |
3049 | + |
3050 | + @mock.patch(ChrootableTargetStr, new=PseudoChrootableTarget) |
3051 | + def test_apt_src_keyidonly(self): |
3052 | + """test_apt_src_keyidonly - Test keyid without source""" |
3053 | + params = self._get_default_params() |
3054 | + cfg = {self.aptlistfile: {'keyid': "03683F77"}} |
3055 | + |
3056 | + with mock.patch.object(util, 'subp', |
3057 | + return_value=('fakekey 1212', '')) as mockobj: |
3058 | + self._add_apt_sources(cfg, TARGET, template_params=params, |
3059 | + aa_repo_match=self.matcher) |
3060 | + |
3061 | + mockobj.assert_any_call(['apt-key', 'add', '-'], data=b'fakekey 1212', |
3062 | + target=TARGET) |
3063 | + |
3064 | + # filename should be ignored on key only |
3065 | + self.assertFalse(os.path.isfile(self.aptlistfile)) |
3066 | + |
3067 | + def apt_src_keyid_real(self, cfg, expectedkey): |
3068 | + """apt_src_keyid_real |
3069 | + Test specification of a keyid without source including |
3070 | + up to addition of the key (add_apt_key_raw mocked to keep the |
3071 | + environment as is) |
3072 | + """ |
3073 | + params = self._get_default_params() |
3074 | + |
3075 | + with mock.patch.object(apt_config, 'add_apt_key_raw') as mockkey: |
3076 | + with mock.patch.object(gpg, 'getkeybyid', |
3077 | + return_value=expectedkey) as mockgetkey: |
3078 | + self._add_apt_sources(cfg, TARGET, template_params=params, |
3079 | + aa_repo_match=self.matcher) |
3080 | + |
3081 | + keycfg = cfg[self.aptlistfile] |
3082 | + mockgetkey.assert_called_with(keycfg['keyid'], |
3083 | + keycfg.get('keyserver', |
3084 | + 'keyserver.ubuntu.com')) |
3085 | + mockkey.assert_called_with(expectedkey, TARGET) |
3086 | + |
3087 | + # filename should be ignored on key only |
3088 | + self.assertFalse(os.path.isfile(self.aptlistfile)) |
3089 | + |
3090 | + def test_apt_src_keyid_real(self): |
3091 | + """test_apt_src_keyid_real - Test keyid including key add""" |
3092 | + keyid = "03683F77" |
3093 | + cfg = {self.aptlistfile: {'keyid': keyid}} |
3094 | + |
3095 | + self.apt_src_keyid_real(cfg, EXPECTEDKEY) |
3096 | + |
3097 | + def test_apt_src_longkeyid_real(self): |
3098 | + """test_apt_src_longkeyid_real Test long keyid including key add""" |
3099 | + keyid = "B59D 5F15 97A5 04B7 E230 6DCA 0620 BBCF 0368 3F77" |
3100 | + cfg = {self.aptlistfile: {'keyid': keyid}} |
3101 | + |
3102 | + self.apt_src_keyid_real(cfg, EXPECTEDKEY) |
3103 | + |
3104 | + def test_apt_src_longkeyid_ks_real(self): |
3105 | + """test_apt_src_longkeyid_ks_real Test long keyid from other ks""" |
3106 | + keyid = "B59D 5F15 97A5 04B7 E230 6DCA 0620 BBCF 0368 3F77" |
3107 | + cfg = {self.aptlistfile: {'keyid': keyid, |
3108 | + 'keyserver': 'keys.gnupg.net'}} |
3109 | + |
3110 | + self.apt_src_keyid_real(cfg, EXPECTEDKEY) |
3111 | + |
3112 | + def test_apt_src_keyid_keyserver(self): |
3113 | + """test_apt_src_keyid_keyserver - Test custom keyserver""" |
3114 | + keyid = "03683F77" |
3115 | + params = self._get_default_params() |
3116 | + cfg = {self.aptlistfile: {'keyid': keyid, |
3117 | + 'keyserver': 'test.random.com'}} |
3118 | + |
3119 | + # in some test environments only *.ubuntu.com is reachable |
3120 | + # so mock the call and check if the config got there |
3121 | + with mock.patch.object(gpg, 'getkeybyid', |
3122 | + return_value="fakekey") as mockgetkey: |
3123 | + with mock.patch.object(apt_config, 'add_apt_key_raw') as mockadd: |
3124 | + self._add_apt_sources(cfg, TARGET, template_params=params, |
3125 | + aa_repo_match=self.matcher) |
3126 | + |
3127 | + mockgetkey.assert_called_with('03683F77', 'test.random.com') |
3128 | + mockadd.assert_called_with('fakekey', TARGET) |
3129 | + |
3130 | + # filename should be ignored on key only |
3131 | + self.assertFalse(os.path.isfile(self.aptlistfile)) |
3132 | + |
3133 | + @mock.patch(ChrootableTargetStr, new=PseudoChrootableTarget) |
3134 | + def test_apt_src_ppa(self): |
3135 | + """test_apt_src_ppa - Test specification of a ppa""" |
3136 | + params = self._get_default_params() |
3137 | + cfg = {self.aptlistfile: {'source': 'ppa:smoser/cloud-init-test'}} |
3138 | + |
3139 | + with mock.patch("curtin.util.subp") as mockobj: |
3140 | + self._add_apt_sources(cfg, TARGET, template_params=params, |
3141 | + aa_repo_match=self.matcher) |
3142 | + mockobj.assert_any_call(['add-apt-repository', |
3143 | + 'ppa:smoser/cloud-init-test'], target=TARGET) |
3144 | + |
3145 | + # adding ppa should ignore filename (uses add-apt-repository) |
3146 | + self.assertFalse(os.path.isfile(self.aptlistfile)) |
3147 | + |
3148 | + @mock.patch(ChrootableTargetStr, new=PseudoChrootableTarget) |
3149 | + def test_apt_src_ppa_tri(self): |
3150 | + """test_apt_src_ppa_tri - Test specification of multiple ppa's""" |
3151 | + params = self._get_default_params() |
3152 | + cfg = {self.aptlistfile: {'source': 'ppa:smoser/cloud-init-test'}, |
3153 | + self.aptlistfile2: {'source': 'ppa:smoser/cloud-init-test2'}, |
3154 | + self.aptlistfile3: {'source': 'ppa:smoser/cloud-init-test3'}} |
3155 | + |
3156 | + with mock.patch("curtin.util.subp") as mockobj: |
3157 | + self._add_apt_sources(cfg, TARGET, template_params=params, |
3158 | + aa_repo_match=self.matcher) |
3159 | + calls = [call(['add-apt-repository', 'ppa:smoser/cloud-init-test'], |
3160 | + target=TARGET), |
3161 | + call(['add-apt-repository', 'ppa:smoser/cloud-init-test2'], |
3162 | + target=TARGET), |
3163 | + call(['add-apt-repository', 'ppa:smoser/cloud-init-test3'], |
3164 | + target=TARGET)] |
3165 | + mockobj.assert_has_calls(calls, any_order=True) |
3166 | + |
3167 | + # adding ppa should ignore all filenames (uses add-apt-repository) |
3168 | + self.assertFalse(os.path.isfile(self.aptlistfile)) |
3169 | + self.assertFalse(os.path.isfile(self.aptlistfile2)) |
3170 | + self.assertFalse(os.path.isfile(self.aptlistfile3)) |
3171 | + |
3172 | + def test_mir_apt_list_rename(self): |
3173 | + """test_mir_apt_list_rename - Test find mirror and apt list renaming""" |
3174 | + pre = "/var/lib/apt/lists" |
3175 | + # filenames are archive dependent |
3176 | + arch = util.get_architecture() |
3177 | + if arch in apt_config.PRIMARY_ARCHES: |
3178 | + component = "ubuntu" |
3179 | + archive = "archive.ubuntu.com" |
3180 | + else: |
3181 | + component = "ubuntu-ports" |
3182 | + archive = "ports.ubuntu.com" |
3183 | + |
3184 | + cfg = {'primary': [{'arches': ["default"], |
3185 | + 'uri': |
3186 | + 'http://test.ubuntu.com/%s/' % component}], |
3187 | + 'security': [{'arches': ["default"], |
3188 | + 'uri': |
3189 | + 'http://testsec.ubuntu.com/%s/' % component}]} |
3190 | + post = ("%s_dists_%s-updates_InRelease" % |
3191 | + (component, util.lsb_release()['codename'])) |
3192 | + fromfn = ("%s/%s_%s" % (pre, archive, post)) |
3193 | + tofn = ("%s/test.ubuntu.com_%s" % (pre, post)) |
3194 | + |
3195 | + mirrors = apt_config.find_apt_mirror_info(cfg, arch) |
3196 | + |
3197 | + self.assertEqual(mirrors['MIRROR'], |
3198 | + "http://test.ubuntu.com/%s/" % component) |
3199 | + self.assertEqual(mirrors['PRIMARY'], |
3200 | + "http://test.ubuntu.com/%s/" % component) |
3201 | + self.assertEqual(mirrors['SECURITY'], |
3202 | + "http://testsec.ubuntu.com/%s/" % component) |
3203 | + |
3204 | + # get_architecture would fail inside the unittest context |
3205 | + with mock.patch.object(util, 'get_architecture', return_value=arch): |
3206 | + with mock.patch.object(os, 'rename') as mockren: |
3207 | + with mock.patch.object(glob, 'glob', |
3208 | + return_value=[fromfn]): |
3209 | + apt_config.rename_apt_lists(mirrors, TARGET) |
3210 | + |
3211 | + mockren.assert_any_call(fromfn, tofn) |
3212 | + |
3213 | + @mock.patch("curtin.commands.apt_config.util.get_architecture") |
3214 | + def test_mir_apt_list_rename_non_slash(self, m_get_architecture): |
3215 | + target = os.path.join(self.tmp, "rename_non_slash") |
3216 | + apt_lists_d = os.path.join(target, "./" + apt_config.APT_LISTS) |
3217 | + |
3218 | + m_get_architecture.return_value = 'amd64' |
3219 | + |
3220 | + mirror_path = "some/random/path/" |
3221 | + primary = "http://test.ubuntu.com/" + mirror_path |
3222 | + security = "http://test-security.ubuntu.com/" + mirror_path |
3223 | + mirrors = {'PRIMARY': primary, 'SECURITY': security} |
3224 | + |
3225 | + # these match default archive prefixes |
3226 | + opri_pre = "archive.ubuntu.com_ubuntu_dists_xenial" |
3227 | + osec_pre = "security.ubuntu.com_ubuntu_dists_xenial" |
3228 | + # this one won't match and should not be renamed defaults. |
3229 | + other_pre = "dl.google.com_linux_chrome_deb_dists_stable" |
3230 | + # these are our new expected prefixes |
3231 | + npri_pre = "test.ubuntu.com_some_random_path_dists_xenial" |
3232 | + nsec_pre = "test-security.ubuntu.com_some_random_path_dists_xenial" |
3233 | + |
3234 | + files = [ |
3235 | + # orig prefix, new prefix, suffix |
3236 | + (opri_pre, npri_pre, "_main_binary-amd64_Packages"), |
3237 | + (opri_pre, npri_pre, "_main_binary-amd64_InRelease"), |
3238 | + (opri_pre, npri_pre, "-updates_main_binary-amd64_Packages"), |
3239 | + (opri_pre, npri_pre, "-updates_main_binary-amd64_InRelease"), |
3240 | + (other_pre, other_pre, "_main_binary-amd64_Packages"), |
3241 | + (other_pre, other_pre, "_Release"), |
3242 | + (other_pre, other_pre, "_Release.gpg"), |
3243 | + (osec_pre, nsec_pre, "_InRelease"), |
3244 | + (osec_pre, nsec_pre, "_main_binary-amd64_Packages"), |
3245 | + (osec_pre, nsec_pre, "_universe_binary-amd64_Packages"), |
3246 | + ] |
3247 | + |
3248 | + expected = sorted([npre + suff for opre, npre, suff in files]) |
3249 | + # create files |
3250 | + for (opre, npre, suff) in files: |
3251 | + fpath = os.path.join(apt_lists_d, opre + suff) |
3252 | + util.write_file(fpath, content=fpath) |
3253 | + |
3254 | + apt_config.rename_apt_lists(mirrors, target) |
3255 | + found = sorted(os.listdir(apt_lists_d)) |
3256 | + self.assertEqual(expected, found) |
3257 | + |
3258 | + @staticmethod |
3259 | + def test_apt_proxy(): |
3260 | + """test_apt_proxy - Test apt_*proxy configuration""" |
3261 | + cfg = {"proxy": "foobar1", |
3262 | + "http_proxy": "foobar2", |
3263 | + "ftp_proxy": "foobar3", |
3264 | + "https_proxy": "foobar4"} |
3265 | + |
3266 | + with mock.patch.object(util, 'write_file') as mockobj: |
3267 | + apt_config.apply_apt_proxy_config(cfg, "proxyfn", "notused") |
3268 | + |
3269 | + mockobj.assert_called_with('proxyfn', |
3270 | + ('Acquire::http::Proxy "foobar1";\n' |
3271 | + 'Acquire::http::Proxy "foobar2";\n' |
3272 | + 'Acquire::ftp::Proxy "foobar3";\n' |
3273 | + 'Acquire::https::Proxy "foobar4";\n')) |
3274 | + |
3275 | + def test_mirror(self): |
3276 | + """test_mirror - Test defining a mirror""" |
3277 | + pmir = "http://us.archive.ubuntu.com/ubuntu/" |
3278 | + smir = "http://security.ubuntu.com/ubuntu/" |
3279 | + cfg = {"primary": [{'arches': ["default"], |
3280 | + "uri": pmir}], |
3281 | + "security": [{'arches': ["default"], |
3282 | + "uri": smir}]} |
3283 | + |
3284 | + mirrors = apt_config.find_apt_mirror_info(cfg, 'amd64') |
3285 | + |
3286 | + self.assertEqual(mirrors['MIRROR'], |
3287 | + pmir) |
3288 | + self.assertEqual(mirrors['PRIMARY'], |
3289 | + pmir) |
3290 | + self.assertEqual(mirrors['SECURITY'], |
3291 | + smir) |
3292 | + |
3293 | + def test_mirror_default(self): |
3294 | + """test_mirror_default - Test without defining a mirror""" |
3295 | + arch = util.get_architecture() |
3296 | + default_mirrors = apt_config.get_default_mirrors(arch) |
3297 | + pmir = default_mirrors["PRIMARY"] |
3298 | + smir = default_mirrors["SECURITY"] |
3299 | + mirrors = apt_config.find_apt_mirror_info({}, arch) |
3300 | + |
3301 | + self.assertEqual(mirrors['MIRROR'], |
3302 | + pmir) |
3303 | + self.assertEqual(mirrors['PRIMARY'], |
3304 | + pmir) |
3305 | + self.assertEqual(mirrors['SECURITY'], |
3306 | + smir) |
3307 | + |
3308 | + def test_mirror_arches(self): |
3309 | + """test_mirror_arches - Test arches selection of mirror""" |
3310 | + pmir = "http://us.archive.ubuntu.com/ubuntu/" |
3311 | + smir = "http://security.ubuntu.com/ubuntu/" |
3312 | + cfg = {"primary": [{'arches': ["default"], |
3313 | + "uri": "notthis"}, |
3314 | + {'arches': [util.get_architecture()], |
3315 | + "uri": pmir}], |
3316 | + "security": [{'arches': [util.get_architecture()], |
3317 | + "uri": smir}, |
3318 | + {'arches': ["default"], |
3319 | + "uri": "nothat"}]} |
3320 | + |
3321 | + mirrors = apt_config.find_apt_mirror_info(cfg, 'amd64') |
3322 | + |
3323 | + self.assertEqual(mirrors['MIRROR'], |
3324 | + pmir) |
3325 | + self.assertEqual(mirrors['PRIMARY'], |
3326 | + pmir) |
3327 | + self.assertEqual(mirrors['SECURITY'], |
3328 | + smir) |
3329 | + |
3330 | + def test_mirror_arches_default(self): |
3331 | + """test_mirror_arches - Test falling back to default arch""" |
3332 | + pmir = "http://us.archive.ubuntu.com/ubuntu/" |
3333 | + smir = "http://security.ubuntu.com/ubuntu/" |
3334 | + cfg = {"primary": [{'arches': ["default"], |
3335 | + "uri": pmir}, |
3336 | + {'arches': ["thisarchdoesntexist"], |
3337 | + "uri": "notthis"}], |
3338 | + "security": [{'arches': ["thisarchdoesntexist"], |
3339 | + "uri": "nothat"}, |
3340 | + {'arches': ["default"], |
3341 | + "uri": smir}]} |
3342 | + |
3343 | + mirrors = apt_config.find_apt_mirror_info(cfg, 'amd64') |
3344 | + |
3345 | + self.assertEqual(mirrors['MIRROR'], |
3346 | + pmir) |
3347 | + self.assertEqual(mirrors['PRIMARY'], |
3348 | + pmir) |
3349 | + self.assertEqual(mirrors['SECURITY'], |
3350 | + smir) |
3351 | + |
3352 | + @mock.patch("curtin.commands.apt_config.util.get_architecture") |
3353 | + def test_get_default_mirrors_non_intel_no_arg(self, m_get_architecture): |
3354 | + arch = 'ppc64el' |
3355 | + m_get_architecture.return_value = arch |
3356 | + expected = {'PRIMARY': 'http://ports.ubuntu.com/ubuntu-ports', |
3357 | + 'SECURITY': 'http://ports.ubuntu.com/ubuntu-ports'} |
3358 | + self.assertEqual(expected, apt_config.get_default_mirrors(arch)) |
3359 | + |
3360 | + @mock.patch("curtin.commands.apt_config.util.get_architecture") |
3361 | + def test_get_default_mirrors_non_intel_with_arch(self, m_get_architecture): |
3362 | + arch = 'ppc64el' |
3363 | + found = apt_config.get_default_mirrors(arch) |
3364 | + |
3365 | + expected = {'PRIMARY': 'http://ports.ubuntu.com/ubuntu-ports', |
3366 | + 'SECURITY': 'http://ports.ubuntu.com/ubuntu-ports'} |
3367 | + self.assertEqual(expected, found) |
3368 | + |
3369 | + def test_mirror_arches_sysdefault(self): |
3370 | + """test_mirror_arches - Test arches falling back to sys default""" |
3371 | + arch = util.get_architecture() |
3372 | + default_mirrors = apt_config.get_default_mirrors(arch) |
3373 | + pmir = default_mirrors["PRIMARY"] |
3374 | + smir = default_mirrors["SECURITY"] |
3375 | + cfg = {"primary": [{'arches': ["thisarchdoesntexist_64"], |
3376 | + "uri": "notthis"}, |
3377 | + {'arches': ["thisarchdoesntexist"], |
3378 | + "uri": "notthiseither"}], |
3379 | + "security": [{'arches': ["thisarchdoesntexist"], |
3380 | + "uri": "nothat"}, |
3381 | + {'arches': ["thisarchdoesntexist_64"], |
3382 | + "uri": "nothateither"}]} |
3383 | + |
3384 | + mirrors = apt_config.find_apt_mirror_info(cfg, 'amd64') |
3385 | + |
3386 | + self.assertEqual(mirrors['MIRROR'], |
3387 | + pmir) |
3388 | + self.assertEqual(mirrors['PRIMARY'], |
3389 | + pmir) |
3390 | + self.assertEqual(mirrors['SECURITY'], |
3391 | + smir) |
3392 | + |
3393 | + def test_mirror_search(self): |
3394 | + """test_mirror_search - Test searching mirrors in a list |
3395 | + mock checks to avoid relying on network connectivity""" |
3396 | + pmir = "http://us.archive.ubuntu.com/ubuntu/" |
3397 | + smir = "http://security.ubuntu.com/ubuntu/" |
3398 | + cfg = {"primary": [{'arches': ["default"], |
3399 | + "search": ["pfailme", pmir]}], |
3400 | + "security": [{'arches': ["default"], |
3401 | + "search": ["sfailme", smir]}]} |
3402 | + |
3403 | + with mock.patch.object(apt_config, 'search_for_mirror', |
3404 | + side_effect=[pmir, smir]) as mocksearch: |
3405 | + mirrors = apt_config.find_apt_mirror_info(cfg, 'amd64') |
3406 | + |
3407 | + calls = [call(["pfailme", pmir]), |
3408 | + call(["sfailme", smir])] |
3409 | + mocksearch.assert_has_calls(calls) |
3410 | + |
3411 | + self.assertEqual(mirrors['MIRROR'], |
3412 | + pmir) |
3413 | + self.assertEqual(mirrors['PRIMARY'], |
3414 | + pmir) |
3415 | + self.assertEqual(mirrors['SECURITY'], |
3416 | + smir) |
3417 | + |
3418 | + def test_mirror_search_many2(self): |
3419 | + """test_mirror_search_many3 - Test both mirrors specs at once""" |
3420 | + pmir = "http://us.archive.ubuntu.com/ubuntu/" |
3421 | + smir = "http://security.ubuntu.com/ubuntu/" |
3422 | + cfg = {"primary": [{'arches': ["default"], |
3423 | + "uri": pmir, |
3424 | + "search": ["pfailme", "foo"]}], |
3425 | + "security": [{'arches': ["default"], |
3426 | + "uri": smir, |
3427 | + "search": ["sfailme", "bar"]}]} |
3428 | + |
3429 | + arch = 'amd64' |
3430 | + |
3431 | + # should be called only once per type, despite two mirror configs |
3432 | + with mock.patch.object(apt_config, 'get_mirror', |
3433 | + return_value="http://mocked/foo") as mockgm: |
3434 | + mirrors = apt_config.find_apt_mirror_info(cfg, arch) |
3435 | + calls = [call(cfg, 'primary', arch), call(cfg, 'security', arch)] |
3436 | + mockgm.assert_has_calls(calls) |
3437 | + |
3438 | + # should not be called, since primary is specified |
3439 | + with mock.patch.object(apt_config, 'search_for_mirror') as mockse: |
3440 | + mirrors = apt_config.find_apt_mirror_info(cfg, arch) |
3441 | + mockse.assert_not_called() |
3442 | + |
3443 | + self.assertEqual(mirrors['MIRROR'], |
3444 | + pmir) |
3445 | + self.assertEqual(mirrors['PRIMARY'], |
3446 | + pmir) |
3447 | + self.assertEqual(mirrors['SECURITY'], |
3448 | + smir) |
3449 | + |
3450 | + def test_url_resolvable(self): |
3451 | + """test_url_resolvable - Test resolving urls""" |
3452 | + |
3453 | + with mock.patch.object(util, 'is_resolvable') as mockresolve: |
3454 | + util.is_resolvable_url("http://1.2.3.4/ubuntu") |
3455 | + mockresolve.assert_called_with("1.2.3.4") |
3456 | + |
3457 | + with mock.patch.object(util, 'is_resolvable') as mockresolve: |
3458 | + util.is_resolvable_url("http://us.archive.ubuntu.com/ubuntu") |
3459 | + mockresolve.assert_called_with("us.archive.ubuntu.com") |
3460 | + |
3461 | + bad = [(None, None, None, "badname", ["10.3.2.1"])] |
3462 | + good = [(None, None, None, "goodname", ["10.2.3.4"])] |
3463 | + with mock.patch.object(socket, 'getaddrinfo', |
3464 | + side_effect=[bad, bad, good, |
3465 | + good]) as mocksock: |
3466 | + ret = util.is_resolvable_url("http://us.archive.ubuntu.com/ubuntu") |
3467 | + ret2 = util.is_resolvable_url("http://1.2.3.4/ubuntu") |
3468 | + calls = [call('does-not-exist.example.com.', None, 0, 0, 1, 2), |
3469 | + call('example.invalid.', None, 0, 0, 1, 2), |
3470 | + call('us.archive.ubuntu.com', None), |
3471 | + call('1.2.3.4', None)] |
3472 | + mocksock.assert_has_calls(calls) |
3473 | + self.assertTrue(ret) |
3474 | + self.assertTrue(ret2) |
3475 | + |
3476 | + # side effect need only bad ret after initial call |
3477 | + with mock.patch.object(socket, 'getaddrinfo', |
3478 | + side_effect=[bad]) as mocksock: |
3479 | + ret3 = util.is_resolvable_url("http://failme.com/ubuntu") |
3480 | + calls = [call('failme.com', None)] |
3481 | + mocksock.assert_has_calls(calls) |
3482 | + self.assertFalse(ret3) |
3483 | + |
3484 | + def test_disable_suites(self): |
3485 | + """test_disable_suites - disable_suites with many configurations""" |
3486 | + release = "xenial" |
3487 | + orig = """deb http://ubuntu.com//ubuntu xenial main |
3488 | +deb http://ubuntu.com//ubuntu xenial-updates main |
3489 | +deb http://ubuntu.com//ubuntu xenial-security main |
3490 | +deb-src http://ubuntu.com//ubuntu universe multiverse |
3491 | +deb http://ubuntu.com/ubuntu/ xenial-proposed main""" |
3492 | + |
3493 | + # disable nothing |
3494 | + disabled = [] |
3495 | + expect = """deb http://ubuntu.com//ubuntu xenial main |
3496 | +deb http://ubuntu.com//ubuntu xenial-updates main |
3497 | +deb http://ubuntu.com//ubuntu xenial-security main |
3498 | +deb-src http://ubuntu.com//ubuntu universe multiverse |
3499 | +deb http://ubuntu.com/ubuntu/ xenial-proposed main""" |
3500 | + result = apt_config.disable_suites(disabled, orig, release) |
3501 | + self.assertEqual(expect, result) |
3502 | + |
3503 | + # single disable release suite |
3504 | + disabled = ["$RELEASE"] |
3505 | + expect = """\ |
3506 | +# suite disabled by curtin: deb http://ubuntu.com//ubuntu xenial main |
3507 | +deb http://ubuntu.com//ubuntu xenial-updates main |
3508 | +deb http://ubuntu.com//ubuntu xenial-security main |
3509 | +deb-src http://ubuntu.com//ubuntu universe multiverse |
3510 | +deb http://ubuntu.com/ubuntu/ xenial-proposed main""" |
3511 | + result = apt_config.disable_suites(disabled, orig, release) |
3512 | + self.assertEqual(expect, result) |
3513 | + |
3514 | + # single disable other suite |
3515 | + disabled = ["$RELEASE-updates"] |
3516 | + expect = """deb http://ubuntu.com//ubuntu xenial main |
3517 | +# suite disabled by curtin: deb http://ubuntu.com//ubuntu xenial-updates main |
3518 | +deb http://ubuntu.com//ubuntu xenial-security main |
3519 | +deb-src http://ubuntu.com//ubuntu universe multiverse |
3520 | +deb http://ubuntu.com/ubuntu/ xenial-proposed main""" |
3521 | + result = apt_config.disable_suites(disabled, orig, release) |
3522 | + self.assertEqual(expect, result) |
3523 | + |
3524 | + # multi disable |
3525 | + disabled = ["$RELEASE-updates", "$RELEASE-security"] |
3526 | + expect = """deb http://ubuntu.com//ubuntu xenial main |
3527 | +# suite disabled by curtin: deb http://ubuntu.com//ubuntu xenial-updates main |
3528 | +# suite disabled by curtin: deb http://ubuntu.com//ubuntu xenial-security main |
3529 | +deb-src http://ubuntu.com//ubuntu universe multiverse |
3530 | +deb http://ubuntu.com/ubuntu/ xenial-proposed main""" |
3531 | + result = apt_config.disable_suites(disabled, orig, release) |
3532 | + self.assertEqual(expect, result) |
3533 | + |
3534 | + # multi line disable (same suite multiple times in input) |
3535 | + disabled = ["$RELEASE-updates", "$RELEASE-security"] |
3536 | + orig = """deb http://ubuntu.com//ubuntu xenial main |
3537 | +deb http://ubuntu.com//ubuntu xenial-updates main |
3538 | +deb http://ubuntu.com//ubuntu xenial-security main |
3539 | +deb-src http://ubuntu.com//ubuntu universe multiverse |
3540 | +deb http://UBUNTU.com//ubuntu xenial-updates main |
3541 | +deb http://UBUNTU.COM//ubuntu xenial-updates main |
3542 | +deb http://ubuntu.com/ubuntu/ xenial-proposed main""" |
3543 | + expect = """deb http://ubuntu.com//ubuntu xenial main |
3544 | +# suite disabled by curtin: deb http://ubuntu.com//ubuntu xenial-updates main |
3545 | +# suite disabled by curtin: deb http://ubuntu.com//ubuntu xenial-security main |
3546 | +deb-src http://ubuntu.com//ubuntu universe multiverse |
3547 | +# suite disabled by curtin: deb http://UBUNTU.com//ubuntu xenial-updates main |
3548 | +# suite disabled by curtin: deb http://UBUNTU.COM//ubuntu xenial-updates main |
3549 | +deb http://ubuntu.com/ubuntu/ xenial-proposed main""" |
3550 | + result = apt_config.disable_suites(disabled, orig, release) |
3551 | + self.assertEqual(expect, result) |
3552 | + |
3553 | + # comment in input |
3554 | + disabled = ["$RELEASE-updates", "$RELEASE-security"] |
3555 | + orig = """deb http://ubuntu.com//ubuntu xenial main |
3556 | +deb http://ubuntu.com//ubuntu xenial-updates main |
3557 | +deb http://ubuntu.com//ubuntu xenial-security main |
3558 | +deb-src http://ubuntu.com//ubuntu universe multiverse |
3559 | +#foo |
3560 | +#deb http://UBUNTU.com//ubuntu xenial-updates main |
3561 | +deb http://UBUNTU.COM//ubuntu xenial-updates main |
3562 | +deb http://ubuntu.com/ubuntu/ xenial-proposed main""" |
3563 | + expect = """deb http://ubuntu.com//ubuntu xenial main |
3564 | +# suite disabled by curtin: deb http://ubuntu.com//ubuntu xenial-updates main |
3565 | +# suite disabled by curtin: deb http://ubuntu.com//ubuntu xenial-security main |
3566 | +deb-src http://ubuntu.com//ubuntu universe multiverse |
3567 | +#foo |
3568 | +#deb http://UBUNTU.com//ubuntu xenial-updates main |
3569 | +# suite disabled by curtin: deb http://UBUNTU.COM//ubuntu xenial-updates main |
3570 | +deb http://ubuntu.com/ubuntu/ xenial-proposed main""" |
3571 | + result = apt_config.disable_suites(disabled, orig, release) |
3572 | + self.assertEqual(expect, result) |
3573 | + |
3574 | + # single disable custom suite |
3575 | + disabled = ["foobar"] |
3576 | + orig = """deb http://ubuntu.com//ubuntu xenial main |
3577 | +deb http://ubuntu.com//ubuntu xenial-updates main |
3578 | +deb http://ubuntu.com//ubuntu xenial-security main |
3579 | +deb http://ubuntu.com/ubuntu/ foobar main""" |
3580 | + expect = """deb http://ubuntu.com//ubuntu xenial main |
3581 | +deb http://ubuntu.com//ubuntu xenial-updates main |
3582 | +deb http://ubuntu.com//ubuntu xenial-security main |
3583 | +# suite disabled by curtin: deb http://ubuntu.com/ubuntu/ foobar main""" |
3584 | + result = apt_config.disable_suites(disabled, orig, release) |
3585 | + self.assertEqual(expect, result) |
3586 | + |
3587 | + # single disable non existing suite |
3588 | + disabled = ["foobar"] |
3589 | + orig = """deb http://ubuntu.com//ubuntu xenial main |
3590 | +deb http://ubuntu.com//ubuntu xenial-updates main |
3591 | +deb http://ubuntu.com//ubuntu xenial-security main |
3592 | +deb http://ubuntu.com/ubuntu/ notfoobar main""" |
3593 | + expect = """deb http://ubuntu.com//ubuntu xenial main |
3594 | +deb http://ubuntu.com//ubuntu xenial-updates main |
3595 | +deb http://ubuntu.com//ubuntu xenial-security main |
3596 | +deb http://ubuntu.com/ubuntu/ notfoobar main""" |
3597 | + result = apt_config.disable_suites(disabled, orig, release) |
3598 | + self.assertEqual(expect, result) |
3599 | + |
3600 | + # single disable suite with option |
3601 | + disabled = ["$RELEASE-updates"] |
3602 | + orig = """deb http://ubuntu.com//ubuntu xenial main |
3603 | +deb [a=b] http://ubu.com//ubu xenial-updates main |
3604 | +deb http://ubuntu.com//ubuntu xenial-security main |
3605 | +deb-src http://ubuntu.com//ubuntu universe multiverse |
3606 | +deb http://ubuntu.com/ubuntu/ xenial-proposed main""" |
3607 | + expect = """deb http://ubuntu.com//ubuntu xenial main |
3608 | +# suite disabled by curtin: deb [a=b] http://ubu.com//ubu xenial-updates main |
3609 | +deb http://ubuntu.com//ubuntu xenial-security main |
3610 | +deb-src http://ubuntu.com//ubuntu universe multiverse |
3611 | +deb http://ubuntu.com/ubuntu/ xenial-proposed main""" |
3612 | + result = apt_config.disable_suites(disabled, orig, release) |
3613 | + self.assertEqual(expect, result) |
3614 | + |
3615 | + # single disable suite with more options and auto $RELEASE expansion |
3616 | + disabled = ["updates"] |
3617 | + orig = """deb http://ubuntu.com//ubuntu xenial main |
3618 | +deb [a=b c=d] http://ubu.com//ubu xenial-updates main |
3619 | +deb http://ubuntu.com//ubuntu xenial-security main |
3620 | +deb-src http://ubuntu.com//ubuntu universe multiverse |
3621 | +deb http://ubuntu.com/ubuntu/ xenial-proposed main""" |
3622 | + expect = """deb http://ubuntu.com//ubuntu xenial main |
3623 | +# suite disabled by curtin: deb [a=b c=d] \ |
3624 | +http://ubu.com//ubu xenial-updates main |
3625 | +deb http://ubuntu.com//ubuntu xenial-security main |
3626 | +deb-src http://ubuntu.com//ubuntu universe multiverse |
3627 | +deb http://ubuntu.com/ubuntu/ xenial-proposed main""" |
3628 | + result = apt_config.disable_suites(disabled, orig, release) |
3629 | + self.assertEqual(expect, result) |
3630 | + |
3631 | + # single disable suite while options at others |
3632 | + disabled = ["$RELEASE-security"] |
3633 | + orig = """deb http://ubuntu.com//ubuntu xenial main |
3634 | +deb [arch=foo] http://ubuntu.com//ubuntu xenial-updates main |
3635 | +deb http://ubuntu.com//ubuntu xenial-security main |
3636 | +deb-src http://ubuntu.com//ubuntu universe multiverse |
3637 | +deb http://ubuntu.com/ubuntu/ xenial-proposed main""" |
3638 | + expect = """deb http://ubuntu.com//ubuntu xenial main |
3639 | +deb [arch=foo] http://ubuntu.com//ubuntu xenial-updates main |
3640 | +# suite disabled by curtin: deb http://ubuntu.com//ubuntu xenial-security main |
3641 | +deb-src http://ubuntu.com//ubuntu universe multiverse |
3642 | +deb http://ubuntu.com/ubuntu/ xenial-proposed main""" |
3643 | + result = apt_config.disable_suites(disabled, orig, release) |
3644 | + self.assertEqual(expect, result) |
3645 | + |
3646 | +# |
3647 | +# vi: ts=4 expandtab |
3648 | |
3649 | === modified file 'tests/unittests/test_util.py' |
3650 | --- tests/unittests/test_util.py 2016-07-28 19:33:01 +0000 |
3651 | +++ tests/unittests/test_util.py 2016-08-02 09:15:49 +0000 |
3652 | @@ -121,13 +121,13 @@ |
3653 | rdata = {'id': 'Ubuntu', 'description': 'Ubuntu 14.04.2 LTS', |
3654 | 'codename': 'trusty', 'release': '14.04'} |
3655 | |
3656 | - def fake_subp(cmd, capture=False): |
3657 | + def fake_subp(cmd, capture=False, target=None): |
3658 | return output, 'No LSB modules are available.' |
3659 | |
3660 | mock_subp.side_effect = fake_subp |
3661 | found = util.lsb_release() |
3662 | mock_subp.assert_called_with( |
3663 | - ['lsb_release', '--all'], capture=True) |
3664 | + ['lsb_release', '--all'], capture=True, target=None) |
3665 | self.assertEqual(found, rdata) |
3666 | |
3667 | @mock.patch("curtin.util.subp") |
3668 | |
3669 | === modified file 'tests/vmtests/__init__.py' |
3670 | --- tests/vmtests/__init__.py 2016-08-01 18:26:06 +0000 |
3671 | +++ tests/vmtests/__init__.py 2016-08-02 09:15:49 +0000 |
3672 | @@ -357,6 +357,7 @@ |
3673 | extra_kern_args = None |
3674 | fstab_expected = {} |
3675 | image_store_class = ImageStore |
3676 | + boot_cloudconf = None |
3677 | install_timeout = INSTALL_TIMEOUT |
3678 | interactive = False |
3679 | multipath = False |
3680 | @@ -365,6 +366,7 @@ |
3681 | recorded_errors = 0 |
3682 | recorded_failures = 0 |
3683 | uefi = False |
3684 | + proxy = None |
3685 | |
3686 | # these get set from base_vm_classes |
3687 | release = None |
3688 | @@ -394,7 +396,8 @@ |
3689 | # set up tempdir |
3690 | cls.td = TempDir( |
3691 | name=cls.__name__, |
3692 | - user_data=generate_user_data(collect_scripts=cls.collect_scripts)) |
3693 | + user_data=generate_user_data(collect_scripts=cls.collect_scripts, |
3694 | + boot_cloudconf=cls.boot_cloudconf)) |
3695 | logger.info('Using tempdir: %s , Image: %s', cls.td.tmpdir, |
3696 | img_verstr) |
3697 | cls.install_log = os.path.join(cls.td.logs, 'install-serial.log') |
3698 | @@ -494,11 +497,11 @@ |
3699 | |
3700 | # proxy config |
3701 | configs = [cls.conf_file] |
3702 | - proxy = get_apt_proxy() |
3703 | - if get_apt_proxy is not None: |
3704 | + cls.proxy = get_apt_proxy() |
3705 | + if cls.proxy is not None: |
3706 | proxy_config = os.path.join(cls.td.install, 'proxy.cfg') |
3707 | with open(proxy_config, "w") as fp: |
3708 | - fp.write(json.dumps({'apt_proxy': proxy}) + "\n") |
3709 | + fp.write(json.dumps({'apt_proxy': cls.proxy}) + "\n") |
3710 | configs.append(proxy_config) |
3711 | |
3712 | uefi_flags = [] |
3713 | @@ -733,8 +736,14 @@ |
3714 | # Misc functions that are useful for many tests |
3715 | def output_files_exist(self, files): |
3716 | for f in files: |
3717 | + logger.debug('checking file %s', f) |
3718 | self.assertTrue(os.path.exists(os.path.join(self.td.collect, f))) |
3719 | |
3720 | + def output_files_dont_exist(self, files): |
3721 | + for f in files: |
3722 | + logger.debug('checking file %s', f) |
3723 | + self.assertFalse(os.path.exists(os.path.join(self.td.collect, f))) |
3724 | + |
3725 | def check_file_strippedline(self, filename, search): |
3726 | with open(os.path.join(self.td.collect, filename), "r") as fp: |
3727 | data = list(i.strip() for i in fp.readlines()) |
3728 | @@ -963,7 +972,8 @@ |
3729 | return None |
3730 | |
3731 | |
3732 | -def generate_user_data(collect_scripts=None, apt_proxy=None): |
3733 | +def generate_user_data(collect_scripts=None, apt_proxy=None, |
3734 | + boot_cloudconf=None): |
3735 | # this returns the user data for the *booted* system |
3736 | # its a cloud-config-archive type, which is |
3737 | # just a list of parts. the 'x-shellscript' parts |
3738 | @@ -988,6 +998,10 @@ |
3739 | 'content': yaml.dump(base_cloudconfig, indent=1)}, |
3740 | {'type': 'text/cloud-config', 'content': ssh_keys}] |
3741 | |
3742 | + if boot_cloudconf is not None: |
3743 | + parts.append({'type': 'text/cloud-config', 'content': |
3744 | + yaml.dump(boot_cloudconf, indent=1)}) |
3745 | + |
3746 | output_dir = '/mnt/output' |
3747 | output_dir_macro = 'OUTPUT_COLLECT_D' |
3748 | output_device = '/dev/disk/by-id/virtio-%s' % OUTPUT_DISK_NAME |
3749 | |
3750 | === added file 'tests/vmtests/test_apt_config_cmd.py' |
3751 | --- tests/vmtests/test_apt_config_cmd.py 1970-01-01 00:00:00 +0000 |
3752 | +++ tests/vmtests/test_apt_config_cmd.py 2016-08-02 09:15:49 +0000 |
3753 | @@ -0,0 +1,55 @@ |
3754 | +""" test_apt_config_cmd |
3755 | + Collection of tests for the apt configuration features when called via the |
3756 | + apt-config standalone command. |
3757 | +""" |
3758 | +import textwrap |
3759 | + |
3760 | +from . import VMBaseClass |
3761 | +from .releases import base_vm_classes as relbase |
3762 | + |
3763 | + |
3764 | +class TestAptConfigCMD(VMBaseClass): |
3765 | + """TestAptConfigCMD - test standalone command""" |
3766 | + conf_file = "examples/tests/apt_config_command.yaml" |
3767 | + interactive = False |
3768 | + extra_disks = [] |
3769 | + fstab_expected = {} |
3770 | + disk_to_check = [] |
3771 | + collect_scripts = [textwrap.dedent(""" |
3772 | + cd OUTPUT_COLLECT_D |
3773 | + cat /etc/fstab > fstab |
3774 | + ls /dev/disk/by-dname > ls_dname |
3775 | + find /etc/network/interfaces.d > find_interfacesd |
3776 | + cp /etc/apt/sources.list.d/curtin-dev-ubuntu-test-archive-*.list . |
3777 | + cp /etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg . |
3778 | + apt-cache policy | grep proposed > proposed-enabled |
3779 | + """)] |
3780 | + |
3781 | + def test_cmd_proposed_enabled(self): |
3782 | + """check if proposed was enabled""" |
3783 | + self.output_files_exist(["proposed-enabled"]) |
3784 | + self.check_file_regex("proposed-enabled", |
3785 | + r"500.*%s-proposed" % self.release) |
3786 | + |
3787 | + def test_cmd_ppa_enabled(self): |
3788 | + """check if specified curtin-dev ppa was enabled""" |
3789 | + self.output_files_exist( |
3790 | + ["curtin-dev-ubuntu-test-archive-%s.list" % self.release]) |
3791 | + self.check_file_regex("curtin-dev-ubuntu-test-archive-%s.list" % |
3792 | + self.release, |
3793 | + (r"http://ppa.launchpad.net/" |
3794 | + r"curtin-dev/test-archive/ubuntu" |
3795 | + r" %s main" % self.release)) |
3796 | + |
3797 | + def test_cmd_preserve_source(self): |
3798 | + """check if cloud-init was prevented from overwriting""" |
3799 | + self.output_files_exist(["curtin-preserve-sources.cfg"]) |
3800 | + self.check_file_regex("curtin-preserve-sources.cfg", |
3801 | + "apt_preserve_sources_list.*true") |
3802 | + |
3803 | + |
3804 | +class XenialTestAptConfigCMDCMD(relbase.xenial, TestAptConfigCMD): |
3805 | + """ XenialTestAptSrcModifyCMD |
3806 | + apt feature Test for Xenial using the standalone command |
3807 | + """ |
3808 | + __test__ = True |
3809 | |
3810 | === added file 'tests/vmtests/test_apt_source.py' |
3811 | --- tests/vmtests/test_apt_source.py 1970-01-01 00:00:00 +0000 |
3812 | +++ tests/vmtests/test_apt_source.py 2016-08-02 09:15:49 +0000 |
3813 | @@ -0,0 +1,238 @@ |
3814 | +""" test_apt_source |
3815 | + Collection of tests for the apt configuration features |
3816 | +""" |
3817 | +import textwrap |
3818 | + |
3819 | +from . import VMBaseClass |
3820 | +from .releases import base_vm_classes as relbase |
3821 | + |
3822 | +from unittest import SkipTest |
3823 | +from curtin import util |
3824 | + |
3825 | + |
3826 | +class TestAptSrcAbs(VMBaseClass): |
3827 | + """TestAptSrcAbs - Basic tests for apt features of curtin""" |
3828 | + interactive = False |
3829 | + extra_disks = [] |
3830 | + fstab_expected = {} |
3831 | + disk_to_check = [] |
3832 | + collect_scripts = [textwrap.dedent(""" |
3833 | + cd OUTPUT_COLLECT_D |
3834 | + cat /etc/fstab > fstab |
3835 | + ls /dev/disk/by-dname > ls_dname |
3836 | + find /etc/network/interfaces.d > find_interfacesd |
3837 | + apt-key list "F430BBA5" > keyid-F430BBA5 |
3838 | + apt-key list "0165013E" > keyppa-0165013E |
3839 | + apt-key list "F470A0AC" > keylongid-F470A0AC |
3840 | + apt-key list "8280B242" > keyraw-8280B242 |
3841 | + ls -laF /etc/apt/sources.list.d/ > sources.list.d |
3842 | + cp /etc/apt/sources.list.d/curtin-dev-ppa.list . |
3843 | + cp /etc/apt/sources.list.d/my-repo2.list . |
3844 | + cp /etc/apt/sources.list.d/my-repo4.list . |
3845 | + cp /etc/apt/sources.list.d/curtin-dev-ubuntu-test-archive-*.list . |
3846 | + find /etc/apt/sources.list.d/ -maxdepth 1 -name "*ignore*" | wc -l > ic |
3847 | + apt-config dump | grep Retries > aptconf |
3848 | + cp /etc/apt/sources.list sources.list |
3849 | + cp /etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg . |
3850 | + """)] |
3851 | + mirror = "http://us.archive.ubuntu.com/ubuntu" |
3852 | + secmirror = "http://security.ubuntu.com/ubuntu" |
3853 | + |
3854 | + def test_output_files_exist(self): |
3855 | + """test_output_files_exist - Check if all output files exist""" |
3856 | + self.output_files_exist( |
3857 | + ["fstab", "ic", "keyid-F430BBA5", "keylongid-F470A0AC", |
3858 | + "keyraw-8280B242", "keyppa-0165013E", "aptconf", "sources.list", |
3859 | + "curtin-dev-ppa.list", "my-repo2.list", "my-repo4.list"]) |
3860 | + self.output_files_exist( |
3861 | + ["curtin-dev-ubuntu-test-archive-%s.list" % self.release]) |
3862 | + |
3863 | + def test_keys_imported(self): |
3864 | + """test_keys_imported - Check if all keys are imported correctly""" |
3865 | + self.check_file_regex("keyid-F430BBA5", |
3866 | + r"Launchpad PPA for Ubuntu Screen Profile") |
3867 | + self.check_file_regex("keylongid-F470A0AC", |
3868 | + r"Ryan Harper") |
3869 | + self.check_file_regex("keyppa-0165013E", |
3870 | + r"Launchpad PPA for curtin developers") |
3871 | + self.check_file_regex("keyraw-8280B242", |
3872 | + r"Christian Ehrhardt") |
3873 | + |
3874 | + def test_preserve_source(self): |
3875 | + """test_preserve_source - no clobbering sources.list by cloud-init""" |
3876 | + self.output_files_exist(["curtin-preserve-sources.cfg"]) |
3877 | + self.check_file_regex("curtin-preserve-sources.cfg", |
3878 | + "apt_preserve_sources_list.*true") |
3879 | + |
3880 | + def test_source_files(self): |
3881 | + """test_source_files - Check generated .lists for correct content""" |
3882 | + # hard coded deb lines |
3883 | + self.check_file_strippedline("curtin-dev-ppa.list", |
3884 | + ("deb http://ppa.launchpad.net/curtin-dev" |
3885 | + "/test-archive/ubuntu xenial main")) |
3886 | + self.check_file_strippedline("my-repo4.list", |
3887 | + ("deb http://ppa.launchpad.net/curtin-dev" |
3888 | + "/test-archive/ubuntu xenial main")) |
3889 | + # mirror and release replacement in deb line |
3890 | + self.check_file_strippedline("my-repo2.list", "deb %s %s multiverse" % |
3891 | + (self.mirror, self.release)) |
3892 | + # auto creation by apt-add-repository |
3893 | + self.check_file_regex("curtin-dev-ubuntu-test-archive-%s.list" % |
3894 | + self.release, |
3895 | + (r"http://ppa.launchpad.net/" |
3896 | + r"curtin-dev/test-archive/ubuntu" |
3897 | + r" %s main" % self.release)) |
3898 | + |
3899 | + def test_ignore_count(self): |
3900 | + """test_ignore_count - Check for files that should not be created""" |
3901 | + self.check_file_strippedline("ic", "0") |
3902 | + |
3903 | + def test_apt_conf(self): |
3904 | + """test_apt_conf - Check if the selected apt conf was set""" |
3905 | + self.check_file_strippedline("aptconf", 'Acquire::Retries "3";') |
3906 | + |
3907 | + |
3908 | +class TestAptSrcCustom(TestAptSrcAbs): |
3909 | + """TestAptSrcNormal - tests valid in the custom sources.list case""" |
3910 | + conf_file = "examples/tests/apt_source_custom.yaml" |
3911 | + |
3912 | + def test_custom_source_list(self): |
3913 | + """test_custom_source_list - Check custom sources with replacement""" |
3914 | + # check that all replacements happened |
3915 | + self.check_file_strippedline("sources.list", |
3916 | + "deb %s %s main restricted" % |
3917 | + (self.mirror, self.release)) |
3918 | + self.check_file_strippedline("sources.list", |
3919 | + "deb-src %s %s main restricted" % |
3920 | + (self.mirror, self.release)) |
3921 | + self.check_file_strippedline("sources.list", |
3922 | + "deb %s %s universe restricted" % |
3923 | + (self.mirror, self.release)) |
3924 | + self.check_file_strippedline("sources.list", |
3925 | + "deb %s %s-security multiverse" % |
3926 | + (self.secmirror, self.release)) |
3927 | + # check for something that guarantees us to come from our test |
3928 | + self.check_file_strippedline("sources.list", |
3929 | + "# nice line to check in test") |
3930 | + |
3931 | + |
3932 | +class TestAptSrcPreserve(TestAptSrcAbs): |
3933 | + """TestAptSrcPreserve - tests valid in the preserved sources.list case""" |
3934 | + conf_file = "examples/tests/apt_source_preserve.yaml" |
3935 | + boot_cloudconf = None |
3936 | + |
3937 | + def test_preserved_source_list(self): |
3938 | + """test_preserved_source_list - Check sources to be preserved as-is""" |
3939 | + # curtin didn't touch it, so we should find what curtin set as default |
3940 | + self.check_file_regex("sources.list", |
3941 | + r"this file is written by cloud-init") |
3942 | + |
3943 | + # overwrite inherited check to match situation here |
3944 | + def test_preserve_source(self): |
3945 | + """test_preserve_source - check apt_preserve_sources_list not set""" |
3946 | + self.output_files_dont_exist(["curtin-preserve-sources.cfg"]) |
3947 | + |
3948 | + |
3949 | +class TestAptSrcModify(TestAptSrcAbs): |
3950 | + """TestAptSrcModify - tests modifying sources.list""" |
3951 | + conf_file = "examples/tests/apt_source_modify.yaml" |
3952 | + |
3953 | + def test_modified_source_list(self): |
3954 | + """test_modified_source_list - Check sources with replacement""" |
3955 | + # we set us.archive which is non default, check for that |
3956 | + # this will catch if a target ever changes the expected defaults we |
3957 | + # have to replace in case there is no custom template |
3958 | + self.check_file_regex("sources.list", |
3959 | + r"us.archive.ubuntu.com") |
3960 | + self.check_file_regex("sources.list", |
3961 | + r"security.ubuntu.com") |
3962 | + |
3963 | + |
3964 | +class TestAptSrcDisablePockets(TestAptSrcAbs): |
3965 | + """TestAptSrcDisablePockets - tests disabling a suite in sources.list""" |
3966 | + conf_file = "examples/tests/apt_source_modify_disable_suite.yaml" |
3967 | + |
3968 | + def test_disabled_suite(self): |
3969 | + """test_disabled_suite - Check if suites were disabled""" |
3970 | + # two not disabled |
3971 | + self.check_file_regex("sources.list", |
3972 | + r"deb.*us.archive.ubuntu.com") |
3973 | + self.check_file_regex("sources.list", |
3974 | + r"deb.*security.ubuntu.com") |
3975 | + # updates disabled |
3976 | + self.check_file_regex("sources.list", |
3977 | + r"# suite disabled by curtin:.*-updates") |
3978 | + |
3979 | + |
3980 | +class TestAptSrcModifyArches(TestAptSrcModify): |
3981 | + """TestAptSrcModify - tests modifying sources.list with per arch mirror""" |
3982 | + # same test, just different yaml to specify the mirrors per arch |
3983 | + conf_file = "examples/tests/apt_source_modify_arches.yaml" |
3984 | + |
3985 | + |
3986 | +class TestAptSrcSearch(TestAptSrcAbs): |
3987 | + """TestAptSrcSearch - tests checking a list of mirror options""" |
3988 | + conf_file = "examples/tests/apt_source_search.yaml" |
3989 | + |
3990 | + def test_mirror_search(self): |
3991 | + """test_mirror_search |
3992 | + Check searching through a mirror list |
3993 | + This is checked in the test (late) intentionally. |
3994 | + No matter if resolution worked or failed it shouldn't fail |
3995 | + fatally (python error and trace). |
3996 | + We just can't rely on the content to be found in that case |
3997 | + so we skip the check then.""" |
3998 | + res1 = util.is_resolvable_url("http://does.not.exist/ubuntu") |
3999 | + res2 = util.is_resolvable_url("http://does.also.not.exist/ubuntu") |
4000 | + res3 = util.is_resolvable_url("http://us.archive.ubuntu.com/ubuntu") |
4001 | + res4 = util.is_resolvable_url("http://security.ubuntu.com/ubuntu") |
4002 | + if res1 or res2 or not res3 or not res4: |
4003 | + raise SkipTest(("Name resolution not as required" |
4004 | + "(%s, %s, %s, %s)" % (res1, res2, res3, res4))) |
4005 | + |
4006 | + self.check_file_regex("sources.list", |
4007 | + r"us.archive.ubuntu.com") |
4008 | + self.check_file_regex("sources.list", |
4009 | + r"security.ubuntu.com") |
4010 | + |
4011 | + |
4012 | +class XenialTestAptSrcCustom(relbase.xenial, TestAptSrcCustom): |
4013 | + """ XenialTestAptSrcCustom |
4014 | + apt feature Test for Xenial with a custom template |
4015 | + """ |
4016 | + __test__ = True |
4017 | + |
4018 | + |
4019 | +class XenialTestAptSrcPreserve(relbase.xenial, TestAptSrcPreserve): |
4020 | + """ XenialTestAptSrcPreserve |
4021 | + apt feature Test for Xenial with apt_preserve_sources_list enabled |
4022 | + """ |
4023 | + __test__ = True |
4024 | + |
4025 | + |
4026 | +class XenialTestAptSrcModify(relbase.xenial, TestAptSrcModify): |
4027 | + """ XenialTestAptSrcModify |
4028 | + apt feature Test for Xenial modifying the sources.list of the image |
4029 | + """ |
4030 | + __test__ = True |
4031 | + |
4032 | + |
4033 | +class XenialTestAptSrcSearch(relbase.xenial, TestAptSrcSearch): |
4034 | + """ XenialTestAptSrcModify |
4035 | + apt feature Test for Xenial searching for mirrors |
4036 | + """ |
4037 | + __test__ = True |
4038 | + |
4039 | + |
4040 | +class XenialTestAptSrcModifyArches(relbase.xenial, TestAptSrcModifyArches): |
4041 | + """ XenialTestAptSrcModifyArches |
4042 | + apt feature Test for Xenial checking per arch mirror specification |
4043 | + """ |
4044 | + __test__ = True |
4045 | + |
4046 | + |
4047 | +class XenialTestAptSrcDisablePockets(relbase.xenial, TestAptSrcDisablePockets): |
4048 | + """ XenialTestAptSrcDisablePockets |
4049 | + apt feature Test for Xenial disabling a suite |
4050 | + """ |
4051 | + __test__ = True |
4052 | |
4053 | === added file 'tests/vmtests/test_old_apt_features.py' |
4054 | --- tests/vmtests/test_old_apt_features.py 1970-01-01 00:00:00 +0000 |
4055 | +++ tests/vmtests/test_old_apt_features.py 2016-08-02 09:15:49 +0000 |
4056 | @@ -0,0 +1,80 @@ |
4057 | +""" testold_apt_features |
4058 | + Testing the former minimal apt features of curtin |
4059 | +""" |
4060 | +import re |
4061 | +import textwrap |
4062 | + |
4063 | +from . import VMBaseClass |
4064 | +from .releases import base_vm_classes as relbase |
4065 | + |
4066 | +from curtin import util |
4067 | + |
4068 | + |
4069 | +class TestOldAptAbs(VMBaseClass): |
4070 | + """TestOldAptAbs - Basic tests for old apt features of curtin""" |
4071 | + interactive = False |
4072 | + extra_disks = [] |
4073 | + fstab_expected = {} |
4074 | + disk_to_check = [] |
4075 | + collect_scripts = [textwrap.dedent(""" |
4076 | + cd OUTPUT_COLLECT_D |
4077 | + cat /etc/fstab > fstab |
4078 | + ls /dev/disk/by-dname > ls_dname |
4079 | + find /etc/network/interfaces.d > find_interfacesd |
4080 | + grep -A 3 "Name: debconf/priority" /var/cache/debconf/config.dat > debc |
4081 | + apt-config dump > aptconf |
4082 | + cp /etc/apt/apt.conf.d/90curtin-aptproxy . |
4083 | + cp /etc/apt/sources.list . |
4084 | + cp /etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg . |
4085 | + """)] |
4086 | + arch = util.get_architecture() |
4087 | + if arch in ['amd64', 'i386']: |
4088 | + conf_file = "examples/tests/test_old_apt_features.yaml" |
4089 | + exp_mirror = "http://us.archive.ubuntu.com/ubuntu" |
4090 | + exp_secmirror = "http://archive.ubuntu.com/ubuntu" |
4091 | + if arch in ['s390x', 'arm64', 'armhf', 'powerpc', 'ppc64el']: |
4092 | + conf_file = "examples/tests/test_old_apt_features_ports.yaml" |
4093 | + exp_mirror = "http://ports.ubuntu.com/ubuntu-ports" |
4094 | + exp_secmirror = "http://ports.ubuntu.com/ubuntu-ports" |
4095 | + |
4096 | + def test_output_files_exist(self): |
4097 | + """test_output_files_exist - Check if all output files exist""" |
4098 | + self.output_files_exist( |
4099 | + ["debc", "aptconf", "sources.list", "90curtin-aptproxy", |
4100 | + "curtin-preserve-sources.cfg"]) |
4101 | + |
4102 | + def test_preserve_source(self): |
4103 | + """test_preserve_source - no clobbering sources.list by cloud-init""" |
4104 | + self.check_file_regex("curtin-preserve-sources.cfg", |
4105 | + "apt_preserve_sources_list.*true") |
4106 | + |
4107 | + def test_debconf(self): |
4108 | + """test_debconf - Check if debconf is in place""" |
4109 | + self.check_file_strippedline("debc", "Value: low") |
4110 | + |
4111 | + def test_aptconf(self): |
4112 | + """test_aptconf - Check if apt conf for proxy is in place""" |
4113 | + # this gets configured by tools/launch and get_apt_proxy in |
4114 | + # tests/vmtests/__init__.py, so compare with those |
4115 | + rproxy = r"Acquire::http::Proxy \"" + re.escape(self.proxy) + r"\";" |
4116 | + self.check_file_regex("aptconf", rproxy) |
4117 | + self.check_file_regex("90curtin-aptproxy", rproxy) |
4118 | + |
4119 | + def test_mirrors(self): |
4120 | + """test_mirrors - Check for mirrors placed in source.list""" |
4121 | + |
4122 | + self.check_file_strippedline("sources.list", |
4123 | + "deb %s %s" % |
4124 | + (self.exp_mirror, self.release) + |
4125 | + " main restricted universe multiverse") |
4126 | + self.check_file_strippedline("sources.list", |
4127 | + "deb %s %s-security" % |
4128 | + (self.exp_secmirror, self.release) + |
4129 | + " main restricted universe multiverse") |
4130 | + |
4131 | + |
4132 | +class XenialTestOldApt(relbase.xenial, TestOldAptAbs): |
4133 | + """ XenialTestOldApt |
4134 | + Old apt features for Xenial |
4135 | + """ |
4136 | + __test__ = True |
This is the "new" MP based on https:/ /code.launchpad .net/~paelzer/ curtin/ bug-1574113- derived- repositories/ +merge/ 296118
I'll keep the old MP open for now to have the discussion history available (not sure if it would go away when rejecting the old MP).