diff --git a/CHANGELOG.md b/CHANGELOG.md index 83c4724d361..a037bbe68ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### 6.8-SNAPSHOT #### Bugs +* Fix #5221: Empty kube config file causes NPE * Fix #5281: Ensure the KubernetesCrudDispatcher's backing map is accessed w/lock #### Improvements diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/Config.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/Config.java index bf9fd9530e8..14a8a43482a 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/Config.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/Config.java @@ -702,73 +702,98 @@ private static String getKubeconfigContents(File kubeConfigFile) { // the kubeconfig references some assets via relative paths. private static boolean loadFromKubeconfig(Config config, String context, String kubeconfigContents) { try { - io.fabric8.kubernetes.api.model.Config kubeConfig = KubeConfigUtils.parseConfigFromString(kubeconfigContents); - config.setContexts(kubeConfig.getContexts()); - Context currentContext = setCurrentContext(context, config, kubeConfig); - Cluster currentCluster = KubeConfigUtils.getCluster(kubeConfig, currentContext); - if (currentContext != null) { - config.setNamespace(currentContext.getNamespace()); - } - if (currentCluster != null) { - config.setMasterUrl(currentCluster.getServer()); - config.setTrustCerts(currentCluster.getInsecureSkipTlsVerify() != null && currentCluster.getInsecureSkipTlsVerify()); - config.setDisableHostnameVerification( - currentCluster.getInsecureSkipTlsVerify() != null && currentCluster.getInsecureSkipTlsVerify()); - config.setCaCertData(currentCluster.getCertificateAuthorityData()); - AuthInfo currentAuthInfo = KubeConfigUtils.getUserAuthInfo(kubeConfig, currentContext); - if (currentAuthInfo != null) { - // rewrite tls asset paths if needed - String caCertFile = currentCluster.getCertificateAuthority(); - String clientCertFile = currentAuthInfo.getClientCertificate(); - String clientKeyFile = currentAuthInfo.getClientKey(); - File configFile = config.file; - if (configFile != null) { - caCertFile = absolutify(configFile, currentCluster.getCertificateAuthority()); - clientCertFile = absolutify(configFile, currentAuthInfo.getClientCertificate()); - clientKeyFile = absolutify(configFile, currentAuthInfo.getClientKey()); - } - config.setCaCertFile(caCertFile); - config.setClientCertFile(clientCertFile); - config.setClientCertData(currentAuthInfo.getClientCertificateData()); - config.setClientKeyFile(clientKeyFile); - config.setClientKeyData(currentAuthInfo.getClientKeyData()); - config.setClientKeyAlgo(getKeyAlgorithm(config.getClientKeyFile(), config.getClientKeyData())); - config.setAutoOAuthToken(currentAuthInfo.getToken()); - config.setUsername(currentAuthInfo.getUsername()); - config.setPassword(currentAuthInfo.getPassword()); - - if (Utils.isNullOrEmpty(config.getAutoOAuthToken()) && currentAuthInfo.getAuthProvider() != null) { - if (currentAuthInfo.getAuthProvider().getConfig() != null) { - config.setAuthProvider(currentAuthInfo.getAuthProvider()); - if (!Utils.isNullOrEmpty(currentAuthInfo.getAuthProvider().getConfig().get(ACCESS_TOKEN))) { - // GKE token - config.setAutoOAuthToken(currentAuthInfo.getAuthProvider().getConfig().get(ACCESS_TOKEN)); - } else if (!Utils.isNullOrEmpty(currentAuthInfo.getAuthProvider().getConfig().get(ID_TOKEN))) { - // OpenID Connect token - config.setAutoOAuthToken(currentAuthInfo.getAuthProvider().getConfig().get(ID_TOKEN)); - } - } - } else if (config.getOauthTokenProvider() == null) { // https://kubernetes.io/docs/reference/access-authn-authz/authentication/#client-go-credential-plugins - ExecConfig exec = currentAuthInfo.getExec(); - if (exec != null) { - ExecCredential ec = getExecCredentialFromExecConfig(exec, configFile); - if (ec != null && ec.status != null && ec.status.token != null) { - config.setAutoOAuthToken(ec.status.token); - } else { - LOGGER.warn("No token returned"); - } - } - } - } + if (kubeconfigContents != null && !kubeconfigContents.isEmpty()) { + io.fabric8.kubernetes.api.model.Config kubeConfig = KubeConfigUtils.parseConfigFromString(kubeconfigContents); + mergeKubeConfigContents(config, context, kubeConfig); return true; } - } catch (Exception e) { - throw KubernetesClientException.launderThrowable("Failed to parse the kubeconfig.", e); + } catch (IOException ioException) { + throw KubernetesClientException.launderThrowable("Failed to parse the kubeconfig.", ioException); } return false; } + private static void mergeKubeConfigContents(Config config, String context, io.fabric8.kubernetes.api.model.Config kubeConfig) + throws IOException { + config.setContexts(kubeConfig.getContexts()); + Context currentContext = setCurrentContext(context, config, kubeConfig); + Cluster currentCluster = KubeConfigUtils.getCluster(kubeConfig, currentContext); + if (currentContext != null) { + config.setNamespace(currentContext.getNamespace()); + } + if (currentCluster != null) { + config.setMasterUrl(currentCluster.getServer()); + config.setTrustCerts(currentCluster.getInsecureSkipTlsVerify() != null && currentCluster.getInsecureSkipTlsVerify()); + config.setDisableHostnameVerification( + currentCluster.getInsecureSkipTlsVerify() != null && currentCluster.getInsecureSkipTlsVerify()); + config.setCaCertData(currentCluster.getCertificateAuthorityData()); + AuthInfo currentAuthInfo = KubeConfigUtils.getUserAuthInfo(kubeConfig, currentContext); + if (currentAuthInfo != null) { + mergeKubeConfigAuthInfo(config, currentCluster, currentAuthInfo); + } + } + } + + private static void mergeKubeConfigAuthInfo(Config config, Cluster currentCluster, AuthInfo currentAuthInfo) + throws IOException { + // rewrite tls asset paths if needed + String caCertFile = currentCluster.getCertificateAuthority(); + String clientCertFile = currentAuthInfo.getClientCertificate(); + String clientKeyFile = currentAuthInfo.getClientKey(); + File configFile = config.file; + if (configFile != null) { + caCertFile = absolutify(configFile, currentCluster.getCertificateAuthority()); + clientCertFile = absolutify(configFile, currentAuthInfo.getClientCertificate()); + clientKeyFile = absolutify(configFile, currentAuthInfo.getClientKey()); + } + config.setCaCertFile(caCertFile); + config.setClientCertFile(clientCertFile); + config.setClientCertData(currentAuthInfo.getClientCertificateData()); + config.setClientKeyFile(clientKeyFile); + config.setClientKeyData(currentAuthInfo.getClientKeyData()); + config.setClientKeyAlgo(getKeyAlgorithm(config.getClientKeyFile(), config.getClientKeyData())); + config.setAutoOAuthToken(currentAuthInfo.getToken()); + config.setUsername(currentAuthInfo.getUsername()); + config.setPassword(currentAuthInfo.getPassword()); + + if (Utils.isNullOrEmpty(config.getAutoOAuthToken()) && currentAuthInfo.getAuthProvider() != null) { + mergeKubeConfigAuthProviderConfig(config, currentAuthInfo); + } else if (config.getOauthTokenProvider() == null) { // https://kubernetes.io/docs/reference/access-authn-authz/authentication/#client-go-credential-plugins + mergeKubeConfigExecCredential(config, currentAuthInfo.getExec(), configFile); + } + } + + private static void mergeKubeConfigAuthProviderConfig(Config config, AuthInfo currentAuthInfo) { + if (currentAuthInfo.getAuthProvider().getConfig() != null) { + config.setAuthProvider(currentAuthInfo.getAuthProvider()); + if (!Utils.isNullOrEmpty(currentAuthInfo.getAuthProvider().getConfig().get(ACCESS_TOKEN))) { + // GKE token + config.setAutoOAuthToken(currentAuthInfo.getAuthProvider().getConfig().get(ACCESS_TOKEN)); + } else if (!Utils.isNullOrEmpty(currentAuthInfo.getAuthProvider().getConfig().get(ID_TOKEN))) { + // OpenID Connect token + config.setAutoOAuthToken(currentAuthInfo.getAuthProvider().getConfig().get(ID_TOKEN)); + } + } + } + + private static void mergeKubeConfigExecCredential(Config config, ExecConfig exec, File configFile) + throws IOException { + if (exec != null) { + try { + ExecCredential ec = getExecCredentialFromExecConfig(exec, configFile); + if (ec != null && ec.status != null && ec.status.token != null) { + config.setAutoOAuthToken(ec.status.token); + } else { + LOGGER.warn("No token returned"); + } + } catch (InterruptedException interruptedException) { + Thread.currentThread().interrupt(); + throw KubernetesClientException.launderThrowable("failure while running exec credential ", interruptedException); + } + } + } + protected static ExecCredential getExecCredentialFromExecConfig(ExecConfig exec, File configFile) throws IOException, InterruptedException { String apiVersion = exec.getApiVersion(); diff --git a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/ConfigTest.java b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/ConfigTest.java index 17646047fa7..8308b7b2eba 100644 --- a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/ConfigTest.java +++ b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/ConfigTest.java @@ -37,6 +37,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertArrayEquals; @@ -824,4 +825,17 @@ void refresh_whenOAuthTokenSourceSetToUser_thenConfigUnchanged() { .isSameAs(config) .hasFieldOrPropertyWithValue("oauthToken", "token-from-user"); } + + @Test + void givenEmptyKubeConfig_whenConfigCreated_thenShouldNotProduceNPE() throws URISyntaxException { + // Given + System.setProperty(Config.KUBERNETES_KUBECONFIG_FILE, + new File(Objects.requireNonNull(getClass().getResource("/test-empty-kubeconfig")).toURI()).getAbsolutePath()); + + // When + Config config = new ConfigBuilder().build(); + + // Then + assertThat(config).isNotNull(); + } } diff --git a/kubernetes-client-api/src/test/resources/test-empty-kubeconfig b/kubernetes-client-api/src/test/resources/test-empty-kubeconfig new file mode 100644 index 00000000000..e69de29bb2d