diff --git a/CHANGELOG.md b/CHANGELOG.md index ef814de72e8..0a19018009f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * Fix #6214: Java generator does not recognize fields in CRDs other than metadata, spec, and status #### Improvements +* Fix #3069: added AdditionalPrinterColumn type annotation to completely specify additional printer columns * Fix #5264: Remove deprecated `Config.errorMessages` field * Fix #6008: removing the optional dependency on bouncy castle * Fix #6230: introduced Quantity.multiply(int) to allow for Quantity multiplication by an integer diff --git a/crd-generator/api-v2/src/main/java/io/fabric8/crdv2/generator/AbstractCustomResourceHandler.java b/crd-generator/api-v2/src/main/java/io/fabric8/crdv2/generator/AbstractCustomResourceHandler.java index 317c6b7baf2..fdbb1149e97 100644 --- a/crd-generator/api-v2/src/main/java/io/fabric8/crdv2/generator/AbstractCustomResourceHandler.java +++ b/crd-generator/api-v2/src/main/java/io/fabric8/crdv2/generator/AbstractCustomResourceHandler.java @@ -15,6 +15,7 @@ */ package io.fabric8.crdv2.generator; +import io.fabric8.crd.generator.annotation.AdditionalPrinterColumn; import io.fabric8.crd.generator.annotation.PrinterColumn; import io.fabric8.crdv2.generator.AbstractJsonSchema.AnnotationMetadata; import io.fabric8.kubernetes.api.model.HasMetadata; @@ -44,28 +45,44 @@ void addPrinterColumn(String path, String column, String format, protected void handlePrinterColumns(AbstractJsonSchema resolver, PrinterColumnHandler handler) { TreeMap sortedCols = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + resolver.getAdditionalPrinterColumns().forEach(apc -> sortedCols.put(apc.path(), new AnnotationMetadata(apc, null))); sortedCols.putAll(resolver.getAllPaths(PrinterColumn.class)); sortedCols.forEach((path, property) -> { - PrinterColumn printerColumn = ((PrinterColumn) property.annotation); - String column = printerColumn.name(); + String column; + String type; + String format; + int priority; + String description; + if (property.annotation instanceof AdditionalPrinterColumn) { + AdditionalPrinterColumn printerColumn = ((AdditionalPrinterColumn) property.annotation); + column = printerColumn.name(); + format = printerColumn.format(); + priority = printerColumn.priority(); + type = printerColumn.getType(); + description = printerColumn.getDescription(); + } else { + PrinterColumn printerColumn = ((PrinterColumn) property.annotation); + column = printerColumn.name(); + format = printerColumn.format(); + priority = printerColumn.priority(); + type = property.schema.getType(); + + // TODO: add description to the annotation? The previous logic considered the comments, which are not available here + description = property.schema.getDescription(); + } + if (Utils.isNullOrEmpty(column)) { column = path.substring(path.lastIndexOf(".") + 1).toUpperCase(); } - String format = printerColumn.format(); format = Utils.isNotNullOrEmpty(format) ? format : null; - int priority = printerColumn.priority(); - String type = property.schema.getType(); if ("object".equals(type) || "array".equals(type)) { LOGGER.warn("Printer column '{}' has a type '{}' that is not allowed, will use string intead", column, type); type = "string"; } else if ("string".equals(type) && "date".equals(property.schema.getFormat())) { type = "date"; } - - // TODO: add description to the annotation? The previous logic considered the comments, which are not available here - String description = property.schema.getDescription(); description = Utils.isNotNullOrEmpty(description) ? description : null; - + handler.addPrinterColumn(path, column, format, priority, type, description); }); } diff --git a/crd-generator/api-v2/src/main/java/io/fabric8/crdv2/generator/AbstractJsonSchema.java b/crd-generator/api-v2/src/main/java/io/fabric8/crdv2/generator/AbstractJsonSchema.java index deb3821a537..8c8083cff2a 100644 --- a/crd-generator/api-v2/src/main/java/io/fabric8/crdv2/generator/AbstractJsonSchema.java +++ b/crd-generator/api-v2/src/main/java/io/fabric8/crdv2/generator/AbstractJsonSchema.java @@ -28,6 +28,7 @@ import com.fasterxml.jackson.module.jsonSchema.types.ReferenceSchema; import com.fasterxml.jackson.module.jsonSchema.types.StringSchema; import com.fasterxml.jackson.module.jsonSchema.types.ValueTypeSchema; +import io.fabric8.crd.generator.annotation.AdditionalPrinterColumn; import io.fabric8.crd.generator.annotation.PreserveUnknownFields; import io.fabric8.crd.generator.annotation.PrinterColumn; import io.fabric8.crd.generator.annotation.SchemaFrom; @@ -93,6 +94,7 @@ public abstract class AbstractJsonSchema dependentClasses = new HashSet<>(); + private Set additionalPrinterColumns = new HashSet<>(); public static class AnnotationMetadata { public final Annotation annotation; @@ -141,6 +143,8 @@ public Map getAllPaths(Class clazz) { private T resolveRoot(Class definition) { InternalSchemaSwaps schemaSwaps = new InternalSchemaSwaps(); JsonSchema schema = resolvingContext.toJsonSchema(definition); + consumeRepeatingAnnotation(definition, AdditionalPrinterColumn.class, + additionalPrinterColumns::add); if (schema instanceof GeneratorObjectSchema) { return resolveObject(new LinkedHashMap<>(), schemaSwaps, schema, "kind", "apiVersion", "metadata"); } @@ -597,5 +601,9 @@ private static String mapNotEmpty(String s) { protected abstract void addToValidationRules(T schema, List validationRules); protected abstract T raw(); + + public Set getAdditionalPrinterColumns() { + return additionalPrinterColumns; + } } diff --git a/crd-generator/api-v2/src/test/java/io/fabric8/crdv2/example/joke/JokeRequest.java b/crd-generator/api-v2/src/test/java/io/fabric8/crdv2/example/joke/JokeRequest.java index 6dd72d30d7d..579fd98523b 100644 --- a/crd-generator/api-v2/src/test/java/io/fabric8/crdv2/example/joke/JokeRequest.java +++ b/crd-generator/api-v2/src/test/java/io/fabric8/crdv2/example/joke/JokeRequest.java @@ -15,6 +15,7 @@ */ package io.fabric8.crdv2.example.joke; +import io.fabric8.crd.generator.annotation.AdditionalPrinterColumn; import io.fabric8.kubernetes.api.model.Namespaced; import io.fabric8.kubernetes.client.CustomResource; import io.fabric8.kubernetes.model.annotation.Group; @@ -24,6 +25,7 @@ @Group("samples.javaoperatorsdk.io") @Version("v1alpha1") @ShortNames("jr") +@AdditionalPrinterColumn(name = "Age", path = ".metadata.creationTimestamp", getType = "date") public class JokeRequest extends CustomResource implements Namespaced { } diff --git a/crd-generator/api-v2/src/test/java/io/fabric8/crdv2/generator/CRDGeneratorTest.java b/crd-generator/api-v2/src/test/java/io/fabric8/crdv2/generator/CRDGeneratorTest.java index e674a360578..17084f18efe 100644 --- a/crd-generator/api-v2/src/test/java/io/fabric8/crdv2/generator/CRDGeneratorTest.java +++ b/crd-generator/api-v2/src/test/java/io/fabric8/crdv2/generator/CRDGeneratorTest.java @@ -387,18 +387,23 @@ void jokerequestCRDShouldWork() { // printer columns should be ordered in the alphabetical order of their json path final List printerColumns = version .getAdditionalPrinterColumns(); - assertEquals(3, printerColumns.size()); + assertEquals(4, printerColumns.size()); CustomResourceColumnDefinition columnDefinition = printerColumns.get(0); + assertEquals("date", columnDefinition.getType()); + assertEquals(".metadata.creationTimestamp", columnDefinition.getJsonPath()); + assertEquals("Age", columnDefinition.getName()); + assertEquals(0, columnDefinition.getPriority()); + columnDefinition = printerColumns.get(1); assertEquals("string", columnDefinition.getType()); assertEquals(".spec.category", columnDefinition.getJsonPath()); assertEquals("jokeCategory", columnDefinition.getName()); assertEquals(1, columnDefinition.getPriority()); - columnDefinition = printerColumns.get(1); + columnDefinition = printerColumns.get(2); assertEquals("string", columnDefinition.getType()); assertEquals(".spec.excluded", columnDefinition.getJsonPath()); assertEquals("excludedTopics", columnDefinition.getName()); assertEquals(0, columnDefinition.getPriority()); - columnDefinition = printerColumns.get(2); + columnDefinition = printerColumns.get(3); assertEquals("string", columnDefinition.getType()); assertEquals(".status.category", columnDefinition.getJsonPath()); assertEquals("jokeCategory", columnDefinition.getName()); diff --git a/generator-annotations/src/main/java/io/fabric8/crd/generator/annotation/AdditionalPrinterColumn.java b/generator-annotations/src/main/java/io/fabric8/crd/generator/annotation/AdditionalPrinterColumn.java new file mode 100644 index 00000000000..d60ccbd8aca --- /dev/null +++ b/generator-annotations/src/main/java/io/fabric8/crd/generator/annotation/AdditionalPrinterColumn.java @@ -0,0 +1,73 @@ +/* + * 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.crd.generator.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Defines an additional printer column. Must be placed at the root of the custom resource. + */ +@Repeatable(AdditionalPrinterColumns.class) +@Target({ ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +public @interface AdditionalPrinterColumn { + + /** + * The name of the column. + * An empty column name implies the use of the last path element + * + * @return the column name, or empty string if the last path element should be used. + */ + String name() default ""; + + /** + * The printer column format. + * + * @return the format or empty string if no format is specified. + */ + String format() default ""; + + /** + * The printer column priority. + * + * @return the priority or 0 if no priority is specified. + */ + int priority() default 0; + + /** + * The JSON Path to the field + * @return + */ + String path(); + + /** + * The type of the printer column + * + * @return the type + */ + String getType(); + + /** + * The description of the printer column + * + * @return the description + */ + String getDescription() default ""; +} diff --git a/generator-annotations/src/main/java/io/fabric8/crd/generator/annotation/AdditionalPrinterColumns.java b/generator-annotations/src/main/java/io/fabric8/crd/generator/annotation/AdditionalPrinterColumns.java new file mode 100644 index 00000000000..876ff49e50b --- /dev/null +++ b/generator-annotations/src/main/java/io/fabric8/crd/generator/annotation/AdditionalPrinterColumns.java @@ -0,0 +1,30 @@ +/* + * 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.crd.generator.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * A container for multiple {@link AdditionalPrinterColumn}s + */ +@Target({ ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +public @interface AdditionalPrinterColumns { + AdditionalPrinterColumn[] value(); +}