cloud-setup: merge branch 'th/cloud-setup-testable'

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1632
This commit is contained in:
Thomas Haller 2023-05-22 17:28:18 +02:00
commit 1b26315a1f
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
8 changed files with 380 additions and 209 deletions

View file

@ -27,38 +27,35 @@ _provider_detect_cb(GObject *source, GAsyncResult *result, gpointer user_data)
{
gs_unref_object NMCSProvider *provider = NMCS_PROVIDER(source);
gs_free_error GError *error = NULL;
ProviderDetectData *dd;
ProviderDetectData *dd = user_data;
gboolean success;
nm_assert(dd->detect_count > 0);
dd->detect_count--;
success = nmcs_provider_detect_finish(provider, result, &error);
nm_assert(success != (!!error));
if (nm_utils_error_is_cancelled(error))
return;
dd = user_data;
nm_assert(dd->detect_count > 0);
dd->detect_count--;
if (nm_utils_error_is_cancelled(error)) {
_LOGD("provider %s detection cancelled", nmcs_provider_get_name(provider));
goto out;
}
if (error) {
_LOGI("provider %s not detected: %s", nmcs_provider_get_name(provider), error->message);
if (dd->detect_count > 0) {
/* wait longer. */
return;
}
_LOGI("no provider detected");
goto done;
goto out;
}
_LOGI("provider %s detected", nmcs_provider_get_name(provider));
dd->provider_result = g_steal_pointer(&provider);
done:
g_cancellable_cancel(dd->cancellable);
g_main_loop_quit(dd->main_loop);
out:
if (dd->detect_count == 0) {
if (!dd->provider_result)
_LOGI("no provider detected");
g_main_loop_quit(dd->main_loop);
}
}
static void
@ -68,8 +65,6 @@ _provider_detect_sigterm_cb(GCancellable *source, gpointer user_data)
g_cancellable_cancel(dd->cancellable);
g_clear_object(&dd->provider_result);
dd->detect_count = 0;
g_main_loop_quit(dd->main_loop);
}
static NMCSProvider *
@ -123,6 +118,107 @@ out:
/*****************************************************************************/
static NMUtilsNamedValue *
_map_interfaces_parse(void)
{
gs_free const char **split = NULL;
NMUtilsNamedValue *map_interfaces;
const char *env_var;
gsize i;
gsize j;
gsize alloc_len;
env_var = g_getenv(NMCS_ENV_NM_CLOUD_SETUP_MAP_INTERFACES);
if (nm_str_is_empty(env_var))
return NULL;
split = nm_strsplit_set_full(env_var, ";", NM_STRSPLIT_SET_FLAGS_STRSTRIP);
alloc_len = NM_PTRARRAY_LEN(split) + 1u;
map_interfaces = g_new(NMUtilsNamedValue, alloc_len);
_LOGD("test: map interfaces via NM_CLOUD_SETUP_MAP_INTERFACES=\"%s\"", env_var);
for (i = 0, j = 0; split && split[i]; i++) {
NMUtilsNamedValue *m;
const char *str = split[i];
char *hwaddr;
const char *s;
s = strchr(str, '=');
if (!s || str == s)
continue;
hwaddr = nmcs_utils_hwaddr_normalize(&s[1], -1);
if (!hwaddr)
continue;
nm_assert(j < alloc_len);
m = &map_interfaces[j++];
*m = (NMUtilsNamedValue){
.name = g_strndup(str, s - str),
.value_str = hwaddr,
};
_LOGD("test: map \"%s\" -> %s", m->name, m->value_str);
}
nm_assert(j < alloc_len);
map_interfaces[j++] = (NMUtilsNamedValue){
.name = NULL,
.value_str = NULL,
};
return g_steal_pointer(&map_interfaces);
}
static const char *
_device_get_hwaddr(NMDeviceEthernet *device)
{
static const NMUtilsNamedValue *gl_map_interfaces_map = NULL;
static gsize gl_initialized = 0;
const NMUtilsNamedValue *map = NULL;
nm_assert(NM_IS_DEVICE_ETHERNET(device));
/* Network interfaces in cloud environments are identified by their permanent
* MAC address.
*
* For testing, we can set NMCS_ENV_NM_CLOUD_SETUP_MAP_INTERFACES
* to a ';' separate list of "$INTERFACE=$HWADDR", which means that we
* pretend that device with ip-interface "$INTERFACE" has the specified permanent
* MAC address. */
if (g_once_init_enter(&gl_initialized)) {
gl_map_interfaces_map = _map_interfaces_parse();
g_once_init_leave(&gl_initialized, 1);
}
map = gl_map_interfaces_map;
if (G_UNLIKELY(map)) {
const char *const iface = nm_device_get_iface(NM_DEVICE(device));
/* For testing, the device<->hwaddr is remapped and the actual permanent
* MAC address of the device ignored. This mapping is configured via
* NMCS_ENV_NM_CLOUD_SETUP_MAP_INTERFACES environment variable. */
if (!iface)
return NULL;
for (; map->name; map++) {
if (nm_streq(map->name, iface))
return map->value_str;
}
return NULL;
}
return nm_device_ethernet_get_permanent_hw_address(device);
}
static char **
_nmc_get_hwaddrs(NMClient *nmc)
{
@ -145,7 +241,7 @@ _nmc_get_hwaddrs(NMClient *nmc)
if (nm_device_get_state(device) < NM_DEVICE_STATE_UNAVAILABLE)
continue;
hwaddr = nm_device_ethernet_get_permanent_hw_address(NM_DEVICE_ETHERNET(device));
hwaddr = _device_get_hwaddr(NM_DEVICE_ETHERNET(device));
if (!hwaddr)
continue;
@ -187,7 +283,7 @@ _nmc_get_device_by_hwaddr(NMClient *nmc, const char *hwaddr)
if (!NM_IS_DEVICE_ETHERNET(device))
continue;
hwaddr_dev = nm_device_ethernet_get_permanent_hw_address(NM_DEVICE_ETHERNET(device));
hwaddr_dev = _device_get_hwaddr(NM_DEVICE_ETHERNET(device));
if (!hwaddr_dev)
continue;
@ -633,7 +729,7 @@ main(int argc, const char *const *argv)
nm_auto_free_nmcs_provider_get_config_result NMCSProviderGetConfigResult *result = NULL;
gs_free_error GError *error = NULL;
_nm_logging_enabled_init(g_getenv(NMCS_ENV_VARIABLE("NM_CLOUD_SETUP_LOG")));
_nm_logging_enabled_init(g_getenv(NMCS_ENV_NM_CLOUD_SETUP_LOG));
_LOGD("nm-cloud-setup %s starting...", NM_DIST_VERSION);

View file

@ -7,9 +7,20 @@
/*****************************************************************************/
/* mark names for variables that can be used as configuration. Search
* for NMCS_ENV_VARIABLE() to find all honored environment variables. */
#define NMCS_ENV_VARIABLE(var) "" var ""
/* Environment variables for configuring nm-cloud-setup */
#define NMCS_ENV_NM_CLOUD_SETUP_ALIYUN "NM_CLOUD_SETUP_ALIYUN"
#define NMCS_ENV_NM_CLOUD_SETUP_AZURE "NM_CLOUD_SETUP_AZURE"
#define NMCS_ENV_NM_CLOUD_SETUP_EC2 "NM_CLOUD_SETUP_EC2"
#define NMCS_ENV_NM_CLOUD_SETUP_GCP "NM_CLOUD_SETUP_GCP"
#define NMCS_ENV_NM_CLOUD_SETUP_LOG "NM_CLOUD_SETUP_LOG"
/* Undocumented/internal environment variables for configuring nm-cloud-setup.
* These are mainly for testing/debugging. */
#define NMCS_ENV_NM_CLOUD_SETUP_ALIYUN_HOST "NM_CLOUD_SETUP_ALIYUN_HOST"
#define NMCS_ENV_NM_CLOUD_SETUP_AZURE_HOST "NM_CLOUD_SETUP_AZURE_HOST"
#define NMCS_ENV_NM_CLOUD_SETUP_EC2_HOST "NM_CLOUD_SETUP_EC2_HOST"
#define NMCS_ENV_NM_CLOUD_SETUP_GCP_HOST "NM_CLOUD_SETUP_GCP_HOST"
#define NMCS_ENV_NM_CLOUD_SETUP_MAP_INTERFACES "NM_CLOUD_SETUP_MAP_INTERFACES"
/*****************************************************************************/
@ -80,6 +91,38 @@ const char *nmcs_utils_parse_get_full_line(GBytes *mem, const char *needle);
/*****************************************************************************/
#define NMCS_DEFINE_HOST_BASE(base_fcn, nmcs_env_host, default_host) \
static const char *base_fcn(void) \
{ \
static const char *base_cached = NULL; \
const char *base; \
\
again: \
base = g_atomic_pointer_get(&base_cached); \
if (G_UNLIKELY(!base)) { \
/* The base URI can be set via environment variable. \
* This is mainly for testing, it's not usually supposed to be configured. \
* Consider this private API! */ \
base = g_getenv("" nmcs_env_host ""); \
base = nmcs_utils_uri_complete_interned(base) ?: ("" default_host ""); \
\
if (!g_atomic_pointer_compare_and_exchange(&base_cached, NULL, base)) \
goto again; \
\
if (!nm_streq(base, ("" default_host ""))) { \
_LOGD("test: mock %s=\"%s\" (default \"%s\")", \
"" nmcs_env_host "", \
base, \
"" default_host ""); \
} \
} \
\
return base; \
} \
_NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON
/*****************************************************************************/
char *nmcs_utils_uri_build_concat_v(const char *base, const char **components, gsize n_components);
#define nmcs_utils_uri_build_concat(base, ...) \

View file

@ -18,26 +18,7 @@
#define NM_ALIYUN_METADATA_URL_BASE /* $NM_ALIYUN_BASE/$NM_ALIYUN_API_VERSION */ \
"/meta-data/network/interfaces/macs/"
static const char *
_aliyun_base(void)
{
static const char *base_cached = NULL;
const char *base;
again:
base = g_atomic_pointer_get(&base_cached);
if (G_UNLIKELY(!base)) {
/* The base URI can be set via environment variable.
* This is mainly for testing, it's not usually supposed to be configured.
* Consider this private API! */
base = g_getenv(NMCS_ENV_VARIABLE("NM_CLOUD_SETUP_ALIYUN_HOST"));
if (!g_atomic_pointer_compare_and_exchange(&base_cached, NULL, base))
goto again;
}
base = nmcs_utils_uri_complete_interned(base) ?: ("" NM_ALIYUN_BASE);
return base;
}
NMCS_DEFINE_HOST_BASE(_aliyun_base, NMCS_ENV_NM_CLOUD_SETUP_ALIYUN_HOST, NM_ALIYUN_HOST);
#define _aliyun_uri_concat(...) nmcs_utils_uri_build_concat(_aliyun_base(), __VA_ARGS__)
#define _aliyun_uri_interfaces(...) \
@ -557,7 +538,7 @@ nmcs_provider_aliyun_class_init(NMCSProviderAliyunClass *klass)
NMCSProviderClass *provider_class = NMCS_PROVIDER_CLASS(klass);
provider_class->_name = "aliyun";
provider_class->_env_provider_enabled = NMCS_ENV_VARIABLE("NM_CLOUD_SETUP_ALIYUN");
provider_class->_env_provider_enabled = NMCS_ENV_NM_CLOUD_SETUP_ALIYUN;
provider_class->detect = detect;
provider_class->get_config = get_config;
}

