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

refactor(crd-generator): YamlDumpSettings as part of KubernetesSerialization settings #6872

Merged
merged 1 commit into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#### Improvements

* Fix #6863: ensuring SerialExecutor does not throw RejectedExecutionException to prevent unnecessary error logs
* Fix #6763: CRDGenerator: YAML output customization
* Fix #6763: (crd-generator) YAML output customization

#### Dependency Upgrade

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.client.utils.ApiVersionUtil;
import io.fabric8.kubernetes.client.utils.KubernetesSerialization;
import io.fabric8.kubernetes.client.utils.YamlDumpSettings;
import io.fabric8.kubernetes.client.utils.YamlDumpSettingsBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -183,7 +182,9 @@ public CRDGenerationInfo detailedGenerate() {

final ResolvingContext context;
if (this.objectMapper == null) {
context = ResolvingContext.defaultResolvingContext(implicitPreserveUnknownFields);
context = ResolvingContext.defaultResolvingContext(
implicitPreserveUnknownFields,
new YamlDumpSettingsBuilder().setMinimizeQuotes(minQuotes).build());
this.kubernetesSerialization = context.kubernetesSerialization;
} else {
context = new ResolvingContext(this.objectMapper, this.kubernetesSerialization, implicitPreserveUnknownFields);
Expand Down Expand Up @@ -220,8 +221,7 @@ public void emitCrd(HasMetadata crd, Set<String> dependentClassNames, CRDGenerat
final String outputName = getOutputName(crdName, version);
try (final OutputStreamWriter writer = new OutputStreamWriter(output.outputFor(outputName), StandardCharsets.UTF_8)) {
writer.write("# Generated by Fabric8 CRDGenerator, manual edits might get overwritten!\n");
YamlDumpSettings yamlSettings = new YamlDumpSettingsBuilder().setMinimizeQuotes(minQuotes).build();
String yaml = kubernetesSerialization.asYaml(crd, yamlSettings);
String yaml = kubernetesSerialization.asYaml(crd);
// strip the explicit start added by default
writer.write(yaml.substring(4));
final URI fileURI = output.crdURI(outputName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory;
import com.fasterxml.jackson.module.jsonSchema.types.ObjectSchema;
import io.fabric8.kubernetes.client.utils.KubernetesSerialization;
import io.fabric8.kubernetes.client.utils.YamlDumpSettings;
import io.fabric8.kubernetes.client.utils.YamlDumpSettingsBuilder;

import java.util.LinkedHashMap;
import java.util.Map;
Expand Down Expand Up @@ -92,15 +94,21 @@ public JsonObjectFormatVisitor expectObjectFormat(JavaType convertedType) {
final Map<String, GeneratorObjectSchema> uriToJacksonSchema;
final boolean implicitPreserveUnknownFields;

private static KubernetesSerialization KUBERNETES_SERIALIZATION;
private static ObjectMapper OBJECT_MAPPER;

public static ResolvingContext defaultResolvingContext(boolean implicitPreserveUnknownFields) {
if (KUBERNETES_SERIALIZATION == null) {
return defaultResolvingContext(implicitPreserveUnknownFields, new YamlDumpSettingsBuilder().build());
}

public static ResolvingContext defaultResolvingContext(boolean implicitPreserveUnknownFields,
YamlDumpSettings yamlDumpSettings) {
if (OBJECT_MAPPER == null) {
OBJECT_MAPPER = new ObjectMapper();
KUBERNETES_SERIALIZATION = new KubernetesSerialization(OBJECT_MAPPER, false);
}
return new ResolvingContext(OBJECT_MAPPER, KUBERNETES_SERIALIZATION, implicitPreserveUnknownFields);
return new ResolvingContext(
OBJECT_MAPPER,
new KubernetesSerialization(OBJECT_MAPPER, false, yamlDumpSettings),
implicitPreserveUnknownFields);
}

public ResolvingContext forkContext() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public class KubernetesSerialization {
private final UnmatchedFieldTypeModule unmatchedFieldTypeModule = new UnmatchedFieldTypeModule();
private KubernetesDeserializer kubernetesDeserializer;
private final boolean searchClassloaders;
private final YamlDumpSettings yamlDumpSettings;

/**
* Creates a new instance with a fresh ObjectMapper
Expand All @@ -81,11 +82,25 @@ public KubernetesSerialization() {
* Creates a new instance with the given ObjectMapper, which will be configured for use for
* kubernetes resource serialization / deserialization.
*
* @param searchClassloaders if {@link KubernetesResource} should be automatically discovered via {@link ServiceLoader}
* @param mapper the ObjectMapper to use.
* @param searchClassloaders if {@link KubernetesResource} should be automatically discovered via {@link ServiceLoader}.
*/
public KubernetesSerialization(ObjectMapper mapper, boolean searchClassloaders) {
this(mapper, searchClassloaders, new YamlDumpSettingsBuilder().build());
}

/**
* Creates a new instance with the given ObjectMapper, which will be configured for use for
* kubernetes resource serialization / deserialization.
*
* @param mapper the ObjectMapper to use.
* @param searchClassloaders if {@link KubernetesResource} should be automatically discovered via {@link ServiceLoader}.
* @param yamlDumpSettings configuration for YAML serialization.
*/
public KubernetesSerialization(ObjectMapper mapper, boolean searchClassloaders, YamlDumpSettings yamlDumpSettings) {
this.mapper = mapper;
this.searchClassloaders = searchClassloaders;
this.yamlDumpSettings = yamlDumpSettings;
configureMapper(mapper);
}

Expand Down Expand Up @@ -188,23 +203,6 @@ public <T> String asJson(T object) {
* @return a String containing a JSON representation of the provided object.
*/
public <T> String asYaml(T object) {
return asYaml(object, new YamlDumpSettingsBuilder().build());
}

/**
* Returns a YAML representation of the given object.
*
* <p>
* If the provided object contains a JsonAnyGetter annotated method with a Map that contains an entry that
* overrides a field of the provided object, the Map entry will take precedence upon serialization. Properties won't
* be duplicated.
*
* @param object the object to serialize.
* @param yamlDumpSettings configuration for YAML serialization.
* @param <T> the type of the object being serialized.
* @return a String containing a JSON representation of the provided object.
*/
public <T> String asYaml(T object, YamlDumpSettings yamlDumpSettings) {
DumpSettings settings = DumpSettings.builder()
.setExplicitStart(true).setDefaultFlowStyle(FlowStyle.BLOCK).build();
final Dump yaml = new Dump(settings, new StandardRepresenter(settings) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
*/
public class YamlDumpSettings {

private boolean minQuotes;
private final boolean minQuotes;

YamlDumpSettings(boolean minQuotes) {
this.minQuotes = minQuotes;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,7 @@
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static org.assertj.core.api.Assertions.assertThat;
Expand Down Expand Up @@ -71,29 +67,6 @@ void withRegisteredKubernetesResourceShouldDeserializeToPod() {
.isInstanceOf(io.fabric8.kubernetes.api.model.Pod.class);
}

@Test
void asYaml() throws Exception {
final String input = readYamlToString("/serialization/test-crd-schema.yml");
final CustomResourceDefinition crd = Serialization.unmarshal(input, CustomResourceDefinition.class);

String result = kubernetesSerialization.asYaml(crd);
assertThat(result).asString().contains("\"widgets.test.fabric8.io\"");

result = kubernetesSerialization.asYaml(crd, new YamlDumpSettingsBuilder().build());
assertThat(result).asString().contains("\"widgets.test.fabric8.io\"");

result = kubernetesSerialization.asYaml(crd, new YamlDumpSettingsBuilder().setMinimizeQuotes(true).build());
assertThat(result).asString().contains("widgets.test.fabric8.io").doesNotContain("\"widgets.test.fabric8.io\"");
}

private String readYamlToString(String path) throws IOException {
return Files.readAllLines(
new File(KubernetesSerializationTest.class.getResource(path).getFile()).toPath(), StandardCharsets.UTF_8)
.stream()
.filter(line -> !line.startsWith("#"))
.collect(Collectors.joining("\n"));
}

@ParameterizedTest(name = "{index}: {0} {1} deserializes to {2}")
@MethodSource("sameGVK")
void withCollidingRegisteredKubernetesResourceShouldDeserializeAppropriate(
Expand All @@ -116,6 +89,42 @@ private Stream<Arguments> sameGVK() {
}
}

@Nested
class AsYaml {

private CustomResourceDefinition inputResource;

@BeforeEach
void loadYamlAsString() throws IOException {
try (var is = KubernetesSerializationTest.class.getResourceAsStream("/serialization/test-crd-schema.yml")) {
inputResource = Serialization.unmarshal(new String(is.readAllBytes()), CustomResourceDefinition.class);
}
}

@Test
void asYamlWithDefaults() {
assertThat(new KubernetesSerialization().asYaml(inputResource))
.contains("\"widgets.test.fabric8.io\"");
}

@Test
void asYamlWithDefaultYamlDumpSettings() {
kubernetesSerialization = new KubernetesSerialization(new ObjectMapper(), true,
new YamlDumpSettingsBuilder().build());
assertThat(kubernetesSerialization.asYaml(inputResource))
.contains("\"widgets.test.fabric8.io\"");
}

@Test
void asYamlWithDefaultYamlDumpSettingsMinimizeQuotes() {
kubernetesSerialization = new KubernetesSerialization(new ObjectMapper(), true,
new YamlDumpSettingsBuilder().setMinimizeQuotes(true).build());
assertThat(kubernetesSerialization.asYaml(inputResource))
.contains("widgets.test.fabric8.io");
}

}

@Version("v1")
@Group("custom.core.kubernetes.io")
@JsonDeserialize(using = JsonDeserializer.None.class)
Expand Down
Loading