Skip to content

Commit

Permalink
only parse configs once (#6240)
Browse files Browse the repository at this point in the history
Signed-off-by: Andre Dietisheim <[email protected]>
  • Loading branch information
adietish committed Sep 2, 2024
1 parent 9e8819d commit 5bc8b16
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,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;
Expand Down Expand Up @@ -240,7 +239,7 @@ public class Config {

private Boolean autoConfigure;

private List<File> files = new ArrayList<>();
private List<KubeConfigFile> kubeConfigFiles = new ArrayList<>();

@JsonIgnore
protected Map<String, Object> additionalProperties = new HashMap<>();
Expand Down Expand Up @@ -605,9 +604,8 @@ public Config(String masterUrl, String apiVersion, String namespace, Boolean tru
this.customHeaders = customHeaders;
this.onlyHttpWatches = onlyHttpWatches;
if (Utils.isNotNullOrEmpty(files)) {
this.files = files;
setFiles(files);
}

}

public static void configFromSysPropsOrEnvVars(Config config) {
Expand Down Expand Up @@ -853,8 +851,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);
}
Expand All @@ -871,44 +869,26 @@ private static boolean tryKubeConfig(Config config, String context) {
if (Utils.isNullOrEmpty(kubeConfigFilenames)) {
return false;
}
List<File> allFiles = Arrays.stream(kubeConfigFilenames)
.map(File::new)
.collect(Collectors.toList());
config.files = allFiles;
io.fabric8.kubernetes.api.model.Config mergedConfig = mergeKubeConfigs(allFiles);
config.setFiles(kubeConfigFilenames);
io.fabric8.kubernetes.api.model.Config mergedConfig = mergeKubeConfigs(config.kubeConfigFiles);
return loadFromKubeconfig(config, context, mergedConfig);
}

private static io.fabric8.kubernetes.api.model.Config mergeKubeConfigs(List<File> files) {
private static io.fabric8.kubernetes.api.model.Config mergeKubeConfigs(List<KubeConfigFile> 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
*/
Expand Down Expand Up @@ -939,17 +919,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;
Expand Down Expand Up @@ -1753,8 +1722,8 @@ public void setCurrentContext(NamedContext context) {
* @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;
}
Expand All @@ -1768,24 +1737,22 @@ public File getFile() {
* @return
*/
public List<File> getFiles() {
return files;
if (this.kubeConfigFiles == null) {
return Collections.emptyList();
} else {
return kubeConfigFiles.stream()
.map(KubeConfigFile::getFile)
.collect(Collectors.toList());
}
}

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);
Expand Down Expand Up @@ -1829,7 +1796,23 @@ public void setFile(File file) {
}

public void setFiles(List<File> files) {
this.files = files;
if (Utils.isNullOrEmpty(files)) {
this.kubeConfigFiles = Collections.emptyList();
} else {
this.kubeConfigFiles = files.stream()
.map(KubeConfigFile::new)
.collect(Collectors.toList());
}
}

private void setFiles(String[] files) {
if (Utils.isNullOrEmpty(files)) {
setFiles(Collections.emptyList());
} else {
setFiles(Arrays.stream(files)
.map(File::new)
.collect(Collectors.toList()));
}
}

public void setAutoConfigure(boolean autoConfigure) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,64 @@
*/
package io.fabric8.kubernetes.client;

import java.io.File;
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;

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 config = null;
try {
if (isReadable(file)) {
LOGGER.debug("Found for Kubernetes config at: [{}].", file.getPath());
config = KubeConfigUtils.parseConfig(file);
}
} catch (IOException e) {
LOGGER.debug("Kubernetes file at [{}] is not a valid config. Ignoring.", file.getPath(), e);
}
return config;
}

public boolean isReadable() {
return isReadable(file);
}

private boolean isReadable(File file) {
try {
return file != null
&& file.isFile();
} catch (SecurityException e) {
return false;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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();
Expand Down

0 comments on commit 5bc8b16

Please sign in to comment.