File: 0001-config-Support-APT-automated-mirror-selection.patch

package info (click to toggle)
cloud-init 25.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 12,304 kB
  • sloc: python: 136,706; sh: 3,883; makefile: 128; javascript: 30; xml: 22
file content (154 lines) | stat: -rw-r--r-- 6,313 bytes parent folder | download
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
143
144
145
146
147
148
149
150
151
152
153
154
From: Bastian Blank <waldi@debian.org>
Date: Tue, 16 Aug 2022 15:45:11 +0200
Subject: [PATCH] config: Support APT automated mirror selection

From 8dffe8af24604e1949cc991c1db181a69695d945 Mon Sep 17 00:00:00 2001
Forwarded: https://github.com/canonical/cloud-init/pull/1670
---
 cloudinit/config/cc_apt_configure.py               | 22 ++++++-
 .../config/schemas/schema-cloud-config-v1.json     |  5 ++
 .../config/test_apt_configure_mirrorlists_v3.py    | 69 ++++++++++++++++++++++
 3 files changed, 95 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
@@ -146,7 +146,10 @@ def apply_apt(cfg, cloud, gpg):
         matcher = re.compile(matchcfg).search
     _ensure_dependencies(cfg, matcher, cloud)
 
-    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)):
         keys = add_mirror_keys(cfg, cloud, gpg)
         generate_sources_list(cfg, release, mirrors, cloud, keys)
         rename_apt_lists(mirrors, arch)
@@ -590,6 +593,23 @@ def generate_sources_list(cfg, release,
             util.del_file(apt_sources_list)
 
 
+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, gpg, hardened=False):
     """
     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
@@ -1054,6 +1054,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,80 @@
+# This file is part of cloud-init. See LICENSE file for license information.
+
+""" test_apt_custom_mirrorlists
+Test generation of apt mirror lists
+"""
+import logging
+import shutil
+import tempfile
+from contextlib import ExitStack
+from unittest import mock
+
+import pytest
+
+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__)
+
+@pytest.fixture
+def mock_lsb_release():
+    """Fixture to mock lsb_release"""
+    with mock.patch("cloudinit.util.lsb_release") as mock_lsr:
+        mock_lsr.return_value = {"codename": "fakerel"}
+        yield mock_lsr
+
+@pytest.fixture
+def mock_dpkg_arch():
+    """Fixture to mock get_dpkg_architecture"""
+    with mock.patch("cloudinit.util.get_dpkg_architecture") as mock_arch:
+        mock_arch.return_value = "amd64"
+        yield mock_arch
+
+@pytest.fixture
+def temp_root():
+    """Fixture to create and clean up a temporary directory"""
+    root_dir = tempfile.mkdtemp()
+    try:
+        yield root_dir
+    finally:
+        shutil.rmtree(root_dir)
+
+def test_apt_v3_mirrors_list(mock_lsb_release, mock_dpkg_arch, temp_root):
+    """Test generation of mirrorlists"""
+    # Configuration for apt mirror list generation
+    cfg = {"apt": {"generate_mirrorlists": True}}
+
+    # Get cloud instance
+    mycloud = get_cloud("ubuntu")
+
+    # Use ExitStack to manage multiple context managers
+    with ExitStack() as stack:
+        # Mock write_file and ensure_dir
+        mock_writefile = stack.enter_context(
+            mock.patch.object(util, "write_file")
+        )
+        stack.enter_context(mock.patch.object(util, "ensure_dir"))
+
+        # Call the apt configuration handler
+        cc_apt_configure.handle("test", cfg, mycloud, None)
+
+    # Assert that write_file was called with expected arguments
+    assert mock_writefile.call_count == 2
+
+    # Check the specific calls to write_file
+    mock_writefile.assert_has_calls([
+        mock.call(
+            "/etc/apt/mirrors/ubuntu.list",
+            "http://archive.ubuntu.com/ubuntu/\n",
+            mode=0o644,
+        ),
+        mock.call(
+            "/etc/apt/mirrors/ubuntu-security.list",
+            "http://security.ubuntu.com/ubuntu/\n",
+            mode=0o644,
+        )
+    ], any_order=True)
+
+# vi: ts=4 expandtab