Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot deserialize to Java code generated from CRD #4845

Closed
lmyslinski opened this issue Feb 7, 2023 · 3 comments
Closed

Cannot deserialize to Java code generated from CRD #4845

lmyslinski opened this issue Feb 7, 2023 · 3 comments

Comments

@lmyslinski
Copy link

lmyslinski commented Feb 7, 2023

Describe the bug

I'm generating Java code from a massive crd (20k lines...). The Java generator works correctly, but when trying to list existing objects Jackson fails with a java.lang.ClassCastException (see full stack trace below). Here's the bit that fails on serialization, as well as relevant code.

Yaml part:

              modelStatus:
                properties:
                  copies:
                    properties:
                      failedCopies:
                        default: 0
                        type: integer
                      totalCopies:
                        type: integer
                    required:
                    - failedCopies
                    type: object
                  lastFailureInfo:
                    properties:
                      exitCode:
                        format: int32
                        type: integer
                      location:
                        type: string
                      message:
                        type: string
                      modelRevisionName:
                        type: string
                      reason:
                        enum:
                        - ModelLoadFailed
                        - RuntimeUnhealthy
                        - RuntimeDisabled
                        - NoSupportingRuntime
                        - RuntimeNotRecognized
                        - InvalidPredictorSpec
                        type: string
                      time:
                        format: date-time
                        type: string
                    type: object
                  states:
                    properties:
                      activeModelState:
                        default: Pending
                        enum:
                        - Pending
                        - Standby
                        - Loading
                        - Loaded
                        - FailedToLoad
                        type: string
                      targetModelState:
                        default: ""
                        enum:
                        - Pending
                        - Standby
                        - Loading
                        - Loaded
                        - FailedToLoad
                        type: string
                    required:
                    - activeModelState
                    type: object
                  transitionStatus:
                    default: UpToDate
                    enum:
                    - UpToDate
                    - InProgress
                    - BlockedByFailedLoad
                    - InvalidSpec
                    type: string
                required:
                - transitionStatus
                type: object

Full Model Status class:

              
package io.kserve.serving.v1beta1.inferenceservicestatus;

@com.fasterxml.jackson.annotation.JsonInclude(com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL)
@com.fasterxml.jackson.annotation.JsonPropertyOrder({"copies","lastFailureInfo","states","transitionStatus"})
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = com.fasterxml.jackson.databind.JsonDeserializer.None.class)
@javax.annotation.processing.Generated("io.fabric8.java.generator.CRGeneratorRunner")
public class ModelStatus implements io.fabric8.kubernetes.api.model.KubernetesResource {

    @com.fasterxml.jackson.annotation.JsonProperty("copies")
    @com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SKIP)
    private io.kserve.serving.v1beta1.inferenceservicestatus.modelstatus.Copies copies;

    public io.kserve.serving.v1beta1.inferenceservicestatus.modelstatus.Copies getCopies() {
        return copies;
    }

    public void setCopies(io.kserve.serving.v1beta1.inferenceservicestatus.modelstatus.Copies copies) {
        this.copies = copies;
    }

    @com.fasterxml.jackson.annotation.JsonProperty("lastFailureInfo")
    @com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SKIP)
    private io.kserve.serving.v1beta1.inferenceservicestatus.modelstatus.LastFailureInfo lastFailureInfo;

    public io.kserve.serving.v1beta1.inferenceservicestatus.modelstatus.LastFailureInfo getLastFailureInfo() {
        return lastFailureInfo;
    }

    public void setLastFailureInfo(io.kserve.serving.v1beta1.inferenceservicestatus.modelstatus.LastFailureInfo lastFailureInfo) {
        this.lastFailureInfo = lastFailureInfo;
    }

    @com.fasterxml.jackson.annotation.JsonProperty("states")
    @com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SKIP)
    private io.kserve.serving.v1beta1.inferenceservicestatus.modelstatus.States states;

    public io.kserve.serving.v1beta1.inferenceservicestatus.modelstatus.States getStates() {
        return states;
    }

    public void setStates(io.kserve.serving.v1beta1.inferenceservicestatus.modelstatus.States states) {
        this.states = states;
    }

    public enum TransitionStatus {

        @com.fasterxml.jackson.annotation.JsonProperty("UpToDate")
        UPTODATE("UpToDate"), @com.fasterxml.jackson.annotation.JsonProperty("InProgress")
        INPROGRESS("InProgress"), @com.fasterxml.jackson.annotation.JsonProperty("BlockedByFailedLoad")
        BLOCKEDBYFAILEDLOAD("BlockedByFailedLoad"), @com.fasterxml.jackson.annotation.JsonProperty("InvalidSpec")
        INVALIDSPEC("InvalidSpec");

        java.lang.String value;

        TransitionStatus(java.lang.String value) {
            this.value = value;
        }

        @com.fasterxml.jackson.annotation.JsonValue()
        public java.lang.String getValue() {
            return value;
        }
    }

    @com.fasterxml.jackson.annotation.JsonProperty("transitionStatus")
    @io.fabric8.generator.annotation.Required()
    @com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SKIP)
    private TransitionStatus transitionStatus = io.fabric8.kubernetes.client.utils.Serialization.unmarshal("\"UpToDate\"", TransitionStatus.class);

    public TransitionStatus getTransitionStatus() {
        return transitionStatus;
    }

    public void setTransitionStatus(TransitionStatus transitionStatus) {
        this.transitionStatus = transitionStatus;
    }
}

