From 6d3b5f3babf152d0bcc56a451feddd6cadb619ef Mon Sep 17 00:00:00 2001 From: "James E. Blair" Date: Wed, 24 Aug 2022 10:28:13 -0700 Subject: [PATCH] Add missing cloud/region/az/host_id info to nodes To the greatest extent possible within the limitation of each provider, this adds cloud, region, az, and host_id to nodes. Each of AWS, Azure, GCE, IBMVPC have the cloud name hard-coded to a value that makes sense for each driver given that each of these are singleton clouds. Their region and az values are added as appropriate. The k8s, openshift, and openshiftpods all have their cloud names set to the k8s context name, which is the closest approximation of what the "cloud" attribute means in its existing usage in the OpenStack driver. If pods are launched, the host_id value is set to the k8s host node name, which is an approximation of the existing usage in the OpenStack driver (where it is typically an opaque uuid that uniquely identifies the hypervisor). Change-Id: I53765fc3914a84d2519f5d4dda4f8dc8feda72f2 --- nodepool/driver/aws/adapter.py | 11 +++++++---- nodepool/driver/azure/adapter.py | 3 ++- nodepool/driver/gce/adapter.py | 5 ++++- nodepool/driver/ibmvpc/adapter.py | 1 + nodepool/driver/kubernetes/handler.py | 8 ++++---- nodepool/driver/kubernetes/provider.py | 1 + nodepool/driver/openshift/handler.py | 1 + nodepool/driver/openshift/provider.py | 1 + nodepool/driver/openshiftpods/handler.py | 4 +++- nodepool/driver/statemachine.py | 3 +++ nodepool/tests/unit/fake_azure.py | 1 + nodepool/tests/unit/test_driver_aws.py | 10 +++++++++- nodepool/tests/unit/test_driver_azure.py | 3 +++ nodepool/tests/unit/test_driver_gce.py | 3 +++ nodepool/tests/unit/test_driver_ibmvpc.py | 3 +++ nodepool/tests/unit/test_driver_kubernetes.py | 7 +++++++ nodepool/tests/unit/test_driver_openshift.py | 5 +++++ nodepool/tests/unit/test_driver_openshiftpods.py | 5 +++++ 18 files changed, 63 insertions(+), 12 deletions(-) diff --git a/nodepool/driver/aws/adapter.py b/nodepool/driver/aws/adapter.py index d5e662182..d91c41ea1 100644 --- a/nodepool/driver/aws/adapter.py +++ b/nodepool/driver/aws/adapter.py @@ -82,7 +82,7 @@ CACHE_TTL = 10 class AwsInstance(statemachine.Instance): - def __init__(self, instance, quota): + def __init__(self, provider, instance, quota): super().__init__() self.external_id = instance.id self.metadata = tag_list_to_dict(instance.tags) @@ -90,7 +90,9 @@ class AwsInstance(statemachine.Instance): self.private_ipv6 = None self.public_ipv4 = instance.public_ip_address self.public_ipv6 = None - self.az = '' + self.cloud = 'AWS' + self.region = provider.region_name + self.az = instance.subnet.availability_zone self.quota = quota for iface in instance.network_interfaces[:1]: @@ -206,7 +208,8 @@ class AwsCreateStateMachine(statemachine.StateMachine): if self.state == self.COMPLETE: self.complete = True - return AwsInstance(self.instance, self.quota) + return AwsInstance(self.adapter.provider, self.instance, + self.quota) class AwsAdapter(statemachine.Adapter): @@ -357,7 +360,7 @@ class AwsAdapter(statemachine.Adapter): if instance.state["Name"].lower() == "terminated": continue quota = self._getQuotaForInstanceType(instance.instance_type) - yield AwsInstance(instance, quota) + yield AwsInstance(self.provider, instance, quota) def getQuotaLimits(self): # Get the instance types that this provider handles diff --git a/nodepool/driver/azure/adapter.py b/nodepool/driver/azure/adapter.py index ecc721a9d..53c50900e 100644 --- a/nodepool/driver/azure/adapter.py +++ b/nodepool/driver/azure/adapter.py @@ -84,8 +84,9 @@ class AzureInstance(statemachine.Instance): self.interface_ip = (self.public_ipv4 or self.public_ipv6 or self.private_ipv4 or self.private_ipv6) + self.cloud = 'Azure' self.region = vm['location'] - self.az = '' + self.az = vm['zones'][0] def getQuotaInformation(self): return quota_info_from_sku(self.sku) diff --git a/nodepool/driver/gce/adapter.py b/nodepool/driver/gce/adapter.py index ffaa57a1e..be1ffca73 100644 --- a/nodepool/driver/gce/adapter.py +++ b/nodepool/driver/gce/adapter.py @@ -44,7 +44,10 @@ class GceInstance(statemachine.Instance): def __init__(self, data, quota): super().__init__() self.external_id = data['name'] - self.az = data['zone'] + self.cloud = 'Google' + zone = data['zone'].rsplit('/', 1)[1] + self.region = zone.rsplit('-', 1)[0] + self.az = zone iface = data.get('networkInterfaces', []) if len(iface): diff --git a/nodepool/driver/ibmvpc/adapter.py b/nodepool/driver/ibmvpc/adapter.py index 9bc9078fa..404a3d18d 100644 --- a/nodepool/driver/ibmvpc/adapter.py +++ b/nodepool/driver/ibmvpc/adapter.py @@ -132,6 +132,7 @@ class IBMVPCInstance(statemachine.Instance): self.public_ipv4 = fip['address'] self.interface_ip = self.public_ipv4 or self.private_ipv4 + self.cloud = 'IBM' self.region = provider.region self.az = vm['zone']['name'] diff --git a/nodepool/driver/kubernetes/handler.py b/nodepool/driver/kubernetes/handler.py index e2763c57f..50c4553ca 100644 --- a/nodepool/driver/kubernetes/handler.py +++ b/nodepool/driver/kubernetes/handler.py @@ -47,10 +47,10 @@ class K8SLauncher(NodeLauncher): else: self.node.connection_type = "kubectl" self.node.interface_ip = resource['pod'] - pool = self.handler.provider.pools.get(self.node.pool) - resources = self.handler.manager.quotaNeededByLabel( - self.node.type[0], pool) - self.node.resources = resources.get_resources() + pool = self.handler.provider.pools.get(self.node.pool) + self.node.resources = self.handler.manager.quotaNeededByLabel( + self.node.type[0], pool).get_resources() + self.node.cloud = self.provider_config.context self.zk.storeNode(self.node) self.log.info("Resource %s is ready" % resource['name']) diff --git a/nodepool/driver/kubernetes/provider.py b/nodepool/driver/kubernetes/provider.py index 1d96f2d7a..652a5a53a 100644 --- a/nodepool/driver/kubernetes/provider.py +++ b/nodepool/driver/kubernetes/provider.py @@ -317,6 +317,7 @@ class KubernetesProvider(Provider, QuotaSupport): "%s: pod failed to initialize (%s)" % ( namespace, pod.status.phase)) resource["pod"] = label.name + node.host_id = pod.spec.node_name return resource def getRequestHandler(self, poolworker, request): diff --git a/nodepool/driver/openshift/handler.py b/nodepool/driver/openshift/handler.py index 1fb3efb2a..7131955f7 100644 --- a/nodepool/driver/openshift/handler.py +++ b/nodepool/driver/openshift/handler.py @@ -50,6 +50,7 @@ class OpenshiftLauncher(NodeLauncher): self.node.shell_type = self.label.shell_type # NOTE: resource access token may be encrypted here self.node.connection_port = resource + self.node.cloud = self.provider_config.context self.zk.storeNode(self.node) self.log.info("Resource %s is ready", project) diff --git a/nodepool/driver/openshift/provider.py b/nodepool/driver/openshift/provider.py index 47f51a04c..9c579a6d8 100644 --- a/nodepool/driver/openshift/provider.py +++ b/nodepool/driver/openshift/provider.py @@ -257,6 +257,7 @@ class OpenshiftProvider(Provider, QuotaSupport): raise exceptions.LaunchNodepoolException( "%s: pod failed to initialize (%s)" % ( project, pod.status.phase)) + return pod.spec.node_name def getRequestHandler(self, poolworker, request): return handler.OpenshiftNodeRequestHandler(poolworker, request) diff --git a/nodepool/driver/openshiftpods/handler.py b/nodepool/driver/openshiftpods/handler.py index fb34e59af..65c733a68 100644 --- a/nodepool/driver/openshiftpods/handler.py +++ b/nodepool/driver/openshiftpods/handler.py @@ -30,7 +30,7 @@ class OpenshiftPodLauncher(OpenshiftLauncher): self.node.interface_ip = pod_name self.zk.storeNode(self.node) - self.handler.manager.waitForPod(project, pod_name) + pod_node_id = self.handler.manager.waitForPod(project, pod_name) self.node.state = zk.READY self.node.python_path = self.label.python_path @@ -47,6 +47,8 @@ class OpenshiftPodLauncher(OpenshiftLauncher): 'user': 'zuul-worker', } self.node.connection_type = "kubectl" + self.node.cloud = self.provider_config.context + self.node.host_id = pod_node_id self.zk.storeNode(self.node) self.log.info("Pod %s is ready" % self.node.external_id) diff --git a/nodepool/driver/statemachine.py b/nodepool/driver/statemachine.py index 108287e6e..de62e982a 100644 --- a/nodepool/driver/statemachine.py +++ b/nodepool/driver/statemachine.py @@ -157,6 +157,7 @@ class StateMachineNodeLauncher(stats.StatsReporter): node.public_ipv4 = instance.public_ipv4 node.private_ipv4 = instance.private_ipv4 node.public_ipv6 = instance.public_ipv6 + node.cloud = instance.cloud node.region = instance.region node.az = instance.az node.driver_data = instance.driver_data @@ -737,6 +738,7 @@ class Instance: * public_ipv4: str * public_ipv6: str * private_ipv4: str + * cloud: str * az: str * region: str * driver_data: any @@ -759,6 +761,7 @@ class Instance: self.public_ipv6 = None self.private_ipv4 = None self.interface_ip = None + self.cloud = None self.az = None self.region = None self.metadata = {} diff --git a/nodepool/tests/unit/fake_azure.py b/nodepool/tests/unit/fake_azure.py index 06b166038..f0f155868 100644 --- a/nodepool/tests/unit/fake_azure.py +++ b/nodepool/tests/unit/fake_azure.py @@ -168,6 +168,7 @@ class VirtualMachinesCRUD(CRUDManager): "networkProfile": data['properties']['networkProfile'], "provisioningState": "Creating" } + data['zones'] = ["1"] self.items.append(data) disk_data = data.copy() disk_data['name'] = 'bionic-azure-' + str(uuid.uuid4()) diff --git a/nodepool/tests/unit/test_driver_aws.py b/nodepool/tests/unit/test_driver_aws.py index 6bf965822..164ea419a 100644 --- a/nodepool/tests/unit/test_driver_aws.py +++ b/nodepool/tests/unit/test_driver_aws.py @@ -386,6 +386,10 @@ class TestDriverAws(tests.DBTestCase): self.assertTrue(node.private_ipv4.startswith('203.0.113.')) self.assertFalse(node.public_ipv4.startswith('203.0.113.')) self.assertEqual(node.python_path, 'auto') + self.assertEqual(node.cloud, 'AWS') + self.assertEqual(node.region, 'us-west-2') + # Like us-west-2x where x is random + self.assertTrue(len(node.az) == len('us-west-2x')) instance = self.ec2.Instance(node.external_id) response = instance.describe_attribute(Attribute='ebsOptimized') @@ -498,10 +502,14 @@ class TestDriverAws(tests.DBTestCase): instance.tags = [] instance.private_ip_address = '10.0.0.1' instance.public_ip_address = '1.2.3.4' + instance.subnet = Dummy() + instance.subnet.availability_zone = 'us-west-2b' iface = Dummy() iface.ipv6_addresses = [{'Ipv6Address': 'fe80::dead:beef'}] instance.network_interfaces = [iface] - awsi = AwsInstance(instance, None) + provider = Dummy() + provider.region_name = 'us-west-2' + awsi = AwsInstance(provider, instance, None) self.assertEqual(awsi.public_ipv4, '1.2.3.4') self.assertEqual(awsi.private_ipv4, '10.0.0.1') self.assertEqual(awsi.public_ipv6, 'fe80::dead:beef') diff --git a/nodepool/tests/unit/test_driver_azure.py b/nodepool/tests/unit/test_driver_azure.py index 35d7fe996..78f4bf585 100644 --- a/nodepool/tests/unit/test_driver_azure.py +++ b/nodepool/tests/unit/test_driver_azure.py @@ -88,6 +88,9 @@ class TestDriverAzure(tests.DBTestCase): {'key1': 'value1', 'key2': 'value2'}) self.assertEqual(node.host_keys, ['ssh-rsa FAKEKEY']) self.assertEqual(node.python_path, 'auto') + self.assertEqual(node.cloud, 'Azure') + self.assertEqual(node.region, 'centralus') + self.assertEqual(node.az, '1') self.assertEqual( self.fake_azure.crud['Microsoft.Compute/virtualMachines']. items[0]['properties']['osProfile']['customData'], diff --git a/nodepool/tests/unit/test_driver_gce.py b/nodepool/tests/unit/test_driver_gce.py index 666e84ac0..39fe8741c 100644 --- a/nodepool/tests/unit/test_driver_gce.py +++ b/nodepool/tests/unit/test_driver_gce.py @@ -303,6 +303,9 @@ class TestDriverGce(tests.DBTestCase): self.assertEqual(node.connection_type, 'ssh') self.assertEqual(node.attributes, {'key1': 'value1', 'key2': 'value2'}) + self.assertEqual(node.cloud, 'Google') + self.assertEqual(node.region, 'us-central1') + self.assertEqual(node.az, 'us-central1-a') if host_key_checking: nodescan.assert_called_with( node.interface_ip, diff --git a/nodepool/tests/unit/test_driver_ibmvpc.py b/nodepool/tests/unit/test_driver_ibmvpc.py index 9aad05b7f..88d8244ef 100644 --- a/nodepool/tests/unit/test_driver_ibmvpc.py +++ b/nodepool/tests/unit/test_driver_ibmvpc.py @@ -95,6 +95,9 @@ class TestDriverIBMVPC(tests.DBTestCase): self.assertEqual(node.connection_type, 'ssh') self.assertEqual(node.host_keys, []) self.assertEqual(node.python_path, 'auto') + self.assertEqual(node.cloud, 'IBM') + self.assertEqual(node.region, 'us-south') + self.assertEqual(node.az, 'us-south-1') node.state = zk.USED self.zk.storeNode(node) diff --git a/nodepool/tests/unit/test_driver_kubernetes.py b/nodepool/tests/unit/test_driver_kubernetes.py index ae6fcc117..e26fb6949 100644 --- a/nodepool/tests/unit/test_driver_kubernetes.py +++ b/nodepool/tests/unit/test_driver_kubernetes.py @@ -75,6 +75,9 @@ class FakeCoreClient(object): class FakePod: class status: phase = "Running" + + class spec: + node_name = "k8s-default-pool-abcd-1234" return FakePod @@ -124,6 +127,8 @@ class TestDriverKubernetes(tests.DBTestCase): self.assertEqual(node.connection_port.get('token'), 'fake-token') self.assertEqual(node.attributes, {'key1': 'value1', 'key2': 'value2'}) + self.assertEqual(node.cloud, 'admin-cluster.local') + self.assertEqual(node.host_id, 'k8s-default-pool-abcd-1234') node.state = zk.DELETING self.zk.storeNode(node) @@ -150,6 +155,8 @@ class TestDriverKubernetes(tests.DBTestCase): self.assertIsNotNone(node.launcher) self.assertEqual(node.connection_type, 'namespace') self.assertEqual(node.connection_port.get('token'), 'fake-token') + self.assertEqual(node.cloud, 'admin-cluster.local') + self.assertIsNone(node.host_id) node.state = zk.DELETING self.zk.storeNode(node) diff --git a/nodepool/tests/unit/test_driver_openshift.py b/nodepool/tests/unit/test_driver_openshift.py index 8efd0d624..011ec778f 100644 --- a/nodepool/tests/unit/test_driver_openshift.py +++ b/nodepool/tests/unit/test_driver_openshift.py @@ -114,6 +114,9 @@ class FakeCoreClient(object): class FakePod: class status: phase = "Running" + + class spec: + node_name = "k8s-default-pool-abcd-1234" return FakePod @@ -157,6 +160,8 @@ class TestDriverOpenshift(tests.DBTestCase): self.assertEqual(node.shell_type, 'csh') self.assertEqual(node.attributes, {'key1': 'value1', 'key2': 'value2'}) + self.assertEqual(node.cloud, 'admin-cluster.local') + self.assertIsNone(node.host_id) node.state = zk.DELETING self.zk.storeNode(node) diff --git a/nodepool/tests/unit/test_driver_openshiftpods.py b/nodepool/tests/unit/test_driver_openshiftpods.py index 1ca287e32..352e8f1f2 100644 --- a/nodepool/tests/unit/test_driver_openshiftpods.py +++ b/nodepool/tests/unit/test_driver_openshiftpods.py @@ -54,6 +54,9 @@ class FakeCoreClient(object): class FakePod: class status: phase = "Running" + + class spec: + node_name = "k8s-default-pool-abcd-1234" return FakePod def delete_namespaced_pod(self, name, project): @@ -104,6 +107,8 @@ class TestDriverOpenshiftPods(tests.DBTestCase): self.assertIn('ca_crt', node.connection_port) self.assertEqual(node.attributes, {'key1': 'value1', 'key2': 'value2'}) + self.assertEqual(node.cloud, 'service-account.local') + self.assertEqual(node.host_id, 'k8s-default-pool-abcd-1234') node.state = zk.DELETING self.zk.storeNode(node)