The apiVersion on a resource being deserialized is required after upgrading Kubernetes-client 6.x

2 minute readKnowledge base

Impacted version

  • February 2023 release of CBCI (2.375.3.1) or higher

Issue

A part of keeping up to date with the latest and greatest, we are upgrading the java library used to access Kubernetes, the fabric8 Kubernetes-client from 5.x to 6.x. This brings us continued security updates as well as better support of new Kubernetes version.

In the context of provisioning controllers, this new version enables customers to provide CRDs to the provisioning lifecycle, even if they are not part of the client java model. For example, adding Istio gateways to deployed objects is now possible.

For customers not relying on the YAML field under « Advanced configuration » in the controller configuration screen in Operations Center, this update has no impact.

For customers using the YAML field under « Advanced configuration », this update has the following impacts:

  • apiVersion on a resource being deserialised is now required. This prevents the unintentional parsing of custom types without a registered class as a built-in type of the same name. Read Deserialization Resolution for more technical details about this breaking change.

How do I know if I am impacted by the breaking changes?

You can run the following script by using the script console ("Manage Jenkins" > "Script Console") in the Operations Center to check for invalid configurations:

def check = { yaml -> def result = [] if (!yaml?.trim()) { result += 'OK (blank)' } else { def fragments = yaml.split('---') result << fragments.size() + ' fragment' + (fragments.size() > 1 ? 's' : '') fragments.each { f -> if (yaml.contains('apiVersion:')) { result << 'OK' } else { result << 'KO (missing apiVersion)' } } } return result.join(', ') } def verbose = false println 'Global configuration' println '--------------------' def d = ExtensionList.lookupSingleton(com.cloudbees.masterprovisioning.kubernetes.KubernetesMasterProvisioning.DescriptorImpl.class) println check(d.yaml) if (verbose) { println d.yaml } println '' println 'Controllers configuration' println '-------------------------' Jenkins.instance.allItems.each { it -> if (it instanceof com.cloudbees.opscenter.server.model.ManagedMaster) { if (it.configuration.hasProperty("yaml")){ def yaml = it.configuration.yaml println '* ' + it.fullName + ' --> ' + check(yaml) if (verbose) { println yaml println '' } } } } return

The output of the script will show the result for the global configuration and each controller:

Global configuration -------------------- 2 fragments, KO (missing apiVersion), KO (missing apiVersion) Controllers configuration ------------------------- * controller1 --> 2 fragments, KO (missing apiVersion), KO (missing apiVersion) * controller2 --> 3 fragments, OK, OK, OK * controller3 --> OK (blank)

If this script returns KO (missing apiVersion) in the output for any controller, then modify the following line to enable verbose mode, and run it again:

def verbose = true

This time, the script will emit the yaml that is missing the apiVersion:

Global configuration -------------------- 2 fragments, KO (missing apiVersion), KO (missing apiVersion) --- kind: StatefulSet spec: template: metadata: annotations: cluster-autoscaler.kubernetes.io/safe-to-evict: "false" spec: tolerations: - key: "dedicated" operator: "Equal" value: "sda-ci" effect: "NoSchedule" Controllers configuration ------------------------- * controller1 --> 2 fragments, KO (missing apiVersion), KO (missing apiVersion) --- kind: StatefulSet spec: template: metadata: annotations: cluster-autoscaler.kubernetes.io/safe-to-evict: "false" spec: tolerations: - key: "dedicated" operator: "Equal" value: "sda-ci" effect: "NoSchedule" * controller2 --> 3 fragments, OK, OK, OK --- apiVersion: "apps/v1" kind: StatefulSet spec: template: metadata: annotations: cluster-autoscaler.kubernetes.io/safe-to-evict: "false" spec: tolerations: - key: "dedicated" operator: "Equal" value: "sda-ci" effect: "NoSchedule"