From d9f03a4b1a054bfa11e1f1695c9804fed068b428 Mon Sep 17 00:00:00 2001 From: Steve Hawkins Date: Thu, 21 Jul 2022 22:06:13 -0400 Subject: [PATCH] fix #3972: moving template handling to a custom deserializer --- .../client/utils/Serialization.java | 16 +----- .../kubernetes-model-common/pom.xml | 4 ++ .../kubernetes/model/annotation/Resource.java | 36 ++++++++++++ .../jackson}/BeanPropertyWriterDelegate.java | 10 ++-- .../SettableBeanPropertyDelegate.java | 32 +++++------ .../jackson}/UnmatchedFieldTypeModule.java | 40 ++++++++++--- .../SettableBeanPropertyDelegateTest.java | 33 +++++------ .../UnmatchedFieldTypeModuleTest.java | 3 +- .../internal/KubernetesDeserializer.java | 20 +------ .../openshift-model/cmd/generate/generate.go | 12 ++++ .../fabric8/openshift/api/model/Template.java | 2 +- .../api/model/TemplateDeserializer.java | 56 +++++++++++++++++++ .../main/resources/schema/kube-schema.json | 3 +- .../resources/schema/validation-schema.json | 3 +- 14 files changed, 189 insertions(+), 81 deletions(-) create mode 100644 kubernetes-model-generator/kubernetes-model-common/src/main/java/io/fabric8/kubernetes/model/annotation/Resource.java rename {kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/serialization => kubernetes-model-generator/kubernetes-model-common/src/main/java/io/fabric8/kubernetes/model/jackson}/BeanPropertyWriterDelegate.java (88%) rename {kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/serialization => kubernetes-model-generator/kubernetes-model-common/src/main/java/io/fabric8/kubernetes/model/jackson}/SettableBeanPropertyDelegate.java (81%) rename {kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/serialization => kubernetes-model-generator/kubernetes-model-common/src/main/java/io/fabric8/kubernetes/model/jackson}/UnmatchedFieldTypeModule.java (72%) rename {kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/serialization => kubernetes-model-generator/kubernetes-model-common/src/test/java/io/fabric8/kubernetes/model/jackson}/SettableBeanPropertyDelegateTest.java (85%) rename {kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/serialization => kubernetes-model-generator/kubernetes-model-common/src/test/java/io/fabric8/kubernetes/model/jackson}/UnmatchedFieldTypeModuleTest.java (98%) create mode 100644 kubernetes-model-generator/openshift-model/src/main/java/io/fabric8/openshift/api/model/TemplateDeserializer.java diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/Serialization.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/Serialization.java index e4564c2cf27..5e0f53810f0 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/Serialization.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/Serialization.java @@ -21,10 +21,9 @@ import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import io.fabric8.kubernetes.api.model.GenericKubernetesResource; import io.fabric8.kubernetes.api.model.KubernetesResource; import io.fabric8.kubernetes.client.KubernetesClientException; -import io.fabric8.kubernetes.client.utils.serialization.UnmatchedFieldTypeModule; +import io.fabric8.kubernetes.model.jackson.UnmatchedFieldTypeModule; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; @@ -422,17 +421,8 @@ public static T clone(T resource) { // if full serialization seems too expensive, there is also //return (T) JSON_MAPPER.convertValue(resource, resource.getClass()); try { - return JSON_MAPPER.readValue( - JSON_MAPPER.writeValueAsString(resource), new TypeReference() { - @Override - public Type getType() { - if (resource instanceof KubernetesResource) { - // Force KubernetesResource so that the KubernetesDeserializer takes over any resource configured deserializer - return resource instanceof GenericKubernetesResource ? resource.getClass() : KubernetesResource.class; - } - return resource.getClass(); - } - }); + return (T) JSON_MAPPER.readValue( + JSON_MAPPER.writeValueAsString(resource), resource.getClass()); } catch (JsonProcessingException e) { throw new IllegalStateException(e); } diff --git a/kubernetes-model-generator/kubernetes-model-common/pom.xml b/kubernetes-model-generator/kubernetes-model-common/pom.xml index ac5cdb37b36..f03cb6849b3 100644 --- a/kubernetes-model-generator/kubernetes-model-common/pom.xml +++ b/kubernetes-model-generator/kubernetes-model-common/pom.xml @@ -33,6 +33,10 @@ com.fasterxml.jackson.core jackson-databind + + org.slf4j + slf4j-api + diff --git a/kubernetes-model-generator/kubernetes-model-common/src/main/java/io/fabric8/kubernetes/model/annotation/Resource.java b/kubernetes-model-generator/kubernetes-model-common/src/main/java/io/fabric8/kubernetes/model/annotation/Resource.java new file mode 100644 index 00000000000..b8ab6bce0cf --- /dev/null +++ b/kubernetes-model-generator/kubernetes-model-common/src/main/java/io/fabric8/kubernetes/model/annotation/Resource.java @@ -0,0 +1,36 @@ +package io.fabric8.kubernetes.model.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Target({ TYPE }) +@Retention(RUNTIME) +public @interface Resource { + + /** + * Allows to specify which API group the annotated class is defined under. Together with version, this + * determines the `apiVersion` field associated with the annotated resource. + * See https://kubernetes.io/docs/reference/using-api/#api-groups for more details. + */ + String group(); + + /** + * Specifies the kind value should be used to refer to instance of the annotated class. If not provided, + * a default value is computed based on the annotated class name. See HasMetadata#getKind for more details. + */ + String kind() default ""; + + /** + * Specifies the plural form associated with a Custom Resource. If not provided, it will default to a computed value. + * See HasMetadata#getPlural for more details. + */ + String plural() default ""; + + boolean namespaced() default true; + + String version(); + +} diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/serialization/BeanPropertyWriterDelegate.java b/kubernetes-model-generator/kubernetes-model-common/src/main/java/io/fabric8/kubernetes/model/jackson/BeanPropertyWriterDelegate.java similarity index 88% rename from kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/serialization/BeanPropertyWriterDelegate.java rename to kubernetes-model-generator/kubernetes-model-common/src/main/java/io/fabric8/kubernetes/model/jackson/BeanPropertyWriterDelegate.java index 0a4d61c1584..7f73c2a76bc 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/serialization/BeanPropertyWriterDelegate.java +++ b/kubernetes-model-generator/kubernetes-model-common/src/main/java/io/fabric8/kubernetes/model/jackson/BeanPropertyWriterDelegate.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.fabric8.kubernetes.client.utils.serialization; +package io.fabric8.kubernetes.model.jackson; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.SerializerProvider; @@ -29,10 +29,12 @@ * Variant of {@link BeanPropertyWriter} which prevents property values present in the {@link AnnotatedMember} anyGetter * to be serialized twice. * - *

