Merge "Fix kubernetes resource existence check in v2 API"
This commit is contained in:
commit
738336d8d9
|
@ -312,6 +312,13 @@ The following is a simple example of ``deployment`` resource.
|
|||
``topology_template.node_templates.VDU1.properties.name``
|
||||
in the helloworld3_df_simple.yaml file.
|
||||
|
||||
.. note:: In version 2 API, the ``tacker_vnf_instance_id`` key and
|
||||
VNF instance ID value are added to the ``metadata.labels`` to
|
||||
identify which VNF instance created the resource.
|
||||
Please note that if you have defined the ``tacker_vnf_instance_id``
|
||||
label in advance, the value will be overwritten with
|
||||
the VNF instance ID.
|
||||
|
||||
3. Create a TOSCA.meta File
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
The TOSCA.Meta file contains version information for the TOSCA.Meta file, CSAR,
|
||||
|
|
|
@ -305,7 +305,7 @@ class Helm(kubernetes_common.KubernetesCommon):
|
|||
def _create_reses_from_manifest(self, k8s_api_client, namespace,
|
||||
k8s_resources):
|
||||
for k8s_res in k8s_resources:
|
||||
if k8s_res['kind'] in kubernetes_utils.SUPPORTED_NAMESPACE_KINDS:
|
||||
if k8s_res['kind'] in kubernetes_utils.SUPPORTED_NAMESPACE_KIND:
|
||||
k8s_res.setdefault('metadata', {})
|
||||
k8s_res['metadata'].setdefault('namespace', namespace)
|
||||
|
||||
|
@ -331,7 +331,7 @@ class Helm(kubernetes_common.KubernetesCommon):
|
|||
# NOTE: {some string} must not include '-'.
|
||||
return {vdu_ids[res.name[:res.name.rfind("-")]]: res
|
||||
for res in k8s_reses
|
||||
if (res.kind in kubernetes_common.TARGET_KIND
|
||||
if (res.kind in kubernetes_utils.TARGET_KIND
|
||||
and res.name[:res.name.rfind("-")] in vdu_ids)}
|
||||
|
||||
def _init_instantiated_vnf_info(self, inst, flavour_id, vdu_reses,
|
||||
|
|
|
@ -28,8 +28,6 @@ LOG = logging.getLogger(__name__)
|
|||
CONF = config.CONF
|
||||
CHECK_INTERVAL = 10
|
||||
|
||||
SCALABLE_KIND = {"Deployment", "ReplicaSet", "StatefulSet"}
|
||||
|
||||
|
||||
class Kubernetes(kubernetes_common.KubernetesCommon):
|
||||
|
||||
|
@ -48,7 +46,7 @@ class Kubernetes(kubernetes_common.KubernetesCommon):
|
|||
|
||||
k8s_reses, namespace = self._setup_k8s_reses(
|
||||
vnfd, target_k8s_files, k8s_api_client,
|
||||
req.additionalParams.get('namespace'))
|
||||
req.additionalParams.get('namespace'), inst.id)
|
||||
|
||||
vdus_num = self._get_vdus_num_from_grant_req_res_defs(
|
||||
grant_req.addResources)
|
||||
|
@ -60,7 +58,7 @@ class Kubernetes(kubernetes_common.KubernetesCommon):
|
|||
f' manifest does not match the VNFD.')
|
||||
continue
|
||||
|
||||
if vdu_res.kind in SCALABLE_KIND:
|
||||
if vdu_res.kind in kubernetes_utils.SCALABLE_KIND:
|
||||
vdu_res.body['spec']['replicas'] = vdus_num[vdu_name]
|
||||
|
||||
# deploy k8s resources
|
||||
|
@ -77,7 +75,7 @@ class Kubernetes(kubernetes_common.KubernetesCommon):
|
|||
self._update_vnfc_info(inst, k8s_api_client)
|
||||
|
||||
def _setup_k8s_reses(self, vnfd, target_k8s_files, k8s_api_client,
|
||||
namespace):
|
||||
namespace, inst_id):
|
||||
# NOTE: this check should be done in STARTING phase.
|
||||
vnf_artifact_files = vnfd.get_vnf_artifact_files()
|
||||
diff_files = set(target_k8s_files) - set(vnf_artifact_files)
|
||||
|
@ -87,7 +85,7 @@ class Kubernetes(kubernetes_common.KubernetesCommon):
|
|||
|
||||
# get k8s content from yaml file
|
||||
return kubernetes_utils.get_k8s_reses_from_json_files(
|
||||
target_k8s_files, vnfd, k8s_api_client, namespace)
|
||||
target_k8s_files, vnfd, k8s_api_client, namespace, inst_id)
|
||||
|
||||
def instantiate_rollback(self, req, inst, grant_req, grant, vnfd):
|
||||
vim_info = inst_utils.select_vim_info(inst.vimConnectionInfo)
|
||||
|
@ -103,7 +101,7 @@ class Kubernetes(kubernetes_common.KubernetesCommon):
|
|||
try:
|
||||
k8s_reses, _ = self._setup_k8s_reses(
|
||||
vnfd, target_k8s_files, k8s_api_client,
|
||||
req.additionalParams.get('namespace'))
|
||||
req.additionalParams.get('namespace'), inst.id)
|
||||
except sol_ex.SolException:
|
||||
# it means it failed in a basic check and it failes always.
|
||||
# nothing to do since instantiate failed in it too.
|
||||
|
@ -113,7 +111,6 @@ class Kubernetes(kubernetes_common.KubernetesCommon):
|
|||
# delete k8s resources
|
||||
body = client.V1DeleteOptions(propagation_policy='Foreground')
|
||||
self._delete_k8s_resource(k8s_reses, body)
|
||||
|
||||
# wait k8s resource delete complete
|
||||
self._wait_k8s_reses_deleted(k8s_reses)
|
||||
|
||||
|
@ -136,7 +133,7 @@ class Kubernetes(kubernetes_common.KubernetesCommon):
|
|||
# get k8s content from yaml file
|
||||
namespace = inst.instantiatedVnfInfo.metadata['namespace']
|
||||
k8s_reses, _ = kubernetes_utils.get_k8s_reses_from_json_files(
|
||||
target_k8s_files, vnfd, k8s_api_client, namespace)
|
||||
target_k8s_files, vnfd, k8s_api_client, namespace, inst.id)
|
||||
k8s_reses.reverse()
|
||||
|
||||
# delete k8s resources
|
||||
|
@ -198,7 +195,7 @@ class Kubernetes(kubernetes_common.KubernetesCommon):
|
|||
if vnfc.vduId in target_vdus}
|
||||
|
||||
k8s_reses, _ = self._setup_k8s_reses(
|
||||
vnfd, target_k8s_files, k8s_api_client, namespace)
|
||||
vnfd, target_k8s_files, k8s_api_client, namespace, inst.id)
|
||||
|
||||
vdu_reses = self._select_vdu_reses(
|
||||
vnfd, inst.instantiatedVnfInfo.flavourId, k8s_reses)
|
||||
|
@ -248,7 +245,7 @@ class Kubernetes(kubernetes_common.KubernetesCommon):
|
|||
vdu_reses = []
|
||||
for vdu_name, vdu_num in vdus_num.items():
|
||||
vdu_res = self._get_vdu_res(inst, k8s_api_client, vdu_name)
|
||||
if vdu_res.kind not in SCALABLE_KIND:
|
||||
if vdu_res.kind not in kubernetes_utils.SCALABLE_KIND:
|
||||
LOG.error(f'scale vdu {vdu_name}'
|
||||
f' is not scalable resource')
|
||||
continue
|
||||
|
@ -301,7 +298,7 @@ class Kubernetes(kubernetes_common.KubernetesCommon):
|
|||
# res.name is properties.name itself
|
||||
return {vdu_ids[res.name]: res
|
||||
for res in k8s_reses
|
||||
if (res.kind in kubernetes_common.TARGET_KIND
|
||||
if (res.kind in kubernetes_utils.TARGET_KIND
|
||||
and res.name in vdu_ids)}
|
||||
|
||||
def _init_instantiated_vnf_info(self, inst, flavour_id, vdu_reses,
|
||||
|
|
|
@ -34,8 +34,6 @@ LOG = logging.getLogger(__name__)
|
|||
CONF = config.CONF
|
||||
CHECK_INTERVAL = 10
|
||||
|
||||
TARGET_KIND = {"Pod", "Deployment", "DaemonSet", "StatefulSet", "ReplicaSet"}
|
||||
|
||||
|
||||
class KubernetesCommon(object):
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
CONF = config.CONF
|
||||
CHECK_INTERVAL = 10
|
||||
VNF_INSTANCE_ID_LABEL = 'tacker_vnf_instance_id'
|
||||
|
||||
|
||||
def convert(name):
|
||||
|
@ -47,6 +48,8 @@ class CommonResource:
|
|||
self.name = k8s_res.get('metadata', {}).get('name')
|
||||
self.metadata = k8s_res.get('metadata', {})
|
||||
self.body = k8s_res
|
||||
self.inst_id = k8s_res.get('metadata', {}).get('labels', {}).get(
|
||||
VNF_INSTANCE_ID_LABEL)
|
||||
|
||||
def create(self):
|
||||
pass
|
||||
|
@ -59,7 +62,25 @@ class CommonResource:
|
|||
|
||||
def is_exists(self):
|
||||
try:
|
||||
return self.read() is not None
|
||||
info = self.read()
|
||||
if info is None:
|
||||
# resource not exists
|
||||
return False
|
||||
# resource exists
|
||||
# check if the operation uses helm.
|
||||
# if helm is used, self.inst_id is None.
|
||||
if self.inst_id is None:
|
||||
# it means check is not necessary
|
||||
return True
|
||||
|
||||
# check whether other made it by "metadata.labels"
|
||||
if info.metadata.labels is None:
|
||||
# labels not exists. it means made by other
|
||||
return False
|
||||
if info.metadata.labels.get(VNF_INSTANCE_ID_LABEL) != self.inst_id:
|
||||
# made by other
|
||||
return False
|
||||
return True
|
||||
except sol_ex.K8sResourceNotFound:
|
||||
return False
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ from tacker.sol_refactored.infra_drivers.kubernetes import kubernetes_resource
|
|||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
SUPPORTED_NAMESPACE_KINDS = {
|
||||
SUPPORTED_NAMESPACE_KIND = {
|
||||
"Binding",
|
||||
"ConfigMap",
|
||||
"ControllerRevision",
|
||||
|
@ -55,10 +55,15 @@ SUPPORTED_NAMESPACE_KINDS = {
|
|||
"ServiceAccount",
|
||||
"StatefulSet",
|
||||
}
|
||||
SCALABLE_KIND = {"Deployment", "ReplicaSet", "StatefulSet"}
|
||||
TARGET_KIND = {"Pod", "Deployment", "DaemonSet", "StatefulSet", "ReplicaSet"}
|
||||
UNLABELED_KIND = {"SubjectAccessReview", "LocalSubjectAccessReview",
|
||||
"SelfSubjectAccessReview", "SelfSubjectRulesReview",
|
||||
"TokenReview"}
|
||||
|
||||
|
||||
def get_k8s_reses_from_json_files(target_k8s_files, vnfd, k8s_api_client,
|
||||
namespace):
|
||||
namespace, inst_id):
|
||||
|
||||
k8s_resources = []
|
||||
|
||||
|
@ -78,18 +83,24 @@ def get_k8s_reses_from_json_files(target_k8s_files, vnfd, k8s_api_client,
|
|||
for k8s_res in k8s_resources:
|
||||
if not k8s_res.get('kind'):
|
||||
raise sol_ex.K8sInvalidManifestFound()
|
||||
if k8s_res['kind'] in SUPPORTED_NAMESPACE_KINDS:
|
||||
if k8s_res['kind'] in SUPPORTED_NAMESPACE_KIND:
|
||||
k8s_res.setdefault('metadata', {})
|
||||
if namespace is None:
|
||||
k8s_res['metadata'].setdefault('namespace', 'default')
|
||||
else:
|
||||
k8s_res['metadata']['namespace'] = namespace
|
||||
# Set label to identify the VnfInstance
|
||||
if k8s_res['kind'] not in UNLABELED_KIND:
|
||||
k8s_res.setdefault('metadata', {})
|
||||
k8s_res['metadata'].setdefault('labels', {})
|
||||
k8s_res['metadata']['labels'][
|
||||
kubernetes_resource.VNF_INSTANCE_ID_LABEL] = inst_id
|
||||
|
||||
# check namespace
|
||||
if namespace is None:
|
||||
namespaces = {k8s_res['metadata']['namespace']
|
||||
for k8s_res in k8s_resources
|
||||
if k8s_res['kind'] in SUPPORTED_NAMESPACE_KINDS}
|
||||
if k8s_res['kind'] in SUPPORTED_NAMESPACE_KIND}
|
||||
if len(namespaces) > 1:
|
||||
raise sol_ex.NamespaceNotUniform()
|
||||
namespace = namespaces.pop() if namespaces else 'default'
|
||||
|
|
|
@ -226,14 +226,16 @@ class ContainerUpdateMgmtDriver(kubernetes.Kubernetes):
|
|||
old_vnfd = vnfd_utils.Vnfd(uuidutils.generate_uuid())
|
||||
old_vnfd.init_from_csar_dir(self.old_csar_dir)
|
||||
old_k8s_objs, _ = self._setup_k8s_reses(
|
||||
old_vnfd, target_k8s_files, k8s_api_client, namespace)
|
||||
old_vnfd, target_k8s_files, k8s_api_client, namespace,
|
||||
vnf_instance['id'])
|
||||
# Get new_k8s_objs
|
||||
target_k8s_files = new_inst_vnf_info['metadata'][
|
||||
'lcm-kubernetes-def-files']
|
||||
new_vnfd = vnfd_utils.Vnfd(self.req['vnfdId'])
|
||||
new_vnfd.init_from_csar_dir(self.new_csar_dir)
|
||||
new_k8s_objs, _ = self._setup_k8s_reses(
|
||||
new_vnfd, target_k8s_files, k8s_api_client, namespace)
|
||||
new_vnfd, target_k8s_files, k8s_api_client, namespace,
|
||||
vnf_instance['id'])
|
||||
# Initialize k8s_pod_objs and k8s_config_objs
|
||||
k8s_pod_objs = []
|
||||
k8s_config_objs = []
|
||||
|
|
|
@ -42,6 +42,11 @@ class VnfLcmKubernetesTest(base_v2.BaseVnfLcmKubernetesV2Test):
|
|||
def setUp(self):
|
||||
super(VnfLcmKubernetesTest, self).setUp()
|
||||
|
||||
def _get_vdu_label(self, inst_vnf_info, vdu_id):
|
||||
vdu_reses = inst_vnf_info['metadata']['vdu_reses']
|
||||
return vdu_reses[vdu_id]['metadata'].get(
|
||||
'labels', {}).get('tacker_vnf_instance_id')
|
||||
|
||||
def test_basic_lcms_max(self):
|
||||
"""Test LCM operations with all attributes set
|
||||
|
||||
|
@ -147,6 +152,14 @@ class VnfLcmKubernetesTest(base_v2.BaseVnfLcmKubernetesV2Test):
|
|||
expected = {'VDU1': 1, 'VDU2': 2, 'VDU3': 1, 'VDU5': 1, 'VDU6': 1}
|
||||
self.assertEqual(expected, vdu_nums)
|
||||
|
||||
# check VDU label
|
||||
inst_vnf_info = body['instantiatedVnfInfo']
|
||||
self.assertEqual(inst_id, self._get_vdu_label(inst_vnf_info, 'VDU1'))
|
||||
self.assertEqual(inst_id, self._get_vdu_label(inst_vnf_info, 'VDU2'))
|
||||
self.assertEqual(inst_id, self._get_vdu_label(inst_vnf_info, 'VDU3'))
|
||||
self.assertEqual(inst_id, self._get_vdu_label(inst_vnf_info, 'VDU5'))
|
||||
self.assertEqual(inst_id, self._get_vdu_label(inst_vnf_info, 'VDU6'))
|
||||
|
||||
# 4. Scale out a VNF instance
|
||||
scale_out_req = paramgen.max_sample_scale_out()
|
||||
resp, body = self.scale_vnf_instance(inst_id, scale_out_req)
|
||||
|
|
|
@ -52,7 +52,7 @@ class TestKubernetes(base.TestCase):
|
|||
target_k8s_files = [not_exist]
|
||||
ex = self.assertRaises(sol_ex.CnfDefinitionNotFound,
|
||||
self.driver._setup_k8s_reses, self.vnfd_1,
|
||||
target_k8s_files, mock.Mock(), mock.Mock())
|
||||
target_k8s_files, mock.Mock(), mock.Mock(), mock.Mock())
|
||||
self.assertEqual(expected_ex.detail, ex.detail)
|
||||
|
||||
def test_wait_k8s_reses_ready(self):
|
||||
|
|
Loading…
Reference in New Issue