Fix validate error when k8s resource init
This patch fixes the issue that k8s failed to initialize object due to version upgrade. Skip initial validation by setting the `client_side_validation` parameter and remove the must_param variable that was previously set in order to pass the validation. In kubernetes v23.3.0, 'available_replicas' must be set into status of `StatefulSet`, so this patch also add a kuryr-kubernetes versioned parameter temporarily to .zuul.yaml to make the response returned by kubernetes correct. Closes-Bug: #1968103 Change-Id: I9495ce0f0893e5f9a1d6c52b98c3db3928bd95a3
This commit is contained in:
parent
acf84a168a
commit
6a0ba25f12
|
@ -439,6 +439,12 @@
|
|||
KURYR_K8S_API_URL: "https://{{ hostvars['controller-k8s']['nodepool']['private_ipv4'] }}:${KURYR_K8S_API_PORT}"
|
||||
KURYR_K8S_CONTAINERIZED_DEPLOYMENT: false
|
||||
KURYR_NEUTRON_DEFAULT_SUBNETPOOL_ID: shared-default-subnetpool-v4
|
||||
# TODO(YiFeng): At present, the version of kubernetes should be
|
||||
# 1.23.3, and the returned response can pass the verification of
|
||||
# kubernetes-client (1.23.3). This configuration will be removed
|
||||
# after kuryr-kubernetes fixes the following bug.
|
||||
# https://bugs.launchpad.net/kuryr-kubernetes/+bug/1968960
|
||||
KURYR_KUBERNETES_VERSION: 1.23.3
|
||||
MYSQL_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
|
||||
OCTAVIA_AMP_IMAGE_FILE: "/tmp/test-only-amphora-x64-haproxy-ubuntu-bionic.qcow2"
|
||||
OCTAVIA_AMP_IMAGE_NAME: "test-only-amphora-x64-haproxy-ubuntu-bionic"
|
||||
|
|
|
@ -290,7 +290,7 @@ def fake_pc():
|
|||
|
||||
|
||||
def fake_persistent_volume(
|
||||
name='curry-sc-pv', phase='UnAvailable'):
|
||||
name='curry-sc-pv', phase='Pending'):
|
||||
return client.V1PersistentVolume(
|
||||
api_version='v1',
|
||||
kind='PersistentVolume',
|
||||
|
@ -395,6 +395,8 @@ def fake_rq():
|
|||
|
||||
|
||||
def fake_stateful_set(ready_replicas=0):
|
||||
client_config = client.Configuration.get_default_copy()
|
||||
client_config.client_side_validation = False
|
||||
return client.V1StatefulSet(
|
||||
api_version='apps/v1',
|
||||
kind='StatefulSet',
|
||||
|
@ -424,7 +426,8 @@ def fake_stateful_set(ready_replicas=0):
|
|||
),
|
||||
status=client.V1StatefulSetStatus(
|
||||
replicas=2,
|
||||
ready_replicas=ready_replicas
|
||||
ready_replicas=ready_replicas,
|
||||
local_vars_configuration=client_config
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
@ -845,7 +845,7 @@ class TestKubernetes(base.BaseTestCase):
|
|||
|
||||
mock_node.return_value = fakes.fake_node(status='False')
|
||||
mock_read_node.side_effect = [
|
||||
fakes.fake_node(type='UnReady'),
|
||||
fakes.fake_node(type='NetworkUnavailable'),
|
||||
fakes.fake_node(), fakes.fake_none()]
|
||||
|
||||
self._normal_execute_procedure(req)
|
||||
|
|
|
@ -238,7 +238,7 @@ def fake_pvc_false():
|
|||
name='curry-sc-pvc'
|
||||
),
|
||||
status=client.V1PersistentVolumeClaimStatus(
|
||||
phase='UnBound'
|
||||
phase='Pending'
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -286,7 +286,7 @@ def fake_namespace_false():
|
|||
name='curry-ns'
|
||||
),
|
||||
status=client.V1NamespaceStatus(
|
||||
phase='NotActive'
|
||||
phase='Terminating'
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -658,6 +658,8 @@ def fake_v1_volume_attachment_error():
|
|||
|
||||
|
||||
def fake_v1_stateful_set():
|
||||
client_config = client.Configuration.get_default_copy()
|
||||
client_config.client_side_validation = False
|
||||
return client.V1StatefulSet(
|
||||
api_version='apps/v1',
|
||||
kind='StatefulSet',
|
||||
|
@ -687,12 +689,15 @@ def fake_v1_stateful_set():
|
|||
),
|
||||
status=client.V1StatefulSetStatus(
|
||||
replicas=1,
|
||||
ready_replicas=1
|
||||
ready_replicas=1,
|
||||
local_vars_configuration=client_config
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def fake_v1_stateful_set_error():
|
||||
client_config = client.Configuration.get_default_copy()
|
||||
client_config.client_side_validation = False
|
||||
return client.V1StatefulSet(
|
||||
api_version='apps/v1',
|
||||
kind='StatefulSet',
|
||||
|
@ -722,7 +727,8 @@ def fake_v1_stateful_set_error():
|
|||
),
|
||||
status=client.V1StatefulSetStatus(
|
||||
replicas=2,
|
||||
ready_replicas=1
|
||||
ready_replicas=1,
|
||||
local_vars_configuration=client_config
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -750,7 +756,7 @@ def fake_v1_persistent_volume_claim_error():
|
|||
namespace='curryns'
|
||||
),
|
||||
status=client.V1PersistentVolumeClaimStatus(
|
||||
phase='Bound1'
|
||||
phase='Pending'
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -800,7 +806,7 @@ def fake_pod_error():
|
|||
namespace='curryns'
|
||||
),
|
||||
status=client.V1PodStatus(
|
||||
phase='Terminated',
|
||||
phase='Failed',
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -850,7 +856,7 @@ def fake_persistent_volume_error():
|
|||
namespace='curryns'
|
||||
),
|
||||
status=client.V1PersistentVolumeStatus(
|
||||
phase='UnBound',
|
||||
phase='Pending',
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -1017,7 +1023,7 @@ def fake_pod_list():
|
|||
name="fake-name"
|
||||
),
|
||||
status=client.V1PodStatus(
|
||||
phase="Successed"
|
||||
phase="Succeeded"
|
||||
)
|
||||
)]
|
||||
)
|
||||
|
|
|
@ -684,7 +684,6 @@ class TestTransformer(base.TestCase):
|
|||
self.assertEqual(k8s_obj.api_version, 'v1')
|
||||
# V1LimitRangeSpec
|
||||
self.assertIsNotNone(k8s_obj.spec.limits)
|
||||
self.assertIsNotNone(k8s_obj.spec.limits[0].type)
|
||||
|
||||
def test_pod_template(self):
|
||||
k8s_objs = self.transfromer.get_k8s_objs_from_yaml(
|
||||
|
|
|
@ -160,169 +160,24 @@ class Transformer(object):
|
|||
|
||||
return kubernetes_objects
|
||||
|
||||
def _trans_to_k8s_object(self, obj_name):
|
||||
client_config = client.Configuration.get_default_copy()
|
||||
client_config.client_side_validation = False
|
||||
config = '(local_vars_configuration=client_config)'
|
||||
try:
|
||||
k8s_obj = eval(
|
||||
'client.' + obj_name + config)
|
||||
return k8s_obj
|
||||
except ValueError as e:
|
||||
msg = '{kind} create failure. Reason={reason}'.format(
|
||||
kind=obj_name, reason=e)
|
||||
raise exceptions.InitApiFalse(error=msg)
|
||||
|
||||
def _create_k8s_object(self, kind, file_content_dict):
|
||||
# must_param referring K8s official object page
|
||||
# *e.g:https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1Service.md
|
||||
# initiating k8s object, you need to
|
||||
# give the must param an empty value.
|
||||
must_param = {
|
||||
'V1LocalSubjectAccessReview': '(spec="")',
|
||||
'V1HTTPGetAction': '(port="")',
|
||||
'V1DeploymentSpec': '(selector="", template="")',
|
||||
'V1PodSpec': '(containers=[])',
|
||||
'V1ConfigMapKeySelector': '(key="")',
|
||||
'V1Container': '(name="")',
|
||||
'V1EnvVar': '(name="")',
|
||||
'V1SecretKeySelector': '(key="")',
|
||||
'V1ContainerPort': '(container_port="")',
|
||||
'V1VolumeMount': '(mount_path="", name="")',
|
||||
'V1PodCondition': '(status="", type="")',
|
||||
'V1ContainerStatus': '('
|
||||
'image="", image_id="", '
|
||||
'name="", ready="", '
|
||||
'restart_count="")',
|
||||
'V1ServicePort': '(port=80)',
|
||||
'V1TypedLocalObjectReference': '(kind="", name="")',
|
||||
'V1LabelSelectorRequirement': '(key="", operator="")',
|
||||
'V1PersistentVolumeClaimCondition': '(status="", type="")',
|
||||
'V1AWSElasticBlockStoreVolumeSource': '(volume_id="")',
|
||||
'V1AzureDiskVolumeSource': '(disk_name="", disk_uri="")',
|
||||
'V1AzureFileVolumeSource': '(secret_name="", share_name="")',
|
||||
'V1CephFSVolumeSource': '(monitors=[])',
|
||||
'V1CinderVolumeSource': '(volume_id="")',
|
||||
'V1KeyToPath': '(key="", path="")',
|
||||
'V1CSIVolumeSource': '(driver="")',
|
||||
'V1DownwardAPIVolumeFile': '(path="")',
|
||||
'V1ObjectFieldSelector': '(field_path="")',
|
||||
'V1ResourceFieldSelector': '(resource="")',
|
||||
'V1FlexVolumeSource': '(driver="")',
|
||||
'V1GCEPersistentDiskVolumeSource': '(pd_name="")',
|
||||
'V1GitRepoVolumeSource': '(repository="")',
|
||||
'V1GlusterfsVolumeSource': '(endpoints="", path="")',
|
||||
'V1HostPathVolumeSource': '(path="")',
|
||||
'V1ISCSIVolumeSource': '(iqn="", lun=0, target_portal="")',
|
||||
'V1Volume': '(name="")',
|
||||
'V1NFSVolumeSource': '(path="", server="")',
|
||||
'V1PersistentVolumeClaimVolumeSource': '(claim_name="")',
|
||||
'V1PhotonPersistentDiskVolumeSource': '(pd_id="")',
|
||||
'V1PortworxVolumeSource': '(volume_id="")',
|
||||
'V1ProjectedVolumeSource': '(sources=[])',
|
||||
'V1ServiceAccountTokenProjection': '(path="")',
|
||||
'V1QuobyteVolumeSource': '(registry="", volume="")',
|
||||
'V1RBDVolumeSource': '(image="", monitors=[])',
|
||||
'V1ScaleIOVolumeSource': '('
|
||||
'gateway="", secret_ref="", '
|
||||
'system="")',
|
||||
'V1VsphereVirtualDiskVolumeSource': '(volume_path="")',
|
||||
'V1LimitRangeSpec': '(limits=[])',
|
||||
'V1Binding': '(target="")',
|
||||
'V1ComponentCondition': '(status="", type="")',
|
||||
'V1NamespaceCondition': '(status="", type="")',
|
||||
'V1ConfigMapNodeConfigSource': '(kubelet_config_key="", '
|
||||
'name="", namespace="")',
|
||||
'V1Taint': '(effect="", key="")',
|
||||
'V1NodeAddress': '(address="", type="")',
|
||||
'V1NodeCondition': '(status="", type="")',
|
||||
'V1DaemonEndpoint': '(port=0)',
|
||||
'V1ContainerImage': '(names=[])',
|
||||
'V1NodeSystemInfo': '(architecture="", boot_id="", '
|
||||
'container_runtime_version="",'
|
||||
'kernel_version="", '
|
||||
'kube_proxy_version="", '
|
||||
'kubelet_version="",'
|
||||
'machine_id="", operating_system="", '
|
||||
'os_image="", system_uuid="")',
|
||||
'V1AttachedVolume': '(device_path="", name="")',
|
||||
'V1ScopedResourceSelectorRequirement':
|
||||
'(operator="", scope_name="")',
|
||||
'V1APIServiceSpec': '(group_priority_minimum=0, '
|
||||
'service="", '
|
||||
'version_priority=0)',
|
||||
'V1APIServiceCondition': '(status="", type="")',
|
||||
'V1DaemonSetSpec': '(selector="", template="")',
|
||||
'V1ReplicaSetSpec': '(selector="")',
|
||||
'V1StatefulSetSpec': '(selector="", '
|
||||
'service_name="", template="")',
|
||||
'V1StatefulSetCondition': '(status="", type="")',
|
||||
'V1StatefulSetStatus': '(replicas=0)',
|
||||
'V1ControllerRevision': '(revision=0)',
|
||||
'V1TokenReview': '(spec="")',
|
||||
'V1SubjectAccessReviewStatus': '(allowed=True)',
|
||||
'V1SelfSubjectAccessReview': '(spec="")',
|
||||
'V1SelfSubjectRulesReview': '(spec="")',
|
||||
'V1SubjectRulesReviewStatus': '(incomplete=True, '
|
||||
'non_resource_rules=[], '
|
||||
'resource_rules=[])',
|
||||
'V1NonResourceRule': '(verbs=[])',
|
||||
'V1SubjectAccessReview': '(spec="")',
|
||||
'V1HorizontalPodAutoscalerSpec':
|
||||
'(max_replicas=0, scale_target_ref="")',
|
||||
'V1CrossVersionObjectReference': '(kind="", name="")',
|
||||
'V1HorizontalPodAutoscalerStatus':
|
||||
'(current_replicas=0, desired_replicas=0)',
|
||||
'V1JobSpec': '(template="")',
|
||||
'V1NetworkPolicySpec': '(pod_selector="")',
|
||||
'V1PolicyRule': '(verbs=[])',
|
||||
'V1ClusterRoleBinding': '(role_ref="")',
|
||||
'V1RoleRef': '(api_group="", kind="", name="")',
|
||||
'V1Subject': '(kind="", name="")',
|
||||
'V1RoleBinding': '(role_ref="")',
|
||||
'V1PriorityClass': '(value=0)',
|
||||
'V1StorageClass': '(provisioner="")',
|
||||
'V1TopologySelectorLabelRequirement': '(key="", values=[])',
|
||||
'V1VolumeAttachment': '(spec="")',
|
||||
'V1VolumeAttachmentSpec':
|
||||
'(attacher="", node_name="", source="")',
|
||||
'V1VolumeAttachmentStatus': '(attached=True)',
|
||||
'V1NodeSelector': '(node_selector_terms=[])',
|
||||
'V1NodeSelectorRequirement': '(key="", operator="")',
|
||||
'V1PreferredSchedulingTerm': '(preference="", weight=1)',
|
||||
'V1PodAffinityTerm': '(topology_key="")',
|
||||
'V1WeightedPodAffinityTerm': '(pod_affinity_term="", weight=1)',
|
||||
'V1OwnerReference': '(api_version="", kind="", name="", uid="")',
|
||||
'V1HTTPHeader': '(name="", value="")',
|
||||
'V1TCPSocketAction': '(port="")',
|
||||
'V1VolumeDevice': '(device_path="", name="")',
|
||||
'V1PodReadinessGate': '(condition_type="")',
|
||||
'V1Sysctl': '(name="", value="")',
|
||||
'V1ContainerStateTerminated': '(exit_code=0)',
|
||||
'V1AzureFilePersistentVolumeSource': '(secret_name="",'
|
||||
' share_name="")',
|
||||
'V1CephFSPersistentVolumeSource': '(monitors=[])',
|
||||
'V1CinderPersistentVolumeSource': '(volume_id="")',
|
||||
'V1CSIPersistentVolumeSource': '(driver="", volume_handle="")',
|
||||
'V1FlexPersistentVolumeSource': '(driver="")',
|
||||
'V1GlusterfsPersistentVolumeSource': '(endpoints="", path="")',
|
||||
'V1ISCSIPersistentVolumeSource': '(iqn="", lun=0,'
|
||||
' target_portal="")',
|
||||
'V1LocalVolumeSource': '(path="")',
|
||||
'V1RBDPersistentVolumeSource': '(image="", monitors=[])',
|
||||
'V1ScaleIOPersistentVolumeSource': '('
|
||||
'gateway="",'
|
||||
' secret_ref="",'
|
||||
' system="")',
|
||||
'V1DaemonSetStatus': '(current_number_scheduled=0, '
|
||||
'desired_number_scheduled=0, '
|
||||
'number_misscheduled=0, '
|
||||
'number_ready=0)',
|
||||
'V1DaemonSetCondition': '(status="", type="")',
|
||||
'V1DeploymentCondition': '(status="", type="")',
|
||||
'V1ReplicaSetStatus': '(replicas=0)',
|
||||
'V1ReplicaSetCondition': '(status="", type="")',
|
||||
'V1ResourceRule': '(verbs=[])',
|
||||
'V1JobCondition': '(status="", type="")',
|
||||
'V1IPBlock': '(cidr="")',
|
||||
'V1EphemeralContainer': '(name="")',
|
||||
'V1TopologySpreadConstraint': '(max_skew=0, topology_key="",'
|
||||
' when_unsatisfiable="")',
|
||||
'V1LimitRangeItem': '(type="")'
|
||||
}
|
||||
whole_kind = 'V1' + kind
|
||||
if whole_kind in must_param.keys():
|
||||
k8s_obj = eval('client.V1' + kind + must_param.get(whole_kind))
|
||||
else:
|
||||
k8s_obj = eval('client.V1' + kind + '()')
|
||||
self._init_k8s_obj(k8s_obj, file_content_dict, must_param)
|
||||
client_config = client.Configuration.get_default_copy()
|
||||
client_config.client_side_validation = False
|
||||
k8s_obj = self._trans_to_k8s_object('V1' + kind)
|
||||
self._init_k8s_obj(k8s_obj, file_content_dict)
|
||||
return k8s_obj
|
||||
|
||||
def _get_k8s_obj_from_file_content_dict(self, file_content_dict,
|
||||
|
@ -499,7 +354,7 @@ class Transformer(object):
|
|||
name = name.strip()
|
||||
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', name).lower()
|
||||
|
||||
def _init_k8s_obj(self, obj, content, must_param):
|
||||
def _init_k8s_obj(self, obj, content):
|
||||
for key, value in content.items():
|
||||
param_value = self._get_lower_case_name(key)
|
||||
if hasattr(obj, param_value) and \
|
||||
|
@ -511,12 +366,8 @@ class Transformer(object):
|
|||
if obj_name == 'dict(str, str)':
|
||||
setattr(obj, param_value, value)
|
||||
else:
|
||||
if obj_name in must_param.keys():
|
||||
rely_obj = eval('client.' + obj_name +
|
||||
must_param.get(obj_name))
|
||||
else:
|
||||
rely_obj = eval('client.' + obj_name + '()')
|
||||
self._init_k8s_obj(rely_obj, value, must_param)
|
||||
rely_obj = self._trans_to_k8s_object(obj_name)
|
||||
self._init_k8s_obj(rely_obj, value)
|
||||
setattr(obj, param_value, rely_obj)
|
||||
elif isinstance(value, list):
|
||||
obj_name = obj.openapi_types.get(param_value)
|
||||
|
@ -527,13 +378,8 @@ class Transformer(object):
|
|||
rely_obj_name = \
|
||||
re.findall(r".*\[([^\[\]]*)\].*", obj_name)[0]
|
||||
for v in value:
|
||||
if rely_obj_name in must_param.keys():
|
||||
rely_obj = eval('client.' + rely_obj_name +
|
||||
must_param.get(rely_obj_name))
|
||||
else:
|
||||
rely_obj = \
|
||||
eval('client.' + rely_obj_name + '()')
|
||||
self._init_k8s_obj(rely_obj, v, must_param)
|
||||
rely_obj = self._trans_to_k8s_object(rely_obj_name)
|
||||
self._init_k8s_obj(rely_obj, v)
|
||||
rely_objs.append(rely_obj)
|
||||
setattr(obj, param_value, rely_objs)
|
||||
|
||||
|
@ -553,9 +399,8 @@ class Transformer(object):
|
|||
return sorted_k8s_objs
|
||||
|
||||
def get_object_meta(self, content):
|
||||
must_param = {}
|
||||
v1_object_meta = client.V1ObjectMeta()
|
||||
self._init_k8s_obj(v1_object_meta, content, must_param)
|
||||
self._init_k8s_obj(v1_object_meta, content)
|
||||
return v1_object_meta
|
||||
|
||||
# config_labels configures label
|
||||
|
|
Loading…
Reference in New Issue