Any property that's present in the anyGetter is ignored upon serialization. The values present in the anyGetter + *

+ * Any property that's present in the anyGetter is ignored upon serialization. The values present in the anyGetter * take precedence over those stored in the Bean's fields. * - *

This BeanPropertyWriter implementation is intended to be used in combination with + *

+ * This BeanPropertyWriter implementation is intended to be used in combination with * the {@link SettableBeanPropertyDelegate} to allow the propagation of deserialized properties that don't match the * target field types. */ @@ -64,7 +66,7 @@ public void serializeAsField(Object bean, JsonGenerator gen, SerializerProvider delegate.serializeAsField(bean, gen, prov); } else if (Boolean.TRUE.equals(logDuplicateWarning.get())) { logger.warn("Value in field '{}' ignored in favor of value in additionalProperties ({}) for {}", - delegate.getName(), valueInAnyGetter, bean.getClass().getName()); + delegate.getName(), valueInAnyGetter, bean.getClass().getName()); } } } diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/serialization/SettableBeanPropertyDelegate.java b/kubernetes-model-generator/kubernetes-model-common/src/main/java/io/fabric8/kubernetes/model/jackson/SettableBeanPropertyDelegate.java similarity index 81% rename from kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/serialization/SettableBeanPropertyDelegate.java rename to kubernetes-model-generator/kubernetes-model-common/src/main/java/io/fabric8/kubernetes/model/jackson/SettableBeanPropertyDelegate.java index 13c86bf66f9..45c4a8c3c2d 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/serialization/SettableBeanPropertyDelegate.java +++ b/kubernetes-model-generator/kubernetes-model-common/src/main/java/io/fabric8/kubernetes/model/jackson/SettableBeanPropertyDelegate.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.fabric8.kubernetes.client.utils.serialization; +package io.fabric8.kubernetes.model.jackson; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationConfig; @@ -25,7 +25,6 @@ import com.fasterxml.jackson.databind.deser.SettableBeanProperty; import com.fasterxml.jackson.databind.exc.MismatchedInputException; import com.fasterxml.jackson.databind.introspect.AnnotatedMember; -import io.fabric8.kubernetes.internal.KubernetesDeserializer; import java.io.IOException; import java.lang.annotation.Annotation; @@ -34,20 +33,21 @@ /** * This concrete sub-class encapsulates a {@link SettableBeanProperty} delegate that is always tried first. * - *

