Merge "Fix kubernetes resource existence check in v2 API"

This commit is contained in:
Zuul 2024-03-13 13:40:47 +00:00 committed by Gerrit Code Review
commit 738336d8d9
9 changed files with 73 additions and 24 deletions

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -34,8 +34,6 @@ LOG = logging.getLogger(__name__)
CONF = config.CONF
CHECK_INTERVAL = 10
TARGET_KIND = {"Pod", "Deployment", "DaemonSet", "StatefulSet", "ReplicaSet"}
class KubernetesCommon(object):

View File

@ -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

View File

@ -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'

View File

@ -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 = []

View File

@ -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)

View File

@ -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):