View file

@ -17,27 +17,7 @@
#define NM_AZURE_METADATA_URL_BASE /* $NM_AZURE_BASE/$NM_AZURE_API_VERSION */ \
"/metadata/instance/network/interface/"
static const char *
_azure_base(void)
{
static const char *base_cached = NULL;
const char *base;
again:
base = g_atomic_pointer_get(&base_cached);
if (G_UNLIKELY(!base)) {
/* The base URI can be set via environment variable.
* This is mainly for testing, it's not usually supposed to be configured.
* Consider this private API! */
base = g_getenv(NMCS_ENV_VARIABLE("NM_CLOUD_SETUP_AZURE_HOST"));
base = nmcs_utils_uri_complete_interned(base) ?: ("" NM_AZURE_BASE);
if (!g_atomic_pointer_compare_and_exchange(&base_cached, NULL, base))
goto again;
}
return base;
}
NMCS_DEFINE_HOST_BASE(_azure_base, NMCS_ENV_NM_CLOUD_SETUP_AZURE_HOST, NM_AZURE_BASE);
#define _azure_uri_concat(...) \
nmcs_utils_uri_build_concat(_azure_base(), __VA_ARGS__, NM_AZURE_API_VERSION)
@ -586,7 +566,7 @@ nmcs_provider_azure_class_init(NMCSProviderAzureClass *klass)
NMCSProviderClass *provider_class = NMCS_PROVIDER_CLASS(klass);
provider_class->_name = "azure";
provider_class->_env_provider_enabled = NMCS_ENV_VARIABLE("NM_CLOUD_SETUP_AZURE");
provider_class->_env_provider_enabled = NMCS_ENV_NM_CLOUD_SETUP_AZURE;
provider_class->detect = detect;
provider_class->get_config = get_config;
}

