kubernetes-client/java

KubeConfig.runExec doesn't work with kubeconfig exec command

Closed this issue · 6 comments

Describe the bug

This issue occurs with any kubeconfig file which uses an exec command to get user credentials.

When instantiating the ApiClient class through ClientBuilder.kubeconfig and a kubeconfig file built with KubeConfig.loadKubeConfig, the following error occurs:

java.lang.NullPointerException: Cannot invoke "java.io.File.toPath()" because "this.file" is null
    at io.kubernetes.client.util.KubeConfig.runExec (KubeConfig.java:333)
    at io.kubernetes.client.util.KubeConfig.getCredentialsViaExecCredential (KubeConfig.java:291)
    at io.kubernetes.client.util.KubeConfig.getCredentials (KubeConfig.java:239)
    at io.kubernetes.client.util.credentials.KubeconfigAuthentication.<init> (KubeconfigAuthentication.java:59)
    at io.kubernetes.client.util.ClientBuilder.kubeconfig (ClientBuilder.java:300)
    at io.kubernetes.client.examples.KubeConfigFileClientExample.main (KubeConfigFileClientExample.java:43)
    at org.codehaus.mojo.exec.ExecJavaMojo.lambda$execute$0 (ExecJavaMojo.java:283)
    at java.lang.Thread.run (Thread.java:833)

It seems that the KubeConfig.runExec method expects the kubeconfig to be provided as a file rather than a KubeConfig object.

The KubeConfigFileClientExample.java example can be fixed with the following changes:

diff --git a/examples/examples-release-20/src/main/java/io/kubernetes/client/examples/KubeConfigFileClientExample.java b/examples/examples-release-20/src/main/java/io/kubernetes/client/examples/KubeConfigFileClientExample.java
index 7a0ba4dcf..bb62b7365 100644
--- a/examples/examples-release-20/src/main/java/io/kubernetes/client/examples/KubeConfigFileClientExample.java
+++ b/examples/examples-release-20/src/main/java/io/kubernetes/client/examples/KubeConfigFileClientExample.java
@@ -22,6 +22,7 @@ import io.kubernetes.client.util.ClientBuilder;
 import io.kubernetes.client.util.KubeConfig;
 import java.io.FileReader;
 import java.io.IOException;
+import java.nio.file.Paths;
 
 /**
  * A simple example of how to use the Java API from an application outside a kubernetes cluster
@@ -37,10 +38,12 @@ public class KubeConfigFileClientExample {
     // file path to your KubeConfig
 
     String kubeConfigPath = System.getenv("HOME") + "/.kube/config";
+    KubeConfig kubeconfig = KubeConfig.loadKubeConfig(new FileReader(kubeConfigPath));
+    kubeconfig.setFile(Paths.get(kubeConfigPath).toFile());
 
     // loading the out-of-cluster config, a kubeconfig from file-system
     ApiClient client =
-        ClientBuilder.kubeconfig(KubeConfig.loadKubeConfig(new FileReader(kubeConfigPath))).build();
+        ClientBuilder.kubeconfig(kubeconfig).build();
 
     // set the global default api-client to the in-cluster one from above
     Configuration.setDefaultApiClient(client);

Client Version

20

Kubernetes Version

1.28.6

Java Version

17

To Reproduce

Generate a kubeconfig file in $HOME/.kube/config which uses an exec command to get user credentials.

cd examples/examples-release-20
mvn -X clean install exec:java -Dexec.mainClass="io.kubernetes.client.examples.KubeConfigFileClientExample"

Expected behavior

Everything works fine

KubeConfig

apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: REDACTED
    server: REDACTED
    tls-server-name: REDACTED
  name: REDACTED
contexts:
- context:
    cluster: REDACTED
    extensions:
    - extension: sandbox
      name: teleport.kube.name
    user: REDACTED
  name: REDACTED
current-context: REDACTED
kind: Config
preferences: {}
users:
- name: REDACTED
  user:
    exec:
      apiVersion: client.authentication.k8s.io/v1beta1
      args:
      - kube
      - credentials
      - --kube-cluster=sandbox
      - --teleport-cluster=REDACTED
      - --proxy=REDACTED
      command: tsh
      env: null
      provideClusterInfo: false

Server (please complete the following information):

  • OS: Linux
  • Environment: system
  • Cloud: Azure

Additional context

Ah yes, I see the problem. I think in cases where the file for the kubeconfig isn't present, we should just use the current working directory.

In this case the kubeconfig file is not even in the current directory, it’s located in its default location ($HOME/.kube/config).

Wouldn’t it be best to just use the data loaded into the KubeConfig object instead? I don’t know Java very well, but from what I understand of the code, it seems that the required information is present in the object.
Since we have the contexts and the users, I guess it should be possible to extract the exec command from the user of the current context. WDYT?

I looked at the code and I don't think that the kubeconfig you supplied could have triggered this exception.

The code is here:

https://github.com/kubernetes-client/java/blob/release-20/util/src/main/java/io/kubernetes/client/util/KubeConfig.java#L333

And in order to throw that exception, your command needs to have a slash in it (/ or \) but in your example above, there's no slash it's just tsh

I think that there actually is a bug here, but to debug properly I need the correct kubeconfig which you used.

And in order to throw that exception, your command needs to have a slash in it (/ or \) but in your example above, there's no slash it's just tsh

I think that there actually is a bug here, but to debug properly I need the correct kubeconfig which you used.

That's my bad, I copied the kubeconfig example on the three issues I opened at once and since I did multiple tests to try to make each of them work, I kept the command as tsh but in fact it's an absolute path from my root.

I don't think the real path is super relevant but it's something like that: /nix/store/xyz-tsh/bin/tsh.

Sorry about that and thanks for the quick fix! If you wish me to, I can test the fix tomorrow and let you know.

The Kubernetes project currently lacks enough contributors to adequately respond to all issues.

This bot triages un-triaged issues according to the following rules:

  • After 90d of inactivity, lifecycle/stale is applied
  • After 30d of inactivity since lifecycle/stale was applied, lifecycle/rotten is applied
  • After 30d of inactivity since lifecycle/rotten was applied, the issue is closed

You can:

  • Mark this issue as fresh with /remove-lifecycle stale
  • Close this issue with /close
  • Offer to help out with Issue Triage

Please send feedback to sig-contributor-experience at kubernetes/community.

/lifecycle stale