Feature 10239: Distributed VCA
- Add vca_id in all calls that invoke libjuju. This is for being able to
talk to the default VCA or the VCA associated to the VIM
- Add store.py: Abstraction to talk to the database.
- DBMongoStore: Use the db from common to talk to the database
- MotorStore: Use motor, an asynchronous mongodb client to talk to the
database
- Add vca/connection.py: Represents the data needed to connect the VCA
- Add EnvironConfig in config.py: Class to get the environment config,
and avoid LCM from passing that
Change-Id: I28625e0c56ce408114022c83d4b7cacbb649434c
Signed-off-by: David Garcia <david.garcia@canonical.com>
diff --git a/n2vc/tests/unit/test_config.py b/n2vc/tests/unit/test_config.py
new file mode 100644
index 0000000..9a4af07
--- /dev/null
+++ b/n2vc/tests/unit/test_config.py
@@ -0,0 +1,58 @@
+# Copyright 2020 Canonical Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from unittest import TestCase
+from unittest.mock import patch
+
+
+from n2vc.config import EnvironConfig, ModelConfig, MODEL_CONFIG_KEYS
+
+
+def generate_os_environ_dict(config, prefix):
+ return {f"{prefix}{k.upper()}": v for k, v in config.items()}
+
+
+class TestEnvironConfig(TestCase):
+ def setUp(self):
+ self.config = {"host": "1.2.3.4", "port": "17070", "k8s_cloud": "k8s"}
+
+ @patch("os.environ.items")
+ def test_environ_config_lcm(self, mock_environ_items):
+ envs = generate_os_environ_dict(self.config, "OSMLCM_VCA_")
+ envs["not_valid_env"] = "something"
+ mock_environ_items.return_value = envs.items()
+ config = EnvironConfig()
+ self.assertEqual(config, self.config)
+
+ @patch("os.environ.items")
+ def test_environ_config_mon(self, mock_environ_items):
+ envs = generate_os_environ_dict(self.config, "OSMMON_VCA_")
+ envs["not_valid_env"] = "something"
+ mock_environ_items.return_value = envs.items()
+ config = EnvironConfig()
+ self.assertEqual(config, self.config)
+
+
+class TestModelConfig(TestCase):
+ def setUp(self):
+ self.config = {
+ f'model_config_{model_key.replace("-", "_")}': "somevalue"
+ for model_key in MODEL_CONFIG_KEYS
+ }
+ self.config["model_config_invalid"] = "something"
+ self.model_config = {model_key: "somevalue" for model_key in MODEL_CONFIG_KEYS}
+
+ def test_model_config(self):
+ model_config = ModelConfig(self.config)
+ self.assertEqual(model_config, self.model_config)
diff --git a/n2vc/tests/unit/test_connection.py b/n2vc/tests/unit/test_connection.py
new file mode 100644
index 0000000..c7f0bb4
--- /dev/null
+++ b/n2vc/tests/unit/test_connection.py
@@ -0,0 +1,68 @@
+# Copyright 2020 Canonical Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import asyncio
+from unittest import TestCase
+from unittest.mock import Mock, patch
+
+
+from n2vc.tests.unit.utils import AsyncMock
+from n2vc.vca import connection
+
+
+class TestConnection(TestCase):
+ def setUp(self):
+ self.loop = asyncio.get_event_loop()
+ self.store = AsyncMock()
+
+ def test_load_from_store(self):
+ self.loop.run_until_complete(connection.get_connection(self.store, "vim_id"))
+
+ self.store.get_vca_connection_data.assert_called_once()
+
+ def test_cloud_properties(self):
+ conn = self.loop.run_until_complete(
+ connection.get_connection(self.store, "vim_id")
+ )
+ conn._data = Mock()
+ conn._data.lxd_cloud = "name"
+ conn._data.k8s_cloud = "name"
+ conn._data.lxd_credentials = "credential"
+ conn._data.k8s_credentials = "credential"
+
+ self.assertEqual(conn.lxd_cloud.name, "name")
+ self.assertEqual(conn.lxd_cloud.credential_name, "credential")
+ self.assertEqual(conn.k8s_cloud.name, "name")
+ self.assertEqual(conn.k8s_cloud.credential_name, "credential")
+
+ @patch("n2vc.vca.connection.EnvironConfig")
+ @patch("n2vc.vca.connection_data.base64_to_cacert")
+ def test_load_from_env(self, mock_base64_to_cacert, mock_env):
+ mock_base64_to_cacert.return_value = "cacert"
+ mock_env.return_value = {
+ "endpoints": "1.2.3.4:17070",
+ "user": "user",
+ "secret": "secret",
+ "cacert": "cacert",
+ "pubkey": "pubkey",
+ "cloud": "cloud",
+ "credentials": "credentials",
+ "k8s_cloud": "k8s_cloud",
+ "k8s_credentials": "k8s_credentials",
+ "model_config": {},
+ "api-proxy": "api_proxy",
+ }
+ self.store.get_vca_endpoints.return_value = ["1.2.3.5:17070"]
+ self.loop.run_until_complete(connection.get_connection(self.store))
+ self.store.get_vca_connection_data.assert_not_called()
diff --git a/n2vc/tests/unit/test_juju_watcher.py b/n2vc/tests/unit/test_juju_watcher.py
index d333b33..5f81274 100644
--- a/n2vc/tests/unit/test_juju_watcher.py
+++ b/n2vc/tests/unit/test_juju_watcher.py
@@ -45,6 +45,7 @@
def test_model_watcher(self, allwatcher):
tests = Deltas
allwatcher.return_value = FakeWatcher()
+ n2vc = AsyncMock()
for test in tests:
with self.assertRaises(asyncio.TimeoutError):
allwatcher.return_value.delta_to_return = [test.delta]
@@ -55,12 +56,12 @@
test.filter.entity_type,
timeout=0,
db_dict={"something"},
- n2vc=self.n2vc,
+ n2vc=n2vc,
+ vca_id=None,
)
)
- self.assertEqual(self.n2vc.last_written_values, test.db.data)
- self.n2vc.last_written_values = None
+ n2vc.write_app_status_to_db.assert_called()
@mock.patch("n2vc.juju_watcher.asyncio.wait")
def test_wait_for(self, wait):
diff --git a/n2vc/tests/unit/test_k8s_juju_conn.py b/n2vc/tests/unit/test_k8s_juju_conn.py
index f454380..208c849 100644
--- a/n2vc/tests/unit/test_k8s_juju_conn.py
+++ b/n2vc/tests/unit/test_k8s_juju_conn.py
@@ -23,172 +23,58 @@
from n2vc.exceptions import (
MethodNotImplemented,
K8sException,
- N2VCBadArgumentsException,
)
+from n2vc.vca.connection_data import ConnectionData
class K8sJujuConnTestCase(asynctest.TestCase):
- @asynctest.mock.patch("juju.controller.Controller.update_endpoints")
- @asynctest.mock.patch("juju.client.connector.Connector.connect")
- @asynctest.mock.patch("juju.controller.Controller.connection")
- @asynctest.mock.patch("n2vc.k8s_juju_conn.base64_to_cacert")
@asynctest.mock.patch("n2vc.k8s_juju_conn.Libjuju")
+ @asynctest.mock.patch("n2vc.k8s_juju_conn.MotorStore")
+ @asynctest.mock.patch("n2vc.k8s_juju_conn.get_connection")
+ @asynctest.mock.patch("n2vc.vca.connection_data.base64_to_cacert")
def setUp(
self,
- mock_libjuju=None,
mock_base64_to_cacert=None,
- mock_connection=None,
- mock_connect=None,
- mock_update_endpoints=None,
+ mock_get_connection=None,
+ mock_store=None,
+ mock_libjuju=None,
):
self.loop = asyncio.get_event_loop()
- mock_libjuju.return_value = AsyncMock()
- db = Mock()
- vca_config = {
- "secret": "secret",
- "api_proxy": "api_proxy",
- "cloud": "cloud",
- "k8s_cloud": "k8s_cloud",
- "user": "user",
- "host": "1.1.1.1",
- "port": 17070,
- "ca_cert": "cacert",
- }
-
+ self.db = Mock()
+ mock_base64_to_cacert.return_value = """
+ -----BEGIN CERTIFICATE-----
+ SOMECERT
+ -----END CERTIFICATE-----"""
+ mock_libjuju.return_value = Mock()
+ mock_store.return_value = AsyncMock()
+ mock_vca_connection = Mock()
+ mock_get_connection.return_value = mock_vca_connection
+ mock_vca_connection.data.return_value = ConnectionData(
+ **{
+ "endpoints": ["1.2.3.4:17070"],
+ "user": "user",
+ "secret": "secret",
+ "cacert": "cacert",
+ "pubkey": "pubkey",
+ "lxd-cloud": "cloud",
+ "lxd-credentials": "credentials",
+ "k8s-cloud": "k8s_cloud",
+ "k8s-credentials": "k8s_credentials",
+ "model-config": {},
+ "api-proxy": "api_proxy",
+ }
+ )
logging.disable(logging.CRITICAL)
self.k8s_juju_conn = K8sJujuConnector(
fs=fslocal.FsLocal(),
- db=db,
+ db=self.db,
log=None,
loop=self.loop,
- vca_config=vca_config,
on_update_db=None,
)
-
-
-class K8sJujuConnInitSuccessTestCase(asynctest.TestCase):
- def setUp(
- self,
- ):
- logging.disable(logging.CRITICAL)
-
- @asynctest.mock.patch("juju.controller.Controller.update_endpoints")
- @asynctest.mock.patch("juju.client.connector.Connector.connect")
- @asynctest.mock.patch("juju.controller.Controller.connection")
- @asynctest.mock.patch("n2vc.k8s_juju_conn.base64_to_cacert")
- @asynctest.mock.patch("n2vc.libjuju.Libjuju.__init__")
- def test_success(
- self,
- mock_libjuju=None,
- mock_base64_to_cacert=None,
- mock_connection=None,
- mock_connect=None,
- mock_update_endpoints=None,
- ):
- mock_libjuju.return_value = None
- loop = asyncio.get_event_loop()
- log = logging.getLogger()
- db = Mock()
- vca_config = {
- "secret": "secret",
- "cloud": "cloud",
- "k8s_cloud": "k8s_cloud",
- "user": "user",
- "host": "1.1.1.1",
- "port": 17070,
- "ca_cert": "cacert",
- }
- K8sJujuConnector(
- fs=fslocal.FsLocal(),
- db=db,
- log=log,
- loop=self.loop,
- vca_config=vca_config,
- on_update_db=None,
- )
-
- mock_libjuju.assert_called_once_with(
- endpoint="1.1.1.1:17070",
- api_proxy=None, # Not needed for k8s charms
- model_config={},
- username="user",
- password="secret",
- cacert=mock_base64_to_cacert.return_value,
- loop=loop,
- log=log,
- db=db,
- )
-
-
-class K8sJujuConnectorInitFailureTestCase(asynctest.TestCase):
- def setUp(
- self,
- ):
- self.loop = asyncio.get_event_loop()
- logging.disable(logging.CRITICAL)
- self.vca_config = {
- "secret": "secret",
- "api_proxy": "api_proxy",
- "cloud": "cloud",
- "k8s_cloud": "k8s_cloud",
- "user": "user",
- "host": "1.1.1.1",
- "port": 17070,
- "ca_cert": "cacert",
- }
-
- def test_missing_vca_config_host(self):
- db = Mock()
- self.vca_config.pop("host")
- with self.assertRaises(N2VCBadArgumentsException):
- self.k8s_juju_conn = K8sJujuConnector(
- fs=fslocal.FsLocal(),
- db=db,
- log=None,
- loop=self.loop,
- vca_config=self.vca_config,
- on_update_db=None,
- )
-
- def test_missing_vca_config_user(self):
- db = Mock()
- self.vca_config.pop("user")
- with self.assertRaises(N2VCBadArgumentsException):
- self.k8s_juju_conn = K8sJujuConnector(
- fs=fslocal.FsLocal(),
- db=db,
- log=None,
- loop=self.loop,
- vca_config=self.vca_config,
- on_update_db=None,
- )
-
- def test_missing_vca_config_secret(self):
- db = Mock()
- self.vca_config.pop("secret")
- with self.assertRaises(N2VCBadArgumentsException):
- self.k8s_juju_conn = K8sJujuConnector(
- fs=fslocal.FsLocal(),
- db=db,
- log=None,
- loop=self.loop,
- vca_config=self.vca_config,
- on_update_db=None,
- )
-
- def test_missing_vca_config_ca_cert(self):
- db = Mock()
- self.vca_config.pop("ca_cert")
- with self.assertRaises(N2VCBadArgumentsException):
- self.k8s_juju_conn = K8sJujuConnector(
- fs=fslocal.FsLocal(),
- db=db,
- log=None,
- loop=self.loop,
- vca_config=self.vca_config,
- on_update_db=None,
- )
+ self.k8s_juju_conn._store.get_vca_id.return_value = None
+ self.k8s_juju_conn.libjuju = Mock()
@asynctest.mock.patch("n2vc.kubectl.Kubectl.get_default_storage_class")
@@ -344,11 +230,7 @@
)
)
self.assertEqual(mock_chdir.call_count, 2)
- self.k8s_juju_conn.libjuju.add_model.assert_called_once_with(
- model_name=self.kdu_instance,
- cloud_name=self.cluster_uuid,
- credential_name="cred-{}".format(self.cluster_uuid),
- )
+ self.k8s_juju_conn.libjuju.add_model.assert_called_once()
self.k8s_juju_conn.libjuju.deploy.assert_called_once_with(
"local:{}".format(self.local_bundle),
model_name=self.kdu_instance,
@@ -368,11 +250,7 @@
timeout=1800,
)
)
- self.k8s_juju_conn.libjuju.add_model.assert_called_once_with(
- model_name=self.kdu_instance,
- cloud_name=self.cluster_uuid,
- credential_name="cred-{}".format(self.cluster_uuid),
- )
+ self.k8s_juju_conn.libjuju.add_model.assert_called_once()
self.k8s_juju_conn.libjuju.deploy.assert_called_once_with(
self.cs_bundle,
model_name=self.kdu_instance,
@@ -392,11 +270,7 @@
timeout=1800,
)
)
- self.k8s_juju_conn.libjuju.add_model.assert_called_once_with(
- model_name=self.kdu_instance,
- cloud_name=self.cluster_uuid,
- credential_name="cred-{}".format(self.cluster_uuid),
- )
+ self.k8s_juju_conn.libjuju.add_model.assert_called_once()
self.k8s_juju_conn.libjuju.deploy.assert_called_once_with(
self.http_bundle,
model_name=self.kdu_instance,
@@ -415,11 +289,7 @@
timeout=1800,
)
)
- self.k8s_juju_conn.libjuju.add_model.assert_called_once_with(
- model_name=self.kdu_instance,
- cloud_name=self.cluster_uuid,
- credential_name="cred-{}".format(self.cluster_uuid),
- )
+ self.k8s_juju_conn.libjuju.add_model.assert_called_once()
self.k8s_juju_conn.libjuju.deploy.assert_called_once_with(
self.cs_bundle,
model_name=self.kdu_instance,
@@ -458,11 +328,7 @@
timeout=1800,
)
)
- self.k8s_juju_conn.libjuju.add_model.assert_called_once_with(
- model_name=self.kdu_instance,
- cloud_name=self.cluster_uuid,
- credential_name="cred-{}".format(self.cluster_uuid),
- )
+ self.k8s_juju_conn.libjuju.add_model.assert_called_once()
self.k8s_juju_conn.libjuju.deploy.assert_called_once_with(
self.cs_bundle,
model_name=self.kdu_instance,
@@ -500,11 +366,7 @@
timeout=1800,
)
)
- self.k8s_juju_conn.libjuju.add_model.assert_called_once_with(
- model_name=self.kdu_instance,
- cloud_name=self.cluster_uuid,
- credential_name="cred-{}".format(self.cluster_uuid),
- )
+ self.k8s_juju_conn.libjuju.add_model.assert_called_once()
self.k8s_juju_conn.libjuju.deploy.assert_called_once_with(
"local:{}".format(self.local_bundle),
model_name=self.kdu_instance,
diff --git a/n2vc/tests/unit/test_libjuju.py b/n2vc/tests/unit/test_libjuju.py
index 29bcb7b..fde6817 100644
--- a/n2vc/tests/unit/test_libjuju.py
+++ b/n2vc/tests/unit/test_libjuju.py
@@ -15,12 +15,12 @@
import asyncio
import asynctest
import tempfile
-from unittest import mock
+from unittest.mock import Mock, patch
import juju
import kubernetes
from juju.errors import JujuAPIError
import logging
-from .utils import FakeN2VC, FakeMachine, FakeApplication
+from .utils import FakeMachine, FakeApplication
from n2vc.libjuju import Libjuju
from n2vc.exceptions import (
JujuControllerFailedConnecting,
@@ -33,122 +33,46 @@
JujuError,
)
from n2vc.k8s_juju_conn import generate_rbac_id
+from n2vc.tests.unit.utils import AsyncMock
+from n2vc.vca.connection import Connection
+from n2vc.vca.connection_data import ConnectionData
+cacert = """-----BEGIN CERTIFICATE-----
+SOMECERT
+-----END CERTIFICATE-----"""
+
+
+@asynctest.mock.patch("n2vc.libjuju.Controller")
class LibjujuTestCase(asynctest.TestCase):
- @asynctest.mock.patch("juju.controller.Controller.update_endpoints")
- @asynctest.mock.patch("juju.client.connector.Connector.connect")
- @asynctest.mock.patch("juju.controller.Controller.connection")
- @asynctest.mock.patch("n2vc.libjuju.Libjuju._get_api_endpoints_db")
+ @asynctest.mock.patch("n2vc.vca.connection_data.base64_to_cacert")
def setUp(
self,
- mock__get_api_endpoints_db=None,
- mock_connection=None,
- mock_connect=None,
- mock_update_endpoints=None,
+ mock_base64_to_cacert=None,
):
- loop = asyncio.get_event_loop()
- n2vc = FakeN2VC()
- mock__get_api_endpoints_db.return_value = ["127.0.0.1:17070"]
- endpoints = "127.0.0.1:17070"
- username = "admin"
- password = "secret"
- cacert = """
- -----BEGIN CERTIFICATE-----
- SOMECERT
- -----END CERTIFICATE-----"""
- self.libjuju = Libjuju(
- endpoints,
- "192.168.0.155:17070",
- username,
- password,
- cacert,
- loop,
- log=None,
- db={"get_one": []},
- n2vc=n2vc,
+ self.loop = asyncio.get_event_loop()
+ self.db = Mock()
+ mock_base64_to_cacert.return_value = cacert
+ Connection._load_vca_connection_data = Mock()
+ vca_connection = Connection(AsyncMock())
+ vca_connection._data = ConnectionData(
+ **{
+ "endpoints": ["1.2.3.4:17070"],
+ "user": "user",
+ "secret": "secret",
+ "cacert": "cacert",
+ "pubkey": "pubkey",
+ "lxd-cloud": "cloud",
+ "lxd-credentials": "credentials",
+ "k8s-cloud": "k8s_cloud",
+ "k8s-credentials": "k8s_credentials",
+ "model-config": {},
+ "api-proxy": "api_proxy",
+ }
)
logging.disable(logging.CRITICAL)
- loop.run_until_complete(self.libjuju.disconnect())
-
-
-@asynctest.mock.patch("n2vc.libjuju.Libjuju._create_health_check_task")
-@asynctest.mock.patch("n2vc.libjuju.Libjuju._update_api_endpoints_db")
-@asynctest.mock.patch("n2vc.libjuju.Libjuju._get_api_endpoints_db")
-class LibjujuInitTestCase(asynctest.TestCase):
- def setUp(self):
- self.loop = asyncio.get_event_loop()
- self.n2vc = FakeN2VC()
- self.endpoint = "192.168.100.100:17070"
- self.username = "admin"
- self.password = "secret"
- self.cacert = """
- -----BEGIN CERTIFICATE-----
- SOMECERT
- -----END CERTIFICATE-----"""
-
- def test_endpoint_not_in_db(
- self,
- mock__get_api_endpoints_db,
- mock_update_endpoints,
- mock_create_health_check_task,
- ):
- mock__get_api_endpoints_db.return_value = ["another_ip"]
- Libjuju(
- self.endpoint,
- "192.168.0.155:17070",
- self.username,
- self.password,
- self.cacert,
- self.loop,
- log=None,
- db={"get_one": []},
- n2vc=self.n2vc,
- )
- mock_update_endpoints.assert_called_once_with([self.endpoint])
- mock__get_api_endpoints_db.assert_called_once()
-
- def test_endpoint_in_db(
- self,
- mock__get_api_endpoints_db,
- mock_update_endpoints,
- mock_create_health_check_task,
- ):
- mock__get_api_endpoints_db.return_value = [self.endpoint, "another_ip"]
- Libjuju(
- self.endpoint,
- "192.168.0.155:17070",
- self.username,
- self.password,
- self.cacert,
- self.loop,
- log=None,
- db={"get_one": []},
- n2vc=self.n2vc,
- )
- mock_update_endpoints.assert_not_called()
- mock__get_api_endpoints_db.assert_called_once()
-
- def test_no_db_endpoints(
- self,
- mock__get_api_endpoints_db,
- mock_update_endpoints,
- mock_create_health_check_task,
- ):
- mock__get_api_endpoints_db.return_value = None
- Libjuju(
- self.endpoint,
- "192.168.0.155:17070",
- self.username,
- self.password,
- self.cacert,
- self.loop,
- log=None,
- db={"get_one": []},
- n2vc=self.n2vc,
- )
- mock_update_endpoints.assert_called_once_with([self.endpoint])
- mock__get_api_endpoints_db.assert_called_once()
+ self.libjuju = Libjuju(vca_connection, self.loop)
+ self.loop.run_until_complete(self.libjuju.disconnect())
@asynctest.mock.patch("juju.controller.Controller.connect")
@@ -156,41 +80,34 @@
"juju.controller.Controller.api_endpoints",
new_callable=asynctest.CoroutineMock(return_value=["127.0.0.1:17070"]),
)
-@asynctest.mock.patch("n2vc.libjuju.Libjuju._update_api_endpoints_db")
class GetControllerTest(LibjujuTestCase):
def setUp(self):
super(GetControllerTest, self).setUp()
- def test_diff_endpoint(
- self, mock__update_api_endpoints_db, mock_api_endpoints, mock_connect
- ):
+ def test_diff_endpoint(self, mock_api_endpoints, mock_connect):
self.libjuju.endpoints = []
controller = self.loop.run_until_complete(self.libjuju.get_controller())
- mock__update_api_endpoints_db.assert_called_once_with(["127.0.0.1:17070"])
self.assertIsInstance(controller, juju.controller.Controller)
@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
def test_exception(
self,
mock_disconnect_controller,
- mock__update_api_endpoints_db,
mock_api_endpoints,
mock_connect,
):
self.libjuju.endpoints = []
- mock__update_api_endpoints_db.side_effect = Exception()
+
+ mock_connect.side_effect = Exception()
controller = None
with self.assertRaises(JujuControllerFailedConnecting):
controller = self.loop.run_until_complete(self.libjuju.get_controller())
self.assertIsNone(controller)
mock_disconnect_controller.assert_called_once()
- def test_same_endpoint_get_controller(
- self, mock__update_api_endpoints_db, mock_api_endpoints, mock_connect
- ):
+ def test_same_endpoint_get_controller(self, mock_api_endpoints, mock_connect):
self.libjuju.endpoints = ["127.0.0.1:17070"]
controller = self.loop.run_until_complete(self.libjuju.get_controller())
- mock__update_api_endpoints_db.assert_not_called()
self.assertIsInstance(controller, juju.controller.Controller)
@@ -231,9 +148,7 @@
mock_model_exists.return_value = True
# This should not raise an exception
- self.loop.run_until_complete(
- self.libjuju.add_model("existing_model", "cloud")
- )
+ self.loop.run_until_complete(self.libjuju.add_model("existing_model", "cloud"))
mock_disconnect_controller.assert_called()
@@ -251,7 +166,7 @@
mock_get_controller.return_value = juju.controller.Controller()
self.loop.run_until_complete(
- self.libjuju.add_model("nonexisting_model", "cloud")
+ self.libjuju.add_model("nonexisting_model", Mock())
)
mock_add_model.assert_called_once()
@@ -1863,7 +1778,7 @@
mock_configuration.key_file = None
self.token = None
self.cert_data = None
- with mock.patch.object(self.libjuju.log, "debug") as mock_debug:
+ with patch.object(self.libjuju.log, "debug") as mock_debug:
credential = self.libjuju.get_k8s_cloud_credential(
mock_configuration,
self.cert_data,
diff --git a/n2vc/tests/unit/test_n2vc_juju_conn.py b/n2vc/tests/unit/test_n2vc_juju_conn.py
index e5e26be..d89de3f 100644
--- a/n2vc/tests/unit/test_n2vc_juju_conn.py
+++ b/n2vc/tests/unit/test_n2vc_juju_conn.py
@@ -22,143 +22,145 @@
from n2vc.n2vc_juju_conn import N2VCJujuConnector
from osm_common import fslocal
from n2vc.exceptions import (
- JujuK8sProxycharmNotSupported,
N2VCBadArgumentsException,
N2VCException,
)
+from n2vc.tests.unit.utils import AsyncMock
+from n2vc.vca.connection_data import ConnectionData
class N2VCJujuConnTestCase(asynctest.TestCase):
- @asynctest.mock.patch("n2vc.libjuju.Libjuju._create_health_check_task")
- @asynctest.mock.patch("juju.controller.Controller.update_endpoints")
- @asynctest.mock.patch("juju.client.connector.Connector.connect")
- @asynctest.mock.patch("juju.controller.Controller.connection")
- @asynctest.mock.patch("n2vc.libjuju.Libjuju._get_api_endpoints_db")
+ @asynctest.mock.patch("n2vc.n2vc_juju_conn.MotorStore")
+ @asynctest.mock.patch("n2vc.n2vc_juju_conn.get_connection")
+ @asynctest.mock.patch("n2vc.vca.connection_data.base64_to_cacert")
def setUp(
self,
- mock__get_api_endpoints_db=None,
- mock_connection=None,
- mock_connect=None,
- mock_update_endpoints=None,
- mock__create_health_check_task=None,
+ mock_base64_to_cacert=None,
+ mock_get_connection=None,
+ mock_store=None,
):
- mock__get_api_endpoints_db.return_value = ["2.2.2.2:17070"]
- loop = asyncio.get_event_loop()
- db = {}
- vca_config = {
- "secret": "secret",
- "api_proxy": "api_proxy",
- "cloud": "cloud",
- "k8s_cloud": "k8s_cloud",
- }
-
+ self.loop = asyncio.get_event_loop()
+ self.db = Mock()
+ mock_base64_to_cacert.return_value = """
+ -----BEGIN CERTIFICATE-----
+ SOMECERT
+ -----END CERTIFICATE-----"""
+ mock_store.return_value = AsyncMock()
+ mock_vca_connection = Mock()
+ mock_get_connection.return_value = mock_vca_connection
+ mock_vca_connection.data.return_value = ConnectionData(
+ **{
+ "endpoints": ["1.2.3.4:17070"],
+ "user": "user",
+ "secret": "secret",
+ "cacert": "cacert",
+ "pubkey": "pubkey",
+ "lxd-cloud": "cloud",
+ "lxd-credentials": "credentials",
+ "k8s-cloud": "k8s_cloud",
+ "k8s-credentials": "k8s_credentials",
+ "model-config": {},
+ "api-proxy": "api_proxy",
+ }
+ )
logging.disable(logging.CRITICAL)
N2VCJujuConnector.get_public_key = Mock()
self.n2vc = N2VCJujuConnector(
- db=db,
+ db=self.db,
fs=fslocal.FsLocal(),
log=None,
- loop=loop,
- url="2.2.2.2:17070",
- username="admin",
- vca_config=vca_config,
+ loop=self.loop,
on_update_db=None,
)
N2VCJujuConnector.get_public_key.assert_not_called()
+ self.n2vc.libjuju = Mock()
-@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_metrics")
class GetMetricssTest(N2VCJujuConnTestCase):
def setUp(self):
super(GetMetricssTest, self).setUp()
+ self.n2vc.libjuju.get_metrics = AsyncMock()
- def test_success(self, mock_get_metrics):
+ def test_success(self):
_ = self.loop.run_until_complete(self.n2vc.get_metrics("model", "application"))
- mock_get_metrics.assert_called_once()
+ self.n2vc.libjuju.get_metrics.assert_called_once()
- def test_except(self, mock_get_metrics):
- mock_get_metrics.side_effect = Exception()
+ def test_except(self):
+ self.n2vc.libjuju.get_metrics.side_effect = Exception()
with self.assertRaises(Exception):
_ = self.loop.run_until_complete(
self.n2vc.get_metrics("model", "application")
)
- mock_get_metrics.assert_called_once()
+ self.n2vc.libjuju.get_metrics.assert_called_once()
-@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
-@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
-@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_executed_actions")
-@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_actions")
-@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_application_configs")
-@asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
class UpdateVcaStatusTest(N2VCJujuConnTestCase):
def setUp(self):
super(UpdateVcaStatusTest, self).setUp()
+ self.n2vc.libjuju.get_controller = AsyncMock()
+ self.n2vc.libjuju.get_model = AsyncMock()
+ self.n2vc.libjuju.get_executed_actions = AsyncMock()
+ self.n2vc.libjuju.get_actions = AsyncMock()
+ self.n2vc.libjuju.get_application_configs = AsyncMock()
+ self.n2vc.libjuju._get_application = AsyncMock()
def test_success(
self,
- mock_get_application,
- mock_get_application_configs,
- mock_get_actions,
- mock_get_executed_actions,
- mock_get_model,
- mock_get_controller,
):
- self.loop.run_until_complete(self.n2vc.update_vca_status(
- {"model": {"applications": {"app": {"actions": {}}}}}))
- mock_get_executed_actions.assert_called_once()
- mock_get_actions.assert_called_once()
- mock_get_application_configs.assert_called_once()
+ self.loop.run_until_complete(
+ self.n2vc.update_vca_status(
+ {"model": {"applications": {"app": {"actions": {}}}}}
+ )
+ )
+ self.n2vc.libjuju.get_executed_actions.assert_called_once()
+ self.n2vc.libjuju.get_actions.assert_called_once()
+ self.n2vc.libjuju.get_application_configs.assert_called_once()
- def test_exception(
- self,
- mock_get_application,
- mock_get_application_configs,
- mock_get_actions,
- mock_get_executed_actions,
- mock_get_model,
- mock_get_controller,
- ):
- mock_get_model.return_value = None
- mock_get_executed_actions.side_effect = Exception()
+ def test_exception(self):
+ self.n2vc.libjuju.get_model.return_value = None
+ self.n2vc.libjuju.get_executed_actions.side_effect = Exception()
with self.assertRaises(Exception):
- self.loop.run_until_complete(self.n2vc.update_vca_status(
- {"model": {"applications": {"app": {"actions": {}}}}}))
- mock_get_executed_actions.assert_not_called()
- mock_get_actions.assert_not_called_once()
- mock_get_application_configs.assert_not_called_once()
+ self.loop.run_until_complete(
+ self.n2vc.update_vca_status(
+ {"model": {"applications": {"app": {"actions": {}}}}}
+ )
+ )
+ self.n2vc.libjuju.get_executed_actions.assert_not_called()
+ self.n2vc.libjuju.get_actions.assert_not_called_once()
+ self.n2vc.libjuju.get_application_configs.assert_not_called_once()
-@asynctest.mock.patch("n2vc.libjuju.Libjuju.model_exists")
@asynctest.mock.patch("osm_common.fslocal.FsLocal.file_exists")
@asynctest.mock.patch(
"osm_common.fslocal.FsLocal.path", new_callable=asynctest.PropertyMock, create=True
)
-@asynctest.mock.patch("n2vc.libjuju.Libjuju.deploy_charm")
-@asynctest.mock.patch("n2vc.libjuju.Libjuju.add_model")
class K8sProxyCharmsTest(N2VCJujuConnTestCase):
def setUp(self):
super(K8sProxyCharmsTest, self).setUp()
+ self.n2vc.libjuju.model_exists = AsyncMock()
+ self.n2vc.libjuju.add_model = AsyncMock()
+ self.n2vc.libjuju.deploy_charm = AsyncMock()
+ self.n2vc.libjuju.model_exists.return_value = False
def test_success(
- self, mock_add_model, mock_deploy_charm, mock_path, mock_file_exists, mock_model_exists
+ self,
+ mock_path,
+ mock_file_exists,
):
- mock_model_exists.return_value = None
mock_file_exists.return_value = True
mock_path.return_value = "/path"
ee_id = self.loop.run_until_complete(
self.n2vc.install_k8s_proxy_charm(
- "charm", "nsi-id.ns-id.vnf-id.vdu", "////path/", {},
+ "charm",
+ "nsi-id.ns-id.vnf-id.vdu",
+ "////path/",
+ {},
)
)
- mock_add_model.assert_called_once_with(
- "ns-id-k8s",
- cloud_name=self.n2vc.k8s_cloud,
- credential_name=self.n2vc.k8s_cloud
- )
- mock_deploy_charm.assert_called_once_with(
+ self.n2vc.libjuju.add_model.assert_called_once()
+ self.n2vc.libjuju.deploy_charm.assert_called_once_with(
model_name="ns-id-k8s",
application_name="app-vnf-vnf-id-vdu-vdu",
path="/path/path/",
@@ -170,74 +172,70 @@
)
self.assertEqual(ee_id, "ns-id-k8s.app-vnf-vnf-id-vdu-vdu.k8s")
- @asynctest.mock.patch(
- "n2vc.n2vc_juju_conn.N2VCJujuConnector.k8s_cloud",
- new_callable=asynctest.PropertyMock,
- create=True,
- )
- def test_no_k8s_cloud(
+ def test_no_artifact_path(
self,
- mock_k8s_cloud,
- mock_add_model,
- mock_deploy_charm,
mock_path,
mock_file_exists,
- mock_model_exists,
- ):
- mock_k8s_cloud.return_value = None
- with self.assertRaises(JujuK8sProxycharmNotSupported):
- ee_id = self.loop.run_until_complete(
- self.n2vc.install_k8s_proxy_charm(
- "charm", "nsi-id.ns-id.vnf-id.vdu", "/path/", {},
- )
- )
- self.assertIsNone(ee_id)
-
- def test_no_artifact_path(
- self, mock_add_model, mock_deploy_charm, mock_path, mock_file_exists, mock_model_exists,
):
with self.assertRaises(N2VCBadArgumentsException):
ee_id = self.loop.run_until_complete(
self.n2vc.install_k8s_proxy_charm(
- "charm", "nsi-id.ns-id.vnf-id.vdu", "", {},
+ "charm",
+ "nsi-id.ns-id.vnf-id.vdu",
+ "",
+ {},
)
)
self.assertIsNone(ee_id)
def test_no_db(
- self, mock_add_model, mock_deploy_charm, mock_path, mock_file_exists, mock_model_exists,
+ self,
+ mock_path,
+ mock_file_exists,
):
with self.assertRaises(N2VCBadArgumentsException):
ee_id = self.loop.run_until_complete(
self.n2vc.install_k8s_proxy_charm(
- "charm", "nsi-id.ns-id.vnf-id.vdu", "/path/", None,
+ "charm",
+ "nsi-id.ns-id.vnf-id.vdu",
+ "/path/",
+ None,
)
)
self.assertIsNone(ee_id)
def test_file_not_exists(
- self, mock_add_model, mock_deploy_charm, mock_path, mock_file_exists, mock_model_exists,
+ self,
+ mock_path,
+ mock_file_exists,
):
mock_file_exists.return_value = False
with self.assertRaises(N2VCBadArgumentsException):
ee_id = self.loop.run_until_complete(
self.n2vc.install_k8s_proxy_charm(
- "charm", "nsi-id.ns-id.vnf-id.vdu", "/path/", {},
+ "charm",
+ "nsi-id.ns-id.vnf-id.vdu",
+ "/path/",
+ {},
)
)
self.assertIsNone(ee_id)
def test_exception(
- self, mock_add_model, mock_deploy_charm, mock_path, mock_file_exists, mock_model_exists,
+ self,
+ mock_path,
+ mock_file_exists,
):
- mock_model_exists.return_value = None
mock_file_exists.return_value = True
mock_path.return_value = "/path"
- mock_deploy_charm.side_effect = Exception()
+ self.n2vc.libjuju.deploy_charm.side_effect = Exception()
with self.assertRaises(N2VCException):
ee_id = self.loop.run_until_complete(
self.n2vc.install_k8s_proxy_charm(
- "charm", "nsi-id.ns-id.vnf-id.vdu", "path/", {},
+ "charm",
+ "nsi-id.ns-id.vnf-id.vdu",
+ "path/",
+ {},
)
)
self.assertIsNone(ee_id)
diff --git a/n2vc/tests/unit/test_store.py b/n2vc/tests/unit/test_store.py
new file mode 100644
index 0000000..c7aa2d6
--- /dev/null
+++ b/n2vc/tests/unit/test_store.py
@@ -0,0 +1,288 @@
+# Copyright 2020 Canonical Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import asyncio
+from base64 import b64decode
+from unittest import TestCase
+from unittest.mock import Mock, patch
+
+
+from n2vc.store import DbMongoStore, MotorStore
+from n2vc.vca.connection_data import ConnectionData
+from n2vc.tests.unit.utils import AsyncMock
+from osm_common.dbmongo import DbException
+
+
+class TestDbMongoStore(TestCase):
+ def setUp(self):
+ self.store = DbMongoStore(Mock())
+ self.loop = asyncio.get_event_loop()
+
+ @patch("n2vc.vca.connection_data.base64_to_cacert")
+ def test_get_vca_connection_data(self, mock_base64_to_cacert):
+ mock_base64_to_cacert.return_value = "cacert"
+ conn_data = {
+ "endpoints": ["1.2.3.4:17070"],
+ "user": "admin",
+ "secret": "1234",
+ "cacert": "cacert",
+ "pubkey": "pubkey",
+ "lxd-cloud": "lxd-cloud",
+ "lxd-credentials": "lxd-credentials",
+ "k8s-cloud": "k8s-cloud",
+ "k8s-credentials": "k8s-credentials",
+ "model-config": {},
+ "api-proxy": None,
+ }
+ db_get_one = conn_data.copy()
+ db_get_one.update({"schema_version": "1.1", "_id": "id"})
+ self.store.db.get_one.return_value = db_get_one
+ connection_data = self.loop.run_until_complete(
+ self.store.get_vca_connection_data("vca_id")
+ )
+ self.assertTrue(
+ all(
+ connection_data.__dict__[k.replace("-", "_")] == v
+ for k, v in conn_data.items()
+ )
+ )
+
+ def test_update_vca_endpoints(self):
+ endpoints = ["1.2.3.4:17070"]
+ self.store.db.get_one.side_effect = [None, {"api_endpoints": []}]
+ self.store.db.create.side_effect = DbException("already exists")
+ self.loop.run_until_complete(self.store.update_vca_endpoints(endpoints))
+ self.assertEqual(self.store.db.get_one.call_count, 2)
+ Mock()
+ self.store.db.set_one.assert_called_once_with(
+ "vca", {"_id": "juju"}, {"api_endpoints": endpoints}
+ )
+
+ def test_update_vca_endpoints_exception(self):
+ endpoints = ["1.2.3.4:17070"]
+ self.store.db.get_one.side_effect = [None, None]
+ self.store.db.create.side_effect = DbException("already exists")
+ with self.assertRaises(DbException):
+ self.loop.run_until_complete(self.store.update_vca_endpoints(endpoints))
+ self.assertEqual(self.store.db.get_one.call_count, 2)
+ self.store.db.set_one.assert_not_called()
+
+ def test_update_vca_endpoints_with_vca_id(self):
+ endpoints = ["1.2.3.4:17070"]
+ self.store.db.get_one.return_value = {}
+ self.loop.run_until_complete(
+ self.store.update_vca_endpoints(endpoints, "vca_id")
+ )
+ self.store.db.get_one.assert_called_once_with("vca", q_filter={"_id": "vca_id"})
+ self.store.db.replace.assert_called_once_with(
+ "vca", "vca_id", {"endpoints": endpoints}
+ )
+
+ def test_get_vca_endpoints(self):
+ endpoints = ["1.2.3.4:17070"]
+ db_data = {"api_endpoints": endpoints}
+ db_returns = [db_data, None]
+ expected_returns = [endpoints, []]
+ returns = []
+ self.store._get_juju_info = Mock()
+ self.store._get_juju_info.side_effect = db_returns
+ for _ in range(len(db_returns)):
+ e = self.loop.run_until_complete(self.store.get_vca_endpoints())
+ returns.append(e)
+ self.assertEqual(expected_returns, returns)
+
+ @patch("n2vc.vca.connection_data.base64_to_cacert")
+ def test_get_vca_endpoints_with_vca_id(self, mock_base64_to_cacert):
+ expected_endpoints = ["1.2.3.4:17070"]
+ mock_base64_to_cacert.return_value = "cacert"
+ self.store.get_vca_connection_data = Mock()
+ self.store.get_vca_connection_data.return_value = ConnectionData(
+ **{
+ "endpoints": expected_endpoints,
+ "user": "admin",
+ "secret": "1234",
+ "cacert": "cacert",
+ }
+ )
+ endpoints = self.loop.run_until_complete(self.store.get_vca_endpoints("vca_id"))
+ self.store.get_vca_connection_data.assert_called_with("vca_id")
+ self.assertEqual(expected_endpoints, endpoints)
+
+ def test_get_vca_id(self):
+ self.assertIsNone(self.loop.run_until_complete(self.store.get_vca_id()))
+
+ def test_get_vca_id_with_vim_id(self):
+ self.store.db.get_one.return_value = {"vca": "vca_id"}
+ vca_id = self.loop.run_until_complete(self.store.get_vca_id("vim_id"))
+ self.store.db.get_one.assert_called_once_with(
+ "vim_accounts", q_filter={"_id": "vim_id"}, fail_on_empty=False
+ )
+ self.assertEqual(vca_id, "vca_id")
+
+
+class TestMotorStore(TestCase):
+ def setUp(self):
+ self.store = MotorStore("uri")
+ self.vca_collection = Mock()
+ self.vca_collection.find_one = AsyncMock()
+ self.vca_collection.insert_one = AsyncMock()
+ self.vca_collection.replace_one = AsyncMock()
+ self.admin_collection = Mock()
+ self.admin_collection.find_one = AsyncMock()
+ self.admin_collection.insert_one = AsyncMock()
+ self.admin_collection.replace_one = AsyncMock()
+ self.vim_accounts_collection = Mock()
+ self.vim_accounts_collection.find_one = AsyncMock()
+ self.store._client = {
+ "osm": {
+ "vca": self.vca_collection,
+ "admin": self.admin_collection,
+ "vim_accounts": self.vim_accounts_collection,
+ }
+ }
+ self.store._config = {"database_commonkey": "osm"}
+ # self.store.decrypt_fields = Mock()
+ self.loop = asyncio.get_event_loop()
+
+ @patch("n2vc.vca.connection_data.base64_to_cacert")
+ def test_get_vca_connection_data(self, mock_base64_to_cacert):
+ mock_base64_to_cacert.return_value = "cacert"
+ conn_data = {
+ "endpoints": ["1.2.3.4:17070"],
+ "user": "admin",
+ "secret": "1234",
+ "cacert": "cacert",
+ "pubkey": "pubkey",
+ "lxd-cloud": "lxd-cloud",
+ "lxd-credentials": "lxd-credentials",
+ "k8s-cloud": "k8s-cloud",
+ "k8s-credentials": "k8s-credentials",
+ "model-config": {},
+ "api-proxy": None,
+ }
+ db_find_one = conn_data.copy()
+ db_find_one.update({"schema_version": "1.1", "_id": "id"})
+ self.vca_collection.find_one.return_value = db_find_one
+ self.store.decrypt_fields = AsyncMock()
+ connection_data = self.loop.run_until_complete(
+ self.store.get_vca_connection_data("vca_id")
+ )
+ self.assertTrue(
+ all(
+ connection_data.__dict__[k.replace("-", "_")] == v
+ for k, v in conn_data.items()
+ )
+ )
+
+ @patch("n2vc.vca.connection_data.base64_to_cacert")
+ def test_get_vca_connection_data_exception(self, mock_base64_to_cacert):
+ mock_base64_to_cacert.return_value = "cacert"
+ self.vca_collection.find_one.return_value = None
+ with self.assertRaises(Exception):
+ self.loop.run_until_complete(self.store.get_vca_connection_data("vca_id"))
+
+ def test_update_vca_endpoints(self):
+ endpoints = ["1.2.3.4:17070"]
+ self.admin_collection.find_one.side_effect = [None, {"api_endpoints": []}]
+ self.admin_collection.insert_one.side_effect = DbException("already exists")
+ self.loop.run_until_complete(self.store.update_vca_endpoints(endpoints))
+ self.assertEqual(self.admin_collection.find_one.call_count, 2)
+ self.admin_collection.replace_one.assert_called_once_with(
+ {"_id": "juju"}, {"api_endpoints": ["1.2.3.4:17070"]}
+ )
+
+ def test_get_vca_connection_data_with_id(self):
+ secret = "e7b253af37785045d1ca08b8d929e556"
+ encrypted_secret = "kI46kRJh828ExSNpr16OG/q5a5/qTsE0bsHrv/W/2/g="
+ cacert = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUQ4ekNDQWx1Z0F3SUJBZ0lVRWlzTTBoQWxiYzQ0Z1ZhZWh6bS80ZUsyNnRZd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0lURU5NQXNHQTFVRUNoTUVTblZxZFRFUU1BNEdBMVVFQXhNSGFuVnFkUzFqWVRBZUZ3MHlNVEEwTWpNeApNRFV3TXpSYUZ3MHpNVEEwTWpNeE1EVTFNelJhTUNFeERUQUxCZ05WQkFvVEJFcDFhblV4RURBT0JnTlZCQU1UCkIycDFhblV0WTJFd2dnR2lNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJqd0F3Z2dHS0FvSUJnUUNhTmFvNGZab2gKTDJWYThtdy9LdCs3RG9tMHBYTlIvbEUxSHJyVmZvbmZqZFVQV01zSHpTSjJZZXlXcUNSd3BiaHlLaE82N1c1dgpUY2RsV3Y3WGFLTGtsdVkraDBZY3BQT3BFTmZZYmxrNGk0QkV1L0wzYVY5MFFkUFFrMG94S01CS2R5QlBNZVNNCkJmS2pPWXdyOGgzM0ZWUWhmVkJnMXVGZ2tGaDdTamNuNHczUFdvc1BCMjNiVHBCbGR3VE9zemN4Qm9TaDNSVTkKTzZjb3lQdDdEN0drOCtHRlA3RGRUQTdoV1RkaUM4cDBkeHp2RUNmY0psMXNFeFEyZVprS1QvVzZyelNtVDhUTApCM0ErM1FDRDhEOEVsQU1IVy9zS25SeHphYU8welpNVmVlQnRnNlFGZ1F3M0dJMGo2ZTY0K2w3VExoOW8wSkZVCjdpUitPY01xUzVDY0NROGpWV3JPSk9Xc2dEbDZ4T2FFREczYnR5SVJHY29jbVcvcEZFQjNZd1A2S1BRTUIrNXkKWDdnZExEWmFGRFBVakZmblhkMnhHdUZlMnpRTDNVbXZEUkZuUlBBaW02QlpQbWo1OFh2emFhZXROa3lyaUZLZwp4Z0Z1dVpTcDUwV2JWdjF0MkdzOTMrRE53NlhFZHRFYnlWWUNBa28xTTY0MkozczFnN3NoQnRFQ0F3RUFBYU1qCk1DRXdEZ1lEVlIwUEFRSC9CQVFEQWdLa01BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUwKQlFBRGdnR0JBRXYxM2o2ZGFVbDBqeERPSnNTV1ZJZS9JdXNXVTRpN2ZXSWlqMHAwRU1GNS9LTE8yemRndTR5SQoreVd2T3N5aVFPanEzMlRYVlo2bTRDSnBkR1dGVE5HK2lLdXVOU3M0N3g3Q3dmVUNBWm5VVzhyamd3ZWJyS3BmCkJMNEVQcTZTcW0rSmltN0VPankyMWJkY2cyUXdZb3A3eUhvaHcveWEvL0l6RTMzVzZxNHlJeEFvNDBVYUhPTEMKTGtGbnNVYitjcFZBeFlPZGp6bjFzNWhnclpuWXlETEl3WmtIdFdEWm94alUzeC9jdnZzZ1FzLytzTWYrRFU4RgpZMkJKRHJjQ1VQM2xzclc0QVpFMFplZkEwOTlncFEvb3dSN0REYnMwSjZUeFM4NGt6Tldjc1FuWnRraXZheHJNClkyVHNnaWVndFExVFdGRWpxLy9sUFV4emJCdmpnd1FBZm5CQXZGeVNKejdTa0VuVm5rUXJGaUlUQVArTHljQVIKMlg4UFI2ZGI1bEt0SitBSENDM3kvZmNQS2k0ZzNTL3djeXRRdmdvOXJ6ODRFalp5YUNTaGJXNG9jNzNrMS9RcAowQWtHRDU0ZGVDWWVPYVJNbW96c0w3ZzdxWkpFekhtODdOcVBYSy9EZFoweWNxaVFhMXY2T3QxNjdXNUlzMUkzCjBWb0IzUzloSlE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCgo=" # noqa: E501
+ encrypted_cacert = "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" # noqa: E501
+
+ self.vca_collection.find_one.return_value = {
+ "_id": "2ade7f0e-9b58-4dbd-93a3-4ec076185d39",
+ "schema_version": "1.11",
+ "endpoints": [],
+ "user": "admin",
+ "secret": encrypted_secret,
+ "cacert": encrypted_cacert,
+ }
+ self.admin_collection.find_one.return_value = {
+ "serial": b"l+U3HDp9td+UjQ+AN+Ypj/Uh7n3C+rMJueQNNxkIpWI="
+ }
+ connection_data = self.loop.run_until_complete(
+ self.store.get_vca_connection_data("vca_id")
+ )
+ self.assertEqual(connection_data.endpoints, [])
+ self.assertEqual(connection_data.user, "admin")
+ self.assertEqual(connection_data.secret, secret)
+ self.assertEqual(
+ connection_data.cacert, b64decode(cacert.encode("utf-8")).decode("utf-8")
+ )
+
+ def test_update_vca_endpoints_exception(self):
+ endpoints = ["1.2.3.4:17070"]
+ self.admin_collection.find_one.side_effect = [None, None]
+ self.admin_collection.insert_one.side_effect = DbException("already exists")
+ with self.assertRaises(DbException):
+ self.loop.run_until_complete(self.store.update_vca_endpoints(endpoints))
+ self.assertEqual(self.admin_collection.find_one.call_count, 2)
+ self.admin_collection.replace_one.assert_not_called()
+
+ def test_update_vca_endpoints_with_vca_id(self):
+ endpoints = ["1.2.3.4:17070"]
+ self.vca_collection.find_one.return_value = {}
+ self.loop.run_until_complete(
+ self.store.update_vca_endpoints(endpoints, "vca_id")
+ )
+ self.vca_collection.find_one.assert_called_once_with({"_id": "vca_id"})
+ self.vca_collection.replace_one.assert_called_once_with(
+ {"_id": "vca_id"}, {"endpoints": endpoints}
+ )
+
+ def test_get_vca_endpoints(self):
+ endpoints = ["1.2.3.4:17070"]
+ db_data = {"api_endpoints": endpoints}
+ db_returns = [db_data, None]
+ expected_returns = [endpoints, []]
+ returns = []
+ self.admin_collection.find_one.side_effect = db_returns
+ for _ in range(len(db_returns)):
+ e = self.loop.run_until_complete(self.store.get_vca_endpoints())
+ returns.append(e)
+ self.assertEqual(expected_returns, returns)
+
+ @patch("n2vc.vca.connection_data.base64_to_cacert")
+ def test_get_vca_endpoints_with_vca_id(self, mock_base64_to_cacert):
+ expected_endpoints = ["1.2.3.4:17070"]
+ mock_base64_to_cacert.return_value = "cacert"
+ self.store.get_vca_connection_data = AsyncMock()
+ self.store.get_vca_connection_data.return_value = ConnectionData(
+ **{
+ "endpoints": expected_endpoints,
+ "user": "admin",
+ "secret": "1234",
+ "cacert": "cacert",
+ }
+ )
+ endpoints = self.loop.run_until_complete(self.store.get_vca_endpoints("vca_id"))
+ self.store.get_vca_connection_data.assert_called_with("vca_id")
+ self.assertEqual(expected_endpoints, endpoints)
+
+ def test_get_vca_id(self):
+ self.assertIsNone(self.loop.run_until_complete((self.store.get_vca_id())))
+
+ def test_get_vca_id_with_vim_id(self):
+ self.vim_accounts_collection.find_one.return_value = {"vca": "vca_id"}
+ vca_id = self.loop.run_until_complete(self.store.get_vca_id("vim_id"))
+ self.vim_accounts_collection.find_one.assert_called_once_with({"_id": "vim_id"})
+ self.assertEqual(vca_id, "vca_id")
diff --git a/n2vc/tests/unit/test_utils.py b/n2vc/tests/unit/test_utils.py
index c5ab84f..bffbc29 100644
--- a/n2vc/tests/unit/test_utils.py
+++ b/n2vc/tests/unit/test_utils.py
@@ -14,7 +14,7 @@
from unittest import TestCase
-from n2vc.utils import Dict, EntityType, JujuStatusToOSM, N2VCDeploymentStatus, DB_DATA
+from n2vc.utils import Dict, EntityType, JujuStatusToOSM, N2VCDeploymentStatus
from juju.machine import Machine
from juju.application import Application
from juju.action import Action
@@ -84,8 +84,3 @@
osm_status = status["osm"]
self.assertTrue(juju_status in JujuStatusToOSM[entity_type])
self.assertEqual(osm_status, JujuStatusToOSM[entity_type][juju_status])
-
- def test_db_data(self):
- self.assertEqual(DB_DATA.api_endpoints.table, "admin")
- self.assertEqual(DB_DATA.api_endpoints.filter, {"_id": "juju"})
- self.assertEqual(DB_DATA.api_endpoints.key, "api_endpoints")
diff --git a/n2vc/tests/unit/utils.py b/n2vc/tests/unit/utils.py
index a727072..2f107a7 100644
--- a/n2vc/tests/unit/utils.py
+++ b/n2vc/tests/unit/utils.py
@@ -82,7 +82,18 @@
detailed_status: str,
vca_status: str,
entity_type: str,
+ vca_id: str = None,
):
+ """
+ Write application status to database
+
+ :param: db_dict: DB dictionary
+ :param: status: Status of the application
+ :param: detailed_status: Detailed status
+ :param: vca_status: VCA status
+ :param: entity_type: Entity type ("application", "machine, and "action")
+ :param: vca_id: Id of the VCA. If None, the default VCA will be used.
+ """
self.last_written_values = Dict(
{
"n2vc_status": status,