Add generate_kdu_instance_name method in K8sConn 17/10417/1
authorDavid Garcia <david.garcia@canonical.com>
Tue, 23 Feb 2021 10:47:29 +0000 (11:47 +0100)
committerDavid Garcia <david.garcia@canonical.com>
Tue, 2 Mar 2021 08:16:49 +0000 (09:16 +0100)
The reason for adding this method is for LCM to call it before calling
K8sConn.install(). That way, LCM can record it in the DB before
finishing the instantiation of the KDU.

This will help fixing bug 1412.

Change-Id: Iacf71c4d2040dbdc966cff2bddd697c4ea1c9a06
Signed-off-by: David Garcia <david.garcia@canonical.com>
n2vc/k8s_conn.py
n2vc/k8s_helm3_conn.py
n2vc/k8s_helm_base_conn.py
n2vc/k8s_helm_conn.py
n2vc/k8s_juju_conn.py
n2vc/tests/unit/test_k8s_helm3_conn.py
n2vc/tests/unit/test_k8s_helm_conn.py
n2vc/tests/unit/test_k8s_juju_conn.py

index 5bdc8ac..058f5ba 100644 (file)
@@ -33,6 +33,9 @@ class K8sConnector(abc.ABC, Loggable):
     ################################### P U B L I C ####################################
     ####################################################################################
     """
+    @staticmethod
+    def generate_kdu_instance_name(**kwargs):
+        raise NotImplementedError("Method not implemented")
 
     def __init__(self, db: object, log: object = None, on_update_db=None):
         """
@@ -136,6 +139,7 @@ class K8sConnector(abc.ABC, Loggable):
         self,
         cluster_uuid: str,
         kdu_model: str,
+        kdu_instance: str,
         atomic: bool = True,
         timeout: float = 300,
         params: dict = None,
@@ -154,6 +158,7 @@ class K8sConnector(abc.ABC, Loggable):
             - a name of chart/bundle available via the repos known by OSM
             - a path to a packaged chart/bundle
             - a path to an unpacked chart/bundle directory or a URL
+        :param kdu_instance: Kdu instance name
         :param atomic: If set, installation process purges chart/bundle on fail, also
             will wait until all the K8s objects are active
         :param timeout: Time in seconds to wait for the install of the chart/bundle
index f79816d..6afadbf 100644 (file)
@@ -71,6 +71,7 @@ class K8sHelm3Connector(K8sHelmBaseConnector):
             self,
             cluster_uuid: str,
             kdu_model: str,
+            kdu_instance: str,
             atomic: bool = True,
             timeout: float = 300,
             params: dict = None,
@@ -95,22 +96,25 @@ class K8sHelm3Connector(K8sHelmBaseConnector):
             if namespace not in namespaces:
                 await self._create_namespace(cluster_id, namespace)
 
-        kdu_instance = await self._install_impl(cluster_id,
-                                                kdu_model,
-                                                paths,
-                                                env,
-                                                atomic=atomic,
-                                                timeout=timeout,
-                                                params=params,
-                                                db_dict=db_dict,
-                                                kdu_name=kdu_name,
-                                                namespace=namespace)
+        await self._install_impl(
+            cluster_id,
+            kdu_model,
+            paths,
+            env,
+            kdu_instance,
+            atomic=atomic,
+            timeout=timeout,
+            params=params,
+            db_dict=db_dict,
+            kdu_name=kdu_name,
+            namespace=namespace,
+        )
 
         # sync fs
         self.fs.reverse_sync(from_path=cluster_id)
 
         self.log.debug("Returning kdu_instance {}".format(kdu_instance))
-        return kdu_instance
+        return True
 
     async def inspect_kdu(self, kdu_model: str, repo_url: str = None) -> str:
 
index af22ecc..0298831 100644 (file)
@@ -293,6 +293,7 @@ class K8sHelmBaseConnector(K8sConnector):
             kdu_model: str,
             paths: dict,
             env: dict,
+            kdu_instance: str,
             atomic: bool = True,
             timeout: float = 300,
             params: dict = None,
@@ -313,23 +314,6 @@ class K8sHelmBaseConnector(K8sConnector):
                 version = str(parts[1])
                 kdu_model = parts[0]
 
