OSM-1033 Juju Key for Model 43/13243/1
authorMark Beierl <mark.beierl@canonical.com>
Thu, 20 Apr 2023 03:46:47 +0000 (03:46 +0000)
committerMark Beierl <mark.beierl@canonical.com>
Thu, 20 Apr 2023 03:46:47 +0000 (03:46 +0000)
Uses authorized_keys in the vim_config to create the model

Change-Id: I969f8879da227c8d82afff2754e5b5f51d17bd8d
Signed-off-by: Mark Beierl <mark.beierl@canonical.com>
osm_lcm/temporal/juju_paas_activities.py
osm_lcm/tests/test_juju_paas_activities.py

index d8cc55c..f964ea7 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 import asyncio
+import base64
 import logging
 from juju.application import Application
 from juju.controller import Controller
-from temporalio import activity
+from n2vc.config import EnvironConfig
 from n2vc.temporal_libjuju import ConnectionInfo, Libjuju
-from osm_common.temporal_constants import (
-    ACTIVITY_TEST_VIM_CONNECTIVITY,
-    ACTIVITY_CREATE_MODEL,
-    ACTIVITY_DEPLOY_CHARM,
-    ACTIVITY_CHECK_CHARM_STATUS,
-)
 from osm_common.dataclasses.temporal_dataclasses import (
     CharmInfo,
     CheckCharmStatusInput,
@@ -32,6 +27,13 @@ from osm_common.dataclasses.temporal_dataclasses import (
     TestVimConnectivityInput,
     VduInstantiateInput,
 )
+from osm_common.temporal_constants import (
+    ACTIVITY_TEST_VIM_CONNECTIVITY,
+    ACTIVITY_CREATE_MODEL,
+    ACTIVITY_DEPLOY_CHARM,
+    ACTIVITY_CHECK_CHARM_STATUS,
+)
+from temporalio import activity
 
 
 class JujuPaasConnector:
@@ -44,6 +46,7 @@ class JujuPaasConnector:
     def __init__(self, db):
         self.db = db
         self.logger = logging.getLogger(f"lcm.act.{self.__class__.__name__}")
+        self.config = EnvironConfig()
 
     def _decrypt_password(self, vim_content: dict) -> str:
         """Decrypt a password.
@@ -155,9 +158,34 @@ class JujuPaasConnector:
             back-off strategy for this activity, as it will naturally block
             and wait on each connection attempt.
         """
-        model_name = create_model_input.model_name
-        libjuju = self._get_libjuju(create_model_input.vim_uuid)
-        await libjuju.add_model(model_name)
+        controller = await self._get_controller(create_model_input.vim_uuid)
+        if create_model_input.model_name in await controller.list_models():
+            self.logger.debug(f"Model {create_model_input.model_name} already created")
+            return
+
+        vim_content = self.db.get_one(
+            "vim_accounts", {"_id": create_model_input.vim_uuid}
+        )
+        vim_config = vim_content["config"]
+
+        config = {
+            "endpoints": ",".join(await controller.api_endpoints),
+            "user": vim_content["vim_user"],
+            "secret": self._decrypt_password(vim_content),
+            "cacert": base64.b64encode(
+                vim_config["ca_cert_content"].encode("utf-8")
+            ).decode("utf-8"),
+            "authorized-keys": vim_config["authorized_keys"],
+        }
+
+        self.logger.debug(f"Creating model {create_model_input.model_name}")
+        await controller.add_model(
+            create_model_input.model_name,
+            config=config,
+            cloud_name=vim_config["cloud"],
+            credential_name=vim_config["cloud_credentials"],
+        )
+        self.logger.debug(f"Model {create_model_input.model_name} created")
 
     @activity.defn(name=ACTIVITY_DEPLOY_CHARM)
     async def deploy_charm(self, deploy_charm_input: VduInstantiateInput) -> None:
index 3e56bd5..ddafd54 100644 (file)
@@ -19,6 +19,7 @@ import asyncio
 import unittest.mock as mock
 
 from juju.application import Application
+from juju.controller import Controller
 from juju.model import Model
 from juju.errors import JujuError
 from juju.unit import Unit
@@ -29,11 +30,10 @@ from osm_common.dataclasses.temporal_dataclasses import (
     ModelInfo,
     VduInstantiateInput,
 )
-from osm_common.dbbase import DbException
 from osm_lcm.temporal.juju_paas_activities import JujuPaasConnector
 from parameterized import parameterized
 from temporalio.testing import ActivityEnvironment
-from unittest.mock import AsyncMock, Mock
+from unittest.mock import ANY, AsyncMock, Mock
 
 vim_id = "some-vim-uuid"
 namespace = "some-namespace"
@@ -41,6 +41,23 @@ model_info = ModelInfo(vim_id, namespace)
 connection_info = ConnectionInfo(
     "1.2.3.4:17070", "user", "password", "cacert", "cloud_name", "cloud_credentials"
 )
+vim_content = {
+    "_id": "82258772-0145-47cf-9a56-98a83aab38cc",
+    "name": "juju-with-key",
+    "vim_type": "paas",
+    "description": "",
+    "vim_url": "10.152.183.83:17070",
+    "vim_user": "admin",
+    "vim_password": "********",
+    "vim_tenant_name": "null",
+    "config": {
+        "paas_provider": "juju",
+        "cloud": "microk8s",
+        "cloud_credentials": "microk8s",
+        "authorized_keys": "1yc2EAAAADAQABAAABAQDM3js",
+        "ca_cert_content": "BEGIN CERTIFICATE\nMIIEEj",
+    },
+}
 
 
 class TestJujuPaasConnector(asynctest.TestCase):
@@ -48,45 +65,35 @@ class TestJujuPaasConnector(asynctest.TestCase):
         self.db = Mock()
         self.env = ActivityEnvironment()
         self.juju_paas_connector = JujuPaasConnector(self.db)
+        self.controller = AsyncMock(spec=Controller)
 
-    @asynctest.mock.patch("osm_lcm.temporal.juju_paas_activities.Libjuju.add_model")
-    @asynctest.mock.patch(
-        "osm_lcm.temporal.juju_paas_activities.JujuPaasConnector._get_connection_info"
-    )
-    async def test_create_model_nominal_case(
-        self, mock_get_connection_info, mock_add_model
-    ):
-        mock_get_connection_info.return_value = connection_info
-        await self.env.run(self.juju_paas_connector.create_model, model_info)
-        mock_get_connection_info.assert_called_once_with(vim_id)
-        mock_add_model.assert_called_once_with(namespace)
+        async def get_controller(_: str):
+            return self.controller
 
-    @asynctest.mock.patch("osm_lcm.temporal.juju_paas_activities.Libjuju.add_model")
-    @asynctest.mock.patch(
-        "osm_lcm.temporal.juju_paas_activities.JujuPaasConnector._get_connection_info"
-    )
-    async def test_create_model_raises_juju_exception(
-        self, mock_get_connection_info, mock_add_model
-    ):
-        mock_get_connection_info.return_value = connection_info
-        mock_add_model.side_effect = JujuError()
-        with self.assertRaises(JujuError):
-            await self.env.run(self.juju_paas_connector.create_model, model_info)
-        mock_get_connection_info.assert_called_once_with(vim_id)
-        mock_add_model.assert_called_once_with(namespace)
+        self.juju_paas_connector._get_controller = get_controller
+        self._api_endpoints = []
+        self.controller.api_endpoints = self.api_endpoints
 
-    @asynctest.mock.patch("osm_lcm.temporal.juju_paas_activities.Libjuju.add_model")
-    @asynctest.mock.patch(
-        "osm_lcm.temporal.juju_paas_activities.JujuPaasConnector._get_connection_info"
-    )
-    async def test_create_model_raises_db_exception(
-        self, mock_get_connection_info, mock_add_model
-    ):
-        mock_get_connection_info.side_effect = DbException("not found")
-        with self.assertRaises(DbException):
-            await self.env.run(self.juju_paas_connector.create_model, model_info)
-        mock_get_connection_info.assert_called_once_with(vim_id)
-        mock_add_model.assert_not_called()
+    @property
+    async def api_endpoints(self):
+        return self._api_endpoints
+
+    async def test_create_model_nominal_case(self):
+        self.db.get_one.side_effect = [vim_content]
+        self.juju_paas_connector._decrypt_password = Mock()
+        self.juju_paas_connector._decrypt_password.side_effect = ["password"]
+        await self.env.run(self.juju_paas_connector.create_model, model_info)
+        self.controller.add_model.assert_called_once_with(
+            model_info.model_name,
+            config=ANY,
+            cloud_name=vim_content["config"]["cloud"],
+            credential_name=vim_content["config"]["cloud_credentials"],
+        )
+
+    async def test_create_model_already_exists(self):
+        self.controller.list_models.return_value = [model_info.model_name]
+        await self.env.run(self.juju_paas_connector.create_model, model_info)
+        self.controller.add_model.assert_not_called()
 
 
 class TestJujuPaasActivitiesBase(asynctest.TestCase):