Skip to content

Commit

Permalink
fix(NoCloudNet): Add network-config support (#5566)
Browse files Browse the repository at this point in the history
This enables support for network config v2 and v1 to NoCloud
when used with http / ftp / etc. 

BREAKING_CHANGE: Adds an additional network request to NoCloud.
  • Loading branch information
holmanb committed Aug 6, 2024
1 parent 1185426 commit efef5da
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 10 deletions.
3 changes: 2 additions & 1 deletion cloudinit/sources/DataSourceNoCloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ def _pp2d_callback(mp, data):

# This could throw errors, but the user told us to do it
# so if errors are raised, let them raise
(md_seed, ud, vd) = util.read_seeded(seedfrom, timeout=None)
md_seed, ud, vd, network = util.read_seeded(seedfrom, timeout=None)
LOG.debug("Using seeded cache data from %s", seedfrom)

# Values in the command line override those from the seed
Expand All @@ -199,6 +199,7 @@ def _pp2d_callback(mp, data):
)
mydata["user-data"] = ud
mydata["vendor-data"] = vd
mydata["network-config"] = network
found.append(seedfrom)

# Now that we have exhausted any other places merge in the defaults
Expand Down
2 changes: 1 addition & 1 deletion cloudinit/sources/DataSourceOVF.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def _get_data(self):
LOG.debug("Seed from %s not supported by %s", seedfrom, self)
return False

(md_seed, ud, vd) = util.read_seeded(seedfrom, timeout=None)
(md_seed, ud, vd, _) = util.read_seeded(seedfrom, timeout=None)
LOG.debug("Using seeded cache data from %s", seedfrom)

md = util.mergemanydict([md, md_seed])
Expand Down
15 changes: 12 additions & 3 deletions cloudinit/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -977,10 +977,11 @@ def read_optional_seed(fill, base="", ext="", timeout=5):
'meta-data' entries
"""
try:
md, ud, vd = read_seeded(base=base, ext=ext, timeout=timeout)
md, ud, vd, network = read_seeded(base=base, ext=ext, timeout=timeout)
fill["user-data"] = ud
fill["vendor-data"] = vd
fill["meta-data"] = md
fill["network-config"] = md
return True
except url_helper.UrlError as e:
if e.code == url_helper.NOT_FOUND:
Expand Down Expand Up @@ -1066,6 +1067,7 @@ def read_seeded(base="", ext="", timeout=5, retries=10):
ud_url = base.replace("%s", "user-data" + ext)
vd_url = base.replace("%s", "vendor-data" + ext)
md_url = base.replace("%s", "meta-data" + ext)
network_url = base.replace("%s", "network-config" + ext)
else:
if features.NOCLOUD_SEED_URL_APPEND_FORWARD_SLASH:
if base[-1] != "/" and parse.urlparse(base).query == "":
Expand All @@ -1074,12 +1076,19 @@ def read_seeded(base="", ext="", timeout=5, retries=10):
ud_url = "%s%s%s" % (base, "user-data", ext)
vd_url = "%s%s%s" % (base, "vendor-data", ext)
md_url = "%s%s%s" % (base, "meta-data", ext)
network_url = "%s%s%s" % (base, "network-config", ext)
network_resp = url_helper.read_file_or_url(
network_url, timeout=timeout, retries=retries
)
network = None
if network_resp.ok():
network = load_yaml(network_resp.contents)
md_resp = url_helper.read_file_or_url(
md_url, timeout=timeout, retries=retries
)
md = None
if md_resp.ok():
md = load_yaml(decode_binary(md_resp.contents), default={})
md = load_yaml(md_resp.contents, default={})

ud_resp = url_helper.read_file_or_url(
ud_url, timeout=timeout, retries=retries
Expand All @@ -1101,7 +1110,7 @@ def read_seeded(base="", ext="", timeout=5, retries=10):
else:
LOG.debug("Error in vendor-data response")

return (md, ud, vd)
return md, ud, vd, network


def read_conf_d(confd, *, instance_data_file=None) -> dict:
Expand Down
32 changes: 27 additions & 5 deletions tests/unittests/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -2452,16 +2452,23 @@ class TestReadSeeded:
def test_unicode_not_messed_up(self, tmpdir):
ud = b"userdatablob"
vd = b"vendordatablob"
network = b"test: 'true'"
helpers.populate_dir(
tmpdir.strpath,
{"meta-data": "key1: val1", "user-data": ud, "vendor-data": vd},
{
"meta-data": "key1: val1",
"user-data": ud,
"vendor-data": vd,
"network-config": network,
},
)
(found_md, found_ud, found_vd) = util.read_seeded(
found_md, found_ud, found_vd, found_network = util.read_seeded(
tmpdir.strpath + os.path.sep
)
assert found_md == {"key1": "val1"}
assert found_ud == ud
assert found_vd == vd
assert found_network == {"test": "true"}

@pytest.mark.parametrize(
"base, feature_flag, req_urls",
Expand All @@ -2470,6 +2477,7 @@ def test_unicode_not_messed_up(self, tmpdir):
"http://10.0.0.1/%s?qs=1",
True,
[
"http://10.0.0.1/network-config?qs=1",
"http://10.0.0.1/meta-data?qs=1",
"http://10.0.0.1/user-data?qs=1",
"http://10.0.0.1/vendor-data?qs=1",
Expand All @@ -2480,6 +2488,7 @@ def test_unicode_not_messed_up(self, tmpdir):
"https://10.0.0.1:8008/",
True,
[
"https://10.0.0.1:8008/network-config",
"https://10.0.0.1:8008/meta-data",
"https://10.0.0.1:8008/user-data",
"https://10.0.0.1:8008/vendor-data",
Expand All @@ -2490,6 +2499,7 @@ def test_unicode_not_messed_up(self, tmpdir):
"https://10.0.0.1:8008",
True,
[
"https://10.0.0.1:8008/network-config",
"https://10.0.0.1:8008/meta-data",
"https://10.0.0.1:8008/user-data",
"https://10.0.0.1:8008/vendor-data",
Expand All @@ -2500,6 +2510,7 @@ def test_unicode_not_messed_up(self, tmpdir):
"https://10.0.0.1:8008",
False,
[
"https://10.0.0.1:8008network-config",
"https://10.0.0.1:8008meta-data",
"https://10.0.0.1:8008user-data",
"https://10.0.0.1:8008vendor-data",
Expand All @@ -2510,6 +2521,7 @@ def test_unicode_not_messed_up(self, tmpdir):
"https://10.0.0.1:8008?qs=",
True,
[
"https://10.0.0.1:8008?qs=network-config",
"https://10.0.0.1:8008?qs=meta-data",
"https://10.0.0.1:8008?qs=user-data",
"https://10.0.0.1:8008?qs=vendor-data",
Expand Down Expand Up @@ -2540,12 +2552,15 @@ def fake_response(url, timeout, retries):
"NOCLOUD_SEED_URL_APPEND_FORWARD_SLASH",
feature_flag,
):
(found_md, found_ud, found_vd) = util.read_seeded(base)
found_md, found_ud, found_vd, found_network = util.read_seeded(
base
)
# Meta-data treated as YAML
assert found_md == {"/meta-data": 1}
# user-data, vendor-data read raw. It could be scripts or other format
assert found_ud == "/user-data: 1"
assert found_vd == "/vendor-data: 1"
assert found_network == {"/network-config": 1}
assert [
mock.call(req_url, timeout=5, retries=10) for req_url in req_urls
] == m_read.call_args_list
Expand All @@ -2560,15 +2575,22 @@ def setUp(self):
def test_unicode_not_messed_up(self):
ud = b"userdatablob"
vd = None
network = b"test: 'true'"
helpers.populate_dir(
self.tmp, {"meta-data": "key1: val1", "user-data": ud}
self.tmp,
{
"meta-data": "key1: val1",
"user-data": ud,
"network-config": network,
},
)
sdir = self.tmp + os.path.sep
(found_md, found_ud, found_vd) = util.read_seeded(sdir)
found_md, found_ud, found_vd, found_network = util.read_seeded(sdir)

self.assertEqual(found_md, {"key1": "val1"})
self.assertEqual(found_ud, ud)
self.assertEqual(found_vd, vd)
self.assertEqual(found_network, {"test": "true"})


class TestEncode(helpers.TestCase):
Expand Down

0 comments on commit efef5da

Please sign in to comment.