Bug 1939 fixed: : added a random suffix to the end of the Juju app name, in order to allow multiple Juju charms per VDU

I followed the second option of the bug's description. Now, Juju
applications have a random suffix with size=5 (the random suffix
size used by K8s), in order to avoid collisions between applications'
names;

Also fixed unit-test on test_n2vc_juju_conn.py

Change-Id: I5d3eb8282889e58361f7c21214b11071a7530d26
Signed-off-by: Pedro Escaleira <escaleira@av.it.pt>
diff --git a/n2vc/n2vc_juju_conn.py b/n2vc/n2vc_juju_conn.py
index 55220d6..4efbd1c 100644
--- a/n2vc/n2vc_juju_conn.py
+++ b/n2vc/n2vc_juju_conn.py
@@ -39,7 +39,7 @@
 from n2vc.n2vc_conn import obj_to_dict, obj_to_yaml
 from n2vc.libjuju import Libjuju
 from n2vc.store import MotorStore
-from n2vc.utils import get_ee_id_components
+from n2vc.utils import get_ee_id_components, generate_random_alfanum_string
 from n2vc.vca.connection import get_connection
 from retrying_async import retry
 
@@ -1140,7 +1140,7 @@
         """
         Build application name from namespace
         :param namespace:
-        :return: app-vnf-<vnf id>-vdu-<vdu-id>-cnt-<vdu-count>
+        :return: app-vnf-<vnf id>-vdu-<vdu-id>-cnt-<vdu-count>-<random_value>
         """
 
         # TODO: Enforce the Juju 50-character application limit
@@ -1167,7 +1167,12 @@
         else:
             vdu_count = "-cnt-" + vdu_count
 
-        application_name = "app-{}{}{}".format(vnf_id, vdu_id, vdu_count)
+        # Generate a random suffix with 5 characters (the default size used by K8s)
+        random_suffix = generate_random_alfanum_string(size=5)
+
+        application_name = "app-{}{}{}-{}".format(
+            vnf_id, vdu_id, vdu_count, random_suffix
+        )
 
         return N2VCJujuConnector._format_app_name(application_name)
 
diff --git a/n2vc/tests/unit/test_n2vc_juju_conn.py b/n2vc/tests/unit/test_n2vc_juju_conn.py
index 2475d01..3caae03 100644
--- a/n2vc/tests/unit/test_n2vc_juju_conn.py
+++ b/n2vc/tests/unit/test_n2vc_juju_conn.py
@@ -16,6 +16,7 @@
 import asyncio
 import logging
 from unittest.mock import Mock
+from unittest.mock import patch
 
 
 import asynctest
@@ -144,8 +145,13 @@
         self.n2vc.libjuju.deploy_charm = AsyncMock()
         self.n2vc.libjuju.model_exists.return_value = False
 
+    @patch(
+        "n2vc.n2vc_juju_conn.generate_random_alfanum_string",
+        **{"return_value": "random"}
+    )
     def test_success(
         self,
+        mock_generate_random_alfanum_string,
         mock_path,
         mock_file_exists,
     ):
@@ -163,7 +169,7 @@
         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",
+            application_name="app-vnf-vnf-id-vdu-vdu-random",
             path="/path/path/",
             machine_id=None,
             db_dict={},
@@ -171,7 +177,7 @@
             total_timeout=None,
             config=None,
         )
-        self.assertEqual(ee_id, "ns-id-k8s.app-vnf-vnf-id-vdu-vdu.k8s")
+        self.assertEqual(ee_id, "ns-id-k8s.app-vnf-vnf-id-vdu-vdu-random.k8s")
 
     def test_no_artifact_path(
         self,
diff --git a/n2vc/utils.py b/n2vc/utils.py
index a661e05..286f0fc 100644
--- a/n2vc/utils.py
+++ b/n2vc/utils.py
@@ -16,6 +16,8 @@
 import re
 import binascii
 import yaml
+import string
+import secrets
 from enum import Enum
 from juju.machine import Machine
 from juju.application import Application
@@ -163,3 +165,15 @@
     application_name = parts[1]
     machine_id = parts[2]
     return model_name, application_name, machine_id
+
+
+def generate_random_alfanum_string(size: int) -> str:
+    """
+    Generate random alfa-numeric string with a size given by argument
+    :param size:
+    :return: random generated string
+    """
+
+    return "".join(
+        secrets.choice(string.ascii_letters + string.digits) for i in range(size)
+    )