Skip to content

Commit 7b55de6

Browse files
feat(crd-generator): enable configuration of whether quotes are used in generated yaml (6773)
Enable configuration of if quotes are used in generated yaml Signed-off-by: MichaelMorris <[email protected]> --- Fix formatting errors Signed-off-by: MichaelMorris <[email protected]> --- Added testcases to CRDGenerator Signed-off-by: MichaelMorris <[email protected]> --- Updated as per review comments Added builder parameter to asYaml() instead of minQuotes to enable future expansion Added to CLI Signed-off-by: MichaelMorris <[email protected]> --- Updated for review comment Signed-off-by: MichaelMorris <[email protected]> --- Merge branch 'main' into issue-6763
1 parent 91d2e87 commit 7b55de6

File tree

10 files changed

+245
-2
lines changed

10 files changed

+245
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#### Improvements
88

99
* Fix #6863: ensuring SerialExecutor does not throw RejectedExecutionException to prevent unnecessary error logs
10+
* Fix #6763: CRDGenerator: YAML output customization
1011

1112
#### Dependency Upgrade
1213

crd-generator/api-v2/src/main/java/io/fabric8/crdv2/generator/CRDGenerator.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import io.fabric8.kubernetes.api.model.HasMetadata;
2121
import io.fabric8.kubernetes.client.utils.ApiVersionUtil;
2222
import io.fabric8.kubernetes.client.utils.KubernetesSerialization;
23+
import io.fabric8.kubernetes.client.utils.YamlDumpSettings;
24+
import io.fabric8.kubernetes.client.utils.YamlDumpSettingsBuilder;
2325
import org.slf4j.Logger;
2426
import org.slf4j.LoggerFactory;
2527

