Merge ~mwhudson/ubuntu-cdimage:deb822-sources into ubuntu-cdimage:main

Proposed by Michael Hudson-Doyle
Status: Merged
Approved by: Łukasz Zemczak
Approved revision: d0db6ef81c6e272bef70e8849665c7c0d192c14b
Merged at revision: 25a5d7e1c7677d279ca413b006dd856cb2ad03d6
Proposed branch: ~mwhudson/ubuntu-cdimage:deb822-sources
Merge into: ubuntu-cdimage:main
Diff against target: 605 lines (+381/-18)
9 files modified
.launchpad.yaml (+11/-1)
lib/cdimage/build.py (+14/-4)
lib/cdimage/config.py (+1/-0)
lib/cdimage/germinate.py (+12/-4)
lib/cdimage/mirror.py (+127/-0)
lib/cdimage/tests/helpers.py (+5/-0)
lib/cdimage/tests/test_build.py (+15/-6)
lib/cdimage/tests/test_germinate.py (+38/-3)
lib/cdimage/tests/test_mirror.py (+158/-0)
Reviewer Review Type Date Requested Status
Łukasz Zemczak Approve
Review via email: mp+457014@code.launchpad.net

Description of the change

This changes ubuntu-cdimage to set up a chdist-like apt environment for each architecture, adds code to use that for germination (although that cannot be used until https://code.launchpad.net/~mwhudson/germinate/+git/germinate-1/+merge/456723 is in place) and tells debian-cd about it via an environment variable (although the debian-cd branches to make use of this are not even in review yet).

As such this should be harmless if a little wasteful to merge and deploy at this point. But it would be still interesting to have a review of it!

To post a comment you must log in.
Revision history for this message
Steve Langasek (vorlon) wrote :

appears that this currently fails CI, holding review until resolved https://code.launchpad.net/~mwhudson/ubuntu-cdimage/+git/ubuntu-cdimage-1/+build/67241

Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

I fixed that a while ago now...

Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

I think this is ready for review. I think it will be slightly wasteful (basically it will download the package lists an extra time) but not change behaviour at all in its current form.

Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

So to expand:

1) This can be landed now safely (I think!)
2) Then we can land https://code.launchpad.net/~mwhudson/debian-cd/+git/ubuntu/+merge/460372 which will use the apt configs this branch makes to fetch packages from the pool.
3) The final step is to use the apt configs this branch makes for germination too. The relevant germinate branch has landed now but I don't know if the checkout on the ftpmaster machine has been updated yet.

Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

One thing I am not 100% sure I've got right is handling a build with non-trivial subarch. I should try one of those locally at some point.

Revision history for this message
Łukasz Zemczak (sil2100) wrote :

I think this is good enough. Conceptually, of course, I am not entirely sure if cdimage is the right place for generation of such configuration. Conceptually cdimage should only care about triggering builds, packing them up, publishing, cleaning up... Conceptually... But in reality right now it doesn't.

So I'm fine with merging this as-is, since I assume it's better to do it here in cdimage than debian-cd as we're running germinate here. That being said, actually I'm curious why we're calling germinate here, since I think the only consumer so far is debian-cd? So many questions, so little answers.