The stacktrace bit (see full stacktrace below):

`at io.kserve.serving.v1beta1.inferenceservicestatus.ModelStatus.<init>(ModelStatus.java:68)`

matches the line
private TransitionStatus transitionStatus = io.fabric8.kubernetes.client.utils.Serialization.unmarshal("\"UpToDate\"", TransitionStatus.class);

So I'm guessing this has to do with the fact that it's trying to instantiate transitionStatus field, but where does the Map in the exception come from, I've no idea.

Fabric8 Kubernetes Client version

6.4.1

Steps to reproduce

Not provided for now (Full yaml available here

Expected behavior

The java class can be instantiated

Runtime

Kubernetes (vanilla)

Kubernetes API Server version

1.24

Environment

Amazon

Fabric8 Kubernetes Client Logs

Caused by: io.fabric8.kubernetes.client.KubernetesClientException: An error has occurred.
	at io.fabric8.kubernetes.client.KubernetesClientException.launderThrowable(KubernetesClientException.java:129)
	at io.fabric8.kubernetes.client.KubernetesClientException.launderThrowable(KubernetesClientException.java:122)
	at io.fabric8.kubernetes.client.utils.Serialization.unmarshal(Serialization.java:244)
	at io.fabric8.kubernetes.client.utils.Serialization.unmarshal(Serialization.java:351)
	at io.fabric8.kubernetes.client.utils.Serialization.unmarshal(Serialization.java:320)
	at io.fabric8.kubernetes.client.dsl.internal.OperationSupport.lambda$handleResponse$0(OperationSupport.java:578)
	at java.base/java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:646)
	at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510)
	at java.base/java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2147)
	at io.fabric8.kubernetes.client.dsl.internal.OperationSupport.lambda$retryWithExponentialBackoff$2(OperationSupport.java:618)
	at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:863)
	at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:841)
	at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510)
	at java.base/java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2147)
	at io.fabric8.kubernetes.client.okhttp.OkHttpClientImpl$4.onResponse(OkHttpClientImpl.java:277)
	at okhttp3.RealCall$AsyncCall.execute(RealCall.java:174)
	at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
	... 1 more
Caused by: com.fasterxml.jackson.databind.exc.ValueInstantiationException: Cannot construct instance of `io.kserve.serving.v1beta1.inferenceservicestatus.ModelStatus`, problem: class java.lang.String cannot be cast to class java.util.Map (java.lang.String and java.util.Map are in module java.base of loader 'bootstrap')
 at [Source: UNKNOWN; byte offset: #UNKNOWN] (through reference chain: io.fabric8.kubernetes.api.model.DefaultKubernetesResourceList["items"]->java.util.ArrayList[0]->io.kserve.serving.v1beta1.InferenceService["status"]->io.kserve.serving.v1beta1.InferenceServiceStatus["modelStatus"])
	at com.fasterxml.jackson.databind.exc.ValueInstantiationException.from(ValueInstantiationException.java:47)
	at com.fasterxml.jackson.databind.DeserializationContext.instantiationException(DeserializationContext.java:2052)
	at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.wrapAsJsonMappingException(StdValueInstantiator.java:587)
	at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.rewrapCtorProblem(StdValueInstantiator.java:610)
	at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createUsingDefault(StdValueInstantiator.java:280)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:303)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:177)
	at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
	at io.fabric8.kubernetes.model.jackson.SettableBeanPropertyDelegate.deseridSet(SettableBeanPropertyDelegate.java:134)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:314)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:177)
	at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
	at io.fabric8.kubernetes.model.jackson.SettableBeanPropertyDelegate.deserializeAndSet(SettableBeanPropertyDelegate.java:134)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:314)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:177)
	at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:323)
	at com.fasterxml.jackson.databind.ObjectMapper._readValue(ObjectMapper.java:4706)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2879)
	at com.fasterxml.jackson.databind.ObjectMapper.treeToValue(ObjectMapper.java:3343)
	at io.fabric8.kubernetes.internal.KubernetesDeserializer.fromObjectNode(KubernetesDeserializer.java:125)
	at io.fabric8.kubernetes.internal.KubernetesDeserializer.deserialize(KubernetesDeserializer.java:89)
	at io.fabric8.kubernetes.internal.KubernetesDeserializer.deserialize(KubernetesDeserializer.java:47)
	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer._deserializeFromArray(CollectionDeserializer.java:359)
	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:244)
	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:28)
	at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
	at io.fabric8.kubernetes.model.jackson.SettableBeanPropertyDelegate.deserializeAndSet(SettableBeanPropertyDelegate.java:134)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:314)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:177)
	at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:323)
	at com.fasterxml.jackson.databind.ObjectReader._bindAndClose(ObjectReader.java:2105)
	at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:1481)
	at io.fabric8.kubernetes.client.utils.Serialization.unmarshal(Serialization.java:240)
	... 17 more
