diff --git a/magnum_capi_helm/driver.py b/magnum_capi_helm/driver.py index 336ed95..362b1ec 100644 --- a/magnum_capi_helm/driver.py +++ b/magnum_capi_helm/driver.py @@ -62,6 +62,11 @@ def provides(self): "os": "capi-kubeadm-cloudinit", "coe": "kubernetes", }, + { + "server_type": "vm", + "os": "flatcar", + "coe": "kubernetes", + }, ] def _update_control_plane_nodegroup_status(self, cluster, nodegroup): @@ -499,12 +504,25 @@ def _get_kube_version(self, image): # TODO(johngarbutt) more validation required? return re.sub(r"[^0-9\.]+", "", raw) + def _get_os_distro(self, image): + os_distro = image.get("os_distro") + if not os_distro: + raise exception.MagnumException( + message=f"Image {image.id} does not " + "have an os_distro property." + ) + return re.sub(r"[^a-zA-Z0-9\.\-\/ ]+", "", os_distro) + def _get_image_details(self, context, image_identifier): osc = clients.OpenStackClients(context) image = api_utils.get_openstack_resource( osc.glance().images, image_identifier, "images" ) - return image.id, self._get_kube_version(image) + return ( + image.id, + self._get_kube_version(image), + self._get_os_distro(image), + ) def _get_app_cred_name(self, cluster): return self._sanitized_name( @@ -544,7 +562,7 @@ def _update_helm_release(self, context, cluster, nodegroups=None): if nodegroups is None: nodegroups = cluster.nodegroups - image_id, kube_version = self._get_image_details( + image_id, kube_version, os_distro = self._get_image_details( context, cluster.cluster_template.image_id ) @@ -578,6 +596,7 @@ def _update_helm_release(self, context, cluster, nodegroups=None): ), }, }, + "osDistro": os_distro, "controlPlane": { "machineFlavor": cluster.master_flavor_id, "machineCount": cluster.master_count, diff --git a/magnum_capi_helm/tests/test_driver.py b/magnum_capi_helm/tests/test_driver.py index e47862b..d343853 100644 --- a/magnum_capi_helm/tests/test_driver.py +++ b/magnum_capi_helm/tests/test_driver.py @@ -53,7 +53,12 @@ def test_provides(self): "server_type": "vm", "os": "capi-kubeadm-cloudinit", "coe": "kubernetes", - } + }, + { + "server_type": "vm", + "os": "flatcar", + "coe": "kubernetes", + }, ], self.driver.provides, ) @@ -1017,24 +1022,61 @@ def test_get_kube_version_works(self): self.assertEqual("1.27.9", result) mock_image.get.assert_called_once_with("kube_version") - @mock.patch("magnum.common.clients.OpenStackClients") - @mock.patch("magnum.api.utils.get_openstack_resource") - def test_get_image_details(self, mock_get, mock_osc): + @mock.patch("magnum.common.clients.OpenStackClients", autospec=True) + @mock.patch("magnum.api.utils.get_openstack_resource", autospec=True) + def test_get_image_details_ubuntu(self, mock_get, mock_osc): mock_image = mock.Mock() - mock_image.get.return_value = "v1.27.9" + image_metadata = { + "os_distro": "ubuntu", + "kube_version": "1.27.9", + } + + def image_side_effect(arg): + return image_metadata[arg] + + mock_image.get.side_effect = image_side_effect mock_image.id = "myid" mock_get.return_value = mock_image - id, version = self.driver._get_image_details( + id, version, distro = self.driver._get_image_details( self.context, "myimagename" ) self.assertEqual("1.27.9", version) self.assertEqual("myid", id) - mock_image.get.assert_called_once_with("kube_version") + self.assertEqual("ubuntu", distro) + mock_image.get.assert_any_call("kube_version") + mock_image.get.assert_any_call("os_distro") mock_get.assert_called_once_with(mock.ANY, "myimagename", "images") - def test_get_chart_release_name_lenght(self): + @mock.patch("magnum.common.clients.OpenStackClients", autospec=True) + @mock.patch("magnum.api.utils.get_openstack_resource", autospec=True) + def test_get_image_details_flatcar(self, mock_get, mock_osc): + mock_image = mock.Mock() + image_metadata = { + "os_distro": "flatcar", + "kube_version": "1.28.2", + } + + def image_side_effect(arg): + return image_metadata[arg] + + mock_image.get.side_effect = image_side_effect + mock_image.id = "myid-flatcar" + mock_get.return_value = mock_image + + id, version, distro = self.driver._get_image_details( + self.context, "myimagename" + ) + + self.assertEqual("1.28.2", version) + self.assertEqual("myid-flatcar", id) + self.assertEqual("flatcar", distro) + mock_image.get.assert_any_call("kube_version") + mock_image.get.assert_any_call("os_distro") + mock_get.assert_called_once_with(mock.ANY, "myimagename", "images") + + def test_get_chart_release_name_length(self): self.cluster_obj.stack_id = "foo" result = self.driver._get_chart_release_name(self.cluster_obj) @@ -1109,14 +1151,18 @@ def test_create_cluster( mock_certs, mock_get_net, ): - mock_image.return_value = ("imageid1", "1.27.4") + mock_image.return_value = ( + "imageid1", + "1.27.4", + "capi-kubeadm-cloudinit", + ) mock_client = mock.MagicMock(spec=kubernetes.Client) mock_load.return_value = mock_client mock_get_net.side_effect = ( lambda c, net, source, target, external: f"{net}-{external}" ) - - self.cluster_obj.keypair = "kp1" + expected_keypair = "kp1" + self.cluster_obj.keypair = expected_keypair self.driver.create_cluster(self.context, self.cluster_obj, 10) @@ -1142,6 +1188,7 @@ def test_create_cluster( "enableLoadBalancer": True, "loadBalancerProvider": "amphora", }, + "osDistro": "capi-kubeadm-cloudinit", "controlPlane": { "machineFlavor": "flavor_small", "machineCount": 3, @@ -1156,9 +1203,9 @@ def test_create_cluster( "name": "test-worker", "machineFlavor": "flavor_medium", "machineCount": 3, - } + }, ], - "machineSSHKeyName": "kp1", + "machineSSHKeyName": expected_keypair, }, repo=CONF.capi_helm.helm_chart_repo, version=CONF.capi_helm.default_helm_chart_version, @@ -1184,11 +1231,16 @@ def test_create_cluster_no_dns( mock_appcred, mock_certs, ): - mock_image.return_value = ("imageid1", "1.27.4") + mock_image.return_value = ( + "imageid1", + "1.27.4", + "capi-kubeadm-cloudinit", + ) mock_client = mock.MagicMock(spec=kubernetes.Client) mock_load.return_value = mock_client self.cluster_obj.cluster_template.dns_nameserver = "" - self.cluster_obj.keypair = "kp1" + expected_keypair = "kp1" + self.cluster_obj.keypair = expected_keypair self.cluster_obj.cluster_template.labels["extra_network_name"] = "foo" self.driver.create_cluster(self.context, self.cluster_obj, 10) @@ -1215,6 +1267,7 @@ def test_create_cluster_no_dns( "enableLoadBalancer": True, "loadBalancerProvider": "amphora", }, + "osDistro": "capi-kubeadm-cloudinit", "controlPlane": { "machineFlavor": "flavor_small", "machineCount": 3, @@ -1242,7 +1295,7 @@ def test_create_cluster_no_dns( ], }, }, - "machineSSHKeyName": "kp1", + "machineSSHKeyName": expected_keypair, }, repo=CONF.capi_helm.helm_chart_repo, version=CONF.capi_helm.default_helm_chart_version, @@ -1267,7 +1320,11 @@ def test_create_cluster_no_keypair( mock_appcred, mock_certs, ): - mock_image.return_value = ("imageid1", "1.27.4") + mock_image.return_value = ( + "imageid1", + "1.27.4", + "capi-kubeadm-cloudinit", + ) mock_client = mock.MagicMock(spec=kubernetes.Client) mock_load.return_value = mock_client self.cluster_obj.keypair = "" @@ -1296,6 +1353,7 @@ def test_create_cluster_no_keypair( "enableLoadBalancer": True, "loadBalancerProvider": "amphora", }, + "osDistro": "capi-kubeadm-cloudinit", "controlPlane": { "machineFlavor": "flavor_small", "machineCount": 3, diff --git a/magnum_capi_helm/tests/test_magnum_capi_helm.py b/magnum_capi_helm/tests/test_magnum_capi_helm.py index 74f6010..9676b0d 100644 --- a/magnum_capi_helm/tests/test_magnum_capi_helm.py +++ b/magnum_capi_helm/tests/test_magnum_capi_helm.py @@ -17,8 +17,14 @@ class TestMagnumDriverLoads(base.TestCase): - def test_get_driver(self): + def test_get_ubuntu_driver(self): cluster_driver = common.Driver.get_driver( "vm", "capi-kubeadm-cloudinit", "kubernetes" ) self.assertIsInstance(cluster_driver, driver.Driver) + + def test_get_flatcar_driver(self): + cluster_driver = common.Driver.get_driver( + "vm", "flatcar", "kubernetes" + ) + self.assertIsInstance(cluster_driver, driver.Driver)