Skip to content

Commit

Permalink
starting on v7 crd generator
Browse files Browse the repository at this point in the history
this removes the usage of sundrio and leverages many features already
provided by jackson

Signed-off-by: Steve Hawkins <shawkins@redhat.com>
  • Loading branch information
shawkins committed Apr 23, 2024
1 parent b8eeca4 commit 039b0c6
Show file tree
Hide file tree
Showing 62 changed files with 1,054 additions and 2,372 deletions.
14 changes: 0 additions & 14 deletions crd-generator/api/pom.xml
Expand Up @@ -51,20 +51,6 @@
<artifactId>kubernetes-model-common</artifactId>
</dependency>

<dependency>
<groupId>io.sundr</groupId>
<artifactId>sundr-adapter-reflect</artifactId>
<version>${sundrio.version}</version>
<scope>compile</scope>
</dependency>

<dependency>
<groupId>io.sundr</groupId>
<artifactId>builder-annotations</artifactId>
<scope>compile</scope>
</dependency>


<!-- Testing -->
<dependency>
<groupId>org.junit.jupiter</groupId>
Expand Down
Expand Up @@ -15,26 +15,12 @@
*/
package io.fabric8.crd.generator;

import io.fabric8.crd.generator.AbstractJsonSchema.AnnotationMetadata;
import io.fabric8.crd.generator.annotation.PrinterColumn;
import io.fabric8.crd.generator.decorator.Decorator;
import io.fabric8.crd.generator.visitor.*;
import io.fabric8.kubernetes.client.utils.Utils;
import io.sundr.builder.Visitor;
import io.sundr.model.AnnotationRef;
import io.sundr.model.Property;
import io.sundr.model.TypeDef;
import io.sundr.model.TypeDefBuilder;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
* This class encapsulates the common behavior between v1beta1 and v1 CRD generation logic. The
Expand All @@ -43,110 +29,31 @@
public abstract class AbstractCustomResourceHandler {

protected final Resources resources;
private final boolean parallel;

protected AbstractCustomResourceHandler(Resources resources, boolean parallel) {
protected AbstractCustomResourceHandler(Resources resources) {
this.resources = resources;
this.parallel = parallel;
}

public void handle(CustomResourceInfo config) {
final String name = config.crdName();
final String version = config.version();
public abstract void handle(CustomResourceInfo config);

TypeDef def = config.definition();

SpecReplicasPathDetector specReplicasPathDetector = new SpecReplicasPathDetector();
StatusReplicasPathDetector statusReplicasPathDetector = new StatusReplicasPathDetector();
LabelSelectorPathDetector labelSelectorPathDetector = new LabelSelectorPathDetector();
AdditionalPrinterColumnDetector additionalPrinterColumnDetector = new AdditionalPrinterColumnDetector();

ClassDependenciesVisitor traversedClassesVisitor = new ClassDependenciesVisitor(config.crClassName(), name);

List<Visitor<TypeDefBuilder>> visitors = new ArrayList<>();
if (config.specClassName().isPresent()) {
visitors.add(specReplicasPathDetector);
}
if (config.statusClassName().isPresent()) {
visitors.add(statusReplicasPathDetector);
}
visitors.add(labelSelectorPathDetector);
visitors.add(additionalPrinterColumnDetector);
visitors.add(traversedClassesVisitor);

visitTypeDef(def, visitors);

addDecorators(config, def, specReplicasPathDetector.getPath(),
statusReplicasPathDetector.getPath(), labelSelectorPathDetector.getPath());

Map<String, Property> additionalPrinterColumns = new HashMap<>(additionalPrinterColumnDetector.getProperties());
protected void handlePrinterColumns(String name, String version, Map<String, AnnotationMetadata> additionalPrinterColumns) {
additionalPrinterColumns.forEach((path, property) -> {
Map<String, Object> parameters = property.getAnnotations().stream()
.filter(a -> a.getClassRef().getName().equals("PrinterColumn")).map(AnnotationRef::getParameters)
.findFirst().orElse(Collections.emptyMap());
String type = AbstractJsonSchema.getSchemaTypeFor(property.getTypeRef());
String column = (String) parameters.get("name");
PrinterColumn printerColumn = ((PrinterColumn)property.annotation);
String column = printerColumn.name();
if (Utils.isNullOrEmpty(column)) {
column = property.getName().toUpperCase();
column = path.substring(path.lastIndexOf("."));
}
String description = property.getComments().stream().filter(l -> !l.trim().startsWith("@"))
.collect(Collectors.joining(" ")).trim();
String format = (String) parameters.get("format");
int priority = (int) parameters.getOrDefault("priority", 0);
String format = printerColumn.format();
int priority = printerColumn.priority();

// TODO: add description to the annotation? The previous logic considered the comments, which are not available here
String description = property.description;

resources.decorate(
getPrinterColumnDecorator(name, version, path, type, column, description, format, priority));
getPrinterColumnDecorator(name, version, path, property.type, column, description, format, priority));
});
}

private TypeDef visitTypeDef(TypeDef def, List<Visitor<TypeDefBuilder>> visitors) {
if (visitors.isEmpty()) {
return def;
}
if (parallel) {
return visitTypeDefInParallel(def, visitors);
} else {
return visitTypeDefSequentially(def, visitors);
}
}

private TypeDef visitTypeDefSequentially(TypeDef def, List<Visitor<TypeDefBuilder>> visitors) {
TypeDefBuilder builder = new TypeDefBuilder(def);
for (Visitor<TypeDefBuilder> visitor : visitors) {
builder.accept(visitor);
}
return builder.build();
}

private TypeDef visitTypeDefInParallel(TypeDef def, List<Visitor<TypeDefBuilder>> visitors) {
final ExecutorService executorService = Executors.newFixedThreadPool(
Math.min(visitors.size(), Runtime.getRuntime().availableProcessors()));
try {
List<CompletableFuture<Void>> futures = new ArrayList<>();
for (Visitor<TypeDefBuilder> visitor : visitors) {
futures.add(CompletableFuture.runAsync(() -> {
// in this case we're not building a new typedef,
// instead we just need to traverse the object graph.
TypeDefBuilder builder = new TypeDefBuilder(def);
builder.accept(visitor);
}, executorService));
}
try {
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get();
} catch (InterruptedException interruptedException) {
Thread.currentThread().interrupt();
} catch (ExecutionException ex) {
if (ex.getCause() instanceof RuntimeException) {
throw (RuntimeException) ex.getCause();
}
throw new RuntimeException(ex.getCause());
}
} finally {
executorService.shutdown();
}
return def;
}

/**
* Provides the decorator implementation associated with the CRD generation version.
*
Expand All @@ -162,19 +69,4 @@ private TypeDef visitTypeDefInParallel(TypeDef def, List<Visitor<TypeDefBuilder>
protected abstract Decorator<?> getPrinterColumnDecorator(String name, String version, String path,
String type, String column, String description, String format, int priority);

/**
* Adds all the necessary decorators to build the specific CRD version. For optional paths, see
* https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#customresourcesubresourcescale-v1-apiextensions-k8s-io
* These paths
*
* @param config the gathered {@link CustomResourceInfo} used as basis for the CRD generation
* @param def the {@link TypeDef} associated with the {@link io.fabric8.kubernetes.client.CustomResource} from which the CRD
* is generated
* @param specReplicasPath an optionally detected path of field defining spec replicas
* @param statusReplicasPath an optionally detected path of field defining status replicas
* @param labelSelectorPath an optionally detected path of field defining `status.selector`
*/
protected abstract void addDecorators(CustomResourceInfo config, TypeDef def,
Optional<String> specReplicasPath, Optional<String> statusReplicasPath,
Optional<String> labelSelectorPath);
}

0 comments on commit 039b0c6

Please sign in to comment.