A fall-back mechanism is implemented in the deserializeAndSet methods to allow field values that don't match the + *

+ * A fall-back mechanism is implemented in the deserializeAndSet methods to allow field values that don't match the * target type to be preserved in the anySetter method if exists. */ public class SettableBeanPropertyDelegate extends SettableBeanProperty { private final SettableBeanProperty delegate; private final SettableAnyProperty anySetter; - private final transient Supplier restrictToTemplates; + private final transient Supplier useAnySetter; - SettableBeanPropertyDelegate(SettableBeanProperty delegate, SettableAnyProperty anySetter, Supplier restrictToTemplates) { + SettableBeanPropertyDelegate(SettableBeanProperty delegate, SettableAnyProperty anySetter, Supplier useAnySetter) { super(delegate); this.delegate = delegate; this.anySetter = anySetter; - this.restrictToTemplates = restrictToTemplates; + this.useAnySetter = useAnySetter; } /** @@ -55,7 +55,7 @@ public class SettableBeanPropertyDelegate extends SettableBeanProperty { */ @Override public SettableBeanProperty withValueDeserializer(JsonDeserializer deser) { - return new SettableBeanPropertyDelegate(delegate.withValueDeserializer(deser), anySetter, restrictToTemplates); + return new SettableBeanPropertyDelegate(delegate.withValueDeserializer(deser), anySetter, useAnySetter); } /** @@ -63,7 +63,7 @@ public SettableBeanProperty withValueDeserializer(JsonDeserializer deser) { */ @Override public SettableBeanProperty withName(PropertyName newName) { - return new SettableBeanPropertyDelegate(delegate.withName(newName), anySetter, restrictToTemplates); + return new SettableBeanPropertyDelegate(delegate.withName(newName), anySetter, useAnySetter); } /** @@ -71,7 +71,7 @@ public SettableBeanProperty withName(PropertyName newName) { */ @Override public SettableBeanProperty withNullProvider(NullValueProvider nva) { - return new SettableBeanPropertyDelegate(delegate.withNullProvider(nva), anySetter, restrictToTemplates); + return new SettableBeanPropertyDelegate(delegate.withNullProvider(nva), anySetter, useAnySetter); } /** @@ -117,13 +117,16 @@ public boolean isIgnorable() { /** * Method called to deserialize appropriate value, given parser (and context), and set it using appropriate mechanism. * - *

Deserialization is first tried through the delegate. In case a {@link MismatchedInputException} is caught, + *

+ * Deserialization is first tried through the delegate. In case a {@link MismatchedInputException} is caught, * the field is stored in the bean's {@link SettableAnyProperty} anySetter field if it exists. * - *

This allows deserialization processes propagate values that initially don't match the target bean type for the + *

+ * This allows deserialization processes propagate values that initially don't match the target bean type for the * applicable field. * - *

An example use-case is the use of placeholders (e.g. {@code ${aValue}}) in a field. + *

