Skip to content

Commit 7af5730

Browse files
committed
Pure Java Project Generator (#2083)
Signed-off-by: Attila Mészáros <[email protected]>
1 parent 22237a0 commit 7af5730

File tree

20 files changed

+624
-8
lines changed

20 files changed

+624
-8
lines changed

Diff for: .github/workflows/pr.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
./mvnw ${MAVEN_ARGS} formatter:validate -Dconfigfile=$PWD/contributing/eclipse-google-style.xml -pl '!operator-framework-bom' --file pom.xml
3030
./mvnw ${MAVEN_ARGS} impsort:check -pl '!operator-framework-bom' --file pom.xml
3131
- name: Run unit tests
32-
run: ./mvnw ${MAVEN_ARGS} -B test --file pom.xml
32+
run: ./mvnw ${MAVEN_ARGS} clean install --file pom.xml
3333

3434
integration_tests:
3535
strategy:

Diff for: bootstrapper-maven-plugin/pom.xml

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
5+
<parent>
6+
<artifactId>java-operator-sdk</artifactId>
7+
<groupId>io.javaoperatorsdk</groupId>
8+
<version>4.5.0-SNAPSHOT</version>
9+
</parent>
10+
11+
<artifactId>bootstrapper</artifactId>
12+
<packaging>maven-plugin</packaging>
13+
<name>Operator SDK - Bootstrapper Maven Plugin</name>
14+
<description>Operator SDK - Bootstrapper Maven Plugin</description>
15+
16+
<properties>
17+
<maven-plugin-annotations.version>3.9.0</maven-plugin-annotations.version>
18+
<maven-plugin-api.version>3.9.5</maven-plugin-api.version>
19+
</properties>
20+
21+
<dependencies>
22+
<dependency>
23+
<groupId>org.apache.maven</groupId>
24+
<artifactId>maven-plugin-api</artifactId>
25+
<version>${maven-plugin-api.version}</version>
26+
<scope>provided</scope>
27+
</dependency>
28+
<dependency>
29+
<groupId>org.apache.maven.plugin-tools</groupId>
30+
<artifactId>maven-plugin-annotations</artifactId>
31+
<version>${maven-plugin-annotations.version}</version>
32+
<scope>provided</scope>
33+
</dependency>
34+
<dependency>
35+
<groupId>org.slf4j</groupId>
36+
<artifactId>slf4j-api</artifactId>
37+
</dependency>
38+
<dependency>
39+
<groupId>org.apache.logging.log4j</groupId>
40+
<artifactId>log4j-slf4j-impl</artifactId>
41+
<scope>test</scope>
42+
</dependency>
43+
<dependency>
44+
<groupId>org.apache.logging.log4j</groupId>
45+
<artifactId>log4j-core</artifactId>
46+
<scope>test</scope>
47+
</dependency>
48+
<dependency>
49+
<groupId>org.junit.jupiter</groupId>
50+
<artifactId>junit-jupiter-api</artifactId>
51+
</dependency>
52+
<dependency>
53+
<groupId>org.junit.jupiter</groupId>
54+
<artifactId>junit-jupiter-engine</artifactId>
55+
</dependency>
56+
<dependency>
57+
<groupId>commons-io</groupId>
58+
<artifactId>commons-io</artifactId>
59+
<version>2.14.0</version>
60+
</dependency>
61+
<dependency>
62+
<groupId>com.github.spullara.mustache.java</groupId>
63+
<artifactId>compiler</artifactId>
64+
</dependency>
65+
<dependency>
66+
<groupId>org.assertj</groupId>
67+
<artifactId>assertj-core</artifactId>
68+
<scope>test</scope>
69+
</dependency>
70+
</dependencies>
71+
72+
<build>
73+
<plugins>
74+
<plugin>
75+
<groupId>org.apache.maven.plugins</groupId>
76+
<artifactId>maven-plugin-plugin</artifactId>
77+
<version>3.9.0</version>
78+
</plugin>
79+
<plugin>
80+
<groupId>org.codehaus.mojo</groupId>
81+
<artifactId>templating-maven-plugin</artifactId>
82+
<version>1.0.0</version>
83+
<executions>
84+
<execution>
85+
<id>filtering-java-templates</id>
86+
<goals>
87+
<goal>filter-sources</goal>
88+
</goals>
89+
</execution>
90+
</executions>
91+
</plugin>
92+
93+
</plugins>
94+
</build>
95+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package io.javaoperatorsdk.bootstrapper;
2+
3+
public final class Versions {
4+
5+
private Versions() {}
6+
7+
public static final String JOSDK = "${project.version}";
8+
public static final String KUBERNETES_CLIENT = "${fabric8-client.version}";
9+
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package io.javaoperatorsdk.boostrapper;
2+
3+
import java.io.File;
4+
import java.io.FileWriter;
5+
import java.io.IOException;
6+
import java.util.Arrays;
7+
import java.util.List;
8+
import java.util.Map;
9+
import java.util.stream.Collectors;
10+
11+
import org.apache.commons.io.FileUtils;
12+
import org.slf4j.Logger;
13+
import org.slf4j.LoggerFactory;
14+
15+
import io.javaoperatorsdk.bootstrapper.Versions;
16+
17+
import com.github.mustachejava.DefaultMustacheFactory;
18+
import com.github.mustachejava.MustacheFactory;
19+
20+
public class Bootstrapper {
21+
22+
private static final Logger log = LoggerFactory.getLogger(Bootstrapper.class);
23+
24+
private MustacheFactory mustacheFactory = new DefaultMustacheFactory();
25+
26+
private static final List<String> TOP_LEVEL_STATIC_FILES =
27+
List.of(".gitignore", "README.md");
28+
private static final List<String> JAVA_FILES =
29+
List.of("CustomResource.java", "Reconciler.java",
30+
"Spec.java", "Status.java");
31+
32+
public void create(File targetDir, String groupId, String artifactId) {
33+
try {
34+
log.info("Generating project to: {}", targetDir.getPath());
35+
var projectDir = new File(targetDir, artifactId);
36+
FileUtils.forceMkdir(projectDir);
37+
addStaticFiles(projectDir);
38+
addTemplatedFiles(projectDir, groupId, artifactId);
39+
addJavaFiles(projectDir, groupId, artifactId);
40+
addResourceFiles(projectDir, groupId, artifactId);
41+
} catch (IOException e) {
42+
throw new RuntimeException(e);
43+
}
44+
}
45+
46+
private void addResourceFiles(File projectDir, String groupId, String artifactId) {
47+
try {
48+
var target = new File(projectDir, "src/main/resources");
49+
FileUtils.forceMkdir(target);
50+
addTemplatedFile(target, "log4j2.xml", groupId, artifactId, target, null);
51+
} catch (IOException e) {
52+
throw new RuntimeException(e);
53+
}
54+
}
55+
56+
private void addJavaFiles(File projectDir, String groupId, String artifactId) {
57+
try {
58+
var packages = groupId.replace(".", File.separator);
59+
var targetDir = new File(projectDir, "src/main/java/" + packages);
60+
FileUtils.forceMkdir(targetDir);
61+
var classFileNamePrefix = artifactClassId(artifactId);
62+
JAVA_FILES.forEach(f -> addTemplatedFile(projectDir, f, groupId, artifactId, targetDir,
63+
classFileNamePrefix + f));
64+
65+
addTemplatedFile(projectDir, "Runner.java", groupId, artifactId, targetDir, null);
66+
addTemplatedFile(projectDir, "ConfigMapDependentResource.java", groupId, artifactId,
67+
targetDir, null);
68+
} catch (IOException e) {
69+
throw new RuntimeException(e);
70+
}
71+
72+
}
73+
74+
private void addTemplatedFiles(File projectDir, String groupId, String artifactId) {
75+
addTemplatedFile(projectDir, "pom.xml", groupId, artifactId);
76+
addTemplatedFile(projectDir, "k8s/test-resource.yaml", groupId, artifactId);
77+
}
78+
79+
private void addTemplatedFile(File projectDir, String fileName, String groupId,
80+
String artifactId) {
81+
addTemplatedFile(projectDir, fileName, groupId, artifactId, null, null);
82+
}
83+
84+
private void addTemplatedFile(File projectDir, String fileName, String groupId, String artifactId,
85+
File targetDir, String targetFileName) {
86+
try {
87+
var values = Map.of("groupId", groupId, "artifactId", artifactId,
88+
"artifactClassId", artifactClassId(artifactId),
89+
"josdkVersion", Versions.JOSDK,
90+
"fabric8Version", Versions.KUBERNETES_CLIENT);
91+
92+
var mustache = mustacheFactory.compile("templates/" + fileName);
93+
var targetFile = new File(targetDir == null ? projectDir : targetDir,
94+
targetFileName == null ? fileName : targetFileName);
95+
FileUtils.forceMkdir(targetFile.getParentFile());
96+
var writer = new FileWriter(targetFile);
97+
mustache.execute(writer, values);
98+
writer.flush();
99+
} catch (IOException e) {
100+
throw new RuntimeException(e);
101+
}
102+
}
103+
104+
private void addStaticFiles(File projectDir) {
105+
TOP_LEVEL_STATIC_FILES.forEach(f -> addStaticFile(projectDir, f));
106+
}
107+
108+
private void addStaticFile(File targetDir, String fileName) {
109+
addStaticFile(targetDir, fileName, null);
110+
}
111+
112+
private void addStaticFile(File targetDir, String fileName, String subDir) {
113+
String sourcePath = subDir == null ? "/static/" : "/static/" + subDir;
114+
String path = sourcePath + fileName;
115+
try (var is = Bootstrapper.class.getResourceAsStream(path)) {
116+
targetDir = subDir == null ? targetDir : new File(targetDir, subDir);
117+
if (subDir != null) {
118+
FileUtils.forceMkdir(targetDir);
119+
}
120+
FileUtils.copyInputStreamToFile(is, new File(targetDir, fileName));
121+
} catch (IOException e) {
122+
throw new RuntimeException("File path: " + path, e);
123+
}
124+
125+
}
126+
127+
public static String artifactClassId(String artifactId) {
128+
var parts = artifactId.split("-");
129+
return Arrays.stream(parts).map(p -> p.substring(0, 1)
130+
.toUpperCase() + p.substring(1))
131+
.collect(Collectors.joining(""));
132+
}
133+
134+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package io.javaoperatorsdk.boostrapper;
2+
3+
import java.io.File;
4+
5+
import org.apache.maven.plugin.AbstractMojo;
6+
import org.apache.maven.plugin.MojoExecutionException;
7+
import org.apache.maven.plugins.annotations.Mojo;
8+
import org.apache.maven.plugins.annotations.Parameter;
9+
10+
@Mojo(name = "create", requiresProject = false)
11+
public class BootstrapperMojo
12+
extends AbstractMojo {
13+
14+
@Parameter(defaultValue = "${projectGroupId}")
15+
protected String projectGroupId;
16+
17+
@Parameter(defaultValue = "${projectArtifactId}")
18+
protected String projectArtifactId;
19+
20+
public void execute()
21+
throws MojoExecutionException {
22+
String userDir = System.getProperty("user.dir");
23+
new Bootstrapper().create(new File(userDir), projectGroupId, projectArtifactId);
24+
}
25+
}
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Configuration name="TestConfig" status="WARN">
3+
<Appenders>
4+
<Console name="Console" target="SYSTEM_OUT">
5+
<PatternLayout pattern="%d %threadId %-30c{1.} [%-5level] %msg%n%throwable"/>
6+
</Console>
7+
</Appenders>
8+
<Loggers>
9+
<Logger level="debug" name="io.javaoperatorsdk" additivity="false">
10+
<AppenderRef ref="Console"/>
11+
</Logger>
12+
<Root level="info">
13+
<AppenderRef ref="Console"/>
14+
</Root>
15+
</Loggers>
16+
</Configuration>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#Maven
2+
target/
3+
pom.xml.tag
4+
pom.xml.releaseBackup
5+
pom.xml.versionsBackup
6+
release.properties
7+
.flattened-pom.xml
8+
9+
# Eclipse
10+
.project
11+
.classpath
12+
.settings/
13+
bin/
14+
15+
# IntelliJ
16+
.idea
17+
*.ipr
18+
*.iml
19+
*.iws
20+
21+
# NetBeans
22+
nb-configuration.xml
23+
24+
# Visual Studio Code
25+
.vscode
26+
.factorypath
27+
28+
# OSX
29+
.DS_Store
30+
31+
# Vim
32+
*.swp
33+
*.swo
34+
35+
# patch
36+
*.orig
37+
*.rej
38+
39+
# Local environment
40+
.env
41+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Generated Project Skeleton
2+
3+
A simple operator that copies the value in a spec to a ConfigMap.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package {{groupId}};
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
6+
import io.fabric8.kubernetes.api.model.ConfigMap;
7+
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
8+
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
9+
import io.javaoperatorsdk.operator.api.reconciler.Context;
10+
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
11+
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent;
12+
import {{groupId}}.{{artifactClassId}}CustomResource;
13+
14+
@KubernetesDependent
15+
public class ConfigMapDependentResource
16+
extends CRUDKubernetesDependentResource<ConfigMap, {{artifactClassId}}CustomResource> {
17+
18+
public ConfigMapDependentResource() {
19+
super(ConfigMap.class);
20+
}
21+
22+
@Override
23+
protected ConfigMap desired({{artifactClassId}}CustomResource primary,
24+
Context<{{artifactClassId}}CustomResource> context) {
25+
return new ConfigMapBuilder()
26+
.withMetadata(
27+
new ObjectMetaBuilder()
28+
.withName(primary.getMetadata().getName())
29+
.withNamespace(primary.getMetadata().getNamespace())
30+
.build())
31+
.withData(Map.of("data", primary.getSpec().getValue()))
32+
.build();
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package {{groupId}};
2+
3+
import io.fabric8.kubernetes.client.CustomResource;
4+
import io.fabric8.kubernetes.api.model.Namespaced;
5+
import io.fabric8.kubernetes.model.annotation.Group;
6+
import io.fabric8.kubernetes.model.annotation.Version;
7+
8+
@Group("{{groupId}}")
9+
@Version("v1")
10+
public class {{artifactClassId}}CustomResource extends CustomResource<{{artifactClassId}}Spec,{{artifactClassId}}Status> implements Namespaced {
11+
}

0 commit comments

Comments
 (0)