View file

@ -21,27 +21,7 @@
#define NM_EC2_TOKEN_TTL_HEADER "X-aws-ec2-metadata-token-ttl-seconds: 180"
#define NM_EC2_TOKEN_HEADER "X-aws-ec2-metadata-token: "
static const char *
_ec2_base(void)
{
static const char *base_cached = NULL;
const char *base;
again:
base = g_atomic_pointer_get(&base_cached);
if (G_UNLIKELY(!base)) {
/* The base URI can be set via environment variable.
* This is mainly for testing, it's not usually supposed to be configured.
* Consider this private API! */
base = g_getenv(NMCS_ENV_VARIABLE("NM_CLOUD_SETUP_EC2_HOST"));
base = nmcs_utils_uri_complete_interned(base) ?: ("" NM_EC2_BASE);
if (!g_atomic_pointer_compare_and_exchange(&base_cached, NULL, base))
goto again;
}
return base;
}
NMCS_DEFINE_HOST_BASE(_ec2_base, NMCS_ENV_NM_CLOUD_SETUP_EC2_HOST, NM_EC2_BASE);
#define _ec2_uri_concat(...) nmcs_utils_uri_build_concat(_ec2_base(), __VA_ARGS__)
#define _ec2_uri_interfaces(...) \
@ -435,7 +415,7 @@ nmcs_provider_ec2_class_init(NMCSProviderEC2Class *klass)
object_class->dispose = dispose;
provider_class->_name = "ec2";
provider_class->_env_provider_enabled = NMCS_ENV_VARIABLE("NM_CLOUD_SETUP_EC2");
provider_class->_env_provider_enabled = NMCS_ENV_NM_CLOUD_SETUP_EC2;
provider_class->detect = detect;
provider_class->get_config = get_config;
}

