Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
* Fix #7048: Support for Kubernetes v1.34 (Of Wind & Will)
* Fix #7371: Update Fabric8 OpenShift Model as per OpenShift 4.20
* Fix #7355: (crd-generator) Add SchemaCustomizer annotation for advanced schema modification
* Fix #7375: (crd-generator) Support @JsonClassDescription for adding descriptions to classes in the generated CRD schema.

#### _**Note**_: Breaking changes
* Fix #7048: **admissionregistration.v1beta1**: Removed `ValidatingAdmissionPolicy` and related classes (`ValidatingAdmissionPolicyBinding`, `AuditAnnotation`, `Validation`, `ValidatingAdmissionPolicyBindingSpec`) from `v1beta1` as they have graduated to GA in `admissionregistration.v1`. `MutatingAdmissionPolicy` has been promoted from `v1alpha1` to `v1beta1`. The `V1beta1AdmissionRegistrationAPIGroupDSL` and `V1beta1AdmissionRegistrationAPIGroupClient` have been updated accordingly.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package io.fabric8.crdv2.generator;

import com.fasterxml.jackson.annotation.JsonClassDescription;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.BeanProperty;
Expand Down Expand Up @@ -370,7 +371,9 @@ private void setMinMax(BeanProperty beanProperty,
}

public void updateSchema(T schema) {
schema.setDescription(description);
if (Utils.isNotNullOrEmpty(description)) {
schema.setDescription(description);
}
schema.setDefault(defaultValue);
if (nullable) {
schema.setNullable(true);
Expand Down Expand Up @@ -430,6 +433,11 @@ private T resolveObject(LinkedHashMap<String, String> visited, InternalSchemaSwa
Class<?> rawClass = gos.javaType.getRawClass();
collectDependentClasses(rawClass);

JsonClassDescription classDescription = rawClass.getAnnotation(JsonClassDescription.class);
if (classDescription != null) {
objectSchema.setDescription(classDescription.value());
}

// while it should not be repeating, we reuse this method to look for preserve unknown on the class hierarchy
consumeRepeatingAnnotation(rawClass, PreserveUnknownFields.class,
ignored -> objectSchema.setXKubernetesPreserveUnknownFields(true));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import io.fabric8.crd.generator.CRDInfo;
import io.fabric8.crd.generator.approvaltests.annotated.Annotated;
import io.fabric8.crd.generator.approvaltests.complex.Complex;
import io.fabric8.crd.generator.approvaltests.described.Described;
import io.fabric8.crd.generator.approvaltests.inherited.Child;
import io.fabric8.crd.generator.approvaltests.json.ContainingJson;
import io.fabric8.crd.generator.approvaltests.k8svalidation.K8sValidation;
Expand Down Expand Up @@ -183,6 +184,7 @@ static Stream<TestCase> crdApprovalCasesBase(String crdVersion) {
static Stream<TestCase> crdApprovalCasesApiV2(String crdVersion) {
final List<TestCase> cases = new ArrayList<>();
for (boolean parallel : new boolean[] { false, true }) {
cases.add(new TestCase("describeds.samples.fabric8.io", crdVersion, parallel, Described.class));
cases.add(new TestCase("printercolumns.sample.fabric8.io", crdVersion, parallel, PrinterColumn.class));
cases.add(new TestCase("requireds.samples.fabric8.io", crdVersion, parallel, Required.class));
cases.add(new TestCase("selectablefields.sample.fabric8.io", crdVersion, parallel, SelectableField.class));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* 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.approvaltests.described;

import com.fasterxml.jackson.annotation.JsonClassDescription;
import io.fabric8.kubernetes.client.CustomResource;
import io.fabric8.kubernetes.model.annotation.Group;
import io.fabric8.kubernetes.model.annotation.Version;

@Group("samples.fabric8.io")
@Version("v1")
@JsonClassDescription("This is a top-level description.")
public class Described extends CustomResource<DescribedSpec, Void> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* 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.approvaltests.described;

import com.fasterxml.jackson.annotation.JsonClassDescription;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import lombok.Data;

/**
* Test spec for @JsonClassDescription support.
*/
@Data
@JsonClassDescription("This is a spec-class-level description.")
public class DescribedSpec {

@JsonPropertyDescription("This is a field-level description.")
private String field;

private Nested nested;

@Data
@JsonClassDescription("This is a nested-class-level description.")
public static class Nested {

@JsonPropertyDescription("This is a nested-field-level description.")
private String nestedField;

private DeeplyNested deeplyNested;
}

@Data
@JsonClassDescription("This is a deeply-nested-class-level description.")
public static class DeeplyNested {

@JsonPropertyDescription("This is a leaf-field-level description.")
private String leafField;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Generated by Fabric8 CRDGenerator, manual edits might get overwritten!
apiVersion: "apiextensions.k8s.io/v1"
kind: "CustomResourceDefinition"
metadata:
name: "describeds.samples.fabric8.io"
spec:
group: "samples.fabric8.io"
names:
kind: "Described"
plural: "describeds"
singular: "described"
scope: "Cluster"
versions:
- name: "v1"
schema:
openAPIV3Schema:
description: "This is a top-level description."
properties:
spec:
description: "This is a spec-class-level description."
properties:
field:
description: "This is a field-level description."
type: "string"
nested:
description: "This is a nested-class-level description."
properties:
deeplyNested:
description: "This is a deeply-nested-class-level description."
properties:
leafField:
description: "This is a leaf-field-level description."
type: "string"
type: "object"
nestedField:
description: "This is a nested-field-level description."
type: "string"
type: "object"
type: "object"
status:
type: "object"
type: "object"
served: true
storage: true
34 changes: 33 additions & 1 deletion doc/CRD-generator.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,37 @@ The generated field in the CRD will be named after the value provided in the ann
type: object
```

### com.fasterxml.jackson.annotation.JsonClassDescription

If a class is annotated with `com.fasterxml.jackson.annotation.JsonClassDescription`, the provided text will be used as the `description` for that object type in the generated CRD schema.

This ensures the description is associated with the class itself in the resulting schema:

```java
@JsonClassDescription("Description for the Example resource")
public class Example extends CustomResource<ExampleSpec, ExampleStatus> implements Namespaced {}

@JsonClassDescription("Description for the spec object")
public class ExampleSpec {
private String field;
}

```

The generated CRD will include these descriptions for each object type:

```yaml
schema:
openAPIV3Schema:
description: "Description for the Example resource"
type: object
properties:
spec:
description: "Description for the spec object"
type: object

```

### com.fasterxml.jackson.annotation.JsonPropertyDescription

If a field or one of its accessors is annotated with `com.fasterxml.jackson.annotation.JsonPropertyDescription`
Expand Down Expand Up @@ -884,7 +915,8 @@ for directly manipulating the JSONSchemaProps of the annotated resource. This an
| Annotation | Description |
|-----------------------------------------------------------------|-----------------------------------------------------------------------------------------------------|
| `com.fasterxml.jackson.annotation.JsonProperty` | The field is named after the provided value instead of looking up the java field name |
| `com.fasterxml.jackson.annotation.JsonPropertyDescription` | The provided text is be embedded in the `description` of the field |
| `com.fasterxml.jackson.annotation.JsonClassDescription` | The provided text is embedded in the `description` of the class |
| `com.fasterxml.jackson.annotation.JsonPropertyDescription` | The provided text is embedded in the `description` of the field |
| `com.fasterxml.jackson.annotation.JsonIgnore` | The field is ignored |
| `io.fabric8.crd.generator.annotation.PreserveUnknownFields` | The field have `x-kubernetes-preserve-unknown-fields: true` defined |
| `com.fasterxml.jackson.annotation.JsonAnyGetter` | The corresponding object have `x-kubernetes-preserve-unknown-fields: true` defined |
Expand Down
Loading