1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
|
From 8dffe8af24604e1949cc991c1db181a69695d945 Mon Sep 17 00:00:00 2001
From: Bastian Blank <waldi@debian.org>
Date: Tue, 16 Aug 2022 15:45:11 +0200
Subject: [PATCH] config: Support APT automated mirror selection
Forwarded: https://github.com/canonical/cloud-init/pull/1670
---
cloudinit/config/cc_apt_configure.py | 22 +++++-
.../schemas/schema-cloud-config-v1.json | 5 ++
.../test_apt_configure_mirrorlists_v3.py | 68 +++++++++++++++++++
3 files changed, 94 insertions(+), 1 deletion(-)
create mode 100644 tests/unittests/config/test_apt_configure_mirrorlists_v3.py
Index: cloud-init/cloudinit/config/cc_apt_configure.py
===================================================================
--- cloud-init.orig/cloudinit/config/cc_apt_configure.py
+++ cloud-init/cloudinit/config/cc_apt_configure.py
@@ -220,7 +220,10 @@ def apply_apt(cfg, cloud, target):
mirrors = find_apt_mirror_info(cfg, cloud, arch=arch)
LOG.debug("Apt Mirror info: %s", mirrors)
- if util.is_false(cfg.get("preserve_sources_list", False)):
+ if util.is_true(cfg.get("generate_mirrorlists", False)):
+ generate_mirrorlists(cfg, mirrors, cloud)
+
+ elif util.is_false(cfg.get("preserve_sources_list", False)):
add_mirror_keys(cfg, target)
generate_sources_list(cfg, release, mirrors, cloud)
rename_apt_lists(mirrors, target, arch)
@@ -485,6 +488,23 @@ def generate_sources_list(cfg, release,
util.write_file(aptsrc, disabled, mode=0o644)
+def generate_mirrorlists(cfg, mirrors, cloud):
+ """generate_mirrorlists
+ create one file for every mirror for apt-transport-mirror(1)"""
+ aptmir = pathlib.Path("/etc/apt/mirrors")
+ util.ensure_dir(str(aptmir))
+ util.write_file(
+ str(aptmir / f"{cloud.distro.name}.list"),
+ f"{mirrors['PRIMARY']}\n",
+ mode=0o644,
+ )
+ util.write_file(
+ str(aptmir / f"{cloud.distro.name}-security.list"),
+ f"{mirrors['SECURITY']}\n",
+ mode=0o644,
+ )
+
+
def add_apt_key_raw(key, file_name, hardened=False, target=None):
"""
actual adding of a key as defined in key argument
Index: cloud-init/cloudinit/config/schemas/schema-cloud-config-v1.json
===================================================================
--- cloud-init.orig/cloudinit/config/schemas/schema-cloud-config-v1.json
+++ cloud-init/cloudinit/config/schemas/schema-cloud-config-v1.json
@@ -749,6 +749,11 @@
"default": false,
"description": "By default, cloud-init will generate a new sources list in ``/etc/apt/sources.list.d`` based on any changes specified in cloud config. To disable this behavior and preserve the sources list from the pristine image, set ``preserve_sources_list`` to ``true``.\n\nThe ``preserve_sources_list`` option overrides all other config keys that would alter ``sources.list`` or ``sources.list.d``, **except** for additional sources to be added to ``sources.list.d``."
},
+ "generate_mirrorlists": {
+ "type": "boolean",
+ "default": false,
+ "description": "Write lists for APT automated mirror selection (``apt-transport-mirror(1)``). It will write separate lists for both the PRIMARY and SECURITY mirror into ``/etc/apt/mirrors/${DIST}.list`` and ``/etc/apt/mirrors/${DIST}-security.list``. Those can then be used in the APT source.list as ``mirror+file:///etc/apt/mirrors/${DIST}.list``. No ``/etc/apt/sources.list`` will be writte in this case."
+ },
"disable_suites": {
"type": "array",
"items": {
Index: cloud-init/tests/unittests/config/test_apt_configure_mirrorlists_v3.py
===================================================================
--- /dev/null
+++ cloud-init/tests/unittests/config/test_apt_configure_mirrorlists_v3.py
@@ -0,0 +1,68 @@
+# This file is part of cloud-init. See LICENSE file for license information.
+
+""" test_apt_custom_mirrorlists
+Test creation of mirrorlists
+"""
+import logging
+import shutil
+import tempfile
+from contextlib import ExitStack
+from unittest import mock
+from unittest.mock import call
+
+from cloudinit import subp, util
+from cloudinit.config import cc_apt_configure
+from tests.unittests import helpers as t_help
+from tests.unittests.util import get_cloud
+
+LOG = logging.getLogger(__name__)
+
+
+class TestAptSourceConfigMirrorlists(t_help.FilesystemMockingTestCase):
+ """TestAptSourceConfigMirrorlists - Class to test mirrorlists rendering"""
+
+ def setUp(self):
+ super().setUp()
+ self.subp = subp.subp
+ self.new_root = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, self.new_root)
+
+ rpatcher = mock.patch("cloudinit.util.lsb_release")
+ get_rel = rpatcher.start()
+ get_rel.return_value = {"codename": "fakerel"}
+ self.addCleanup(rpatcher.stop)
+ apatcher = mock.patch("cloudinit.util.get_dpkg_architecture")
+ get_arch = apatcher.start()
+ get_arch.return_value = "amd64"
+ self.addCleanup(apatcher.stop)
+
+ def test_apt_v3_mirrors_list(self):
+ """test_apt_v3_mirrors_list"""
+ cfg = {"apt": {"generate_mirrorlists": True}}
+
+ mycloud = get_cloud("ubuntu")
+
+ with ExitStack() as stack:
+ mock_writefile = stack.enter_context(
+ mock.patch.object(util, "write_file")
+ )
+ stack.enter_context(mock.patch.object(util, "ensure_dir"))
+ cc_apt_configure.handle("test", cfg, mycloud, LOG, None)
+
+ mock_writefile.assert_has_calls(
+ [
+ call(
+ "/etc/apt/mirrors/ubuntu.list",
+ "http://archive.ubuntu.com/ubuntu/\n",
+ mode=0o644,
+ ),
+ call(
+ "/etc/apt/mirrors/ubuntu-security.list",
+ "http://security.ubuntu.com/ubuntu/\n",
+ mode=0o644,
+ ),
+ ]
+ )
+
+
+# vi: ts=4 expandtab
|