This project is an example of how to extend kubernetes cli (kubectl) with a Java application. It demonstrates the use of Fabric8 Kubernetes Client and access the Kubernetes API. In order to be a kubectl plugin it is necessary to have a file which uses the defined naming convention of kube-<plugin-name>
. This is a challenge in Java. This project shows the use of Graal in order to build a native image using the gradle palantir plugin.
In order to provide a good CLI experience picocli is used.
This work and demo is provided by Marc as a followup to a demo of the same using client-java. His original work is https://github.com/marcnuri-demo/k8s-cli-java. What a great reminder of how the value the internet can provided! Besides providing a great example of fabric8, it has a number of Java improvements worth showcasing.
- Running Kubernetes 1.15+ cluster. Kind 0.7.0 was used for testing.
- Java 1.8 and Graal
- Gradle
Run ./gradlew native
This should boot strap gradle (version 6.1.1), it will also pull down graalvm and nativeImage
command used to build the nativeImage.
The output will show a number of graal warnings which for the purposes of this demonstration can be ignored.
example output:
21:12 $ ./gradlew clean native
> Task :nativeImage
Build on Server(pid: 38354, port: 55534)
[kubectl-java:38354] classlist: 5,357.88 ms
[kubectl-java:38354] (cap): 1,602.01 ms
[kubectl-java:38354] setup: 1,785.98 ms
[kubectl-java:38354] (typeflow): 3,291.87 ms
[kubectl-java:38354] (objects): 3,970.93 ms
[kubectl-java:38354] (features): 199.88 ms
[kubectl-java:38354] analysis: 7,622.07 ms
[kubectl-java:38354] (clinit): 139.81 ms
[kubectl-java:38354] universe: 244.68 ms
Warning: Reflection method java.lang.Class.forName invoked at org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider$2.run(Unknown Source)
Warning: Reflection method java.lang.Class.forName invoked at org.bouncycastle.jcajce.provider.symmetric.util.ClassUtil$1.run(Unknown Source)
Warning: Reflection method java.lang.Class.forName invoked at picocli.CommandLine$BuiltIn$ClassConverter.convert(CommandLine.java:12467)
Warning: Reflection method java.lang.Class.newInstance invoked at picocli.CommandLine$DefaultFactory.create(CommandLine.java:4814)
Warning: Reflection method java.lang.Class.newInstance invoked at org.bouncycastle.jce.provider.BouncyCastleProvider.loadAlgorithms(Unknown Source)
Warning: Reflection method java.lang.Class.newInstance invoked at org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider.loadAlgorithms(Unknown Source)
Warning: Reflection method java.lang.Class.getMethods invoked at picocli.CommandLine.getCommandMethods(CommandLine.java:2980)
Warning: Reflection method java.lang.Class.getDeclaredMethods invoked at picocli.CommandLine.getCommandMethods(CommandLine.java:2982)
Warning: Reflection method java.lang.Class.getDeclaredMethods invoked at picocli.CommandLine$Model$CommandReflection.initFromAnnotatedFields(CommandLine.java:9784)
Warning: Reflection method java.lang.Class.getDeclaredConstructor invoked at picocli.CommandLine$DefaultFactory.create(CommandLine.java:4808)
Warning: Reflection method java.lang.Class.getDeclaredConstructor invoked at picocli.CommandLine$DefaultFactory.create(CommandLine.java:4817)
Warning: Reflection method java.lang.Class.getDeclaredFields invoked at picocli.CommandLine$Model$CommandReflection.initFromAnnotatedFields(CommandLine.java:9781)
Warning: Reflection method java.lang.ClassLoader.loadClass invoked at org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider.loadClass(Unknown Source)
Warning: Reflection method java.lang.ClassLoader.loadClass invoked at org.bouncycastle.jcajce.provider.symmetric.util.ClassUtil.loadClass(Unknown Source)
Warning: Aborting stand-alone image build due to reflection use without configuration.
Warning: Use -H:+ReportExceptionStackTraces to print stacktrace of underlying exception
Build on Server(pid: 38354, port: 55534)
[kubectl-java:38354] classlist: 86.63 ms
[kubectl-java:38354] (cap): 1,163.05 ms
[kubectl-java:38354] setup: 1,325.03 ms
[kubectl-java:38354] (typeflow): 1,351.53 ms
[kubectl-java:38354] (objects): 1,487.00 ms
[kubectl-java:38354] (features): 84.36 ms
[kubectl-java:38354] analysis: 2,962.47 ms
[kubectl-java:38354] (clinit): 46.62 ms
[kubectl-java:38354] universe: 102.52 ms
[kubectl-java:38354] (parse): 109.68 ms
[kubectl-java:38354] (inline): 530.39 ms
[kubectl-java:38354] (compile): 422.22 ms
[kubectl-java:38354] compile: 1,174.70 ms
[kubectl-java:38354] image: 159.67 ms
[kubectl-java:38354] write: 114.39 ms
[kubectl-java:38354] [total]: 5,952.42 ms
Warning: Image 'kubectl-java' is a fallback image that requires a JDK for execution (use --no-fallback to suppress fallback image generation).
native image available at build/graal/kubectl-java (3 MB)
Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/6.1.1/userguide/command_line_interface.html#sec:command_line_warnings
BUILD SUCCESSFUL in 23s
4 actionable tasks: 4 executed
This will result in an executable kubectl-java
being built in build/graal/
under the project root.
Start a cluster: kind create cluster
and run one of the commands: ./build/graal/kubectl-java pod list
Example:
./build/graal/kubectl-java pod list
________________________________________________________________
| Pod Name | namespace |
|===============================================================|
| coredns-6955765f44-f966p | kube-system |
| coredns-6955765f44-xnzbg | kube-system |
| etcd-kind-control-plane | kube-system |
| kindnet-cznll | kube-system |
| kube-apiserver-kind-control-plane | kube-system |
| kube-controller-manager-kind-control-plane| kube-system |
| kube-proxy-tw9cb | kube-system |
| kube-scheduler-kind-control-plane | kube-system |
| local-path-provisioner-7745554f7f-gk5j9 | local-path-storage|
- pod list
- pod delete [-n namespace]
- pod add [-n namespace] [-i image]
- resources
The executable needs to be the path. From the root of the project run: export PATH=$PATH:$PWD/build/graal/
Now give kubectl
a try with java
. run: kubectl java pod list
You should get the same output as above.