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 3, 2024
1 parent 9e8819d commit 9e5dcae
Show file tree
Hide file tree
Showing 9 changed files with 217 additions and 85 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
Expand Down Expand Up @@ -197,6 +197,8 @@ public class Config {

private List<NamedContext> contexts;
private NamedContext currentContext = null;
@Getter
private List<KubeConfigFile> kubeConfigFiles = new ArrayList<>();

/**
* fields not used but needed for builder generation.
Expand Down Expand Up @@ -240,8 +242,6 @@ public class Config {

private Boolean autoConfigure;

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

@JsonIgnore
protected Map<String, Object> additionalProperties = new HashMap<>();

Expand Down Expand Up @@ -422,7 +422,7 @@ public Config(String masterUrl, String apiVersion, String namespace, Boolean tru
String impersonateUsername, String[] impersonateGroups, Map<String, List<String>> impersonateExtras,
OAuthTokenProvider oauthTokenProvider, Map<String, String> customHeaders, Integer requestRetryBackoffLimit,
Integer requestRetryBackoffInterval, Integer uploadRequestTimeout, Boolean onlyHttpWatches, NamedContext currentContext,
List<NamedContext> contexts, Boolean autoConfigure, Boolean shouldSetDefaultValues, List<File> files) {
List<NamedContext> contexts, Boolean autoConfigure, Boolean shouldSetDefaultValues, List<KubeConfigFile> files) {
if (Boolean.TRUE.equals(shouldSetDefaultValues)) {
this.masterUrl = DEFAULT_MASTER_URL;
this.apiVersion = "v1";
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
}
Expand All @@ -871,44 +870,34 @@ private static boolean tryKubeConfig(Config config, String context) {
if (Utils.isNullOrEmpty(kubeConfigFilenames)) {
return false;
}
List<File> allFiles = Arrays.stream(kubeConfigFilenames)

List<File> files = toFiles(kubeConfigFilenames);
config.setFiles(files);
io.fabric8.kubernetes.api.model.Config mergedConfig = mergeKubeConfigs(config.getKubeConfigFiles());
return loadFromKubeconfig(config, context, mergedConfig);
}

private static List<File> 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<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 +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;
Expand Down Expand Up @@ -1753,39 +1731,20 @@ 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;
}
}

/**
* 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<File> 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);
Expand Down Expand Up @@ -1829,7 +1788,38 @@ 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());
}
}

public void setKubeConfigFiles(List<KubeConfigFile> files) {
if (Utils.isNullOrEmpty(files)) {
this.kubeConfigFiles = Collections.emptyList();
} else {
this.kubeConfigFiles = kubeConfigFiles;
}
}

/**
* 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<File> getFiles() {
if (this.kubeConfigFiles == null) {
return Collections.emptyList();
} else {
return kubeConfigFiles.stream()
.map(KubeConfigFile::getFile)
.collect(Collectors.toList());
}
}

public void setAutoConfigure(boolean autoConfigure) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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<A extends ConfigFluent<A>> extends SundrioConfigFluent<A> {
public ConfigFluent() {
super();
Expand Down Expand Up @@ -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());
}
}

Expand Down Expand Up @@ -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<KubeConfigFile> configFiles = Arrays.stream(files)
.map(KubeConfigFile::new)
.collect(Collectors.toList());
withKubeConfigFiles(configFiles);
}
return (A) this;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,68 @@
*/
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(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 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 @@ -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;

Expand Down Expand Up @@ -51,14 +50,15 @@ public SundrioConfig(String masterUrl, String apiVersion, String namespace, Bool
String impersonateUsername, String[] impersonateGroups, Map<String, List<String>> impersonateExtras,
OAuthTokenProvider oauthTokenProvider, Map<String, String> customHeaders, Integer requestRetryBackoffLimit,
Integer requestRetryBackoffInterval, Integer uploadRequestTimeout, Boolean onlyHttpWatches, NamedContext currentContext,
List<NamedContext> contexts, Boolean autoConfigure, List<File> files) {
List<NamedContext> contexts, Boolean autoConfigure, List<KubeConfigFile> kubeConfigFiles) {
super(masterUrl, apiVersion, namespace, trustCerts, disableHostnameVerification, caCertFile, caCertData,
clientCertFile, clientCertData, clientKeyFile, clientKeyData, clientKeyAlgo, clientKeyPassphrase, username,
password, oauthToken, autoOAuthToken, watchReconnectInterval, watchReconnectLimit, connectionTimeout, requestTimeout,
scaleTimeout, loggingInterval, maxConcurrentRequests, maxConcurrentRequestsPerHost, http2Disable,
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);
}

}
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
Loading

0 comments on commit 9e5dcae

Please sign in to comment.