-        # generate a name for the release. Then, check if already exists
-        kdu_instance = None
-        while kdu_instance is None:
-            kdu_instance = self._generate_release_name(kdu_model)
-            try:
-                result = await self._status_kdu(
-                    cluster_id=cluster_id,
-                    kdu_instance=kdu_instance,
-                    namespace=namespace,
-                    show_error_log=False,
-                )
-                if result is not None:
-                    # instance already exists: generate a new one
-                    kdu_instance = None
-            except K8sException:
-                pass
-
         command = self._get_install_command(kdu_model, kdu_instance, namespace,
                                             params_str, version, atomic, timeout)
 
@@ -389,8 +373,6 @@ class K8sHelmBaseConnector(K8sConnector):
             self.log.error(msg)
             raise K8sException(msg)
 
-        return kdu_instance
-
     async def upgrade(
         self,
         cluster_uuid: str,
@@ -1377,7 +1359,8 @@ class K8sHelmBaseConnector(K8sConnector):
         return params_str
 
     @staticmethod
-    def _generate_release_name(chart_name: str):
+    def generate_kdu_instance_name(**kwargs):
+        chart_name = kwargs["chart_name"]
         # check embeded chart (file or dir)
         if chart_name.startswith("/"):
             # extract file or directory name
index fdae32f..ad230b5 100644 (file)
@@ -92,6 +92,7 @@ class K8sHelmConnector(K8sHelmBaseConnector):
             self,
             cluster_uuid: str,
             kdu_model: str,
+            kdu_instance: str,
             atomic: bool = True,
             timeout: float = 300,
             params: dict = None,
@@ -110,22 +111,25 @@ class K8sHelmConnector(K8sHelmBaseConnector):
             cluster_name=cluster_id, create_if_not_exist=True
         )
 
-        kdu_instance = await self._install_impl(cluster_id,
-                                                kdu_model,
-                                                paths,
-                                                env,
-                                                atomic=atomic,
-                                                timeout=timeout,
-                                                params=params,
-                                                db_dict=db_dict,
-                                                kdu_name=kdu_name,
-                                                namespace=namespace)
+        await self._install_impl(
+            cluster_id,
+            kdu_model,
+            paths,
+            env,
+            kdu_instance,
+            atomic=atomic,
+            timeout=timeout,
+            params=params,
+            db_dict=db_dict,
+            kdu_name=kdu_name,
+            namespace=namespace,
+        )
 
         # sync fs
         self.fs.reverse_sync(from_path=cluster_id)
 
         self.log.debug("Returning kdu_instance {}".format(kdu_instance))
-        return kdu_instance
+        return True
 
     async def inspect_kdu(self, kdu_model: str, repo_url: str = None) -> str:
 
index 3d58385..e3d8a67 100644 (file)
@@ -456,6 +456,7 @@ class K8sJujuConnector(K8sConnector):
         self,
         cluster_uuid: str,
         kdu_model: str,
+        kdu_instance: str,
         atomic: bool = True,
         timeout: float = 1800,
         params: dict = None,
@@ -467,6 +468,7 @@ class K8sJujuConnector(K8sConnector):
 
         :param cluster_uuid str: The UUID of the cluster to install to
         :param kdu_model str: The name or path of a bundle to install
+        :param kdu_instance: Kdu instance name
         :param atomic bool: If set, waits until the model is active and resets
                             the cluster on failure.
         :param timeout int: The time, in seconds, to wait for the install
@@ -500,11 +502,6 @@ class K8sJujuConnector(K8sConnector):
             os.chdir(new_workdir)
             bundle = "local:{}".format(kdu_model)
 
-        if kdu_name:
-            kdu_instance = "{}-{}".format(kdu_name, db_dict["filter"]["_id"])
-        else:
-            kdu_instance = db_dict["filter"]["_id"]
-
         self.log.debug("Checking for model named {}".format(kdu_instance))
 
         # Create the new model
@@ -571,8 +568,7 @@ class K8sJujuConnector(K8sConnector):
         #     await model.disconnect()
         # await controller.disconnect()
         os.chdir(previous_workdir)
