CI Setup
Projectless setup
Projectless setup means you do use Java 21 in preview mode or Java >= 25.
It enables you to do java main.java (or whatever java file name) directly without any project.
To ease this mode we do provide on central all jars (fatjars) which have JSON-B and the descriptor model.
Using them, it is sufficient to add this jar in the classpath to run the script:
java -cp kubernetes-java-1.34.x-1.0.1-all.jar main.java
|
Tip
|
for vscode (and Java Project extension) you can add the jar in the editor completion scope by creating a file .vscode/settings.json containing { "java.project.referencedLibraries": [ "/path/to/kubernetes-java-1.34.x-1.0.1-all.jar" ] } or by putting the jar in lib/ folder.
|
A sample main can look like:
import static java.util.stream.Collectors.joining;
import io.yupiik.kubernetes.bindings.v1_34_x.v1.*;
void main() {
// (1)
var name = "nginx";
var labels = Map.of("app", name);
// (2)
var deployment = new Deployment()
.metadata(new ObjectMeta()
.name(name)
.labels(labels))
.spec(new DeploymentSpec()
.replicas(2)
.selector(new LabelSelector()
.matchLabels(labels))
.template(new PodTemplateSpec()
.metadata(new ObjectMeta()
.labels(labels))
.spec(new PodSpec()
.containers(List.of(
new Container()
.name("nginx")
.image("nginx")
.imagePullPolicy("Always"))))))
.validate() // (3)
.asJson();
// (4)
var service = new Service()
.metadata(new ObjectMeta()
.name(name)
.labels(labels))
.spec(new ServiceSpec()
.type("ClusterIP")
.selector(Map.of("app", "nginx"))
.ports(List.of(new ServicePort()
.port(80)
.targetPort("nginx"))))
.validate()
.asJson();
// (5)
System.out.println(Stream.of(
deployment,
service)
.collect(joining("\n---\n")));
}
-
We create shared variables reused accross all instances (avoids to copy/paste or recompute them),
-
Create a deployment for nginx server,
-
Do not forget to call
validatewhich will fill theapiVersionandkindif not set (or explicitly set them), -
Create a service for the nginx server,
-
Concatenate all the descriptors in a multi-document YAML file so and dump it over stdout so it can be piped to
kubectlcommand.
Then to apply it you can simply do:
java -cp /path/to/kubernetes-java-1.34.x-1.0.1-all.jar main.java | kubectl apply -f -
|
Tip
|
it is also possible to generate a fake helm chart or kustomization descriptors this way (writing the descriptors instead of printing them over stdout) to reuse helm deployment "reconciliation"/diff/drift handling. |
Maven setup
Maven setup is likely the easiest, what you have to do is:
-
Create a descriptor generation module,
-
Add the needed dependencies in this pom module (see bindings page if needed),
-
Write a main with the generation,
-
Use
exec-maven-pluginto run your main.
Here is a skeleton:
public class MyGenerator {
public static void main(final String... args){
final var output = Files.createDirectories(Path.of(args[0])); (1)
final var deployment = new Deployment() (2)
.spec(new DeploymentSpec()
// ... complete the generation as needed
);
Files.writeString( (3)
output.resolve("my-deployment.json"),
deployment.asJson());
}
private MyGenerator() {
// no-op
}
}
-
Get and create the output directory for this run (generally in
target//${project.build.directory}), -
Create your in memory descriptor,
-
Write your descriptor on the disk (or in a zip directly if you prefer).
Now to execute this main you can just use this exec-maven-plugin in your Apache Maven configuration:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>generate-k8s-descriptors</id>
<phase>prepare-package</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<stopUnresponsiveDaemonThreads>false</stopUnresponsiveDaemonThreads>
<cleanupDaemonThreads>false</cleanupDaemonThreads>
<mainClass>org.superbiz.MyGenerator</mainClass>
<arguments>
<argument>${project.build.directory}/k8s-descriptors</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
Now if you run mvn prepare-package you will get your descriptors dumped in target/k8s-descriptors folder.
|
Tip
|
with java >= 21 (as preview) or >= 25 (default feature) you can write an unamed main() entrypoint which also simplifies running the generation for a CI build.
|
JShell setup
You can use this project with JShell, you just need to ensure the JShell classpath is correct when executing your script. As of today, you must ensure it contains:
-
(optional for some setup but recommended) a JSON-P API and implementation,
-
(optional) a JSON-B API and implementation,
-
The
kubernetes-java-xxxversion you need, -
(optional)
bundlebee-javaif you use it.
Here is how to run a custom JShell script (note it assumes it runs on Linux and the jar were downloaded in current folder):
jshell \
--class-path \
geronimo-json_1.1_spec-1.4-jakarta.jar:johnzon-core-1.2.18-jakarta.jar:kubernetes-java-1.24.3-1.0.0.jar \
my-script.jsh
Then you just need to write your script almost as in plain java:
import io.yupiik.kubernetes.bindings.v1_24_3.v1.*; // (1)
{ // (2)
final var output = Files.createDirectories(Path.of("target/k8s-descriptors"));
final var deployment = new Deployment()
.spec(new DeploymentSpec()
/* ... */);
Files.writeString(
output.resolve("my-deployment.json"),
deployment.asJson());
}
/exit // (3)
-
Import the classes you need (here we use a wildcard import on the model because it is safe for our script but you can use explicit imports),
-
A small trick to write a readable script is to enable multiline snippet by wrapping the code in a block (rest of the code is similar to previous ones),
-
Finally when finished we exit JShell.
|
Tip
|
a way to write the script as a standard Java code with classes and a final standard main class (Runner). This will enable you to setup the dependencies in your IDE and run the main as a standard class (ensure to name your file <scriptname>.java).
Then to execute it through JShell - where /exit is needed - use this command: echo 'Runner.main();/exit' | jshell --class-path …. --startup <scriptname>.java.
This will automatically load your script and exeute the main before exiting the shell.
|
import io.yupiik.kubernetes.bindings.v1_24_3.v1.*;
class Generator {
public void generate() {
final var output = Files.createDirectories(Path.of("target/k8s-descriptors"));
final var deployment = new Deployment()
.spec(new DeploymentSpec()
/* ... */);
Files.writeString(
output.resolve("my-deployment.json"),
deployment.asJson());
}
}
class Runner { // enables to code in an IDE even if not needed by JShell
public static void main(final String... args) throws Exception {
new Generator().generate();
}
}
JBang Setup
JBang can also be used to generate your descriptors. Here is a script directly runnable with one of these runner:
-
From Maven/Gradle
-
From the command line if you installed
jbang(either making your script executable -///usr/bin/env jbang "$0" "$@" ; exit $?or usingjbang $script), -
From
jbang-actions(Github Actions integration)
//DEPS org.apache.geronimo.specs:geronimo-json_1.1_spec:1.4:jakarta
//DEPS org.apache.johnzon:johnzon-core:1.2.18:jakarta
//DEPS io.yupiik.kubernetes:kubernetes-java-1.24.3:1.0-SNAPSHOT
import io.yupiik.kubernetes.bindings.v1_24_3.v1.*;
import java.io.*;
import java.nio.file.*;
class Generator {
public static void main(final String... args) throws IOException {
final var output = Files.createDirectories(Path.of("target/k8s-descriptors"));
final var deployment = new Deployment()
.spec(new DeploymentSpec()
/*...*/);
Files.writeString(
output.resolve("my-deployment.json"),
deployment.asJson());
}
}
It is pretty much the same script than before but we handled the classpath thanks to //DEPS directive which can be convenient if you are already using JBang.
To set it with Github Actions use:
on: [push]
jobs:
jbang:
runs-on: ubuntu-20.04
name: Generate descriptors
steps:
- name: checkout
uses: actions/checkout@v3
- uses: actions/cache@v3
with:
path: /root/.jbang
key: $-jbang-$
restore-keys: |
$-jbang-
- name: jbang
uses: jbangdev/jbang-action@v0.97.0
with:
script: descriptors.generator.java
env:
JBANG_REPO: /root/.jbang/repository