+ * An example use-case is the use of placeholders (e.g. {@code ${aValue}}) in a field. */ @Override public void deserializeAndSet(JsonParser p, DeserializationContext ctxt, Object instance) throws IOException { @@ -171,9 +174,6 @@ private boolean shouldUseAnySetter() { if (anySetter == null) { return false; } - if (Boolean.TRUE.equals(restrictToTemplates.get()) ) { - return KubernetesDeserializer.isInTemplate(); - } - return true; + return Boolean.TRUE.equals(useAnySetter.get()); } } diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/serialization/UnmatchedFieldTypeModule.java b/kubernetes-model-generator/kubernetes-model-common/src/main/java/io/fabric8/kubernetes/model/jackson/UnmatchedFieldTypeModule.java similarity index 72% rename from kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/serialization/UnmatchedFieldTypeModule.java rename to kubernetes-model-generator/kubernetes-model-common/src/main/java/io/fabric8/kubernetes/model/jackson/UnmatchedFieldTypeModule.java index 6df84461d8b..83d28f72d91 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/serialization/UnmatchedFieldTypeModule.java +++ b/kubernetes-model-generator/kubernetes-model-common/src/main/java/io/fabric8/kubernetes/model/jackson/UnmatchedFieldTypeModule.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.fabric8.kubernetes.client.utils.serialization; +package io.fabric8.kubernetes.model.jackson; import com.fasterxml.jackson.databind.BeanDescription; import com.fasterxml.jackson.databind.DeserializationConfig; @@ -40,6 +40,8 @@ public class UnmatchedFieldTypeModule extends SimpleModule { private boolean logWarnings; private boolean restrictToTemplates; + private static final ThreadLocal IN_TEMPLATE = ThreadLocal.withInitial(() -> false); + public UnmatchedFieldTypeModule() { this(true, true); } @@ -50,19 +52,22 @@ public UnmatchedFieldTypeModule(boolean logWarnings, boolean restrictToTemplates setDeserializerModifier(new BeanDeserializerModifier() { @Override - public BeanDeserializerBuilder updateBuilder(DeserializationConfig config, BeanDescription beanDesc, BeanDeserializerBuilder builder) { - builder.getProperties().forEachRemaining(p -> - builder.addOrReplaceProperty(new SettableBeanPropertyDelegate(p, builder.getAnySetter(), UnmatchedFieldTypeModule.this::isRestrictToTemplates) { - }, true)); + public BeanDeserializerBuilder updateBuilder(DeserializationConfig config, BeanDescription beanDesc, + BeanDeserializerBuilder builder) { + builder.getProperties().forEachRemaining(p -> builder.addOrReplaceProperty( + new SettableBeanPropertyDelegate(p, builder.getAnySetter(), UnmatchedFieldTypeModule.this::useAnySetter) { + }, true)); return builder; } }); setSerializerModifier(new BeanSerializerModifier() { @Override - public BeanSerializerBuilder updateBuilder(SerializationConfig config, BeanDescription beanDesc, BeanSerializerBuilder builder) { - builder.setProperties(builder.getProperties().stream().map(p -> - new BeanPropertyWriterDelegate(p, builder.getBeanDescription().findAnyGetter(), UnmatchedFieldTypeModule.this::isLogWarnings)) - .collect(Collectors.toList())); + public BeanSerializerBuilder updateBuilder(SerializationConfig config, BeanDescription beanDesc, + BeanSerializerBuilder builder) { + builder.setProperties(builder.getProperties().stream() + .map(p -> new BeanPropertyWriterDelegate(p, builder.getBeanDescription().findAnyGetter(), + UnmatchedFieldTypeModule.this::isLogWarnings)) + .collect(Collectors.toList())); return builder; } }); @@ -85,6 +90,10 @@ boolean isRestrictToTemplates() { return restrictToTemplates; } + boolean useAnySetter() { + return !restrictToTemplates || isInTemplate(); + } + /** * Sets if the DeserializerModifier should only be applied to Templates or object trees contained in Templates. * @@ -93,4 +102,17 @@ boolean isRestrictToTemplates() { public void setRestrictToTemplates(boolean restrictToTemplates) { this.restrictToTemplates = restrictToTemplates; } + + public static boolean isInTemplate() { + return Boolean.TRUE.equals(IN_TEMPLATE.get()); + } + + public static void setInTemplate() { + IN_TEMPLATE.set(true); + } + + public static void removeInTemplate() { + IN_TEMPLATE.remove(); + } + } diff --git a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/serialization/SettableBeanPropertyDelegateTest.java b/kubernetes-model-generator/kubernetes-model-common/src/test/java/io/fabric8/kubernetes/model/jackson/SettableBeanPropertyDelegateTest.java similarity index 85% rename from kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/serialization/SettableBeanPropertyDelegateTest.java rename to kubernetes-model-generator/kubernetes-model-common/src/test/java/io/fabric8/kubernetes/model/jackson/SettableBeanPropertyDelegateTest.java index fe1b0243784..adc9998d130 100644 --- a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/serialization/SettableBeanPropertyDelegateTest.java +++ b/kubernetes-model-generator/kubernetes-model-common/src/test/java/io/fabric8/kubernetes/model/jackson/SettableBeanPropertyDelegateTest.java @@ -13,12 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.fabric8.kubernetes.client.utils.serialization; +package io.fabric8.kubernetes.model.jackson; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.deser.SettableAnyProperty; import com.fasterxml.jackson.databind.deser.SettableBeanProperty; import com.fasterxml.jackson.databind.exc.MismatchedInputException; +import io.fabric8.kubernetes.model.jackson.SettableBeanPropertyDelegate; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -46,7 +47,7 @@ class SettableBeanPropertyDelegateTest { void setUp() { delegateMock = mock(SettableBeanProperty.class, RETURNS_DEEP_STUBS); anySetterMock = mock(SettableAnyProperty.class); - settableBeanPropertyDelegate = new SettableBeanPropertyDelegate(delegateMock, anySetterMock, () -> false); + settableBeanPropertyDelegate = new SettableBeanPropertyDelegate(delegateMock, anySetterMock, () -> true); } @Test @@ -58,10 +59,10 @@ void withValueDeserializer() { final SettableBeanProperty result = settableBeanPropertyDelegate.withValueDeserializer(null); // Then assertThat(result) - .isInstanceOf(SettableBeanPropertyDelegate.class) - .isNotSameAs(settableBeanPropertyDelegate) - .hasFieldOrPropertyWithValue("anySetter", anySetterMock) - .hasFieldOrPropertyWithValue("delegate", delegateMock); + .isInstanceOf(SettableBeanPropertyDelegate.class) + .isNotSameAs(settableBeanPropertyDelegate) + .hasFieldOrPropertyWithValue("anySetter", anySetterMock) + .hasFieldOrPropertyWithValue("delegate", delegateMock); } @Test @@ -73,10 +74,10 @@ void withName() { final SettableBeanProperty result = settableBeanPropertyDelegate.withName(null); // Then assertThat(result) - .isInstanceOf(SettableBeanPropertyDelegate.class) - .isNotSameAs(settableBeanPropertyDelegate) - .hasFieldOrPropertyWithValue("anySetter", anySetterMock) - .hasFieldOrPropertyWithValue("delegate", delegateMock); + .isInstanceOf(SettableBeanPropertyDelegate.class) + .isNotSameAs(settableBeanPropertyDelegate) + .hasFieldOrPropertyWithValue("anySetter", anySetterMock) + .hasFieldOrPropertyWithValue("delegate", delegateMock); } @Test @@ -88,10 +89,10 @@ void withNullProvider() { final SettableBeanProperty result = settableBeanPropertyDelegate.withNullProvider(null); // Then assertThat(result) - .isInstanceOf(SettableBeanPropertyDelegate.class) - .isNotSameAs(settableBeanPropertyDelegate) - .hasFieldOrPropertyWithValue("anySetter", anySetterMock) - .hasFieldOrPropertyWithValue("delegate", delegateMock); + .isInstanceOf(SettableBeanPropertyDelegate.class) + .isNotSameAs(settableBeanPropertyDelegate) + .hasFieldOrPropertyWithValue("anySetter", anySetterMock) + .hasFieldOrPropertyWithValue("delegate", delegateMock); } @Test @@ -186,9 +187,9 @@ void deserializeSetAndReturnWithException() throws IOException { final Object instance = new Object(); when(delegateMock.getName()).thenReturn("the-property"); when(delegateMock.deserializeSetAndReturn(any(), any(), eq(instance))) - .thenThrow(MismatchedInputException.from(null, Integer.class, "The Mocked Exception")); + .thenThrow(MismatchedInputException.from(null, Integer.class, "The Mocked Exception")); doThrow(MismatchedInputException.from(null, Integer.class, "The Mocked Exception")) - .when(delegateMock).deserializeAndSet(any(), any(), eq(instance)); + .when(delegateMock).deserializeAndSet(any(), any(), eq(instance)); // When final Object result = settableBeanPropertyDelegate.deserializeSetAndReturn(mock(JsonParser.class), null, instance); // Then diff --git a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/serialization/UnmatchedFieldTypeModuleTest.java b/kubernetes-model-generator/kubernetes-model-common/src/test/java/io/fabric8/kubernetes/model/jackson/UnmatchedFieldTypeModuleTest.java similarity index 98% rename from kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/serialization/UnmatchedFieldTypeModuleTest.java rename to kubernetes-model-generator/kubernetes-model-common/src/test/java/io/fabric8/kubernetes/model/jackson/UnmatchedFieldTypeModuleTest.java index 6b4deb70ec4..cda92dfd393 100644 --- a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/serialization/UnmatchedFieldTypeModuleTest.java +++ b/kubernetes-model-generator/kubernetes-model-common/src/test/java/io/fabric8/kubernetes/model/jackson/UnmatchedFieldTypeModuleTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.fabric8.kubernetes.client.utils.serialization; +package io.fabric8.kubernetes.model.jackson; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -22,6 +22,7 @@ import io.fabric8.kubernetes.api.model.ConfigMapBuilder; import io.fabric8.kubernetes.api.model.KubernetesResource; import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.fabric8.kubernetes.model.jackson.UnmatchedFieldTypeModule; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/internal/KubernetesDeserializer.java b/kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/internal/KubernetesDeserializer.java index 9034cbc9187..8e684ee17c6 100644 --- a/kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/internal/KubernetesDeserializer.java +++ b/kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/internal/KubernetesDeserializer.java @@ -75,18 +75,11 @@ public boolean equals(Object obj) { } - private static final String TEMPLATE_CLASS_NAME = "io.fabric8.openshift.api.model.Template"; private static final String KIND = "kind"; private static final String API_VERSION = "apiVersion"; private static final Mapping mapping = new Mapping(); - private static final ThreadLocal IN_TEMPLATE = ThreadLocal.withInitial(() -> false); - - public static boolean isInTemplate() { - return IN_TEMPLATE.get(); - } - @Override public KubernetesResource deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { JsonNode node = jp.readValueAsTree(); @@ -121,18 +114,7 @@ private static KubernetesResource fromObjectNode(JsonParser jp, JsonNode node) t if (resourceType == null) { return jp.getCodec().treeToValue(node, GenericKubernetesResource.class); } else if (KubernetesResource.class.isAssignableFrom(resourceType)) { - boolean inTemplate = false; - if (TEMPLATE_CLASS_NAME.equals(resourceType.getName())) { - inTemplate = true; - IN_TEMPLATE.set(true); - } - try { - return jp.getCodec().treeToValue(node, resourceType); - } finally { - if (inTemplate) { - IN_TEMPLATE.remove(); - } - } + return jp.getCodec().treeToValue(node, resourceType); } } return null; diff --git a/kubernetes-model-generator/openshift-model/cmd/generate/generate.go b/kubernetes-model-generator/openshift-model/cmd/generate/generate.go index ae248eafc27..cc9e3c5cc1f 100644 --- a/kubernetes-model-generator/openshift-model/cmd/generate/generate.go +++ b/kubernetes-model-generator/openshift-model/cmd/generate/generate.go @@ -198,6 +198,18 @@ func main() { fmt.Fprintf(os.Stderr, "An error occurred: %v", err) return } + + serdes := map[string]*schemagen.JavaSerDeDescriptor{ + "os_template_Template": &schemagen.JavaSerDeDescriptor{ + Deserializer: "io.fabric8.openshift.api.model.TemplateDeserializer.class", + }, + } + + for definitionKey, descriptor := range serdes { + val := schema.Definitions[definitionKey] + val.JavaSerDeDescriptor = descriptor + schema.Definitions[definitionKey] = val + } args := os.Args[1:] if len(args) < 1 || args[0] != "validation" { diff --git a/kubernetes-model-generator/openshift-model/src/generated/java/io/fabric8/openshift/api/model/Template.java b/kubernetes-model-generator/openshift-model/src/generated/java/io/fabric8/openshift/api/model/Template.java index 56f9ae58551..1f3ccfa07a6 100644 --- a/kubernetes-model-generator/openshift-model/src/generated/java/io/fabric8/openshift/api/model/Template.java +++ b/kubernetes-model-generator/openshift-model/src/generated/java/io/fabric8/openshift/api/model/Template.java @@ -34,7 +34,7 @@ import lombok.ToString; import lombok.experimental.Accessors; -@JsonDeserialize(using = com.fasterxml.jackson.databind.JsonDeserializer.None.class) +@JsonDeserialize(using = io.fabric8.openshift.api.model.TemplateDeserializer.class) @JsonInclude(JsonInclude.Include.NON_NULL) @JsonPropertyOrder({ "apiVersion", diff --git a/kubernetes-model-generator/openshift-model/src/main/java/io/fabric8/openshift/api/model/TemplateDeserializer.java b/kubernetes-model-generator/openshift-model/src/main/java/io/fabric8/openshift/api/model/TemplateDeserializer.java new file mode 100644 index 00000000000..3198f698ada --- /dev/null +++ b/kubernetes-model-generator/openshift-model/src/main/java/io/fabric8/openshift/api/model/TemplateDeserializer.java @@ -0,0 +1,56 @@ +/** + * Copyright (C) 2015 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fabric8.openshift.api.model; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.deser.BeanDeserializer; +import com.fasterxml.jackson.databind.deser.ResolvableDeserializer; +import io.fabric8.kubernetes.model.jackson.UnmatchedFieldTypeModule; + +import java.io.IOException; + +/** + * Essentially wraps a {@link BeanDeserializer} to allow for unmatched fields + */ +public class TemplateDeserializer extends JsonDeserializer