Skip to content

Commit e7dcb32

Browse files
committed
fix: add a back door to modify the schema
closes: #7355 Signed-off-by: Steve Hawkins <[email protected]>
1 parent 82aa03b commit e7dcb32

File tree

8 files changed

+234
-1
lines changed

8 files changed

+234
-1
lines changed

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

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141
import io.fabric8.crd.generator.annotation.SelectableField;
4242
import io.fabric8.crdv2.generator.InternalSchemaSwaps.SwapResult;
4343
import io.fabric8.crdv2.generator.ResolvingContext.GeneratorObjectSchema;
44+
import io.fabric8.crdv2.generator.v1.JsonSchema.V1JSONSchemaProps;
45+
import io.fabric8.crdv2.generator.v1.SchemaCustomizer;
4446
import io.fabric8.generator.annotation.Default;
4547
import io.fabric8.generator.annotation.Max;
4648
import io.fabric8.generator.annotation.Min;
@@ -54,6 +56,7 @@
5456
import io.fabric8.kubernetes.api.model.HasMetadata;
5557
import io.fabric8.kubernetes.api.model.IntOrString;
5658
import io.fabric8.kubernetes.api.model.Quantity;
59+
import io.fabric8.kubernetes.api.model.apiextensions.v1.JSONSchemaProps;
5760
import io.fabric8.kubernetes.api.model.runtime.RawExtension;
5861
import io.fabric8.kubernetes.client.utils.Utils;
5962
import io.fabric8.kubernetes.model.annotation.LabelSelector;
@@ -412,7 +415,7 @@ private T resolveObject(LinkedHashMap<String, String> visited, InternalSchemaSwa
412415
String... ignore) {
413416
Set<String> ignores = ignore.length > 0 ? new LinkedHashSet<>(Arrays.asList(ignore)) : Collections.emptySet();
414417

415-
final T objectSchema = singleProperty("object");
418+
T objectSchema = singleProperty("object");
416419

417420
schemaSwaps = schemaSwaps.branchAnnotations();
418421
final InternalSchemaSwaps swaps = schemaSwaps;
@@ -505,6 +508,28 @@ private T resolveObject(LinkedHashMap<String, String> visited, InternalSchemaSwa
505508
consumeRepeatingAnnotation(rawClass, ValidationRule.class,
506509
v -> validationRules.add(from(v)));
507510
addToValidationRules(objectSchema, validationRules);
511+
return handleSchemaCustomizer(objectSchema, rawClass);
512+
}
513+
514+
private T handleSchemaCustomizer(T objectSchema, Class<?> rawClass) {
515+
if (objectSchema instanceof JSONSchemaProps) {
516+
JSONSchemaProps[] props = new JSONSchemaProps[] { (JSONSchemaProps) objectSchema };
517+
consumeRepeatingAnnotation(rawClass, SchemaCustomizer.class, sc -> {
518+
try {
519+
props[0] = sc.value().getConstructor().newInstance().apply(props[0], sc.input(),
520+
this.resolvingContext.kubernetesSerialization);
521+
} catch (Exception e) {
522+
if (!(e instanceof RuntimeException)) {
523+
e = new RuntimeException(e);
524+
}
525+
throw (RuntimeException) e;
526+
}
527+
});
528+
if (props[0] != objectSchema) {
529+
// hack to convert back to V1JSONSchemaProps
530+
objectSchema = (T) resolvingContext.kubernetesSerialization.convertValue(props[0], V1JSONSchemaProps.class);
531+
}
532+
}
508533
return objectSchema;
509534
}
510535

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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.crdv2.generator.v1;
17+
18+
import io.fabric8.kubernetes.api.model.apiextensions.v1.JSONSchemaProps;
19+
import io.fabric8.kubernetes.client.utils.KubernetesSerialization;
20+
21+
import java.lang.annotation.ElementType;
22+
import java.lang.annotation.Retention;
23+
import java.lang.annotation.RetentionPolicy;
24+
import java.lang.annotation.Target;
25+
26+
@Target({ ElementType.TYPE })
27+
@Retention(RetentionPolicy.RUNTIME)
28+
public @interface SchemaCustomizer {
29+
30+
public interface Customizer {
31+
32+
/**
33+
* Customizes the given {@link JSONSchemaProps}
34+
*
35+
* @param props the {@link JSONSchemaProps} to customize
36+
* @param input the input String from the {@link SchemaCustomizer} annotation
37+
* @param kubernetesSerialization
38+
* @return the customized {@link JSONSchemaProps}
39+
*/
40+
JSONSchemaProps apply(JSONSchemaProps props, String input, KubernetesSerialization kubernetesSerialization);
41+
42+
}
43+
44+
/**
45+
* Replace the incoming schema with the given the input of JSONSchemaProps in yaml or json,
46+
*/
47+
public static class RawCustomizer implements Customizer {
48+
49+
@Override
50+
public JSONSchemaProps apply(JSONSchemaProps props, String input, KubernetesSerialization kubernetesSerialization) {
51+
return kubernetesSerialization.unmarshal(input, JSONSchemaProps.class);
52+
}
53+
54+
}
55+
56+
/**
57+
* Patch the incoming schema with the given JSON merge patch input.
58+
*/
59+
public static class MergePatchCustomizer implements Customizer {
60+
61+
@Override
62+
public JSONSchemaProps apply(JSONSchemaProps props, String input, KubernetesSerialization kubernetesSerialization) {
63+
kubernetesSerialization.mergePatch(props, input);
64+
return props;
65+
}
66+
67+
}
68+
69+
Class<? extends Customizer> value();
70+
71+
String input() default "";
72+
73+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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.crdv2.example.customized;
17+
18+
import io.fabric8.crdv2.generator.v1.SchemaCustomizer;
19+
20+
@SchemaCustomizer(input = "my description", value = DescriptionCustomizer.class)
21+
public class Customized {
22+
23+
public int field;
24+
25+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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.crdv2.example.customized;
17+
18+
import io.fabric8.crdv2.generator.v1.SchemaCustomizer;
19+
import io.fabric8.kubernetes.api.model.apiextensions.v1.JSONSchemaProps;
20+
import io.fabric8.kubernetes.client.utils.KubernetesSerialization;
21+
22+
public class DescriptionCustomizer implements SchemaCustomizer.Customizer {
23+
24+
@Override
25+
public JSONSchemaProps apply(JSONSchemaProps props, String input, KubernetesSerialization kubernetesSerialization) {
26+
props.setDescription(input);
27+
return props;
28+
}
29+
30+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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.crdv2.example.customized;
17+
18+
import io.fabric8.crdv2.generator.v1.SchemaCustomizer;
19+
20+
@SchemaCustomizer(input = "properties:\n"
21+
+ " prop:\n"
22+
+ " type: \"integer\"", value = SchemaCustomizer.RawCustomizer.class)
23+
public class RawCustomized {
24+
25+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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.crdv2.generator.v1;
17+
18+
import io.fabric8.crdv2.example.customized.Customized;
19+
import io.fabric8.crdv2.example.customized.RawCustomized;
20+
import io.fabric8.kubernetes.api.model.apiextensions.v1.JSONSchemaProps;
21+
import org.junit.jupiter.api.Test;
22+
23+
import static org.junit.jupiter.api.Assertions.assertEquals;
24+
25+
class SchemaCustomizerTest {
26+
27+
@Test
28+
void applyCustomizer() {
29+
JSONSchemaProps schema = JsonSchema.from(Customized.class);
30+
assertEquals("my description", schema.getDescription());
31+
}
32+
33+
@Test
34+
void applyRawCustomizer() {
35+
JSONSchemaProps schema = JsonSchema.from(RawCustomized.class);
36+
assertEquals(1, schema.getProperties().size());
37+
assertEquals("integer", schema.getProperties().get("prop").getType());
38+
}
39+
40+
}

doc/CRD-generator.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,11 @@ spec:
873873
# [...]
874874
```
875875

876+
### Schema Customization
877+
878+
In some instances the built-in set of annotations and logic may not produce the desired CRD output. There is a mechanism
879+
included in the crd-generator-api-v2 module for this. See the `io.fabric8.crdv2.generator.v1.SchemaCustomizer` annotation
880+
for directly manipulating the JSONSchemaProps of the annotated resource. This annotation is applied last, after all of the other annotations are processed.
876881

877882
## Features cheatsheet
878883

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.fasterxml.jackson.databind.JsonSerializer;
2727
import com.fasterxml.jackson.databind.KeyDeserializer;
2828
import com.fasterxml.jackson.databind.ObjectMapper;
29+
import com.fasterxml.jackson.databind.ObjectReader;
2930
import com.fasterxml.jackson.databind.SerializationConfig;
3031
import com.fasterxml.jackson.databind.SerializationFeature;
3132
import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
@@ -429,4 +430,13 @@ public String convertToJson(String input) {
429430
}
430431
}
431432

433+
public void mergePatch(Object updatable, String patch) {
434+
ObjectReader reader = mapper.readerForUpdating(updatable);
435+
try {
436+
reader.readValue(patch);
437+
} catch (JsonProcessingException e) {
438+
throw KubernetesClientException.launderThrowable(e);
439+
}
440+
}
441+
432442
}

0 commit comments

Comments
 (0)