View file

@ -20,27 +20,7 @@
#define NM_GCP_METADATA_HEADER "Metadata-Flavor: Google"
static const char *
_gcp_base(void)
{
static const char *base_cached = NULL;
const char *base;
again:
base = g_atomic_pointer_get(&base_cached);
if (G_UNLIKELY(!base)) {
/* The base URI can be set via environment variable.
* This is mainly for testing, it's not usually supposed to be configured.
* Consider this private API! */
base = g_getenv(NMCS_ENV_VARIABLE("NM_CLOUD_SETUP_GCP_HOST"));
base = nmcs_utils_uri_complete_interned(base) ?: ("" NM_GCP_BASE);
if (!g_atomic_pointer_compare_and_exchange(&base_cached, NULL, base))
goto again;
}
return base;
}
NMCS_DEFINE_HOST_BASE(_gcp_base, NMCS_ENV_NM_CLOUD_SETUP_GCP_HOST, NM_GCP_BASE);
#define _gcp_uri_concat(...) \
nmcs_utils_uri_build_concat(_gcp_base(), \
@ -482,7 +462,7 @@ nmcs_provider_gcp_class_init(NMCSProviderGCPClass *klass)
NMCSProviderClass *provider_class = NMCS_PROVIDER_CLASS(klass);
provider_class->_name = "GCP";
provider_class->_env_provider_enabled = NMCS_ENV_VARIABLE("NM_CLOUD_SETUP_GCP");
provider_class->_env_provider_enabled = NMCS_ENV_NM_CLOUD_SETUP_GCP;
provider_class->detect = detect;
provider_class->get_config = get_config;
}

View file

