If you use kubectl
frequently, you might be find yourself running the same sequence of commands over and over again.
A frequent use case yielding such sequences is for viewing a secret
which requires to find the secret and decode its base64 form.
In this tutorial, I'll go through how to create a kubectl
plugin in Java. The plugin will do the following after we execute the kubectl lp
command :
- List the pods with their name, status and a default empty message
- If the pod is not in running state, display a π₯ icon in front of its line, otherwise display a β icon
- Display a message why the pod is not running
Jbang
To write the plugin in Java, I will use jbang.
Jbang is a framework that brings scripting to the Java land.
If you don't have Jbang yet installed, just run the following :
brew install jbangdev/tap/jbang
Or check the documentation to find the instructions for your operating system.
The next step is to create a template with the initializer, I'll call the script lp
for list pods
:
jbang init --template=cli lp.java
It will create a fully functional script lp.java
with the following content :
///usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS info.picocli:picocli:4.5.0
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Parameters;
import java.util.concurrent.Callable;
@Command(name = "lp", mixinStandardHelpOptions = true, version = "lp 0.1",
description = "lp made with jbang")
class lp implements Callable<Integer> {
@Parameters(index = "0", description = "The greeting to print", defaultValue = "World!")
private String greeting;
public static void main(String... args) {
int exitCode = new CommandLine(new lp()).execute(args);
System.exit(exitCode);
}
@Override
public Integer call() throws Exception { // your business logic goes here...
System.out.println("Hello " + greeting);
return 0;
}
}
Which is a simple template that greets you, try it with the following command :
$ jbang lp.java Chris
Hello Chris
$
Adding dependencies to your scripts is as simple as adding a comment in the beginning of the file with the dependency artifact :
//DEPS io.fabric8:kubernetes-client:4.13.0
Here, we added the fabric8
kubernetes client library. While we're at it, let's see how this client works.
Fabric8
The fabric8 kubernetes client is a library that provides access to the Kubernetes Rest API via a simple and human friendly DSL.
For eg, here is how you find only the pods that are not in a RUNNING
status :
kc.pods().list().getItems()
.stream()
.filter(pod -> !PodStatusUtil.isRunning(pod))
.collect(Collectors.toList());
For our scenario, we will use the client to retrieve all the pods and create a new domain model PodInfo
with the name, the state and an optional message :
private static List<PodInfo> getPods() {
KubernetesClient kc;
try {
kc = new DefaultKubernetesClient();
} catch (Exception e) {
throw new RuntimeException("Unable to create default Kubernetes client", e);
}
return kc.pods().list().getItems().stream().map(pod -> {
PodInfoState state = PodStatusUtil.isRunning(pod) ? PodInfoState.RUNNING : PodInfoState.FAILING;
String message = null;
if (!state.equals(PodInfoState.RUNNING)) {
message = PodStatusUtil.getContainerStatus(pod).get(0).getState().getWaiting().getMessage();
}
return new PodInfo(pod.getMetadata().getName(), state, message);
}).collect(Collectors.toList());
}
static class PodInfo {
private final String name;
private final PodInfoState state;
private final String message;
public PodInfo(String name, PodInfoState state, String message) {
this.name = name;
this.state = state;
this.message = message;
}
public String getName() {
return name;
}
public PodInfoState getState() {
return state;
}
public String getMessage() {
return message;
}
}
enum PodInfoState {
RUNNING,
FAILING
}
We'll also add the j-text-utils
java library so we can display everything in a table
//DEPS com.massisframework:j-text-utils:0.3.4
private static void printTable(List<PodInfo> list) {
final Object[][] tableData = list.stream()
.map(podInfo -> new Object[]{
podInfo.getState().equals(PodInfoState.RUNNING) ? CHECK_MARK : FIRE,
podInfo.getName(),
podInfo.getState(),
podInfo.getMessage()
})
.toArray(Object[][]::new);
String[] columnNames = {"", "name", "state", "message"};
new TextTable(columnNames, tableData).printTable();
}
The full code can be found here : https://github.com/ikwattro/kubectl-plugin-java-jbang
If we run our script, we can see it in action :
$ jbang lp.java
[jbang] Building jar...
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
________________________________________________________________________________________
| | name | state | message |
|=======================================================================================|
| β
| neo4j-neo4j-core-0 | RUNNING| |
| β
| nginx-app | RUNNING| |
| π₯| nginx-deployment-77fbdb7874-8gkcr| FAILING| Back-off pulling image "nginx2:1.16.1"|
| π₯| nginx-deployment-77fbdb7874-8jps7| FAILING| Back-off pulling image "nginx2:1.16.1"|
| π₯| nginx-deployment-77fbdb7874-vx27r| FAILING| Back-off pulling image "nginx2:1.16.1"|
| β
| postgres | RUNNING| |
| β
| coredns-f9fd979d6-qwgfm | RUNNING| |
| β
| etcd-minikube | RUNNING| |
| β
| kube-apiserver-minikube | RUNNING| |
| β
| kube-controller-manager-minikube | RUNNING| |
| β
| kube-proxy-s5bcc | RUNNING| |
| β
| kube-scheduler-minikube | RUNNING| |
| β
| storage-provisioner | RUNNING| |
$
Extending kubectl
Extending kubectl
is very easy as it has mainly 3 conventions :
- You need to place your script in your path
- The name of the script should start with
kubectl-
and not have an extension - The script should be executable
We will need to pay attention, because when generating the jbang
template, the very first line was commented for playing nicely with IDEs when editing the script.
Just uncomment the line :
#!/usr/bin/env jbang
Secondly, we will copy the script to our path :
cp lp.java /usr/local/bin/kubectl-lp
And make it executable :
chmod +x /usr/local/bin/kubectl-lp
You can now run kubectl lp
for listing your pods :
$ kubectl lp
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
________________________________________________________________________________________
| | name | state | message |
|=======================================================================================|
| β
| neo4j-neo4j-core-0 | RUNNING| |
| β
| nginx-app | RUNNING| |
| π₯| nginx-deployment-77fbdb7874-8gkcr| FAILING| Back-off pulling image "nginx2:1.16.1"|
| π₯| nginx-deployment-77fbdb7874-8jps7| FAILING| Back-off pulling image "nginx2:1.16.1"|
| π₯| nginx-deployment-77fbdb7874-vx27r| FAILING| Back-off pulling image "nginx2:1.16.1"|
| β
| postgres | RUNNING| |
| β
| coredns-f9fd979d6-qwgfm | RUNNING| |
| β
| etcd-minikube | RUNNING| |
| β
| kube-apiserver-minikube | RUNNING| |
| β
| kube-controller-manager-minikube | RUNNING| |
| β
| kube-proxy-s5bcc | RUNNING| |
| β
| kube-scheduler-minikube | RUNNING| |
| β
| storage-provisioner | RUNNING| |
$
Conclusion
The Kubectl tool is easily extensible and Jbang allows to write scripts in Java in no time, where we would probably have turned to Python before that tool existed.
References :
- The code from this blog post : https://github.com/ikwattro/kubectl-plugin-java-jbang
-
kubectl
: plugins documentation : https://kubernetes.io/docs/tasks/extend-kubectl/kubectl-plugins/ -
jbang
: https://github.com/jbangdev/jbang#installation -
fabric8
: https://github.com/fabric8io/kubernetes-client
Top comments (1)
Great post. I usually create aliases for the frequently used commands, but this is a much more powerful and scalable solution π