summaryrefslogtreecommitdiff
path: root/cloudinit/sources/tests
diff options
context:
space:
mode:
authorChad Smith <chad.smith@canonical.com>2021-11-18 15:08:33 -0700
committerGitHub <noreply@github.com>2021-11-18 15:08:33 -0700
commit7ebf04e3a1920b451a0bb4b6302706a2b6632d6b (patch)
tree064594d23bd05dcebcf74e550b2d739a829e9304 /cloudinit/sources/tests
parent3c6b594b90b0647f398f41962def45fd0953ce5b (diff)
downloadvyos-cloud-init-7ebf04e3a1920b451a0bb4b6302706a2b6632d6b.tar.gz
vyos-cloud-init-7ebf04e3a1920b451a0bb4b6302706a2b6632d6b.zip
lxd: add preference for LXD cloud-init.* config keys over user keys (#1108)
LXD now adds cloud-init scoped configuration keys network-config, user-data and vendor-data. The existing user.user-data, user.vendor-data, user.network-config and meta-data will be deprecated in newer LXD. cloud-init will prefer LXD config keys cloud-init.* keys above user.* keys even if both are present. Warnings will be emitted for ignored user.* keys if cloud-init.* overrides are present. Expectation is that the configuration user.network-config, user.meta-data, user.user-data and user.vendor-data* keys should not be present at the same time as the comparable cloud-init.* keys.
Diffstat (limited to 'cloudinit/sources/tests')
-rw-r--r--cloudinit/sources/tests/test_lxd.py252
1 files changed, 225 insertions, 27 deletions
diff --git a/cloudinit/sources/tests/test_lxd.py b/cloudinit/sources/tests/test_lxd.py
index c2027616..fc2a41df 100644
--- a/cloudinit/sources/tests/test_lxd.py
+++ b/cloudinit/sources/tests/test_lxd.py
@@ -2,13 +2,17 @@
from collections import namedtuple
from copy import deepcopy
+import json
+import re
import stat
from unittest import mock
import yaml
import pytest
-from cloudinit.sources import DataSourceLXD as lxd, UNSET
+from cloudinit.sources import (
+ DataSourceLXD as lxd, InvalidMetaDataException, UNSET
+)
DS_PATH = "cloudinit.sources.DataSourceLXD."
@@ -24,8 +28,6 @@ NETWORK_V1 = {
}
]
}
-NETWORK_V1_MANUAL = deepcopy(NETWORK_V1)
-NETWORK_V1_MANUAL["config"][0]["subnets"][0]["control"] = "manual"
def _add_network_v1_device(devname) -> dict:
@@ -79,18 +81,15 @@ def lxd_ds(request, paths, lxd_metadata):
class TestGenerateFallbackNetworkConfig:
@pytest.mark.parametrize(
- "uname_machine,systemd_detect_virt,network_mode,expected", (
+ "uname_machine,systemd_detect_virt,expected", (
# None for systemd_detect_virt returns None from which
- ({}, None, "", NETWORK_V1),
- ({}, None, "dhcp", NETWORK_V1),
- # invalid network_mode logs warning
- ({}, None, "bogus", NETWORK_V1),
- ({}, None, "link-local", NETWORK_V1_MANUAL),
- ("anything", "lxc\n", "", NETWORK_V1),
+ ({}, None, NETWORK_V1),
+ ({}, None, NETWORK_V1),
+ ("anything", "lxc\n", NETWORK_V1),
# `uname -m` on kvm determines devname
- ("x86_64", "kvm\n", "", _add_network_v1_device("enp5s0")),
- ("ppc64le", "kvm\n", "", _add_network_v1_device("enp0s5")),
- ("s390x", "kvm\n", "", _add_network_v1_device("enc9"))
+ ("x86_64", "kvm\n", _add_network_v1_device("enp5s0")),
+ ("ppc64le", "kvm\n", _add_network_v1_device("enp0s5")),
+ ("s390x", "kvm\n", _add_network_v1_device("enc9"))
)
)
@mock.patch(DS_PATH + "util.system_info")
@@ -103,22 +102,14 @@ class TestGenerateFallbackNetworkConfig:
m_system_info,
uname_machine,
systemd_detect_virt,
- network_mode,
expected,
- caplog
):
- """Return network config v2 based on uname -m, systemd-detect-virt.
-
- LXC config network_mode of "link-local" will determine whether to set
- "activation-mode: manual", leaving the interface down.
- """
+ """Return network config v2 based on uname -m, systemd-detect-virt."""
if systemd_detect_virt is None:
m_which.return_value = None
m_system_info.return_value = {"uname": ["", "", "", "", uname_machine]}
m_subp.return_value = (systemd_detect_virt, "")
- assert expected == lxd.generate_fallback_network_config(
- network_mode=network_mode
- )
+ assert expected == lxd.generate_fallback_network_config()
if systemd_detect_virt is None:
assert 0 == m_subp.call_count
assert 0 == m_system_info.call_count
@@ -130,10 +121,6 @@ class TestGenerateFallbackNetworkConfig:
assert 0 == m_system_info.call_count
else:
assert 1 == m_system_info.call_count
- if network_mode not in ("dhcp", "", "link-local"):
- assert "Ignoring unexpected value user.network_mode: {}".format(
- network_mode
- ) in caplog.text
class TestDataSourceLXD:
@@ -182,4 +169,215 @@ class TestIsPlatformViable:
else:
assert 0 == m_lstat.call_count
+
+class TestReadMetadata:
+ @pytest.mark.parametrize(
+ "url_responses,expected,logs", (
+ ( # Assert non-JSON format from config route
+ {
+ "http://lxd/1.0/meta-data": "local-hostname: md\n",
+ "http://lxd/1.0/config": "[NOT_JSON",
+ },
+ InvalidMetaDataException(
+ "Unable to determine cloud-init config from"
+ " http://lxd/1.0/config. Expected JSON but found:"
+ " [NOT_JSON"),
+ ["[GET] [HTTP:200] http://lxd/1.0/meta-data",
+ "[GET] [HTTP:200] http://lxd/1.0/config"],
+ ),
+ ( # Assert success on just meta-data
+ {
+ "http://lxd/1.0/meta-data": "local-hostname: md\n",
+ "http://lxd/1.0/config": "[]",
+ },
+ {"1.0": {"config": {}, "meta-data": "local-hostname: md\n"},
+ "meta-data": "local-hostname: md\n"},
+ ["[GET] [HTTP:200] http://lxd/1.0/meta-data",
+ "[GET] [HTTP:200] http://lxd/1.0/config"],
+ ),
+ ( # Assert 404s for config routes log skipping
+ {
+ "http://lxd/1.0/meta-data": "local-hostname: md\n",
+ "http://lxd/1.0/config":
+ '["/1.0/config/user.custom1",'
+ ' "/1.0/config/user.meta-data",'
+ ' "/1.0/config/user.network-config",'
+ ' "/1.0/config/user.user-data",'
+ ' "/1.0/config/user.vendor-data"]',
+ "http://lxd/1.0/config/user.custom1": "custom1",
+ "http://lxd/1.0/config/user.meta-data": "", # 404
+ "http://lxd/1.0/config/user.network-config": "net-config",
+ "http://lxd/1.0/config/user.user-data": "", # 404
+ "http://lxd/1.0/config/user.vendor-data": "", # 404
+ },
+ {
+ "1.0": {
+ "config": {
+ "user.custom1": "custom1", # Not promoted
+ "user.network-config": "net-config",
+ },
+ "meta-data": "local-hostname: md\n",
+ },
+ "meta-data": "local-hostname: md\n",
+ "network-config": "net-config",
+ },
+ [
+ "Skipping http://lxd/1.0/config/user.vendor-data on"
+ " [HTTP:404]",
+ "Skipping http://lxd/1.0/config/user.meta-data on"
+ " [HTTP:404]",
+ "Skipping http://lxd/1.0/config/user.user-data on"
+ " [HTTP:404]",
+ "[GET] [HTTP:200] http://lxd/1.0/config",
+ "[GET] [HTTP:200] http://lxd/1.0/config/user.custom1",
+ "[GET] [HTTP:200]"
+ " http://lxd/1.0/config/user.network-config",
+ ],
+ ),
+ ( # Assert all CONFIG_KEY_ALIASES promoted to top-level keys
+ {
+ "http://lxd/1.0/meta-data": "local-hostname: md\n",
+ "http://lxd/1.0/config":
+ '["/1.0/config/user.custom1",'
+ ' "/1.0/config/user.meta-data",'
+ ' "/1.0/config/user.network-config",'
+ ' "/1.0/config/user.user-data",'
+ ' "/1.0/config/user.vendor-data"]',
+ "http://lxd/1.0/config/user.custom1": "custom1",
+ "http://lxd/1.0/config/user.meta-data": "meta-data",
+ "http://lxd/1.0/config/user.network-config": "net-config",
+ "http://lxd/1.0/config/user.user-data": "user-data",
+ "http://lxd/1.0/config/user.vendor-data": "vendor-data",
+ },
+ {
+ "1.0": {
+ "config": {
+ "user.custom1": "custom1", # Not promoted
+ "user.meta-data": "meta-data",
+ "user.network-config": "net-config",
+ "user.user-data": "user-data",
+ "user.vendor-data": "vendor-data",
+ },
+ "meta-data": "local-hostname: md\n",
+ },
+ "meta-data": "local-hostname: md\n",
+ "network-config": "net-config",
+ "user-data": "user-data",
+ "vendor-data": "vendor-data",
+ },
+ [
+ "[GET] [HTTP:200] http://lxd/1.0/meta-data",
+ "[GET] [HTTP:200] http://lxd/1.0/config",
+ "[GET] [HTTP:200] http://lxd/1.0/config/user.custom1",
+ "[GET] [HTTP:200] http://lxd/1.0/config/user.meta-data",
+ "[GET] [HTTP:200]"
+ " http://lxd/1.0/config/user.network-config",
+ "[GET] [HTTP:200] http://lxd/1.0/config/user.user-data",
+ "[GET] [HTTP:200] http://lxd/1.0/config/user.vendor-data",
+ ],
+ ),
+ ( # Assert cloud-init.* config key values prefered over user.*
+ {
+ "http://lxd/1.0/meta-data": "local-hostname: md\n",
+ "http://lxd/1.0/config":
+ '["/1.0/config/user.meta-data",'
+ ' "/1.0/config/user.network-config",'
+ ' "/1.0/config/user.user-data",'
+ ' "/1.0/config/user.vendor-data",'
+ ' "/1.0/config/cloud-init.network-config",'
+ ' "/1.0/config/cloud-init.user-data",'
+ ' "/1.0/config/cloud-init.vendor-data"]',
+ "http://lxd/1.0/config/user.meta-data": "user.meta-data",
+ "http://lxd/1.0/config/user.network-config":
+ "user.network-config",
+ "http://lxd/1.0/config/user.user-data": "user.user-data",
+ "http://lxd/1.0/config/user.vendor-data":
+ "user.vendor-data",
+ "http://lxd/1.0/config/cloud-init.meta-data":
+ "cloud-init.meta-data",
+ "http://lxd/1.0/config/cloud-init.network-config":
+ "cloud-init.network-config",
+ "http://lxd/1.0/config/cloud-init.user-data":
+ "cloud-init.user-data",
+ "http://lxd/1.0/config/cloud-init.vendor-data":
+ "cloud-init.vendor-data",
+ },
+ {
+ "1.0": {
+ "config": {
+ "user.meta-data": "user.meta-data",
+ "user.network-config": "user.network-config",
+ "user.user-data": "user.user-data",
+ "user.vendor-data": "user.vendor-data",
+ "cloud-init.network-config":
+ "cloud-init.network-config",
+ "cloud-init.user-data": "cloud-init.user-data",
+ "cloud-init.vendor-data":
+ "cloud-init.vendor-data",
+ },
+ "meta-data": "local-hostname: md\n",
+ },
+ "meta-data": "local-hostname: md\n",
+ "network-config": "cloud-init.network-config",
+ "user-data": "cloud-init.user-data",
+ "vendor-data": "cloud-init.vendor-data",
+ },
+ [
+ "[GET] [HTTP:200] http://lxd/1.0/meta-data",
+ "[GET] [HTTP:200] http://lxd/1.0/config",
+ "[GET] [HTTP:200] http://lxd/1.0/config/user.meta-data",
+ "[GET] [HTTP:200]"
+ " http://lxd/1.0/config/user.network-config",
+ "[GET] [HTTP:200] http://lxd/1.0/config/user.user-data",
+ "[GET] [HTTP:200] http://lxd/1.0/config/user.vendor-data",
+ "[GET] [HTTP:200]"
+ " http://lxd/1.0/config/cloud-init.network-config",
+ "[GET] [HTTP:200]"
+ " http://lxd/1.0/config/cloud-init.user-data",
+ "[GET] [HTTP:200]"
+ " http://lxd/1.0/config/cloud-init.vendor-data",
+ "Ignoring LXD config user.user-data in favor of"
+ " cloud-init.user-data value.",
+ "Ignoring LXD config user.network-config in favor of"
+ " cloud-init.network-config value.",
+ "Ignoring LXD config user.vendor-data in favor of"
+ " cloud-init.vendor-data value.",
+ ],
+ ),
+ )
+ )
+ @mock.patch.object(lxd.requests.Session, 'get')
+ def test_read_metadata_handles_unexpected_content_or_http_status(
+ self, session_get, url_responses, expected, logs, caplog
+ ):
+ """read_metadata handles valid and invalid content and status codes."""
+
+ def fake_get(url):
+ """Mock Response json, ok, status_code, text from url_responses."""
+ m_resp = mock.MagicMock()
+ content = url_responses.get(url, '')
+ m_resp.json.side_effect = lambda: json.loads(content)
+ if content:
+ mock_ok = mock.PropertyMock(return_value=True)
+ mock_status_code = mock.PropertyMock(return_value=200)
+ else:
+ mock_ok = mock.PropertyMock(return_value=False)
+ mock_status_code = mock.PropertyMock(return_value=404)
+ type(m_resp).ok = mock_ok
+ type(m_resp).status_code = mock_status_code
+ mock_text = mock.PropertyMock(return_value=content)
+ type(m_resp).text = mock_text
+ return m_resp
+
+ session_get.side_effect = fake_get
+
+ if isinstance(expected, Exception):
+ with pytest.raises(type(expected), match=re.escape(str(expected))):
+ lxd.read_metadata()
+ else:
+ assert expected == lxd.read_metadata()
+ caplogs = caplog.text
+ for log in logs:
+ assert log in caplogs
+
# vi: ts=4 expandtab