Skip to content

Commit

Permalink
Add default support for the crd-generator (#5431)
Browse files Browse the repository at this point in the history
  • Loading branch information
andreaTP authored Sep 28, 2023
1 parent 4848234 commit f93f5dc
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#### Improvements
* Fix #5327: added proactive shutdown of informers on client close
* Fix #5432: [java-generator] Add the possibility to always emit `additionalProperties` on generated POJOs
* Fix #5410: [crd-generator] added support for `default`
* Fix #5368: added support for additional ListOptions fields
* Fix #5377: added a createOr and unlock function to provide a straight-forward replacement for createOrReplace.
* Fix #4624: added Config.onlyHttpWatches to control whether watches should only use regular HTTP requests, and not attempt WebSocket connections.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ public abstract class AbstractJsonSchema<T, B> {
public static final String ANNOTATION_JSON_IGNORE = "com.fasterxml.jackson.annotation.JsonIgnore";
public static final String ANNOTATION_JSON_ANY_GETTER = "com.fasterxml.jackson.annotation.JsonAnyGetter";
public static final String ANNOTATION_JSON_ANY_SETTER = "com.fasterxml.jackson.annotation.JsonAnySetter";
public static final String ANNOTATION_DEFAULT = "io.fabric8.generator.annotation.Default";
public static final String ANNOTATION_MIN = "io.fabric8.generator.annotation.Min";
public static final String ANNOTATION_MAX = "io.fabric8.generator.annotation.Max";
public static final String ANNOTATION_PATTERN = "io.fabric8.generator.annotation.Pattern";
Expand Down Expand Up @@ -143,6 +144,7 @@ public static String getSchemaTypeFor(TypeRef typeRef) {
}

protected static class SchemaPropsOptions {
final String defaultValue;
final Double min;
final Double max;
final String pattern;
Expand All @@ -152,6 +154,7 @@ protected static class SchemaPropsOptions {
final boolean preserveUnknownFields;

SchemaPropsOptions() {
defaultValue = null;
min = null;
max = null;
pattern = null;
Expand All @@ -160,8 +163,9 @@ protected static class SchemaPropsOptions {
preserveUnknownFields = false;
}

public SchemaPropsOptions(Double min, Double max, String pattern,
public SchemaPropsOptions(String defaultValue, Double min, Double max, String pattern,
boolean nullable, boolean required, boolean preserveUnknownFields) {
this.defaultValue = defaultValue;
this.min = min;
this.max = max;
this.pattern = pattern;
Expand All @@ -170,6 +174,10 @@ public SchemaPropsOptions(Double min, Double max, String pattern,
this.preserveUnknownFields = preserveUnknownFields;
}

public Optional<String> getDefault() {
return Optional.ofNullable(defaultValue);
}

public Optional<Double> getMin() {
return Optional.ofNullable(min);
}
Expand Down Expand Up @@ -320,6 +328,7 @@ private T internalFromImpl(TypeDef definition, Set<String> visited, InternalSche
}

SchemaPropsOptions options = new SchemaPropsOptions(
facade.defaultValue,
facade.min,
facade.max,
facade.pattern,
Expand Down Expand Up @@ -349,6 +358,7 @@ private static class PropertyOrAccessor {
private final String propertyName;
private final String type;
private String renamedTo;
private String defaultValue;
private Double min;
private Double max;
private String pattern;
Expand Down Expand Up @@ -377,6 +387,9 @@ static PropertyOrAccessor fromMethod(Method method, String propertyName) {
public void process() {
annotations.forEach(a -> {
switch (a.getClassRef().getFullyQualifiedName()) {
case ANNOTATION_DEFAULT:
defaultValue = (String) a.getParameters().get(VALUE);
break;
case ANNOTATION_NULLABLE:
nullable = true;
break;
Expand Down Expand Up @@ -427,6 +440,10 @@ public boolean isNullable() {
return nullable;
}

public Optional<String> getDefault() {
return Optional.ofNullable(defaultValue);
}

public Optional<Double> getMax() {
return Optional.ofNullable(max);
}
Expand Down Expand Up @@ -481,6 +498,7 @@ private static class PropertyFacade {
private final List<PropertyOrAccessor> propertyOrAccessors = new ArrayList<>(4);
private String renamedTo;
private String description;
private String defaultValue;
private Double min;
private Double max;
private String pattern;
Expand Down Expand Up @@ -511,6 +529,7 @@ public PropertyFacade(Property property, Map<String, Method> potentialAccessors,
propertyOrAccessors.add(PropertyOrAccessor.fromMethod(method, name));
}
schemaFrom = schemaSwap;
defaultValue = null;
min = null;
max = null;
pattern = null;
Expand Down Expand Up @@ -539,6 +558,7 @@ public Property process() {
LOGGER.debug("Description for property {} has already been contributed by: {}", name, descriptionContributedBy);
}
}
defaultValue = p.getDefault().orElse(null);
min = p.getMin().orElse(min);
max = p.getMax().orElse(max);
pattern = p.getPattern().orElse(pattern);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public class CRDGenerator {
private boolean parallel;
private Map<String, CustomResourceInfo> infos;

private static final ObjectMapper YAML_MAPPER = JsonMapper.builder(new YAMLFactory()
public static final ObjectMapper YAML_MAPPER = JsonMapper.builder(new YAMLFactory()
.enable(YAMLGenerator.Feature.MINIMIZE_QUOTES)
.enable(YAMLGenerator.Feature.ALWAYS_QUOTE_NUMBERS_AS_STRINGS)
.disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package io.fabric8.crd.generator.v1;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import io.fabric8.crd.generator.AbstractJsonSchema;
import io.fabric8.kubernetes.api.model.apiextensions.v1.JSONSchemaProps;
Expand All @@ -25,6 +26,8 @@

import java.util.List;

import static io.fabric8.crd.generator.CRDGenerator.YAML_MAPPER;

public class JsonSchema extends AbstractJsonSchema<JSONSchemaProps, JSONSchemaPropsBuilder> {

private static final JsonSchema instance = new JsonSchema();
Expand Down Expand Up @@ -63,6 +66,13 @@ public JSONSchemaPropsBuilder newBuilder(String type) {
public void addProperty(Property property, JSONSchemaPropsBuilder builder,
JSONSchemaProps schema, SchemaPropsOptions options) {
if (schema != null) {
options.getDefault().ifPresent(s -> {
try {
schema.setDefault(YAML_MAPPER.readTree(s));
} catch (JsonProcessingException e) {
throw new IllegalArgumentException("Cannot parse default value: '" + s + "' as valid YAML.");
}
});
options.getMin().ifPresent(schema::setMinimum);
options.getMax().ifPresent(schema::setMaximum);
options.getPattern().ifPresent(schema::setPattern);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package io.fabric8.crd.generator.v1beta1;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import io.fabric8.crd.generator.AbstractJsonSchema;
import io.fabric8.kubernetes.api.model.apiextensions.v1beta1.JSONSchemaProps;
Expand All @@ -25,6 +26,8 @@

import java.util.List;

import static io.fabric8.crd.generator.CRDGenerator.YAML_MAPPER;

public class JsonSchema extends AbstractJsonSchema<JSONSchemaProps, JSONSchemaPropsBuilder> {

private static final JsonSchema instance = new JsonSchema();
Expand Down Expand Up @@ -64,6 +67,13 @@ public JSONSchemaPropsBuilder newBuilder(String type) {
public void addProperty(Property property, JSONSchemaPropsBuilder builder,
JSONSchemaProps schema, SchemaPropsOptions options) {
if (schema != null) {
options.getDefault().ifPresent(s -> {
try {
schema.setDefault(YAML_MAPPER.readTree(s));
} catch (JsonProcessingException e) {
throw new IllegalArgumentException("Cannot parse default value: '" + s + "' as valid YAML.");
}
});
options.getMin().ifPresent(schema::setMinimum);
options.getMax().ifPresent(schema::setMaximum);
options.getPattern().ifPresent(schema::setPattern);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import io.fabric8.generator.annotation.Default;
import io.fabric8.generator.annotation.Max;
import io.fabric8.generator.annotation.Min;
import io.fabric8.generator.annotation.Nullable;
Expand All @@ -35,6 +36,7 @@ public class AnnotatedSpec {
private int max;
private String singleDigit;
private String nullable;
private String defaultValue;
@Required
private boolean emptySetter;
@Required
Expand Down Expand Up @@ -84,6 +86,11 @@ public String getNullable() {
return null;
}

@Default("my-value")
public String getDefaultValue() {
return "foo";
}

@JsonProperty
public void setEmptySetter(boolean emptySetter) {
this.emptySetter = emptySetter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package io.fabric8.crd.generator.v1;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import io.fabric8.crd.example.annotated.Annotated;
import io.fabric8.crd.example.basic.Basic;
Expand All @@ -39,6 +40,7 @@
import java.util.Map;
import java.util.stream.Collectors;

import static io.fabric8.crd.generator.CRDGenerator.YAML_MAPPER;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
Expand Down Expand Up @@ -94,13 +96,13 @@ void shouldCreateJsonSchemaFromClass() {
}

@Test
void shouldAugmentPropertiesSchemaFromAnnotations() {
void shouldAugmentPropertiesSchemaFromAnnotations() throws JsonProcessingException {
TypeDef annotated = Types.typeDefFrom(Annotated.class);
JSONSchemaProps schema = JsonSchema.from(annotated);
assertNotNull(schema);
Map<String, JSONSchemaProps> properties = assertSchemaHasNumberOfProperties(schema, 2);
final JSONSchemaProps specSchema = properties.get("spec");
Map<String, JSONSchemaProps> spec = assertSchemaHasNumberOfProperties(specSchema, 11);
Map<String, JSONSchemaProps> spec = assertSchemaHasNumberOfProperties(specSchema, 12);

// check descriptions are present
assertTrue(spec.containsKey("from-field"));
Expand All @@ -118,29 +120,40 @@ void shouldAugmentPropertiesSchemaFromAnnotations() {
assertTrue(spec.containsKey("anEnum"));

final JSONSchemaProps min = spec.get("min");
assertNull(min.getDefault());
assertEquals(-5.0, min.getMinimum());
assertNull(min.getMaximum());
assertNull(min.getPattern());
assertNull(min.getNullable());

final JSONSchemaProps max = spec.get("max");
assertNull(max.getDefault());
assertEquals(5.0, max.getMaximum());
assertNull(max.getMinimum());
assertNull(max.getPattern());
assertNull(max.getNullable());

final JSONSchemaProps pattern = spec.get("singleDigit");
assertNull(pattern.getDefault());
assertEquals("\\b[1-9]\\b", pattern.getPattern());
assertNull(pattern.getMinimum());
assertNull(pattern.getMaximum());
assertNull(pattern.getNullable());

final JSONSchemaProps nullable = spec.get("nullable");
assertNull(nullable.getDefault());
assertTrue(nullable.getNullable());
assertNull(nullable.getMinimum());
assertNull(nullable.getMaximum());
assertNull(nullable.getPattern());

final JSONSchemaProps defaultValue = spec.get("defaultValue");
assertEquals("my-value", YAML_MAPPER.writeValueAsString(defaultValue.getDefault()).trim());
assertNull(defaultValue.getNullable());
assertNull(defaultValue.getMinimum());
assertNull(defaultValue.getMaximum());
assertNull(defaultValue.getPattern());

// check required list, should register properties with their modified name if needed
final List<String> required = specSchema.getRequired();
assertEquals(3, required.size());
Expand Down
24 changes: 24 additions & 0 deletions doc/CRD-generator.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,30 @@ The field will be skipped in the generated CRD and will not appear in the schema

If a field or one of its accessors is annotated with `io.fabric8.generator.annotation.Min`

```java
public class ExampleSpec {
@Default("foo")
String someValue;
}
```

The field will have the `default` property in the generated CRD, such as:

```yaml
spec:
properties:
someValue:
default: foo
type: string
required:
- someValue
type: object
```

### io.fabric8.generator.annotation.Min

If a field or one of its accessors is annotated with `io.fabric8.generator.annotation.Min`

```java
public class ExampleSpec {
@Min(-1)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* 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.generator.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/*
* Java representation of the `default` field of JSONSchemaProps
* https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#jsonschemaprops-v1-apiextensions-k8s-io
*/
@Target({ ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface Default {
String value();
}

0 comments on commit f93f5dc

Please sign in to comment.