I think subarch handling should work fine, since if it does not then it means cpuarches was broken (which I don't know if we actually use that much anyway).

review: Approve
Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

I think eventually the bulk of this should move into livecd-rootfs or ubuntu-image or imagecraft or whatever. But one step at a time!

Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

Thanks for the review btw!

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/.launchpad.yaml b/.launchpad.yaml
2index bef522b..bb396c1 100644
3--- a/.launchpad.yaml
4+++ b/.launchpad.yaml
5@@ -5,7 +5,17 @@ jobs:
6 run-tests:
7 series: jammy
8 architectures: amd64
9- packages: [dctrl-tools, python3, python3, python3-mock, python3-simplestreams, procmail, squashfs-tools, file, pycodestyle, pyflakes3]
10+ packages:
11+ - dctrl-tools
12+ - file
13+ - procmail
14+ - pycodestyle
15+ - pyflakes3
16+ - python3
17+ - python3-apt
18+ - python3-mock
19+ - python3-simplestreams
20+ - squashfs-tools
21 run: ./run-tests >output
22 output:
23 paths: [output]
24diff --git a/lib/cdimage/build.py b/lib/cdimage/build.py
25index b3b296e..02e59ee 100644
26--- a/lib/cdimage/build.py
27+++ b/lib/cdimage/build.py
28@@ -40,7 +40,7 @@ from cdimage.livefs import (
29 )
30 from cdimage.log import logger, reset_logging
31 from cdimage.mail import get_notify_addresses, send_mail
32-from cdimage.mirror import trigger_mirrors
33+from cdimage.mirror import AptStateManager, trigger_mirrors
34 from cdimage.tracker import tracker_set_rebuild_status
35 from cdimage.tree import Publisher, Tree
36
37@@ -532,10 +532,13 @@ def configure_splash(config):
38 config[key] = generic_image
39
40
41-def run_debian_cd(config):
42+def run_debian_cd(config, apt_state_mgr):
43 log_marker("Building %s daily CDs" % config.capproject)
44 debian_cd_dir = os.path.join(config.root, "debian-cd")
45- subprocess.call(["./build_all.sh"], cwd=debian_cd_dir, env=config.export())
46+ env = config.export()
47+ for cpuarch in config.cpuarches:
48+ env["APT_CONFIG_" + cpuarch] = apt_state_mgr.apt_conf_for_arch(cpuarch)
49+ subprocess.call(["./build_all.sh"], cwd=debian_cd_dir, env=env)
50
51
52 def fix_permissions(config):
53@@ -652,7 +655,14 @@ def build_image_set_locked(config, options):
54 else:
55 assert not config["CDIMAGE_PREINSTALLED"]
56
57+ apt_state_mgr = AptStateManager(config)
58+ apt_state_mgr.setup()
59+
60 log_marker("Germinating")
61+ # Cannot use apt_state_mgr for germination until
62+ # https://code.launchpad.net/~mwhudson/germinate/+git/
63+ # germinate-1/+merge/456723
64+ # is merged.
65 germination = Germination(config)
66 germination.run()
67
68@@ -669,7 +679,7 @@ def build_image_set_locked(config, options):
69
70 configure_splash(config)
71
72- run_debian_cd(config)
73+ run_debian_cd(config, apt_state_mgr)
74 copy_netboot_tarballs(config)
75 fix_permissions(config)
76
77diff --git a/lib/cdimage/config.py b/lib/cdimage/config.py
78index 3de5c65..1c263cc 100644
79--- a/lib/cdimage/config.py
80+++ b/lib/cdimage/config.py
81@@ -288,6 +288,7 @@ _allowed_keys = (
82 "EXTRA_PPAS",
83 "CHANNEL",
84 "SIMPLESTREAMS",
85+ "APT_PROXY",
86 )
87
88
89diff --git a/lib/cdimage/germinate.py b/lib/cdimage/germinate.py
90index f33ef6a..dcc5e88 100644
91--- a/lib/cdimage/germinate.py
92+++ b/lib/cdimage/germinate.py
93@@ -39,10 +39,11 @@ class GerminateNotInstalled(Exception):
94
95
96 class Germination:
97- def __init__(self, config, prefer_vcs=True):
98+ def __init__(self, config, prefer_vcs=True, apt_state_mgr=None):
99 self.config = config
100 # Set to False to use old-style seed checkouts.
101 self.prefer_vcs = prefer_vcs
102+ self.apt_state_mgr = apt_state_mgr
103
104 @property
105 def germinate_path(self):
106@@ -152,13 +153,20 @@ class Germination:
107 command = [
108 self.germinate_path,
109 "--seed-source", ",".join(self.seed_sources()),
110- "--mirror", find_mirror(self.config.project, arch),
111 "--seed-dist", self.seed_dist(),
112- "--dist", ",".join(self.germinate_dists),
113 "--arch", cpuarch,
114- "--components", ",".join(self.components),
115 "--no-rdepends",
116 ]
117+ if self.apt_state_mgr is not None:
118+ command.extend([
119+ "--apt-config", self.apt_state_mgr.apt_conf_for_arch(cpuarch),
120+ ])
121+ else:
122+ command.extend([
123+ "--mirror", find_mirror(self.config.project, arch),
124+ "--components", ",".join(self.components),
125+ "--dist", ",".join(self.germinate_dists),
126+ ])
127 if self.use_vcs:
128 command.append("--vcs=git")
129 proxy_check_call(
130diff --git a/lib/cdimage/mirror.py b/lib/cdimage/mirror.py
131index c02bda6..3d0d2b5 100644
132--- a/lib/cdimage/mirror.py
133+++ b/lib/cdimage/mirror.py
134@@ -22,6 +22,7 @@ import os
135 import subprocess
136
137 from cdimage.log import logger
138+from cdimage import osextras
139
140 __metaclass__ = type
141
142@@ -124,3 +125,129 @@ def trigger_mirrors(config):
143
144 for host in _get_mirrors_async(config):
145 _trigger_mirror(config, key, "archvsync", host, background=True)
146+
147+
148+APT_CONF_TMPL = """\
149+Apt {{
150+ Architecture "{ARCH}";
151+ Architectures "{OTHERARCH}";
152+}};
153+
154+Dir "{DIR}";
155+Dir::state::status "/dev/null";
156+"""
157+
158+SOURCES_TMPL = """\
159+Types: deb deb-src
160+URIs: {MIRROR}
161+Suites: {SUITES}
162+Components: {COMPONENTS}
163+Signed-By: {KEYRING}
164+"""
165+
166+
167+class AptStateManager:
168+ def __init__(self, config):
169+ self.config = config
170+ self._apt_conf_per_arch = {}
171+
172+ def _output_dir(self, arch):
173+ return os.path.join(
174+ self.config.root, "scratch", self.config.subtree,
175+ self.config.project, self.config.full_series,
176+ self.config.image_type, "apt-state", arch)
177+
178+ def _otherarch(self, arch):
179+ # XXX should probably move this knowledge into ubuntu-cdimage
180+ # once debian-cd no longer cares.
181+ debian_cd_dir = os.path.join(self.config.root, "debian-cd")
182+ archlist_file = os.path.join(
183+ debian_cd_dir, "data", self.config.series, "multiarch", arch)
184+ if os.path.exists(archlist_file):
185+ with open(archlist_file) as f:
186+ return f.read().strip()
187+ return arch
188+
189+ def _components(self):
190+ components = ["main"]
191+ if not self.config["CDIMAGE_ONLYFREE"]:
192+ components.append("restricted")
193+ if self.config["CDIMAGE_UNSUPPORTED"]:
194+ components.append("universe")
195+ if not self.config["CDIMAGE_ONLYFREE"]:
196+ components.append("multiverse")
197+ return " ".join(components)
198+
199+ def _suites(self):
200+ suite_patterns = ["%s", "%s-security", "%s-updates"]
201+ if self.config.get("PROPOSED", "0") not in ("", "0"):
202+ suite_patterns.append("%s-proposed")
203+ return " ".join(
204+ [pattern % self.config.series for pattern in suite_patterns])
205+
206+ def _get_sources_text(self, arch):
207+ if self.config["CDIMAGE_POOL_SOURCES"]:
208+ with open(self.config["CDIMAGE_POOL_SOURCES"]) as fp:
209+ return fp.read()
210+
211+ keyring = "/etc/apt/trusted.gpg.d/ubuntu-keyring-2018-archive.gpg"
212+
213+ return SOURCES_TMPL.format(
214+ MIRROR=find_mirror(self.config, arch),
215+ SUITES=self._suites(),
216+ COMPONENTS=self._components(),
217+ KEYRING=keyring)
218+
219+ def _setup_arch(self, arch):
220+ state_dir = self._output_dir(arch)
221+ osextras.mkemptydir(state_dir)
222+
223+ conf_path = os.path.join(state_dir, 'base.conf')
224+ with open(conf_path, 'w') as conf:
225+ conf.write(APT_CONF_TMPL.format(
226+ ARCH=arch,
227+ OTHERARCH=self._otherarch(arch),
228+ DIR=state_dir))
229+
230+ needed_dirs = [
231+ 'etc/apt/sources.list.d',
232+ 'etc/apt/apt.conf.d',
233+ 'etc/apt/preferences.d',
234+ 'var/lib/apt/lists/partial',
235+ ]
236+
237+ for path in needed_dirs:
238+ osextras.mkemptydir(os.path.join(state_dir, path))
239+
240+ sources_path = os.path.join(
241+ state_dir, 'etc/apt/sources.list.d/default.sources')
242+
243+ with open(sources_path, 'w') as sources:
244+ sources.write(self._get_sources_text(arch))
245+
246+ if self.config["APT_PROXY"]:
247+ proxy_conf_path = os.path.join(
248+ state_dir, "etc/apt/apt.conf.d/proxy.conf")
249+ with open(proxy_conf_path, "w") as proxy_conf:
250+ proxy_conf.write(
251+ 'Acquire::http::Proxy "{PROXY}";\n'.format(
252+ PROXY=self.config["APT_PROXY"]))
253+ proxy_conf.write(
254+ 'Acquire::https::Proxy "{PROXY}";\n'.format(
255+ PROXY=self.config["APT_PROXY"]))
256+
257+ subprocess.check_call(
258+ ['apt-get', 'update'],
259+ env=dict(os.environ, APT_CONFIG=conf_path))
260+
261+ return conf_path
262+
263+ def setup(self):
264+ for arch in self.config.cpuarches:
265+ logger.info(
266+ "Setting up apt state for %s/%s ...",
267+ self.config.series, arch)
268+ self._apt_conf_per_arch[arch] = self._setup_arch(arch)
269+
270+ def apt_conf_for_arch(self, arch):
271+ return self._apt_conf_per_arch[arch]
272diff --git a/lib/cdimage/tests/helpers.py b/lib/cdimage/tests/helpers.py
273index 5e107ce..d1d9db5 100644
274--- a/lib/cdimage/tests/helpers.py
275+++ b/lib/cdimage/tests/helpers.py
276@@ -145,3 +145,8 @@ def touch(path):
277 def date_to_time(date):
278 """Return UTC seconds-since-epoch for a string in the form "20130321"."""
279 return calendar.timegm(time.strptime(date, "%Y%m%d"))
280+
281+
282+class StubAptStateManager:
283+ def apt_conf_for_arch(self, arch):
284+ return arch + "/apt.conf"
285diff --git a/lib/cdimage/tests/test_build.py b/lib/cdimage/tests/test_build.py
286index c893613..d38a91c 100644
287--- a/lib/cdimage/tests/test_build.py
288+++ b/lib/cdimage/tests/test_build.py
289@@ -59,7 +59,7 @@ from cdimage.build import (
290 from cdimage.config import Config
291 from cdimage.log import logger
292 from cdimage.mail import text_file_type
293-from cdimage.tests.helpers import TestCase, mkfile, touch
294+from cdimage.tests.helpers import TestCase, mkfile, touch, StubAptStateManager
295
296 __metaclass__ = type
297
298@@ -687,8 +687,10 @@ class TestBuildImageSet(TestCase):
299 @mock.patch("subprocess.call", return_value=0)
300 def test_run_debian_cd(self, mock_call):
301 self.config["CAPPROJECT"] = "Ubuntu"
302+ self.config["ARCHES"] = "amd64 arm64"
303+ self.config.set_default_cpuarches()
304 self.capture_logging()
305- run_debian_cd(self.config)
306+ run_debian_cd(self.config, StubAptStateManager())
307 self.assertLogEqual([
308 "===== Building Ubuntu daily CDs =====",
309 self.epoch_date,
310@@ -696,6 +698,9 @@ class TestBuildImageSet(TestCase):
311 expected_cwd = os.path.join(self.temp_dir, "debian-cd")
312 mock_call.assert_called_once_with(
313 ["./build_all.sh"], cwd=expected_cwd, env=mock.ANY)
314+ env = mock_call.call_args.kwargs["env"]
315+ self.assertEqual("amd64/apt.conf", env["APT_CONFIG_amd64"])
316+ self.assertEqual("arm64/apt.conf", env["APT_CONFIG_arm64"])
317
318 @mock.patch("subprocess.call", return_value=0)
319 def test_run_debian_cd_reexports_config(self, mock_call):
320@@ -712,7 +717,7 @@ class TestBuildImageSet(TestCase):
321 os.environ["CDIMAGE_ROOT"] = self.temp_dir
322 config = Config()
323 self.capture_logging()
324- run_debian_cd(config)
325+ run_debian_cd(config, StubAptStateManager())
326 self.assertLogEqual([
327 "===== Building Ubuntu daily CDs =====",
328 self.epoch_date,
329@@ -914,18 +919,20 @@ class TestBuildImageSet(TestCase):
330 return mock.call([
331 germinate_path,
332 "--seed-source", mock.ANY,
333- "--mirror", "http://ftpmaster.internal/ubuntu/",
334 "--seed-dist", "ubuntu.bionic",
335- "--dist", "bionic,bionic-security,bionic-updates",
336 "--arch", arch,
337- "--components", "main,restricted",
338 "--no-rdepends",
339+ "--mirror", "http://ftpmaster.internal/ubuntu/",
340+ "--components", "main,restricted",
341+ "--dist", "bionic,bionic-security,bionic-updates",
342 "--vcs=git",
343 ], cwd=os.path.join(germinate_output, arch), env=mock.ANY)
344
345 mock_call.assert_has_calls([
346 mock.call([
347 "make", "-C", os.path.dirname(britney_makefile)]),
348+ mock.call(["apt-get", "update"], env=mock.ANY),
349+ mock.call(["apt-get", "update"], env=mock.ANY),
350 germinate_command("amd64"),
351 germinate_command("i386"),
352 mock.call(
353@@ -964,6 +971,8 @@ class TestBuildImageSet(TestCase):
354 DATE
355 ===== Building britney =====
356 DATE
357+ Setting up apt state for bionic/amd64 ...
358+ Setting up apt state for bionic/i386 ...
359 ===== Germinating =====
360 DATE
361 Germinating for bionic/amd64 ...
362diff --git a/lib/cdimage/tests/test_germinate.py b/lib/cdimage/tests/test_germinate.py
363index dd409e1..f567eae 100644
364--- a/lib/cdimage/tests/test_germinate.py
365+++ b/lib/cdimage/tests/test_germinate.py
366@@ -37,7 +37,7 @@ from cdimage.germinate import (
367 NoMasterSeeds,
368 )
369 from cdimage.mail import text_file_type
370-from cdimage.tests.helpers import TestCase, mkfile, touch
371+from cdimage.tests.helpers import TestCase, mkfile, touch, StubAptStateManager
372
373 __metaclass__ = type
374
375@@ -190,12 +190,47 @@ class TestGermination(TestCase):
376 germinate_path,
377 "--seed-source",
378 "https://git.launchpad.net/~ubuntu-core-dev/ubuntu-seeds/+git/",
379- "--mirror", "http://ftpmaster.internal/ubuntu/",
380 "--seed-dist", "ubuntu.bionic",
381- "--dist", "bionic,bionic-security,bionic-updates",
382 "--arch", "amd64",
383+ "--no-rdepends",
384+ "--mirror", "http://ftpmaster.internal/ubuntu/",
385 "--components", "main,restricted",
386+ "--dist", "bionic,bionic-security,bionic-updates",
387+ "--vcs=git",
388+ ]
389+ self.assertEqual(1, mock_check_call.call_count)
390+ self.assertEqual(expected_command, mock_check_call.call_args[0][0])
391+ self.assertEqual(
392+ "%s/amd64" % output_dir, mock_check_call.call_args[1]["cwd"])
393+
394+ @mock.patch("subprocess.check_call")
395+ def test_germinate_arch_with_apt_state_manager(self, mock_check_call):
396+ self.config.root = self.use_temp_dir()
397+ germinate_path = os.path.join(
398+ self.temp_dir, "germinate", "bin", "germinate")
399+ touch(germinate_path)
400+ os.chmod(germinate_path, 0o755)
401+ self.config["DIST"] = "bionic"
402+ self.config["IMAGE_TYPE"] = "daily"
403+ self.config["PROJECT"] = "ubuntu"
404+
405+ output_dir = "%s/scratch/ubuntu/bionic/daily/germinate" % self.temp_dir
406+
407+ def check_call_side_effect(*args, **kwargs):
408+ touch(os.path.join(output_dir, "amd64", "structure"))
409+
410+ mock_check_call.side_effect = check_call_side_effect
411+
412+ self.germination.apt_state_mgr = StubAptStateManager()
413+ self.germination.germinate_arch("amd64")
414+ expected_command = [
415+ germinate_path,
416+ "--seed-source",
417+ "https://git.launchpad.net/~ubuntu-core-dev/ubuntu-seeds/+git/",
418+ "--seed-dist", "ubuntu.bionic",
419+ "--arch", "amd64",
420 "--no-rdepends",
421+ "--apt-config", "amd64/apt.conf",
422 "--vcs=git",
423 ]
424 self.assertEqual(1, mock_check_call.call_count)
425diff --git a/lib/cdimage/tests/test_mirror.py b/lib/cdimage/tests/test_mirror.py
426index b3b913d..56b00fe 100644
427--- a/lib/cdimage/tests/test_mirror.py
428+++ b/lib/cdimage/tests/test_mirror.py
429@@ -19,7 +19,10 @@
430
431 from __future__ import print_function, with_statement
432
433+import glob
434 import os
435+import subprocess
436+import sys
437
438 try:
439 from unittest import mock
440@@ -28,6 +31,7 @@ except ImportError:
441
442 from cdimage.config import Config, all_series
443 from cdimage.mirror import (
444+ AptStateManager,
445 UnknownManifestFile,
446 _get_mirror_key,
447 _get_mirrors,
448@@ -220,3 +224,157 @@ class TestTriggerMirrors(TestCase):
449 "w"):
450 trigger_mirrors(self.config)
451 mock_trigger_mirror.assert_not_called()
452+
453+
454+class TestAptStateManager(TestCase):
455+ def test_output_dir(self):
456+ config = Config(read=False)
457+ config.root = "/cdimage"
458+ config["PROJECT"] = "ubuntu"
459+ config["DIST"] = "noble"
460+ config["IMAGE_TYPE"] = "daily"
461+ mgr = AptStateManager(config)
462+ self.assertEqual(
463+ "/cdimage/scratch/ubuntu/noble/daily/apt-state/amd64",
464+ mgr._output_dir("amd64"))
465+
466+ def test_otherarch_no_foreign_arch(self):
467+ config = Config(read=False)
468+ config.root = self.use_temp_dir()
469+ mgr = AptStateManager(config)
470+ self.assertEqual("arm64", mgr._otherarch("arm64"))
471+
472+ def test_otherarch_with_foreign_arch(self):
473+ config = Config(read=False)
474+ config.root = self.use_temp_dir()
475+ config["DIST"] = "noble"
476+ # This is duplicating the implementation a bit too much to be
477+ # a truly useful tests. But really this information should be
478+ # moved into ubuntu-cdimage somewhere.
479+ archlist_path = os.path.join(
480+ config.root, "debian-cd", "data", config.series,
481+ "multiarch", "arm64")
482+ os.makedirs(os.path.dirname(archlist_path))
483+ with open(archlist_path, 'w') as fp:
484+ fp.write("armhf\n")
485+ mgr = AptStateManager(config)
486+ self.assertEqual("armhf", mgr._otherarch("arm64"))
487+
488+ def test_components(self):
489+ config = Config(read=False)
490+ mgr = AptStateManager(config)
491+ self.assertEqual("main restricted", mgr._components())
492+ config["CDIMAGE_ONLYFREE"] = "1"
493+ self.assertEqual("main", mgr._components())
494+ config["CDIMAGE_UNSUPPORTED"] = "1"
495+ self.assertEqual("main universe", mgr._components())
496+ del config["CDIMAGE_ONLYFREE"]
497+ self.assertEqual(
498+ "main restricted universe multiverse", mgr._components())
499+
500+ def test_suites(self):
501+ config = Config(read=False)
502+ config["DIST"] = "noble"
503+ mgr = AptStateManager(config)
504+ self.assertEqual("noble noble-security noble-updates", mgr._suites())
505+ config["PROPOSED"] = "1"
506+ self.assertEqual(
507+ "noble noble-security noble-updates noble-proposed", mgr._suites())
508+
509+ @mock.patch("cdimage.mirror.find_mirror")
510+ @mock.patch("cdimage.mirror.AptStateManager._components")
511+ @mock.patch("cdimage.mirror.AptStateManager._suites")
512+ def test_get_sources_text(self, m_suites, m_components, m_find_mirror):
513+ m_find_mirror.return_value = "MIRROR"
514+ m_components.return_value = "COMPONENTS"
515+ m_suites.return_value = "SUITES"
516+ config = Config(read=False)
517+ config["DIST"] = "noble"
518+ mgr = AptStateManager(config)
519+ expected = """\
520+Types: deb deb-src
521+URIs: MIRROR
522+Suites: SUITES
523+Components: COMPONENTS
524+Signed-By: /etc/apt/trusted.gpg.d/ubuntu-keyring-2018-archive.gpg
525+"""
526+ self.assertEqual(expected, mgr._get_sources_text("s390x"))
527+
528+ def test_get_sources_text_override(self):
529+ config = Config(read=False)
530+ sources_path = os.path.join(self.use_temp_dir(), "my.sources")
531+ content = "# content\n"
532+ with open(sources_path, 'w') as fp:
533+ fp.write(content)
534+ config["CDIMAGE_POOL_SOURCES"] = sources_path
535+ mgr = AptStateManager(config)
536+ self.assertEqual(content, mgr._get_sources_text("s390x"))
537+
538+ def _get_apt_config(self, apt_config, var, meth='find'):
539+ env = dict(os.environ, APT_CONFIG=apt_config)
540+ cmd = [
541+ sys.executable,
542+ "-c",
543+ "import apt_pkg, sys; apt_pkg.init_config()\n"
544+ "print(apt_pkg.config.{}(sys.argv[1]))".format(meth),
545+ var
546+ ]
547+ cp = subprocess.run(
548+ cmd, env=env, encoding='utf-8', stdout=subprocess.PIPE, check=True)
549+ return cp.stdout.strip()
550+
551+ @mock.patch("cdimage.mirror.AptStateManager._output_dir")
552+ @mock.patch("cdimage.mirror.AptStateManager._get_sources_text")
553+ @mock.patch("cdimage.mirror.AptStateManager._otherarch")
554+ def test_setup_arch(self, m_otherarch, m_get_sources_text, m_output_dir):
555+ m_otherarch.return_value = "OTHERARCH"
556+ # We *could* create a local apt repo with apt-ftparchive and
557+ # arrange to point the apt update call _setup_arch does to it
558+ # but that seems like a lot of effort.
559+ m_get_sources_text.return_value = "# Our content\n"
560+ m_output_dir.return_value = self.use_temp_dir()
561+ config = Config(read=False)
562+ config["DIST"] = "noble"
563+ mgr = AptStateManager(config)
564+ apt_conf = mgr._setup_arch("ARCH")
565+ self.assertEqual(
566+ "ARCH",
567+ self._get_apt_config(apt_conf, "Apt::Architecture"))
568+ self.assertEqual(
569+ "OTHERARCH",
570+ self._get_apt_config(apt_conf, "Apt::Architectures"))
571+ sources_list_d = self._get_apt_config(
572+ apt_conf, "Dir::Etc::sourceparts", meth='find_dir')
573+ sources_files = glob.glob(os.path.join(sources_list_d, "*.sources"))
574+ self.assertEqual(1, len(sources_files))
575+ with open(sources_files[0]) as fp:
576+ self.assertEqual("# Our content\n", fp.read())
577+
578+ @mock.patch("subprocess.check_call")
579+ def test_setup_arch_proxy(self, m_check_call):
580+ config = Config(read=False)
581+ config["DIST"] = "noble"
582+ config["APT_PROXY"] = "http://localhost:3128"
583+ mgr = AptStateManager(config)
584+ apt_conf = mgr._setup_arch("ARCH")
585+ self.assertEqual(
586+ "http://localhost:3128",
587+ self._get_apt_config(apt_conf, "Acquire::http::Proxy"))
588+ self.assertEqual(
589+ "http://localhost:3128",
590+ self._get_apt_config(apt_conf, "Acquire::https::Proxy"))
591+
592+ @mock.patch("cdimage.mirror.AptStateManager._setup_arch")
593+ def test_setup(self, m_setup_arch):
594+ self.capture_logging()
595+ m_setup_arch.side_effect = lambda arch: arch + 'dir'
596+ config = Config(read=False)
597+ config["ARCHES"] = (
598+ "arch1 arch1+subarch arch2+subarch1 arch2+subarch2 arch3")
599+ config.set_default_cpuarches()
600+ mgr = AptStateManager(config)
601+ mgr.setup()
602+ self.assertCountEqual(
603+ [mock.call("arch1"), mock.call("arch2"), mock.call("arch3")],
604+ m_setup_arch.mock_calls)
605+ self.assertEqual('arch1dir', mgr.apt_conf_for_arch('arch1'))

Subscribers

People subscribed via source and target branches