@ -2203,8 +2203,8 @@ class TestNmCloudSetup(unittest.TestCase):
Util.skip_without_NM()
self.ctx = NMTestContext(self._testMethodName)
_mac1 = "9e:c0:3e:92:24:2d"
_mac2 = "53:e9:7e:52:8d:a8"
_mac1 = "cc:00:00:00:00:01"
_mac2 = "cc:00:00:00:00:02"
_ip1 = "172.31.26.249"
_ip2 = "172.31.176.249"
@ -2271,7 +2271,7 @@ class TestNmCloudSetup(unittest.TestCase):
def _mock_devices(self):
# Add a device with an active connection that has IPv4 configured
self.ctx.srv.op_AddObj("WiredDevice", iface="eth0", mac="9e:c0:3e:92:24:2d")
self.ctx.srv.op_AddObj("WiredDevice", iface="eth0", mac="cc:00:00:00:00:01")
self.ctx.srv.addAndActivateConnection(
{
"connection": {"type": "802-3-ethernet", "id": "con-eth0"},
@ -2282,7 +2282,7 @@ class TestNmCloudSetup(unittest.TestCase):
)
# The second connection has no IPv4
self.ctx.srv.op_AddObj("WiredDevice", iface="eth1", mac="53:e9:7e:52:8d:a8")
self.ctx.srv.op_AddObj("WiredDevice", iface="eth1", mac="cc:00:00:00:00:02")
self.ctx.srv.addAndActivateConnection(
{"connection": {"type": "802-3-ethernet", "id": "con-eth1"}},
"/org/freedesktop/NetworkManager/Devices/2",
@ -2351,7 +2351,7 @@ class TestNmCloudSetup(unittest.TestCase):
)
nmc.pexp.expect("provider aliyun detected")
nmc.pexp.expect("found interfaces: 9E:C0:3E:92:24:2D, 53:E9:7E:52:8D:A8")
nmc.pexp.expect("found interfaces: CC:00:00:00:00:01, CC:00:00:00:00:02")
nmc.pexp.expect("get-config: start fetching meta data")
nmc.pexp.expect("get-config: success")
nmc.pexp.expect("meta data received")
@ -2373,7 +2373,7 @@ class TestNmCloudSetup(unittest.TestCase):
)
nmc.pexp.expect("provider aliyun detected")
nmc.pexp.expect("found interfaces: 9E:C0:3E:92:24:2D, 53:E9:7E:52:8D:A8")
nmc.pexp.expect("found interfaces: CC:00:00:00:00:01, CC:00:00:00:00:02")
nmc.pexp.expect("get-config: starting")
nmc.pexp.expect("get-config: success")
nmc.pexp.expect("meta data received")
@ -2430,7 +2430,7 @@ class TestNmCloudSetup(unittest.TestCase):
)
nmc.pexp.expect("provider azure detected")
nmc.pexp.expect("found interfaces: 9E:C0:3E:92:24:2D, 53:E9:7E:52:8D:A8")
nmc.pexp.expect("found interfaces: CC:00:00:00:00:01, CC:00:00:00:00:02")
nmc.pexp.expect("found azure interfaces: 2")
nmc.pexp.expect("interface\[0]: found a matching device with hwaddr")
nmc.pexp.expect(
@ -2459,7 +2459,7 @@ class TestNmCloudSetup(unittest.TestCase):
)
nmc.pexp.expect("provider azure detected")
nmc.pexp.expect("found interfaces: 9E:C0:3E:92:24:2D, 53:E9:7E:52:8D:A8")
nmc.pexp.expect("found interfaces: CC:00:00:00:00:01, CC:00:00:00:00:02")
nmc.pexp.expect("get-config: starting")
nmc.pexp.expect("get-config: success")
nmc.pexp.expect("meta data received")
@ -2506,7 +2506,7 @@ class TestNmCloudSetup(unittest.TestCase):
)
nmc.pexp.expect("provider ec2 detected")
nmc.pexp.expect("found interfaces: 9E:C0:3E:92:24:2D, 53:E9:7E:52:8D:A8")
nmc.pexp.expect("found interfaces: CC:00:00:00:00:01, CC:00:00:00:00:02")
nmc.pexp.expect("get-config: starting")
nmc.pexp.expect("get-config: success")
nmc.pexp.expect("meta data received")
@ -2528,7 +2528,7 @@ class TestNmCloudSetup(unittest.TestCase):
)
nmc.pexp.expect("provider ec2 detected")
nmc.pexp.expect("found interfaces: 9E:C0:3E:92:24:2D, 53:E9:7E:52:8D:A8")
nmc.pexp.expect("found interfaces: CC:00:00:00:00:01, CC:00:00:00:00:02")
nmc.pexp.expect("get-config: starting")
nmc.pexp.expect("get-config: success")
nmc.pexp.expect("meta data received")
@ -2566,7 +2566,7 @@ class TestNmCloudSetup(unittest.TestCase):
)
nmc.pexp.expect("provider GCP detected")
nmc.pexp.expect("found interfaces: 9E:C0:3E:92:24:2D, 53:E9:7E:52:8D:A8")
nmc.pexp.expect("found interfaces: CC:00:00:00:00:01, CC:00:00:00:00:02")
nmc.pexp.expect("found GCP interfaces: 2")
nmc.pexp.expect("GCP interface\[0]: found a requested device with hwaddr")
nmc.pexp.expect("get-config: success")
@ -2589,7 +2589,7 @@ class TestNmCloudSetup(unittest.TestCase):
)
nmc.pexp.expect("provider GCP detected")
nmc.pexp.expect("found interfaces: 9E:C0:3E:92:24:2D, 53:E9:7E:52:8D:A8")
nmc.pexp.expect("found interfaces: CC:00:00:00:00:01, CC:00:00:00:00:02")
nmc.pexp.expect("get-config: starting")
nmc.pexp.expect("get-config: success")
nmc.pexp.expect("meta data received")