Caused by: java.lang.ClassCastException: class java.lang.String cannot be cast to class java.util.Map (java.lang.String and java.util.Map are in module java.base of loader 'bootstrap')
	at io.fabric8.kubernetes.client.utils.Serialization.unmarshal(Serialization.java:237)
	at io.fabric8.kubernetes.client.utils.Serialization.unmarshal(Serialization.java:351)
	at io.fabric8.kubernetes.client.utils.Serialization.unmarshal(Serialization.java:287)
	at io.fabric8.kubernetes.client.utils.Serialization.unmarshal(Serialization.java:272)
	at io.kserve.serving.v1beta1.inferenceservicestatus.ModelStatus.<init>(ModelStatus.java:68)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
	at com.fasterxml.jackson.databind.introspect.AnnotatedConstructor.call(AnnotatedConstructor.java:123)
	at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createUsingDefault(StdValueInstantiator.java:278)
	... 45 more

Additional context

Potentially related to #4530

@andreaTP
Copy link
Member

andreaTP commented Feb 7, 2023

Hi @lmyslinski , thank you for taking the time to report this issue!

I'm not managing to reproduce the issue, can you please confirm that you are using the version 6.4.1 for both, the java-generator and the kubernetes-client runtime?

I do notice slight differences between the yaml snippet above and the CRD at the provided link, more specifically I see that the enums have empty strings which were not supported before #4769 .

You should be able to reproduce the issue using a script like this:

#! /bin/bash
set -x

CRD_NAME="kserve"
TMP_DIR=$(mktemp -d -t $CRD_NAME.XXXXXXX)
mkdir -p $TMP_DIR/src

jbang io.fabric8:java-generator-cli:6.4.1 -u https://github.com/kserve/kserve/releases/download/v0.10.0/kserve.yaml --target=$TMP_DIR/src

cat <<EOF >> $TMP_DIR/$CRD_NAME.java
//DEPS io.fabric8:kubernetes-client:6.4.1
//DEPS io.fabric8:generator-annotations:6.4.1
//SOURCES src/**.java
EOF

(
  cd $TMP_DIR
  jbang export portable --force $TMP_DIR/$CRD_NAME.java
  jbang --class-path $TMP_DIR/$CRD_NAME.jar -i
)

rm -rf $TMP_DIR

please make sure that the code generation successfully completes as I have been hitting a few minor issues (maybe related to #4723 ?)

after you are in the jshell commands like:

import io.kserve.serving.v1beta1.*
io.fabric8.kubernetes.client.utils.Serialization.unmarshal("{}", InferenceService.class)

should statically reproduce the issue (as it seems to be triggered during the object initialization.

Please let me know how it goes 🙏 !

@lmyslinski
Copy link
Author

Wow, thanks for looking into this - you're correct, it was a problem with version mismatch. Once I made sure the k8s client was also 6.4.1 (instead of 6.2.0), the error went away. I did run into some more problems with Jackson but after applying tweaks as described in #1308 everything worked. Thanks a lot for the help!

@andreaTP
Copy link
Member

andreaTP commented Feb 8, 2023

Nice! Happy to hear!
And thanks a lot for getting back @lmyslinski

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants