Skip to content

Commit

Permalink
update token in file listed in KUBECONFIG env var (fabric8io#6240)
Browse files Browse the repository at this point in the history
Signed-off-by: Andre Dietisheim <[email protected]>
  • Loading branch information
adietish committed Aug 26, 2024
1 parent 7dc77ee commit 3960a3c
Show file tree
Hide file tree
Showing 6 changed files with 340 additions and 123 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,11 @@
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;
import java.util.stream.Collectors;

import static io.fabric8.kubernetes.client.internal.KubeConfigUtils.addTo;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(allowGetters = true, allowSetters = true)
public class Config {
Expand Down Expand Up @@ -119,7 +116,9 @@ public class Config {
public static final String KUBERNETES_NAMESPACE_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/namespace";
public static final String KUBERNETES_NAMESPACE_FILE = "kubenamespace";
public static final String KUBERNETES_NAMESPACE_SYSTEM_PROPERTY = "kubernetes.namespace";
@Deprecated
public static final String KUBERNETES_KUBECONFIG_FILE = "kubeconfig";
public static final String KUBERNETES_KUBECONFIG_FILES = "kubeconfig";
public static final String KUBERNETES_SERVICE_HOST_PROPERTY = "KUBERNETES_SERVICE_HOST";
public static final String KUBERNETES_SERVICE_PORT_PROPERTY = "KUBERNETES_SERVICE_PORT";
public static final String KUBERNETES_SERVICE_ACCOUNT_TOKEN_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/token";
Expand Down Expand Up @@ -151,6 +150,8 @@ public class Config {
public static final String HTTP_PROTOCOL_PREFIX = "http://";
public static final String HTTPS_PROTOCOL_PREFIX = "https://";

public static final File DEFAULT_KUBECONFIG_FILE = Paths.get(System.getProperty("user.home"), ".kube", "config").toFile();

private static final String ACCESS_TOKEN = "access-token";
private static final String ID_TOKEN = "id-token";
private static final int DEFAULT_WATCH_RECONNECT_INTERVAL = 1000;
Expand Down Expand Up @@ -235,7 +236,6 @@ public class Config {
private Boolean autoConfigure;

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

@JsonIgnore
Expand Down Expand Up @@ -813,7 +813,7 @@ public static Config fromKubeconfig(String context, String kubeconfigContents, S
// we allow passing context along here, since downstream accepts it
Config config = new Config(false);
if (kubeconfigPath != null) {
config.file = new File(kubeconfigPath);
config.files = Arrays.asList(new File(kubeconfigPath));
}
if (!loadFromKubeconfig(config, context, kubeconfigContents)) {
throw new KubernetesClientException("Could not create Config from kubeconfig");
Expand All @@ -838,14 +838,15 @@ public Config refresh() {
if (this.autoConfigure) {
return Config.autoConfigure(currentContextName);
}
if (this.file != null) {
String kubeconfigContents = getKubeconfigContents(this.file);
if (kubeconfigContents == null) {
return this; // getKubeconfigContents will have logged an exception
File file = getFile();
// if file is null there's nothing to refresh - the kubeconfig was directly supplied
if (file != null) {
String kubeconfigContents = getKubeconfigContents(file);
// getKubeconfigContents will have logged an exception if content is null
if (kubeconfigContents != null) {
return Config.fromKubeconfig(currentContextName, kubeconfigContents, file.getPath());
}
return Config.fromKubeconfig(currentContextName, kubeconfigContents, this.file.getPath());
}
// nothing to refresh - the kubeconfig was directly supplied
return this;
}

Expand All @@ -854,56 +855,42 @@ private static boolean tryKubeConfig(Config config, String context) {
if (!Utils.getSystemPropertyOrEnvVar(KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, true)) {
return false;
}
List<String> kubeConfigFilenames = Arrays.asList(getKubeconfigFilenames());
if (kubeConfigFilenames.isEmpty()) {
return false;
}
List<File> allKubeConfigFiles = kubeConfigFilenames.stream()
.map(File::new)
.collect(Collectors.toList());
File mainKubeConfig = allKubeConfigFiles.get(0);
io.fabric8.kubernetes.api.model.Config kubeConfig = createKubeconfig(mainKubeConfig);
if (kubeConfig == null) {
String[] kubeConfigFilenames = getKubeconfigFilenames();
if (kubeConfigFilenames == null
|| kubeConfigFilenames.length == 0) {
return false;
}
config.file = mainKubeConfig;
config.files = allKubeConfigFiles;

List<File> additionalConfigs = config.files.subList(1, allKubeConfigFiles.size());
addAdditionalConfigs(kubeConfig, additionalConfigs);

return loadFromKubeconfig(config, context, mainKubeConfig);
List<File> allFiles = Arrays.stream(kubeConfigFilenames)
.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 void addAdditionalConfigs(io.fabric8.kubernetes.api.model.Config kubeConfig, List<File> files) {
private static io.fabric8.kubernetes.api.model.Config mergeKubeConfigs(List<File> files) {
if (files == null
|| files.isEmpty()) {
return;
|| files.isEmpty()) {
return null;
}
files.stream()
.map(Config::createKubeconfig)
.filter(Objects::nonNull)
.forEach(additionalConfig -> {
addTo(additionalConfig.getContexts(), kubeConfig::getContexts, kubeConfig::setContexts);
addTo(additionalConfig.getClusters(), kubeConfig::getClusters, kubeConfig::setClusters);
addTo(additionalConfig.getUsers(), kubeConfig::getUsers, kubeConfig::setUsers);
});
return files.stream()
.map(Config::createKubeconfig)
.reduce(null, (merged, additionalConfig) -> {
if (additionalConfig != null) {
return KubeConfigUtils.merge(additionalConfig, merged);
} else {
return merged;
}
});
}

private static io.fabric8.kubernetes.api.model.Config createKubeconfig(File file) {
if (file == null) {
return null;
}
if (!file.isFile()) {
LOGGER.debug("Did not find Kubernetes config at: [{}]. Ignoring.", file.getPath());
return null;
}
io.fabric8.kubernetes.api.model.Config kubeConfig = null;
LOGGER.debug("Found for Kubernetes config at: [{}].", file.getPath());
try {
String content = getKubeconfigContents(file);
if (content != null
&& !content.isEmpty()) {
&& !content.isEmpty()) {
kubeConfig = KubeConfigUtils.parseConfigFromString(content);
}
} catch (KubernetesClientException e) {
Expand All @@ -926,24 +913,44 @@ public static String getKubeconfigFilename() {
fileName = fileNames[0];
if (fileNames.length > 1) {
LOGGER.info("Found multiple Kubernetes config files [{}], returning the first one. Use #getKubeconfigFilenames instead",
fileNames[0]);
fileNames[0]);
}
}
return fileName;
}

public static String[] getKubeconfigFilenames() {
String[] fileNames = null;
String fileName = Utils.getSystemPropertyOrEnvVar(KUBERNETES_KUBECONFIG_FILE);
String fileName = Utils.getSystemPropertyOrEnvVar(KUBERNETES_KUBECONFIG_FILES);
if (fileName != null
&& !fileName.isEmpty()) {
fileNames = fileName.split(File.pathSeparator);
}
if (fileNames == null
|| fileNames.length == 0) {
fileNames = new String[] { DEFAULT_KUBECONFIG_FILE.toString() };
}
return Arrays.stream(fileNames)
.filter(filename ->
isReadableKubeconfFile(new File(filename)))
.toArray(String[]::new);
}

fileNames = fileName.split(File.pathSeparator);
if (fileNames.length == 0) {
fileNames = new String[] { new File(getHomeDir(), ".kube" + File.separator + "config").toString() };
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 fileNames;
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);
Expand All @@ -954,21 +961,20 @@ private static String getKubeconfigContents(File kubeConfigFile) {
return kubeconfigContents;
}

private static boolean loadFromKubeconfig(Config config, String context, File kubeConfigFile) {
String contents = getKubeconfigContents(kubeConfigFile);
if (contents == null) {
return false;
}
return loadFromKubeconfig(config, context, contents);
}

// 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.
private static boolean loadFromKubeconfig(Config config, String context, String kubeconfigContents) {
if (kubeconfigContents != null && !kubeconfigContents.isEmpty()) {
return loadFromKubeconfig(config, context, KubeConfigUtils.parseConfigFromString(kubeconfigContents));
} else {
return false;
}
}

private static boolean loadFromKubeconfig(Config config, String context, io.fabric8.kubernetes.api.model.Config kubeConfig) {
try {
if (kubeconfigContents != null && !kubeconfigContents.isEmpty()) {
io.fabric8.kubernetes.api.model.Config kubeConfig = KubeConfigUtils.parseConfigFromString(kubeconfigContents);
if (kubeConfig != null) {
mergeKubeConfigContents(config, context, kubeConfig);
return true;
}
Expand Down Expand Up @@ -1006,7 +1012,7 @@ private static void mergeKubeConfigAuthInfo(Config config, Cluster currentCluste
String caCertFile = currentCluster.getCertificateAuthority();
String clientCertFile = currentAuthInfo.getClientCertificate();
String clientKeyFile = currentAuthInfo.getClientKey();
File configFile = config.file;
File configFile = config.getFile();
if (configFile != null) {
caCertFile = absolutify(configFile, currentCluster.getCertificateAuthority());
clientCertFile = absolutify(configFile, currentAuthInfo.getClientCertificate());
Expand Down Expand Up @@ -1144,6 +1150,7 @@ protected static String getCommandWithFullyQualifiedPath(String command, String

private static Context setCurrentContext(String context, Config config, io.fabric8.kubernetes.api.model.Config kubeConfig) {
if (context != null) {
// override existing current-context
kubeConfig.setCurrentContext(context);
}
Context currentContext = null;
Expand Down Expand Up @@ -1738,11 +1745,74 @@ 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 path to the kubeConfig file
* @return the kubeConfig file
*/
@Deprecated
public File getFile() {
return file;
if (files != null
&& !files.isEmpty()) {
return files.get(0);
} 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 getFile(String username) {
if (username == null
|| username.isEmpty()) {
return null;
}
return Arrays.stream(getKubeconfigFilenames())
.map(File::new)
.map(file -> {
try {
return new KubeConfigFile(file, KubeConfigUtils.parseConfig(file));
} catch (IOException e) {
return null;
}
})
.filter(entry -> entry != null
&& entry.getConfig() != null
&& hasAuthInfo(username, entry.getConfig()))
.findFirst()
.orElse(null);
}

private boolean hasAuthInfo(String username, io.fabric8.kubernetes.api.model.Config kubeConfig) {
return kubeConfig.getUsers().stream()
.anyMatch(namedAuthInfo -> username.equals(namedAuthInfo.getUser().getUsername()));
}

public static class KubeConfigFile {
private final File file;
private final io.fabric8.kubernetes.api.model.Config config;

private KubeConfigFile(File file, io.fabric8.kubernetes.api.model.Config config) {
this.file = file;
this.config = config;
}

public File getFile() {
return file;
}

public io.fabric8.kubernetes.api.model.Config getConfig() {
return config;
}
}

@JsonIgnore
Expand All @@ -1768,8 +1838,13 @@ public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}

@Deprecated
public void setFile(File file) {
this.file = file;
setFiles(Collections.singletonList(file));
}

public void setFiles(List<File> files) {
this.files = files;
}

public void setAutoConfigure(boolean autoConfigure) {
Expand Down
Loading

0 comments on commit 3960a3c

Please sign in to comment.