View file

@ -17,13 +17,45 @@
import os
import socket
from sys import argv
import sys
from http.server import HTTPServer
from http.server import BaseHTTPRequestHandler
from socketserver import BaseServer
PROVIDERS = [
"aliyun",
"azure",
"ec2",
"gcp",
]
def _s_to_bool(s):
s0 = s
if isinstance(s, bytes):
s = s.encode("utf-8", errors="replace")
if isinstance(s, str):
s = s.lower()
if s in ["yes", "y", "true", "1"]:
return True
if s in ["no", "n", "false", "0"]:
return False
if isinstance(s, int):
if s in [0, 1]:
return s == 1
raise ValueError(f'Not a boolean value ("{s0}")')
DEBUG = _s_to_bool(os.environ.get("NM_TEST_CLOUD_SETUP_MOCK_DEBUG", "0"))
def dbg(msg):
if DEBUG:
print("DBG: %s" % (msg,))
class MockCloudMDRequestHandler(BaseHTTPRequestHandler):
"""
Respond to cloud metadata service requests.
@ -33,39 +65,72 @@ class MockCloudMDRequestHandler(BaseHTTPRequestHandler):
def log_message(self, format, *args):
pass
def _response_and_end(self, code, write=None):
self.send_response(code)
self.end_headers()
if write is None:
dbg("response %s" % (code,))
else:
if isinstance(write, str):
write = write.encode("utf-8")
dbg("response %s, %s" % (code, write))
self.wfile.write(write)
def _read(self):
length = int(self.headers["content-length"])
v = self.rfile.read(length)
dbg('receive "%s"' % (v,))
return v
def do_GET(self):
path = self.path.encode("ascii")
dbg("GET %s" % (path,))
r = None
if path in self.server._resources:
self.send_response(200)
self.end_headers()
self.wfile.write(self.server._resources[path])
else:
self.send_response(404)
self.end_headers()
r = self.server._resources[path]
elif self.server.config_get_allow_default():
for p in self.server.config_get_providers():
if path in DEFAULT_RESOURCES[p]:
r = DEFAULT_RESOURCES[p][path]
break
if r is None:
self._response_and_end(404)
return
self._response_and_end(200, write=r)
def do_PUT(self):
path = self.path.encode("ascii")
if path == b"/latest/api/token":
self.send_response(200)
self.end_headers()
self.wfile.write(
b"AQAAALH-k7i18JMkK-ORLZQfAa7nkNjQbKwpQPExNHqzk1oL_7eh-A=="
)
dbg("PUT %s" % (path,))
if path.startswith(b"/.nmtest/"):
conf_name = path[len(b"/.nmtest/") :]
v = self._read()
self.server._config[conf_name] = v
assert self.server.config_get_providers() is not None
assert self.server.config_get_allow_default() is not None
self._response_and_end(201)
elif path == b"/latest/api/token":
if "ec2" not in self.server.config_get_providers():
self._response_and_end(404)
else:
self._response_and_end(
200,
write="AQAAALH-k7i18JMkK-ORLZQfAa7nkNjQbKwpQPExNHqzk1oL_7eh-A==",
)
else:
length = int(self.headers["content-length"])
self.server._resources[path] = self.rfile.read(length)
self.send_response(201)
self.end_headers()
self.server._resources[path] = self._read()
self._response_and_end(201)
def do_DELETE(self):
path = self.path.encode("ascii")
dbg("DELETE %s" % (path,))
if path in self.server._resources:
del self.server._resources[path]
self.send_response(204)
self.end_headers()
self._response_and_end(204)
else:
self.send_response(404)
self.end_headers()
self._response_and_end(404)
class SocketHTTPServer(HTTPServer):
@ -75,82 +140,123 @@ class SocketHTTPServer(HTTPServer):
fron the test runner.
"""
def __init__(self, server_address, RequestHandlerClass, socket, resources):
def __init__(
self,
server_address,
RequestHandlerClass,
socket,
resources=None,
allow_default=True,
):
BaseServer.__init__(self, server_address, RequestHandlerClass)
self.socket = socket
self.server_address = self.socket.getsockname()
self._resources = resources
self._resources = resources or {}
self._config = {
"allow-default": "yes" if allow_default else "no",
}
def config_get_providers(self):
conf = self._config.get(b"providers", None)
if not conf:
return PROVIDERS
parsed = [s.lower() for s in conf.decode("utf-8", errors="replace").split(" ")]
assert all(p in PROVIDERS for p in parsed)
return parsed
def config_get_allow_default(self):
return _s_to_bool(self._config.get(b"allow-default", "yes"))
def default_resources():
ec2_macs = b"/2018-09-24/meta-data/network/interfaces/macs/"
def create_default_resources_for_provider(provider):
aliyun_meta = b"/2016-01-01/meta-data/"
aliyun_macs = aliyun_meta + b"network/interfaces/macs/"
azure_meta = b"/metadata/instance"
azure_iface = azure_meta + b"/network/interface/"
azure_query = b"?format=text&api-version=2017-04-02"
gcp_meta = b"/computeMetadata/v1/instance/"
gcp_iface = gcp_meta + b"network-interfaces/"
mac1 = b"9e:c0:3e:92:24:2d"
mac2 = b"53:e9:7e:52:8d:a8"
mac1 = b"cc:00:00:00:00:01"
mac2 = b"cc:00:00:00:00:02"
ip1 = b"172.31.26.249"
ip2 = b"172.31.176.249"
return {
b"/latest/meta-data/": b"ami-id\n",
ec2_macs: mac2 + b"\n" + mac1,
ec2_macs + mac2 + b"/subnet-ipv4-cidr-block": b"172.31.16.0/20",
ec2_macs + mac2 + b"/local-ipv4s": ip1,
ec2_macs + mac1 + b"/subnet-ipv4-cidr-block": b"172.31.166.0/20",
ec2_macs + mac1 + b"/local-ipv4s": ip2,
aliyun_meta: b"ami-id\n",
aliyun_macs: mac2 + b"\n" + mac1,
aliyun_macs + mac2 + b"/vpc-cidr-block": b"172.31.16.0/20",
aliyun_macs + mac2 + b"/private-ipv4s": ip1,
aliyun_macs + mac2 + b"/primary-ip-address": ip1,
aliyun_macs + mac2 + b"/netmask": b"255.255.255.0",
aliyun_macs + mac2 + b"/gateway": b"172.31.26.2",
aliyun_macs + mac1 + b"/vpc-cidr-block": b"172.31.166.0/20",
aliyun_macs + mac1 + b"/private-ipv4s": ip2,
aliyun_macs + mac1 + b"/primary-ip-address": ip2,
aliyun_macs + mac1 + b"/netmask": b"255.255.255.0",
aliyun_macs + mac1 + b"/gateway": b"172.31.176.2",
azure_meta + azure_query: b"",
azure_iface + azure_query: b"0\n1\n",
azure_iface + b"0/macAddress" + azure_query: mac1,
azure_iface + b"1/macAddress" + azure_query: mac2,
azure_iface + b"0/ipv4/ipAddress/" + azure_query: b"0\n",
azure_iface + b"1/ipv4/ipAddress/" + azure_query: b"0\n",
azure_iface + b"0/ipv4/ipAddress/0/privateIpAddress" + azure_query: ip1,
azure_iface + b"1/ipv4/ipAddress/0/privateIpAddress" + azure_query: ip2,
azure_iface + b"0/ipv4/subnet/0/address/" + azure_query: b"172.31.16.0",
azure_iface + b"1/ipv4/subnet/0/address/" + azure_query: b"172.31.166.0",
azure_iface + b"0/ipv4/subnet/0/prefix/" + azure_query: b"20",
azure_iface + b"1/ipv4/subnet/0/prefix/" + azure_query: b"20",
gcp_meta + b"id": b"",
gcp_iface: b"0\n1\n",
gcp_iface + b"0/mac": mac1,
gcp_iface + b"1/mac": mac2,
gcp_iface + b"0/forwarded-ips/": b"0\n",
gcp_iface + b"0/forwarded-ips/0": ip1,
gcp_iface + b"1/forwarded-ips/": b"0\n",
gcp_iface + b"1/forwarded-ips/0": ip2,
}
if provider == "aliyun":
aliyun_meta = b"/2016-01-01/meta-data/"
aliyun_macs = aliyun_meta + b"network/interfaces/macs/"
return {
aliyun_meta: b"ami-id\n",
aliyun_macs: mac2 + b"\n" + mac1,
aliyun_macs + mac2 + b"/vpc-cidr-block": b"172.31.16.0/20",
aliyun_macs + mac2 + b"/private-ipv4s": ip1,
aliyun_macs + mac2 + b"/primary-ip-address": ip1,
aliyun_macs + mac2 + b"/netmask": b"255.255.255.0",
aliyun_macs + mac2 + b"/gateway": b"172.31.26.2",
aliyun_macs + mac1 + b"/vpc-cidr-block": b"172.31.166.0/20",
aliyun_macs + mac1 + b"/private-ipv4s": ip2,
aliyun_macs + mac1 + b"/primary-ip-address": ip2,
aliyun_macs + mac1 + b"/netmask": b"255.255.255.0",
aliyun_macs + mac1 + b"/gateway": b"172.31.176.2",
}
if provider == "azure":
azure_meta = b"/metadata/instance"
azure_iface = azure_meta + b"/network/interface/"
azure_query = b"?format=text&api-version=2017-04-02"
return {
azure_meta + azure_query: b"",
azure_iface + azure_query: b"0\n1\n",
azure_iface + b"0/macAddress" + azure_query: mac1,
azure_iface + b"1/macAddress" + azure_query: mac2,
azure_iface + b"0/ipv4/ipAddress/" + azure_query: b"0\n",
azure_iface + b"1/ipv4/ipAddress/" + azure_query: b"0\n",
azure_iface + b"0/ipv4/ipAddress/0/privateIpAddress" + azure_query: ip1,
azure_iface + b"1/ipv4/ipAddress/0/privateIpAddress" + azure_query: ip2,
azure_iface + b"0/ipv4/subnet/0/address/" + azure_query: b"172.31.16.0",
azure_iface + b"1/ipv4/subnet/0/address/" + azure_query: b"172.31.166.0",
azure_iface + b"0/ipv4/subnet/0/prefix/" + azure_query: b"20",
azure_iface + b"1/ipv4/subnet/0/prefix/" + azure_query: b"20",
}
if provider == "ec2":
ec2_macs = b"/2018-09-24/meta-data/network/interfaces/macs/"
return (
{
b"/latest/meta-data/": b"ami-id\n",
ec2_macs: mac2 + b"\n" + mac1,
ec2_macs + mac2 + b"/subnet-ipv4-cidr-block": b"172.31.16.0/20",
ec2_macs + mac2 + b"/local-ipv4s": ip1,
ec2_macs + mac1 + b"/subnet-ipv4-cidr-block": b"172.31.166.0/20",
ec2_macs + mac1 + b"/local-ipv4s": ip2,
},
)
if provider == "gcp":
gcp_meta = b"/computeMetadata/v1/instance/"
gcp_iface = gcp_meta + b"network-interfaces/"
return {
gcp_meta + b"id": b"",
gcp_iface: b"0\n1\n",
gcp_iface + b"0/mac": mac1,
gcp_iface + b"1/mac": mac2,
gcp_iface + b"0/forwarded-ips/": b"0\n",
gcp_iface + b"0/forwarded-ips/0": ip1,
gcp_iface + b"1/forwarded-ips/": b"0\n",
gcp_iface + b"1/forwarded-ips/0": ip2,
}
raise ValueError("invalid provider %s" % (provider,))
resources = None
def create_default_resources():
return {p: create_default_resources_for_provider(p) for p in PROVIDERS}
DEFAULT_RESOURCES = create_default_resources()
allow_default = True
try:
if argv[1] == "--empty":
resources = {}
if sys.argv[1] == "--empty":
allow_default = False
except IndexError:
pass
if resources is None:
resources = default_resources()
# See sd_listen_fds(3)
fileno = os.getenv("LISTEN_FDS")
@ -165,7 +271,12 @@ else:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
s.bind(addr)
httpd = SocketHTTPServer(None, MockCloudMDRequestHandler, socket=s, resources=resources)
httpd = SocketHTTPServer(
None,
MockCloudMDRequestHandler,
socket=s,
allow_default=allow_default,
)
print("Listening on http://%s:%d" % (httpd.server_address[0], httpd.server_address[1]))
httpd.server_activate()