diff --git a/magnum_capi_helm/driver.py b/magnum_capi_helm/driver.py index a90627b..c653e39 100644 --- a/magnum_capi_helm/driver.py +++ b/magnum_capi_helm/driver.py @@ -62,6 +62,11 @@ def provides(self): "os": "ubuntu", "coe": "kubernetes", }, + { + "server_type": "vm", + "os": "flatcar", + "coe": "kubernetes", + }, ] def _update_control_plane_nodegroup_status(self, cluster, nodegroup): @@ -502,12 +507,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( @@ -547,7 +565,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 ) @@ -581,6 +599,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 2f8bf6c..a863125 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": "ubuntu", "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") + + @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_lenght(self): + def test_get_chart_release_name_length(self): self.cluster_obj.stack_id = "foo" result = self.driver._get_chart_release_name(self.cluster_obj) @@ -1137,6 +1179,7 @@ def get_cluster_helm_standard_values(self): "machineCount": 3, }, ], + "osDistro": "ubuntu", "machineSSHKeyName": None, } @@ -1155,7 +1198,11 @@ 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", + "ubuntu", + ) mock_client = mock.MagicMock(spec=kubernetes.Client) mock_load.return_value = mock_client mock_get_net.side_effect = ( @@ -1194,7 +1241,7 @@ 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", "ubuntu") mock_client = mock.MagicMock(spec=kubernetes.Client) mock_load.return_value = mock_client @@ -1205,6 +1252,9 @@ def test_create_cluster_no_dns( expected_values = self.get_cluster_helm_standard_values() expected_values["clusterNetworking"]["dnsNameservers"] = None + helm_install_values = mock_install.call_args[0][2] + self.assertDictEqual(helm_install_values, expected_values) + mock_install.assert_called_once_with( "cluster-example-a-111111111111", "openstack-cluster", @@ -1234,7 +1284,7 @@ def test_create_cluster_boot_volume( mock_appcred, mock_certs, ): - mock_image.return_value = ("imageid1", "1.27.4") + mock_image.return_value = ("imageid1", "1.27.4", "ubuntu") mock_client = mock.MagicMock(spec=kubernetes.Client) mock_load.return_value = mock_client @@ -1266,6 +1316,7 @@ def test_create_cluster_boot_volume( "enableLoadBalancer": True, "loadBalancerProvider": "amphora", }, + "osDistro": "ubuntu", "controlPlane": { "machineFlavor": "flavor_small", "machineCount": 3, @@ -1323,7 +1374,11 @@ def test_create_cluster_boot_volume_extra_network( mock_appcred, mock_certs, ): - mock_image.return_value = ("imageid1", "1.27.4") + mock_image.return_value = ( + "imageid1", + "1.27.4", + "ubuntu", + ) mock_client = mock.MagicMock(spec=kubernetes.Client) mock_load.return_value = mock_client @@ -1357,6 +1412,7 @@ def test_create_cluster_boot_volume_extra_network( "enableLoadBalancer": True, "loadBalancerProvider": "amphora", }, + "osDistro": "ubuntu", "controlPlane": { "machineFlavor": "flavor_small", "machineCount": 3, @@ -1423,7 +1479,11 @@ def test_create_cluster_with_keypair( mock_appcred, mock_certs, ): - mock_image.return_value = ("imageid1", "1.27.4") + mock_image.return_value = ( + "imageid1", + "1.27.4", + "ubuntu", + ) mock_client = mock.MagicMock(spec=kubernetes.Client) mock_load.return_value = mock_client @@ -1448,6 +1508,46 @@ def test_create_cluster_with_keypair( mock_appcred.assert_called_once_with(self.context, self.cluster_obj) mock_certs.assert_called_once_with(self.context, self.cluster_obj) + @mock.patch.object(driver.Driver, "_ensure_certificate_secrets") + @mock.patch.object(driver.Driver, "_create_appcred_secret") + @mock.patch.object(kubernetes.Client, "load") + @mock.patch.object(driver.Driver, "_get_image_details") + @mock.patch.object(helm.Client, "install_or_upgrade") + def test_create_cluster_flatcar( + self, + mock_install, + mock_image, + mock_load, + mock_appcred, + mock_certs, + ): + mock_image.return_value = ( + "imageid1", + "1.27.4", + "flatcar", + ) + mock_client = mock.MagicMock(spec=kubernetes.Client) + mock_load.return_value = mock_client + + self.driver.create_cluster(self.context, self.cluster_obj, 10) + + expected_values = self.get_cluster_helm_standard_values() + expected_values["osDistro"] = "flatcar" + + mock_install.assert_called_once_with( + "cluster-example-a-111111111111", + "openstack-cluster", + expected_values, + repo=CONF.capi_helm.helm_chart_repo, + version=CONF.capi_helm.default_helm_chart_version, + namespace="magnum-fakeproject", + ) + mock_client.ensure_namespace.assert_called_once_with( + "magnum-fakeproject" + ) + mock_appcred.assert_called_once_with(self.context, self.cluster_obj) + mock_certs.assert_called_once_with(self.context, self.cluster_obj) + @mock.patch.object(app_creds, "get_app_cred_string_data") @mock.patch.object(kubernetes.Client, "load") def test_create_appcred_secret(self, mock_load, mock_sd): diff --git a/magnum_capi_helm/tests/test_magnum_capi_helm.py b/magnum_capi_helm/tests/test_magnum_capi_helm.py index 7e2db9c..4e2a614 100644 --- a/magnum_capi_helm/tests/test_magnum_capi_helm.py +++ b/magnum_capi_helm/tests/test_magnum_capi_helm.py @@ -20,3 +20,9 @@ class TestMagnumDriverLoads(base.TestCase): def test_get_driver(self): cluster_driver = common.Driver.get_driver("vm", "ubuntu", "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)