@@ -54,6 +56,7 @@ public class CRDGenerator {
5456
private ObjectMapper objectMapper;
5557
private KubernetesSerialization kubernetesSerialization;
5658
private Map<String, CustomResourceInfo> infos;
59+
private boolean minQuotes = false;
5760

5861
public CRDGenerator inOutputDir(File outputDir) {
5962
output = new DirCRDOutput(outputDir);
@@ -70,6 +73,11 @@ public CRDGenerator withImplicitPreserveUnknownFields(boolean implicitPreserveUn
7073
return this;
7174
}
7275

76+
public CRDGenerator withMinQuotes(boolean minQuotes) {
77+
this.minQuotes = minQuotes;
78+
return this;
79+
}
80+
7381
public CRDGenerator withParallelGenerationEnabled(boolean parallel) {
7482
this.parallel = parallel;
7583
return this;
@@ -212,7 +220,8 @@ public void emitCrd(HasMetadata crd, Set<String> dependentClassNames, CRDGenerat
212220
final String outputName = getOutputName(crdName, version);
213221
try (final OutputStreamWriter writer = new OutputStreamWriter(output.outputFor(outputName), StandardCharsets.UTF_8)) {
214222
writer.write("# Generated by Fabric8 CRDGenerator, manual edits might get overwritten!\n");
215-
String yaml = kubernetesSerialization.asYaml(crd);
223+
YamlDumpSettings yamlSettings = new YamlDumpSettingsBuilder().setMinimizeQuotes(minQuotes).build();
224+
String yaml = kubernetesSerialization.asYaml(crd, yamlSettings);
216225
// strip the explicit start added by default
217226
writer.write(yaml.substring(4));
218227
final URI fileURI = output.crdURI(outputName);
@@ -284,4 +293,5 @@ public URI crdURI(String crdName) {
284293
return getCRDFile(crdName).toURI();
285294
}
286295
}
296+
287297
}

crd-generator/api-v2/src/test/java/io/fabric8/crdv2/generator/CRDGeneratorTest.java

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060

6161
import static io.fabric8.crdv2.generator.CRDGeneratorAssertions.assertFileEquals;
6262
import static org.junit.jupiter.api.Assertions.assertEquals;
63+
import static org.junit.jupiter.api.Assertions.assertFalse;
6364
import static org.junit.jupiter.api.Assertions.assertNotNull;
6465
import static org.junit.jupiter.api.Assertions.assertNull;
6566
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -103,6 +104,7 @@ void choosingCRDVersionsShouldWork() {
103104
@Test
104105
void addingCustomResourceInfosShouldWork() {
105106
CRDGenerator generator = newCRDGenerator();
107+
106108
assertTrue(generator.getCustomResourceInfos().isEmpty());
107109

108110
generator.customResourceClasses();
@@ -477,6 +479,66 @@ void checkGenerationIsDeterministic() throws Exception {
477479
assertTrue(outputDir.delete());
478480
}
479481

482+
@Test
483+
void checkMinQuotesDefault() throws Exception {
484+
final File outputDir = Files.createTempDirectory("crd-").toFile();
485+
final String crdName = CustomResourceInfo.fromClass(Complex.class).crdName();
486+
CRDGenerationInfo crdInfo = newCRDGenerator()
487+
.inOutputDir(outputDir)
488+
.forCRDVersions("v1")
489+
.customResourceClasses(Complex.class)
490+
.detailedGenerate();
491+
492+
File crdFile = new File(crdInfo.getCRDInfos(crdName).get("v1").getFilePath());
493+
String crd = Files.readString(crdFile.toPath());
494+
assertTrue(crd.contains("\"complexkinds.example.com\""));
495+
496+
// only delete the generated files if the test is successful
497+
assertTrue(crdFile.delete());
498+
assertTrue(outputDir.delete());
499+
}
500+
501+
@Test
502+
void checkMinQuotesFalse() throws Exception {
503+
final File outputDir = Files.createTempDirectory("crd-").toFile();
504+
final String crdName = CustomResourceInfo.fromClass(Complex.class).crdName();
505+
CRDGenerationInfo crdInfo = newCRDGenerator()
506+
.inOutputDir(outputDir)
507+
.forCRDVersions("v1")
508+
.customResourceClasses(Complex.class)
509+
.withMinQuotes(false)
510+
.detailedGenerate();
511+
512+
File crdFile = new File(crdInfo.getCRDInfos(crdName).get("v1").getFilePath());
513+
String crd = Files.readString(crdFile.toPath());
514+
assertTrue(crd.contains("\"complexkinds.example.com\""));
515+
516+
// only delete the generated files if the test is successful
517+
assertTrue(crdFile.delete());
518+
assertTrue(outputDir.delete());
519+
}
520+
521+
@Test
522+
void checkMinQuotesTrue() throws Exception {
523+
final File outputDir = Files.createTempDirectory("crd-").toFile();
524+
final String crdName = CustomResourceInfo.fromClass(Complex.class).crdName();
525+
CRDGenerationInfo crdInfo = newCRDGenerator()
526+
.inOutputDir(outputDir)
527+
.forCRDVersions("v1")
528+
.customResourceClasses(Complex.class)
529+
.withMinQuotes(true)
530+
.detailedGenerate();
531+
532+
File crdFile = new File(crdInfo.getCRDInfos(crdName).get("v1").getFilePath());
533+
String crd = Files.readString(crdFile.toPath());
534+
assertTrue(crd.contains("complexkinds.example.com"));
535+
assertFalse(crd.contains("\"complexkinds.example.com\""));
536+
537+
// only delete the generated files if the test is successful
538+
assertTrue(crdFile.delete());
539+
assertTrue(outputDir.delete());
540+
}
541+
480542
@RepeatedTest(value = 10)
481543
void checkGenerationMultipleVersionsOfCRDsIsDeterministic() throws Exception {
482544
// generated CRD

crd-generator/cli/src/main/java/io/fabric8/crd/generator/cli/CRDGeneratorCLI.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,15 @@ public class CRDGeneratorCLI implements Runnable {
135135
// spotless:on
136136
Boolean implicitPreserveUnknownFields;
137137

138+
// spotless:off
139+
@CommandLine.Option(
140+
names = {"--min-quotes"},
141+
description = "If `true`, quotes will only be included where necessary",
142+
defaultValue = "false"
143+
)
144+
// spotless:on
145+
Boolean minQuotes;
146+
138147
// spotless:off
139148
@CommandLine.Option(
140149
names = {"--include-package"},
@@ -218,6 +227,7 @@ public void run() {
218227
.customResourceClasses(customResourceClasses)
219228
.withParallelGenerationEnabled(!parallelDisabled)
220229
.withImplicitPreserveUnknownFields(implicitPreserveUnknownFields)
230+
.withMinQuotes(minQuotes)
221231
.inOutputDir(sanitizedOutputDirectory);
222232

223233
crdGenerationInfo = crdGenerator.detailedGenerate();

crd-generator/maven-plugin/src/main/java/io/fabric8/crd/generator/maven/plugin/CrdGeneratorMojo.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,12 @@ public class CrdGeneratorMojo extends AbstractMojo {
129129
@Parameter(property = "fabric8.crd-generator.skip", defaultValue = "false")
130130
boolean skip;
131131

132+
/**
133+
* If {@code true}, quotes will only be included where necessary
134+
*/
135+
@Parameter(property = "fabric8.crd-generator.minimizeQuotes", defaultValue = "false")
136+
boolean minimizeQuotes;
137+
132138
private final CustomResourceCollector customResourceCollector;
133139
private final CRDGenerator crdGenerator;
134140

@@ -178,6 +184,7 @@ public void execute() throws MojoExecutionException {
178184
.customResourceClasses(customResourceClassesLoaded)
179185
.withParallelGenerationEnabled(parallel)
180186
.withImplicitPreserveUnknownFields(implicitPreserveUnknownFields)
187+
.withMinQuotes(minimizeQuotes)
181188
.inOutputDir(outputDirectory);
182189

183190
CRDGenerationInfo crdGenerationInfo = crdGenerator.detailedGenerate();

kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/KubernetesSerialization.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,23 @@ public <T> String asJson(T object) {
188188
* @return a String containing a JSON representation of the provided object.
189189
*/
190190
public <T> String asYaml(T object) {
191+
return asYaml(object, new YamlDumpSettingsBuilder().build());
192+
}
193+
194+
/**
195+
* Returns a YAML representation of the given object.
196+
*
197+
* <p>
198+
* If the provided object contains a JsonAnyGetter annotated method with a Map that contains an entry that
199+
* overrides a field of the provided object, the Map entry will take precedence upon serialization. Properties won't
200+
* be duplicated.
201+
*
202+
* @param object the object to serialize.
203+
* @param yamlDumpSettings configuration for YAML serialization.
204+
* @param <T> the type of the object being serialized.
205+
* @return a String containing a JSON representation of the provided object.
206+
*/
207+
public <T> String asYaml(T object, YamlDumpSettings yamlDumpSettings) {
191208
DumpSettings settings = DumpSettings.builder()
192209
.setExplicitStart(true).setDefaultFlowStyle(FlowStyle.BLOCK).build();
193210
final Dump yaml = new Dump(settings, new StandardRepresenter(settings) {
@@ -207,7 +224,7 @@ protected NodeTuple representMappingEntry(java.util.Map.Entry<?, ?> entry) {
207224
}
208225
}
209226
org.snakeyaml.engine.v2.nodes.Node nodeKey = representData(key);
210-
quote = true;
227+
quote = !yamlDumpSettings.isMinQuotes();
211228
return new NodeTuple(nodeKey, representData(entry.getValue()));
212229
}
213230

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright (C) 2015 Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.fabric8.kubernetes.client.utils;
17+
18+
/**
19+
* Configuration for serialization to YAML.
20+
*/
21+
public class YamlDumpSettings {
22+
23+
private boolean minQuotes;
24+
25+
YamlDumpSettings(boolean minQuotes) {
26+
this.minQuotes = minQuotes;
27+
}
28+
29+
public boolean isMinQuotes() {
30+
return minQuotes;
31+
}
32+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright (C) 2015 Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.fabric8.kubernetes.client.utils;
17+
18+
/**
19+
* Builder for YamlDumpSettings.
20+
*/
21+
public class YamlDumpSettingsBuilder {
22+
23+
private boolean minQuotes = false;
24+
25+
/**
26+
* Set whether use of quotes should be minimized.
27+
*
28+
* @param minQuotes if {@code true}, quotes will only be included where necessary.
29+
*/
30+
public YamlDumpSettingsBuilder setMinimizeQuotes(boolean minQuotes) {
31+
this.minQuotes = minQuotes;
32+
return this;
33+
}
34+
35+
/**
36+
* Create immutable YamlDumpSettings
37+
*
38+
* @return YamlDumpSettings with the provided values
39+
*/
40+
public YamlDumpSettings build() {
41+
return new YamlDumpSettings(minQuotes);
42+
}
43+
}

kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/KubernetesSerializationTest.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import io.fabric8.kubernetes.api.model.GenericKubernetesResource;
2323
import io.fabric8.kubernetes.api.model.HasMetadata;
2424
import io.fabric8.kubernetes.api.model.ObjectMeta;
25+
import io.fabric8.kubernetes.api.model.apiextensions.v1beta1.CustomResourceDefinition;
2526
import io.fabric8.kubernetes.model.annotation.Group;
2627
import io.fabric8.kubernetes.model.annotation.Version;
2728
import org.junit.jupiter.api.BeforeEach;
@@ -33,6 +34,11 @@
3334
import org.junit.jupiter.params.provider.Arguments;
3435
import org.junit.jupiter.params.provider.MethodSource;
3536

37+
import java.io.File;
38+
import java.io.IOException;
39+
import java.nio.charset.StandardCharsets;
40+
import java.nio.file.Files;
41+
import java.util.stream.Collectors;
3642
import java.util.stream.Stream;
3743

3844
import static org.assertj.core.api.Assertions.assertThat;
@@ -65,6 +71,29 @@ void withRegisteredKubernetesResourceShouldDeserializeToPod() {
6571
.isInstanceOf(io.fabric8.kubernetes.api.model.Pod.class);
6672
}
6773

74+
@Test
75+
void asYaml() throws Exception {
76+
final String input = readYamlToString("/serialization/test-crd-schema.yml");
77+
final CustomResourceDefinition crd = Serialization.unmarshal(input, CustomResourceDefinition.class);
78+
79+
String result = kubernetesSerialization.asYaml(crd);
80+
assertThat(result).asString().contains("\"widgets.test.fabric8.io\"");
81+
82+
result = kubernetesSerialization.asYaml(crd, new YamlDumpSettingsBuilder().build());
83+
assertThat(result).asString().contains("\"widgets.test.fabric8.io\"");
84+
85+
result = kubernetesSerialization.asYaml(crd, new YamlDumpSettingsBuilder().setMinimizeQuotes(true).build());
86+
assertThat(result).asString().contains("widgets.test.fabric8.io").doesNotContain("\"widgets.test.fabric8.io\"");
87+
}
88+
89+
private String readYamlToString(String path) throws IOException {
90+
return Files.readAllLines(
91+
new File(KubernetesSerializationTest.class.getResource(path).getFile()).toPath(), StandardCharsets.UTF_8)
92+
.stream()
93+
.filter(line -> !line.startsWith("#"))
94+
.collect(Collectors.joining("\n"));
95+
}
96+
6897
@ParameterizedTest(name = "{index}: {0} {1} deserializes to {2}")
6998
@MethodSource("sameGVK")
7099
void withCollidingRegisteredKubernetesResourceShouldDeserializeAppropriate(
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright (C) 2015 Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.fabric8.kubernetes.client.utils;
17+
18+
import org.junit.jupiter.api.Test;
19+
20+
import static org.junit.jupiter.api.Assertions.assertTrue;
21+
22+
class YamlDumpSettingsTest {
23+
24+
@Test
25+
void createSettingsWithMinQuotes() {
26+
YamlDumpSettingsBuilder builder = new YamlDumpSettingsBuilder();
27+
builder.setMinimizeQuotes(true);
28+
YamlDumpSettings settings = builder.build();
29+
30+
assertTrue(settings.isMinQuotes());
31+
}
32+
}

0 commit comments

Comments
 (0)