Skip to content

Commit

Permalink
fix(crd-generator): adds an additional printer column annotation (6390)
Browse files Browse the repository at this point in the history
fix: adds an additional printer column annotation

closes: #3069

Signed-off-by: Steve Hawkins <shawkins@redhat.com>
---
switching to enums for format and type

based upon Bernhard Strähle's review

Signed-off-by: Steve Hawkins <shawkins@redhat.com>
---
updating to jsonPath

Signed-off-by: Steve Hawkins <shawkins@redhat.com>
  • Loading branch information
shawkins authored Oct 18, 2024
1 parent 4d1fd2b commit 66dbab1
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 23 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 #6407: sundrio builder-annotations is not available via bom import
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package io.fabric8.crdv2.generator;

import io.fabric8.crd.generator.annotation.AdditionalPrinterColumn;
import io.fabric8.crd.generator.annotation.AdditionalPrinterColumn.Format;
import io.fabric8.crd.generator.annotation.PrinterColumn;
import io.fabric8.crdv2.generator.AbstractJsonSchema.AnnotationMetadata;
import io.fabric8.kubernetes.api.model.HasMetadata;
Expand Down Expand Up @@ -44,29 +46,35 @@ void addPrinterColumn(String path, String column, String format,

protected void handlePrinterColumns(AbstractJsonSchema<?, ?> resolver, PrinterColumnHandler handler) {
TreeMap<String, AnnotationMetadata> sortedCols = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
resolver.getAdditionalPrinterColumns().forEach(apc -> sortedCols.put(apc.jsonPath(), new AnnotationMetadata(apc, null)));
sortedCols.putAll(resolver.getAllPaths(PrinterColumn.class));
sortedCols.forEach((path, property) -> {
PrinterColumn printerColumn = ((PrinterColumn) property.annotation);
String column = printerColumn.name();
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";
}
if (property.annotation instanceof AdditionalPrinterColumn) {
AdditionalPrinterColumn printerColumn = ((AdditionalPrinterColumn) property.annotation);
String column = printerColumn.name();
String format = printerColumn.format() == Format.NONE ? null : printerColumn.format().getValue();
String type = printerColumn.type().getValue();
int priority = printerColumn.priority();
String description = printerColumn.getDescription();
handler.addPrinterColumn(path, column, format, priority, type, description);
} else {
PrinterColumn printerColumn = ((PrinterColumn) property.annotation);
String column = printerColumn.name();
String format = printerColumn.format();
format = Utils.isNotNullOrEmpty(format) ? format : null;
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";
}
int priority = printerColumn.priority();

// 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);
// TODO: add description to the annotation? The previous logic considered the comments, which are not available here
String description = property.schema.getDescription();
handler.addPrinterColumn(path, column, format, priority, type, description);
}
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -93,6 +94,7 @@ public abstract class AbstractJsonSchema<T extends KubernetesJSONSchemaProps, V
private ResolvingContext resolvingContext;
private T root;
private Set<String> dependentClasses = new HashSet<>();
private Set<AdditionalPrinterColumn> additionalPrinterColumns = new HashSet<>();

public static class AnnotationMetadata {
public final Annotation annotation;
Expand Down Expand Up @@ -141,6 +143,8 @@ public Map<String, AnnotationMetadata> getAllPaths(Class<PrinterColumn> 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");
}
Expand Down Expand Up @@ -598,4 +602,8 @@ private static String mapNotEmpty(String s) {

protected abstract T raw();

public Set<AdditionalPrinterColumn> getAdditionalPrinterColumns() {
return additionalPrinterColumns;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ public void handle(CustomResourceInfo config, ResolvingContext resolvingContext)
handlePrinterColumns(resolver, new PrinterColumnHandler() {
@Override
public void addPrinterColumn(String path, String column, String format, int priority, String type, String description) {
if (Utils.isNullOrEmpty(column)) {
column = path.substring(path.lastIndexOf(".") + 1).toUpperCase();
}
description = Utils.isNotNullOrEmpty(description) ? description : null;

builder.addNewAdditionalPrinterColumn()
.withType(type)
.withName(column)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package io.fabric8.crdv2.example.joke;

import io.fabric8.crd.generator.annotation.AdditionalPrinterColumn;
import io.fabric8.crd.generator.annotation.AdditionalPrinterColumn.Type;
import io.fabric8.kubernetes.api.model.Namespaced;
import io.fabric8.kubernetes.client.CustomResource;
import io.fabric8.kubernetes.model.annotation.Group;
Expand All @@ -24,6 +26,7 @@
@Group("samples.javaoperatorsdk.io")
@Version("v1alpha1")
@ShortNames("jr")
@AdditionalPrinterColumn(name = "Age", jsonPath = ".metadata.creationTimestamp", type = Type.DATE)
public class JokeRequest extends CustomResource<JokeRequestSpec, JokeRequestStatus> implements Namespaced {

}
Original file line number Diff line number Diff line change
Expand Up @@ -387,18 +387,23 @@ void jokerequestCRDShouldWork() {
// printer columns should be ordered in the alphabetical order of their json path
final List<CustomResourceColumnDefinition> 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());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* 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 {

//https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#type
enum Type {

STRING("string"),
INTEGER("integer"),
NUMBER("number"),
BOOLEAN("boolean"),
DATE("date");

public final String value;

Type(String value) {
this.value = value;
}

public String getValue() {
return value;
}
}

// https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#format
enum Format {

NONE(""),
INT32("int32"),
INT64("int64"),
FLOAT("float"),
DOUBLE("double"),
BYTE("byte"),
DATE("date"),
DATE_TIME("date-time"),
PASSWORD("password");

public final String value;

Format(String value) {
this.value = value;
}

public String getValue() {
return value;
}
}

/**
* 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 NONE if no format is specified.
*/
Format format() default Format.NONE;

/**
* 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 jsonPath();

/**
* The type of the printer column
*
* @return the type
*/
Type type() default Type.STRING;

/**
* The description of the printer column
*
* @return the description
*/
String getDescription() default "";
}
Original file line number Diff line number Diff line change
@@ -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();
}

0 comments on commit 66dbab1

Please sign in to comment.