From e266e8e16e67f5560e3fd2d2318ecb1b36a90cd3 Mon Sep 17 00:00:00 2001 From: Andre Dietisheim Date: Mon, 2 Sep 2024 15:37:39 +0200 Subject: [PATCH] only parse configs once (#6240) Signed-off-by: Andre Dietisheim --- .../io/fabric8/kubernetes/client/Config.java | 142 +++++++----------- .../kubernetes/client/ConfigBuilder.java | 3 +- .../kubernetes/client/ConfigFluent.java | 18 ++- .../kubernetes/client/KubeConfigFile.java | 73 +++++++-- .../kubernetes/client/SundrioConfig.java | 6 +- .../client/utils/OpenIDConnectionUtils.java | 3 +- .../fabric8/kubernetes/client/ConfigTest.java | 4 +- .../kubernetes/client/KubeConfigFileTest.java | 113 ++++++++++++++ .../OpenShiftOAuthInterceptorTest.java | 10 +- 9 files changed, 270 insertions(+), 102 deletions(-) create mode 100644 kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/KubeConfigFileTest.java 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 4bd78388aba..d3736673005 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 @@ -37,6 +37,7 @@ import io.fabric8.kubernetes.client.utils.IOHelpers; import io.fabric8.kubernetes.client.utils.Serialization; import io.fabric8.kubernetes.client.utils.Utils; +import lombok.Getter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -56,7 +57,6 @@ import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Objects; import java.util.Optional; import java.util.function.Predicate; import java.util.function.UnaryOperator; @@ -197,6 +197,8 @@ public class Config { private List contexts; private NamedContext currentContext = null; + @Getter + private List kubeConfigFiles = new ArrayList<>(); /** * fields not used but needed for builder generation. @@ -240,8 +242,6 @@ public class Config { private Boolean autoConfigure; - private List files = new ArrayList<>(); - @JsonIgnore protected Map additionalProperties = new HashMap<>(); @@ -422,7 +422,7 @@ public Config(String masterUrl, String apiVersion, String namespace, Boolean tru String impersonateUsername, String[] impersonateGroups, Map> impersonateExtras, OAuthTokenProvider oauthTokenProvider, Map customHeaders, Integer requestRetryBackoffLimit, Integer requestRetryBackoffInterval, Integer uploadRequestTimeout, Boolean onlyHttpWatches, NamedContext currentContext, - List contexts, Boolean autoConfigure, Boolean shouldSetDefaultValues, List files) { + List contexts, Boolean autoConfigure, Boolean shouldSetDefaultValues, List files) { if (Boolean.TRUE.equals(shouldSetDefaultValues)) { this.masterUrl = DEFAULT_MASTER_URL; this.apiVersion = "v1"; @@ -605,9 +605,8 @@ public Config(String masterUrl, String apiVersion, String namespace, Boolean tru this.customHeaders = customHeaders; this.onlyHttpWatches = onlyHttpWatches; if (Utils.isNotNullOrEmpty(files)) { - this.files = files; + this.kubeConfigFiles = files; } - } public static void configFromSysPropsOrEnvVars(Config config) { @@ -853,8 +852,8 @@ public Config refresh() { return Config.autoConfigure(currentContextName); } // if files is null there's nothing to refresh - the kubeconfigs were directly supplied - if (!Utils.isNullOrEmpty(files)) { - io.fabric8.kubernetes.api.model.Config mergedConfig = mergeKubeConfigs(files); + if (!Utils.isNullOrEmpty(kubeConfigFiles)) { + io.fabric8.kubernetes.api.model.Config mergedConfig = mergeKubeConfigs(kubeConfigFiles); if (mergedConfig != null) { loadFromKubeconfig(this, mergedConfig.getCurrentContext(), mergedConfig); } @@ -871,44 +870,34 @@ private static boolean tryKubeConfig(Config config, String context) { if (Utils.isNullOrEmpty(kubeConfigFilenames)) { return false; } - List allFiles = Arrays.stream(kubeConfigFilenames) + + List files = toFiles(kubeConfigFilenames); + config.setFiles(files); + io.fabric8.kubernetes.api.model.Config mergedConfig = mergeKubeConfigs(config.getKubeConfigFiles()); + return loadFromKubeconfig(config, context, mergedConfig); + } + + private static List toFiles(String[] filenames) { + return Arrays.stream(filenames) .map(File::new) .collect(Collectors.toList()); - config.files = allFiles; - io.fabric8.kubernetes.api.model.Config mergedConfig = mergeKubeConfigs(allFiles); - return loadFromKubeconfig(config, context, mergedConfig); } - private static io.fabric8.kubernetes.api.model.Config mergeKubeConfigs(List files) { + private static io.fabric8.kubernetes.api.model.Config mergeKubeConfigs(List files) { if (Utils.isNullOrEmpty(files)) { return null; } return files.stream() - .map(Config::createKubeconfig) + .map(KubeConfigFile::getConfig) .reduce(null, (merged, additionalConfig) -> { - if (additionalConfig != null) { - return KubeConfigUtils.merge(additionalConfig, merged); - } else { + if (additionalConfig == null) { return merged; + } else { + return KubeConfigUtils.merge(additionalConfig, merged); } }); } - private static io.fabric8.kubernetes.api.model.Config createKubeconfig(File file) { - io.fabric8.kubernetes.api.model.Config kubeConfig = null; - LOGGER.debug("Found for Kubernetes config at: [{}].", file.getPath()); - try { - String content = getKubeconfigContents(file); - if (Utils.isNotNullOrEmpty(content)) { - kubeConfig = KubeConfigUtils.parseConfigFromString(content); - } - } catch (KubernetesClientException e) { - LOGGER.error("Could not load Kubernetes config [{}].", file.getPath(), e); - } - - return kubeConfig; - } - /** * @deprecated use {@link #getKubeconfigFilenames()} instead */ @@ -917,7 +906,7 @@ public static String getKubeconfigFilename() { String fileName = null; String[] fileNames = getKubeconfigFilenames(); // if system property/env var contains multiple files take the first one based on the environment - if (!Utils.isNullOrEmpty(fileNames)) { + if (Utils.isNotNullOrEmpty(fileNames)) { fileName = fileNames[0]; if (fileNames.length > 1) { LOGGER.info("Found multiple Kubernetes config files [{}], returning the first one. Use #getKubeconfigFilenames instead", @@ -939,31 +928,6 @@ public static String[] getKubeconfigFilenames() { return fileNames; } - private static boolean isReadableKubeconfFile(File file) { - if (file == null) { - return false; - } - if (!file.isFile()) { - LOGGER.debug("Did not find Kubernetes config at: [{}]. Ignoring.", file.getPath()); - return false; - } - return true; - } - - private static String getKubeconfigContents(File kubeConfigFile) { - if (kubeConfigFile == null) { - return null; - } - String kubeconfigContents = null; - try (FileReader reader = new FileReader(kubeConfigFile)) { - kubeconfigContents = IOHelpers.readFully(reader); - } catch (IOException e) { - LOGGER.error("Could not load Kubernetes config file from {}", kubeConfigFile.getPath(), e); - return null; - } - return kubeconfigContents; - } - // Note: kubeconfigPath is optional // It is only used to rewrite relative tls asset paths inside kubeconfig when a file is passed, and in the case that // the kubeconfig references some assets via relative paths. @@ -1748,44 +1712,23 @@ public void setCurrentContext(NamedContext context) { * * Returns the path to the file that this configuration was loaded from. Returns {@code null} if no file was used. * - * @deprecated use {@link #getFiles} instead. - * * @return the kubeConfig file */ public File getFile() { - if (Utils.isNotNullOrEmpty(files)) { - return files.get(0); + if (Utils.isNotNullOrEmpty(kubeConfigFiles)) { + return kubeConfigFiles.get(0).getFile(); } else { return null; } } - /** - * Returns the kube config files that are used to configure this client. - * Returns the files that are listed in the KUBERNETES_KUBECONFIG_FILES env or system variables. - * Returns the default kube config file if it's not set'. - * - * @return - */ - public List getFiles() { - return files; - } - public KubeConfigFile getFileWithAuthInfo(String name) { if (Utils.isNullOrEmpty(name) || Utils.isNullOrEmpty(getFiles())) { return null; } - return getFiles().stream() - .filter(Config::isReadableKubeconfFile) - .map(file -> { - try { - return new KubeConfigFile(file, KubeConfigUtils.parseConfig(file)); - } catch (IOException e) { - return null; - } - }) - .filter(Objects::nonNull) + return kubeConfigFiles.stream() + .filter(KubeConfigFile::isReadable) .filter(entry -> hasAuthInfoNamed(name, entry.getConfig())) .findFirst() .orElse(null); @@ -1829,7 +1772,38 @@ public void setFile(File file) { } public void setFiles(List files) { - this.files = files; + if (Utils.isNullOrEmpty(files)) { + this.kubeConfigFiles = Collections.emptyList(); + } else { + this.kubeConfigFiles = files.stream() + .map(KubeConfigFile::new) + .collect(Collectors.toList()); + } + } + + public void setKubeConfigFiles(List files) { + if (Utils.isNullOrEmpty(files)) { + this.kubeConfigFiles = Collections.emptyList(); + } else { + this.kubeConfigFiles = files; + } + } + + /** + * Returns the kube config files that are used to configure this client. + * Returns the files that are listed in the KUBERNETES_KUBECONFIG_FILES env or system variables. + * Returns the default kube config file if it's not set'. + * + * @return + */ + public List getFiles() { + if (this.kubeConfigFiles == null) { + return Collections.emptyList(); + } else { + return kubeConfigFiles.stream() + .map(KubeConfigFile::getFile) + .collect(Collectors.toList()); + } } public void setAutoConfigure(boolean autoConfigure) { diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/ConfigBuilder.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/ConfigBuilder.java index 3c3c6ae30fb..c5c99bc529b 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/ConfigBuilder.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/ConfigBuilder.java @@ -57,8 +57,9 @@ public Config build() { fluent.getOauthTokenProvider(), fluent.getCustomHeaders(), fluent.getRequestRetryBackoffLimit(), fluent.getRequestRetryBackoffInterval(), fluent.getUploadRequestTimeout(), fluent.getOnlyHttpWatches(), fluent.getCurrentContext(), fluent.getContexts(), - Optional.ofNullable(fluent.getAutoConfigure()).orElse(!disableAutoConfig()), true, fluent.getFiles()); + Optional.ofNullable(fluent.getAutoConfigure()).orElse(!disableAutoConfig()), true, fluent.getKubeConfigFiles()); buildable.setAuthProvider(fluent.getAuthProvider()); return buildable; } + } diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/ConfigFluent.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/ConfigFluent.java index 91458b3a684..ae6696d5950 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/ConfigFluent.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/ConfigFluent.java @@ -15,6 +15,11 @@ */ package io.fabric8.kubernetes.client; +import java.io.File; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + public class ConfigFluent> extends SundrioConfigFluent { public ConfigFluent() { super(); @@ -78,7 +83,7 @@ public void copyInstance(Config instance) { this.withContexts(instance.getContexts()); this.withAutoConfigure(instance.getAutoConfigure()); this.withAuthProvider(instance.getAuthProvider()); - this.withFiles(instance.getFiles()); + this.withKubeConfigFiles(instance.getKubeConfigFiles()); } } @@ -148,4 +153,15 @@ public A withAutoConfigure(boolean autoConfigure) { return this.withAutoConfigure(Boolean.valueOf(autoConfigure)); } + public A withFiles(File... files) { + if (files != null + && files.length > 0) { + List configFiles = Arrays.stream(files) + .map(KubeConfigFile::new) + .collect(Collectors.toList()); + withKubeConfigFiles(configFiles); + } + return (A) this; + } + } diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/KubeConfigFile.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/KubeConfigFile.java index 54d9d4dc782..bce3f2ba61c 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/KubeConfigFile.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/KubeConfigFile.java @@ -15,18 +15,73 @@ */ package io.fabric8.kubernetes.client; -import java.io.File; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; import io.fabric8.kubernetes.api.model.Config; +import io.fabric8.kubernetes.client.internal.KubeConfigUtils; import lombok.Getter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; -@Getter public class KubeConfigFile { - private final File file; - private final Config config; - /** for testing purposes **/ - public KubeConfigFile(File file, Config config) { - this.file = file; - this.config = config; + private static final Logger LOGGER = LoggerFactory.getLogger(KubeConfigFile.class); + + @Getter + private final File file; + private boolean parsed = false; + private Config config; + + @JsonCreator + public KubeConfigFile(@JsonProperty("file") String file) { + this(new File(file), null); + } + + public KubeConfigFile(File file) { + this(file, null); + } + + private KubeConfigFile(File file, Config config) { + this.file = file; + this.config = config; + } + + public Config getConfig() { + if (!parsed) { + this.config = createConfig(file); + this.parsed = true; + } + return config; + } + + private Config createConfig(File file) { + Config created = null; + try { + if (isReadable(file)) { + LOGGER.debug("Found for Kubernetes config at: [{}].", file.getPath()); + created = KubeConfigUtils.parseConfig(file); + } + } catch (IOException e) { + LOGGER.debug("Kubernetes file at [{}] is not a valid config. Ignoring.", file.getPath(), e); + } + return created; + } + + @JsonIgnore + public boolean isReadable() { + return isReadable(file); + } + + private boolean isReadable(File file) { + try { + return file != null + && file.isFile(); + } catch (SecurityException e) { + return false; } - } \ No newline at end of file + } +} \ No newline at end of file diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/SundrioConfig.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/SundrioConfig.java index c4e789b110b..91c8afee732 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/SundrioConfig.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/SundrioConfig.java @@ -19,7 +19,6 @@ import io.fabric8.kubernetes.client.http.TlsVersion; import io.sundr.builder.annotations.Buildable; -import java.io.File; import java.util.List; import java.util.Map; @@ -51,7 +50,7 @@ public SundrioConfig(String masterUrl, String apiVersion, String namespace, Bool String impersonateUsername, String[] impersonateGroups, Map> impersonateExtras, OAuthTokenProvider oauthTokenProvider, Map customHeaders, Integer requestRetryBackoffLimit, Integer requestRetryBackoffInterval, Integer uploadRequestTimeout, Boolean onlyHttpWatches, NamedContext currentContext, - List contexts, Boolean autoConfigure, List files) { + List contexts, Boolean autoConfigure, List kubeConfigFiles) { super(masterUrl, apiVersion, namespace, trustCerts, disableHostnameVerification, caCertFile, caCertData, clientCertFile, clientCertData, clientKeyFile, clientKeyData, clientKeyAlgo, clientKeyPassphrase, username, password, oauthToken, autoOAuthToken, watchReconnectInterval, watchReconnectLimit, connectionTimeout, requestTimeout, @@ -59,6 +58,7 @@ public SundrioConfig(String masterUrl, String apiVersion, String namespace, Bool httpProxy, httpsProxy, noProxy, userAgent, tlsVersions, websocketPingInterval, proxyUsername, proxyPassword, trustStoreFile, trustStorePassphrase, keyStoreFile, keyStorePassphrase, impersonateUsername, impersonateGroups, impersonateExtras, oauthTokenProvider, customHeaders, requestRetryBackoffLimit, requestRetryBackoffInterval, - uploadRequestTimeout, onlyHttpWatches, currentContext, contexts, autoConfigure, true, files); + uploadRequestTimeout, onlyHttpWatches, currentContext, contexts, autoConfigure, true, kubeConfigFiles); } + } diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/OpenIDConnectionUtils.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/OpenIDConnectionUtils.java index 13b02769d6f..6cc77d74d62 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/OpenIDConnectionUtils.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/OpenIDConnectionUtils.java @@ -205,7 +205,8 @@ private static void persistOAuthTokenToFile(Config currentConfig, String token, try { final String userName = currentConfig.getCurrentContext().getContext().getUser(); KubeConfigFile kubeConfigFile = currentConfig.getFileWithAuthInfo(userName); - if (kubeConfigFile == null) { + if (kubeConfigFile == null + || kubeConfigFile.getConfig() == null) { LOGGER.warn("oidc: failure while persisting new tokens into KUBECONFIG: file for user {} not found", userName); return; } 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 d5057e4ee2a..c2b334dad72 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 @@ -1262,7 +1262,7 @@ void builder_given_severalKubeConfigsAndCurrentContextInSecondFile_then_shouldUs // Given System.setProperty("kubeconfig", getResourceAbsolutePath("/test-kubeconfig-empty") + File.pathSeparator + - getResourceAbsolutePath("/test-kubeconfig")); + getResourceAbsolutePath("/test-kubeconfig")); // When Config config = new ConfigBuilder().build(); @@ -1403,7 +1403,7 @@ void getFilenames_given_KUBECONFNotSet_then_returnsDefault() throws URISyntaxExc String fileWithToken = getResourceAbsolutePath("/test-kubeconfig-oidc"); System.setProperty("kubeconfig", fileWithToken + File.pathSeparator + - Config.DEFAULT_KUBECONFIG_FILE.getAbsolutePath()); + Config.DEFAULT_KUBECONFIG_FILE.getAbsolutePath()); Config config = new ConfigBuilder().build(); // When diff --git a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/KubeConfigFileTest.java b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/KubeConfigFileTest.java new file mode 100644 index 00000000000..d231716808e --- /dev/null +++ b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/KubeConfigFileTest.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.fabric8.kubernetes.client; + +import io.fabric8.kubernetes.api.model.Config; +import io.fabric8.kubernetes.client.internal.KubeConfigUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + +import java.io.File; +import java.net.URISyntaxException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Objects; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; + +class KubeConfigFileTest { + + private KubeConfigFile kubeConfig; + private KubeConfigFile noFileConfig; + + @BeforeEach + void setUp() throws URISyntaxException { + Path path = Paths.get(Objects.requireNonNull(ConfigTest.class.getResource("/test-kubeconfig")).toURI()); + this.kubeConfig = new KubeConfigFile(path.toFile()); + this.noFileConfig = new KubeConfigFile(new File("bogus")); + } + + @Test + void isReadable_whenFileDoesNotExist_returnsFalse() { + // given + // when + boolean readable = noFileConfig.isReadable(); + // then + assertThat(readable).isFalse(); + } + + @Test + void isReadable_whenFileIsDirectory_returnsFalse() { + // given + KubeConfigFile config = new KubeConfigFile(new File(System.getProperty("user.dir"))); + // when + boolean readable = config.isReadable(); + // then + assertThat(readable).isFalse(); + } + + @Test + void isReadable_whenFileExist_returnsTrue() { + // given + boolean readable = kubeConfig.isReadable(); + // then + assertThat(readable).isTrue(); + } + + @Test + void getConfig_whenFileExists_returnsConfig() { + // given + Config config = kubeConfig.getConfig(); + // then + assertThat(config).isNotNull(); + } + + @Test + void getConfig_whenGettingConfig_isParsingLazily() { + // given + try (MockedStatic utilsMock = Mockito.mockStatic(KubeConfigUtils.class)) { + utilsMock.verify(() -> KubeConfigUtils.parseConfig(any()), Mockito.never()); + // when + kubeConfig.getConfig(); + // then + utilsMock.verify(() -> KubeConfigUtils.parseConfig(kubeConfig.getFile()), Mockito.times(1)); + } + } + + @Test + void getConfig_whenGettingConfig_isNotParsingASecondTime() { + // given + try (MockedStatic utilsMock = Mockito.mockStatic(KubeConfigUtils.class)) { + kubeConfig.getConfig(); + // when + kubeConfig.getConfig(); + // then + utilsMock.verify(() -> KubeConfigUtils.parseConfig(kubeConfig.getFile()), Mockito.times(1)); + } + } + + @Test + void getConfig_whenFileDoesntExist_returnsNull() { + // given + Config config = noFileConfig.getConfig(); + // then + assertThat(config).isNull(); + } + +} diff --git a/openshift-client/src/test/java/io/fabric8/openshift/client/internal/OpenShiftOAuthInterceptorTest.java b/openshift-client/src/test/java/io/fabric8/openshift/client/internal/OpenShiftOAuthInterceptorTest.java index 61ef0f53b21..986b50dcd04 100644 --- a/openshift-client/src/test/java/io/fabric8/openshift/client/internal/OpenShiftOAuthInterceptorTest.java +++ b/openshift-client/src/test/java/io/fabric8/openshift/client/internal/OpenShiftOAuthInterceptorTest.java @@ -185,7 +185,8 @@ void afterFailure_withUsernamePassword_thenShouldAuthorize() { when(kubeConfigContent.getUsers()).thenReturn(users); File file = new File("kube/config"); when(config.getFiles()).thenReturn(Collections.singletonList(file)); - when(config.getFileWithAuthInfo(any())).thenReturn(new KubeConfigFile(file, kubeConfigContent)); + KubeConfigFile kubeConfigFile = mockKubeConfigFile(file, kubeConfigContent); + when(config.getFileWithAuthInfo(any())).thenReturn(kubeConfigFile); kubeConfigUtilsMockedStatic.when(() -> KubeConfigUtils.parseConfig(any())).thenReturn(kubeConfigContent); when(client.newBuilder()).thenReturn(derivedClientBuilder); when(client.newHttpRequestBuilder()).thenReturn(builder); @@ -216,6 +217,13 @@ void afterFailure_withUsernamePassword_thenShouldAuthorize() { } } + private static KubeConfigFile mockKubeConfigFile(File file, io.fabric8.kubernetes.api.model.Config kubeConfigContent) { + KubeConfigFile kubeConfigFile = mock(KubeConfigFile.class, RETURNS_SELF); + when(kubeConfigFile.getFile()).thenReturn(file); + when(kubeConfigFile.getConfig()).thenReturn(kubeConfigContent); + return kubeConfigFile; + } + @Test void testTokenRefreshedFromConfigForWebSocketBuilder() { Config config = mockConfigRefresh();