-
-        return kdu_instance
+        return True
 
     async def instances_list(self, cluster_uuid: str) -> list:
         """
@@ -1477,3 +1473,13 @@ class K8sJujuConnector(K8sConnector):
             base64.b64decode(token).decode("utf-8"),
             base64.b64decode(client_certificate_data).decode("utf-8"),
         )
+
+    @staticmethod
+    def generate_kdu_instance_name(**kwargs):
+        db_dict = kwargs.get("db_dict")
+        kdu_name = kwargs.get("kdu_name", None)
+        if kdu_name:
+            kdu_instance = "{}-{}".format(kdu_name, db_dict["filter"]["_id"])
+        else:
+            kdu_instance = db_dict["filter"]["_id"]
+        return kdu_instance
index b1b5a60..0403004 100644 (file)
@@ -134,25 +134,24 @@ class TestK8sHelm3Conn(asynctest.TestCase):
         self.helm_conn._local_async_exec = asynctest.CoroutineMock(return_value=("", 0))
         self.helm_conn._status_kdu = asynctest.CoroutineMock(return_value=None)
         self.helm_conn._store_status = asynctest.CoroutineMock()
-        self.helm_conn._generate_release_name = Mock(return_value="stable-openldap-0005399828")
+        self.kdu_instance = "stable-openldap-0005399828"
+        self.helm_conn.generate_kdu_instance_name = Mock(return_value=self.kdu_instance)
         self.helm_conn._get_namespaces = asynctest.CoroutineMock(return_value=[])
         self.helm_conn._create_namespace = asynctest.CoroutineMock()
 
-        kdu_instance = await self.helm_conn.install(self.cluster_uuid,
-                                                    kdu_model,
-                                                    atomic=True,
-                                                    namespace=self.namespace,
-                                                    db_dict=db_dict)
+        await self.helm_conn.install(
+            self.cluster_uuid,
+            kdu_model,
+            self.kdu_instance,
+            atomic=True,
+            namespace=self.namespace,
+            db_dict=db_dict
+        )
 
         self.helm_conn._get_namespaces.assert_called_once()
         self.helm_conn._create_namespace.assert_called_once_with(self.cluster_id, self.namespace)
         self.helm_conn.fs.sync.assert_called_once_with(from_path=self.cluster_id)
         self.helm_conn.fs.reverse_sync.assert_called_once_with(from_path=self.cluster_id)
-        self.helm_conn._generate_release_name.assert_called_once_with("stable/openldap")
-        self.helm_conn._status_kdu.assert_called_once_with(cluster_id=self.cluster_id,
-                                                           kdu_instance=kdu_instance,
-                                                           namespace=self.namespace,
-                                                           show_error_log=False)
         self.helm_conn._store_status.assert_called_with(cluster_id=self.cluster_id,
                                                         kdu_instance=kdu_instance,
                                                         namespace=self.namespace,
index 51edbd6..1eb5775 100644 (file)
@@ -118,21 +118,19 @@ class TestK8sHelmConn(asynctest.TestCase):
         self.helm_conn._local_async_exec = asynctest.CoroutineMock(return_value=("", 0))
         self.helm_conn._status_kdu = asynctest.CoroutineMock(return_value=None)
         self.helm_conn._store_status = asynctest.CoroutineMock()
-        self.helm_conn._generate_release_name = Mock(return_value="stable-openldap-0005399828")
+        self.helm_conn.generate_kdu_instance_name = Mock(return_value=kdu_instance)
 
-        kdu_instance = await self.helm_conn.install(self.cluster_uuid,
-                                                    kdu_model,
-                                                    atomic=True,
-                                                    namespace=self.namespace,
-                                                    db_dict=db_dict)
+        await self.helm_conn.install(
+            self.cluster_uuid,
+            kdu_model,
+            kdu_instance,
+            atomic=True,
+            namespace=self.namespace,
+            db_dict=db_dict
+        )
 
         self.helm_conn.fs.sync.assert_called_once_with(from_path=self.cluster_id)
         self.helm_conn.fs.reverse_sync.assert_called_once_with(from_path=self.cluster_id)
-        self.helm_conn._generate_release_name.assert_called_once_with("stable/openldap")
-        self.helm_conn._status_kdu.assert_called_once_with(cluster_id=self.cluster_id,
-                                                           kdu_instance=kdu_instance,
-                                                           namespace=self.namespace,
-                                                           show_error_log=False)
         self.helm_conn._store_status.assert_called_with(cluster_id=self.cluster_id,
                                                         kdu_instance=kdu_instance,
                                                         namespace=self.namespace,
index 388c08f..f486649 100644 (file)
@@ -325,105 +325,102 @@ class InstallTest(K8sJujuConnTestCase):
         self.http_bundle = "https://example.com/bundle.yaml"
         self.kdu_name = "kdu_name"
         self.cluster_uuid = "cluster"
+        self.kdu_instance = "{}-{}".format(self.kdu_name, "id")
         self.k8s_juju_conn.libjuju.add_model = AsyncMock()
         self.k8s_juju_conn.libjuju.deploy = AsyncMock()
 
     def test_success_local(self, mock_chdir):
-        expected_kdu_instance = "{}-{}".format(self.kdu_name, "id")
-        kdu_instance = self.loop.run_until_complete(
+        self.loop.run_until_complete(
             self.k8s_juju_conn.install(
                 self.cluster_uuid,
                 self.local_bundle,
+                self.kdu_instance,
                 atomic=True,
                 kdu_name=self.kdu_name,
                 db_dict=self.db_dict,
                 timeout=1800,
             )
         )
-        self.assertEqual(kdu_instance, expected_kdu_instance)
         self.assertEqual(mock_chdir.call_count, 2)
         self.k8s_juju_conn.libjuju.add_model.assert_called_once_with(
-            model_name=expected_kdu_instance,
+            model_name=self.kdu_instance,
             cloud_name=self.cluster_uuid,
             credential_name="cred-{}".format(self.cluster_uuid),
         )
         self.k8s_juju_conn.libjuju.deploy.assert_called_once_with(
             "local:{}".format(self.local_bundle),
-            model_name=expected_kdu_instance,
+            model_name=self.kdu_instance,
             wait=True,
             timeout=1800,
         )
 
     def test_success_cs(self, mock_chdir):
-        expected_kdu_instance = "{}-{}".format(self.kdu_name, "id")
-        kdu_instance = self.loop.run_until_complete(
+        self.loop.run_until_complete(
             self.k8s_juju_conn.install(
                 self.cluster_uuid,
                 self.cs_bundle,
+                self.kdu_instance,
                 atomic=True,
                 kdu_name=self.kdu_name,
                 db_dict=self.db_dict,
                 timeout=1800,
             )
         )
-        self.assertEqual(kdu_instance, expected_kdu_instance)
         self.k8s_juju_conn.libjuju.add_model.assert_called_once_with(
-            model_name=expected_kdu_instance,
+            model_name=self.kdu_instance,
             cloud_name=self.cluster_uuid,
             credential_name="cred-{}".format(self.cluster_uuid),
         )
         self.k8s_juju_conn.libjuju.deploy.assert_called_once_with(
             self.cs_bundle,
-            model_name=expected_kdu_instance,
+            model_name=self.kdu_instance,
             wait=True,
             timeout=1800,
         )
 
     def test_success_http(self, mock_chdir):
-        expected_kdu_instance = "{}-{}".format(self.kdu_name, "id")
-        kdu_instance = self.loop.run_until_complete(
+        self.loop.run_until_complete(
             self.k8s_juju_conn.install(
                 self.cluster_uuid,
                 self.http_bundle,
+                self.kdu_instance,
                 atomic=True,
                 kdu_name=self.kdu_name,
                 db_dict=self.db_dict,
                 timeout=1800,
             )
         )
-        self.assertEqual(kdu_instance, expected_kdu_instance)
         self.k8s_juju_conn.libjuju.add_model.assert_called_once_with(
-            model_name=expected_kdu_instance,
+            model_name=self.kdu_instance,
             cloud_name=self.cluster_uuid,
             credential_name="cred-{}".format(self.cluster_uuid),
         )
         self.k8s_juju_conn.libjuju.deploy.assert_called_once_with(
             self.http_bundle,
-            model_name=expected_kdu_instance,
+            model_name=self.kdu_instance,
             wait=True,
             timeout=1800,
         )
 
     def test_success_not_kdu_name(self, mock_chdir):
-        expected_kdu_instance = "id"
-        kdu_instance = self.loop.run_until_complete(
+        self.loop.run_until_complete(
             self.k8s_juju_conn.install(
                 self.cluster_uuid,
                 self.cs_bundle,
+                self.kdu_instance,
                 atomic=True,
                 db_dict=self.db_dict,
                 timeout=1800,
             )
         )
-        self.assertEqual(kdu_instance, expected_kdu_instance)
         self.k8s_juju_conn.libjuju.add_model.assert_called_once_with(
-            model_name=expected_kdu_instance,
+            model_name=self.kdu_instance,
             cloud_name=self.cluster_uuid,
             credential_name="cred-{}".format(self.cluster_uuid),
         )
         self.k8s_juju_conn.libjuju.deploy.assert_called_once_with(
             self.cs_bundle,
-            model_name=expected_kdu_instance,
+            model_name=self.kdu_instance,
             wait=True,
             timeout=1800,
         )
@@ -431,10 +428,11 @@ class InstallTest(K8sJujuConnTestCase):
     def test_missing_db_dict(self, mock_chdir):
         kdu_instance = None
         with self.assertRaises(K8sException):
-            kdu_instance = self.loop.run_until_complete(
+            self.loop.run_until_complete(
                 self.k8s_juju_conn.install(
                     self.cluster_uuid,
                     self.cs_bundle,
+                    self.kdu_instance,
                     atomic=True,
                     kdu_name=self.kdu_name,
                     timeout=1800,
@@ -447,71 +445,67 @@ class InstallTest(K8sJujuConnTestCase):
     @asynctest.mock.patch("os.getcwd")
     def test_getcwd_exception(self, mock_getcwd, mock_chdir):
         mock_getcwd.side_effect = FileNotFoundError()
-        expected_kdu_instance = "{}-{}".format(self.kdu_name, "id")
-        kdu_instance = self.loop.run_until_complete(
+        self.loop.run_until_complete(
             self.k8s_juju_conn.install(
                 self.cluster_uuid,
                 self.cs_bundle,
+                self.kdu_instance,
                 atomic=True,
                 kdu_name=self.kdu_name,
                 db_dict=self.db_dict,
                 timeout=1800,
             )
         )
-        self.assertEqual(kdu_instance, expected_kdu_instance)
         self.k8s_juju_conn.libjuju.add_model.assert_called_once_with(
-            model_name=expected_kdu_instance,
+            model_name=self.kdu_instance,
             cloud_name=self.cluster_uuid,
             credential_name="cred-{}".format(self.cluster_uuid),
         )
         self.k8s_juju_conn.libjuju.deploy.assert_called_once_with(
             self.cs_bundle,
-            model_name=expected_kdu_instance,
+            model_name=self.kdu_instance,
             wait=True,
             timeout=1800,
         )
 
     def test_missing_bundle(self, mock_chdir):
-        kdu_instance = None
         with self.assertRaises(K8sException):
-            kdu_instance = self.loop.run_until_complete(
+            self.loop.run_until_complete(
                 self.k8s_juju_conn.install(
                     self.cluster_uuid,
                     "",
+                    self.kdu_instance,
                     atomic=True,
                     kdu_name=self.kdu_name,
                     timeout=1800,
                     db_dict=self.db_dict,
                 )
             )
-        self.assertIsNone(kdu_instance)
         self.k8s_juju_conn.libjuju.add_model.assert_not_called()
         self.k8s_juju_conn.libjuju.deploy.assert_not_called()
 
     def test_missing_exception(self, mock_chdir):
-        expected_kdu_instance = "{}-{}".format(self.kdu_name, "id")
-        kdu_instance = None
         self.k8s_juju_conn.libjuju.deploy.side_effect = Exception()
         with self.assertRaises(Exception):
-            kdu_instance = self.loop.run_until_complete(
+            self.loop.run_until_complete(
                 self.k8s_juju_conn.install(
                     self.cluster_uuid,
                     self.local_bundle,
+                    self.kdu_instance,
                     atomic=True,
                     kdu_name=self.kdu_name,
                     db_dict=self.db_dict,
                     timeout=1800,
                 )
             )
-        self.assertIsNone(kdu_instance)
         self.k8s_juju_conn.libjuju.add_model.assert_called_once_with(
-            model_name=expected_kdu_instance,
+            model_name=self.kdu_instance,
             cloud_name=self.cluster_uuid,
             credential_name="cred-{}".format(self.cluster_uuid),
         )
         self.k8s_juju_conn.libjuju.deploy.assert_called_once_with(
             "local:{}".format(self.local_bundle),
-            model_name=expected_kdu_instance,
+            model_name=self.kdu_instance,
             wait=True,
             timeout=1800,
         )