Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
update token in file listed in KUBECONFIG env var (#6240)
Browse files Browse the repository at this point in the history
Signed-off-by: Andre Dietisheim <adietish@redhat.com>
adietish committed Aug 27, 2024
1 parent c938cad commit 20ad89a
Showing 6 changed files with 338 additions and 123 deletions.
Original file line number Diff line number Diff line change
@@ -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 {
@@ -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";
@@ -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;
@@ -235,7 +236,6 @@ public class Config {
private Boolean autoConfigure;

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

@JsonIgnore
@@ -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.setFile(new File(kubeconfigPath));
}
if (!loadFromKubeconfig(config, context, kubeconfigContents)) {
throw new KubernetesClientException("Could not create Config from kubeconfig");
@@ -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;
}

@@ -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) {
@@ -926,24 +913,43 @@ 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);
@@ -954,21 +960,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;
}
@@ -1006,7 +1011,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());
@@ -1144,6 +1149,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;
@@ -1738,11 +1744,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
@@ -1769,7 +1838,11 @@ public void setAdditionalProperty(String name, Object value) {
}

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) {
Original file line number Diff line number Diff line change
@@ -18,20 +18,20 @@
import io.fabric8.kubernetes.api.model.AuthInfo;
import io.fabric8.kubernetes.api.model.Cluster;
import io.fabric8.kubernetes.api.model.Config;
import io.fabric8.kubernetes.api.model.ConfigBuilder;
import io.fabric8.kubernetes.api.model.Context;
import io.fabric8.kubernetes.api.model.NamedAuthInfo;
import io.fabric8.kubernetes.api.model.NamedCluster;
import io.fabric8.kubernetes.api.model.NamedContext;
import io.fabric8.kubernetes.api.model.NamedExtension;
import io.fabric8.kubernetes.api.model.PreferencesBuilder;
import io.fabric8.kubernetes.client.utils.Serialization;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Supplier;

/**
* Helper class for working with the YAML config file thats located in
@@ -165,27 +165,41 @@ public static void persistKubeConfigIntoFile(Config kubeConfig, String kubeConfi
}
}

/**
* Adds the given source list to the destination list that's provided by the given supplier
* and then set to the destination by the given setter.
* Creates the list if it doesn't exist yet (supplier returns {@code null}.
* Does not copy if the given list is {@code null}.
*
* @param source the source list to add to the destination
* @param destinationSupplier supplies the list that the source shall be added to
* @param destinationSetter sets the list, once the source was added to it
*/
public static <T> void addTo(List<T> source, Supplier<List<T>> destinationSupplier, Consumer<List<T>> destinationSetter) {
if (source == null) {
return;
public static Config merge(Config thisConfig, Config thatConfig) {
if (thisConfig == null) {
return thatConfig;
}

List<T> list = destinationSupplier.get();
if (list == null) {
list = new ArrayList<>();
ConfigBuilder builder = new ConfigBuilder(thatConfig);
if (thisConfig.getClusters() != null) {
builder.addAllToClusters(thisConfig.getClusters());
}
if (thisConfig.getContexts() != null) {
builder.addAllToContexts(thisConfig.getContexts());
}
if (thisConfig.getUsers() != null) {
builder.addAllToUsers(thisConfig.getUsers());
}
list.addAll(source);
destinationSetter.accept(list);
if (thisConfig.getExtensions() != null) {
builder.addAllToExtensions(thisConfig.getExtensions());
}
if (!builder.hasCurrentContext()
&& thisConfig.getCurrentContext() != null
&& !thisConfig.getCurrentContext().isEmpty()) {
builder.withCurrentContext(thisConfig.getCurrentContext());
}
Config merged = builder.build();
mergePreferences(thisConfig, merged);
return merged;

}

public static void mergePreferences(io.fabric8.kubernetes.api.model.Config source, io.fabric8.kubernetes.api.model.Config destination) {
if (source.getPreferences() != null) {
PreferencesBuilder builder = new PreferencesBuilder(destination.getPreferences());
if (source.getPreferences() != null) {
builder.addToExtensions(source.getExtensions().toArray(new NamedExtension[] {}));
}
destination.setPreferences(builder.build());
}
}
}
Original file line number Diff line number Diff line change
@@ -21,7 +21,6 @@
import io.fabric8.kubernetes.api.model.AuthProviderConfig;
import io.fabric8.kubernetes.api.model.NamedAuthInfo;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.Config.KubeConfigFile;
import io.fabric8.kubernetes.client.KubernetesClientException;
import io.fabric8.kubernetes.client.http.HttpClient;
import io.fabric8.kubernetes.client.http.HttpRequest;
@@ -84,22 +83,22 @@ private OpenIDConnectionUtils() {
* @return access token for interacting with Kubernetes API
*/
public static CompletableFuture<String> resolveOIDCTokenFromAuthConfig(
Config currentConfig, Map<String, String> currentAuthProviderConfig, HttpClient.Builder clientBuilder) {
Config currentConfig, Map<String, String> currentAuthProviderConfig, HttpClient.Builder clientBuilder) {
String originalToken = currentAuthProviderConfig.get(ID_TOKEN_KUBECONFIG);
String idpCert = currentAuthProviderConfig.getOrDefault(IDP_CERT_DATA, getClientCertDataFromConfig(currentConfig));
if (isTokenRefreshSupported(currentAuthProviderConfig)) {
final HttpClient httpClient = initHttpClientWithPemCert(idpCert, clientBuilder);
final CompletableFuture<String> result = getOpenIdConfiguration(httpClient, currentAuthProviderConfig)
.thenCompose(openIdConfiguration -> refreshOpenIdToken(httpClient, currentAuthProviderConfig, openIdConfiguration))
.thenApply(oAuthToken -> persistOAuthToken(currentConfig, oAuthToken, null))
.thenApply(oAuthToken -> {
if (oAuthToken == null || Utils.isNullOrEmpty(oAuthToken.idToken)) {
LOGGER.warn("token response did not contain an id_token, either the scope \\\"openid\\\" wasn't " +
"requested upon login, or the provider doesn't support id_tokens as part of the refresh response.");
return originalToken;
}
return oAuthToken.idToken;
});
.thenCompose(openIdConfiguration -> refreshOpenIdToken(httpClient, currentAuthProviderConfig, openIdConfiguration))
.thenApply(oAuthToken -> persistOAuthToken(currentConfig, oAuthToken, null))
.thenApply(oAuthToken -> {
if (oAuthToken == null || Utils.isNullOrEmpty(oAuthToken.idToken)) {
LOGGER.warn("token response did not contain an id_token, either the scope \\\"openid\\\" wasn't " +
"requested upon login, or the provider doesn't support id_tokens as part of the refresh response.");
return originalToken;
}
return oAuthToken.idToken;
});
result.whenComplete((s, t) -> httpClient.close());
return result;
}
@@ -128,9 +127,9 @@ static boolean isTokenRefreshSupported(Map<String, String> currentAuthProviderCo
* @return the OpenID Configuration as returned by the OpenID provider
*/
private static CompletableFuture<OpenIdConfiguration> getOpenIdConfiguration(HttpClient client,
Map<String, String> authProviderConfig) {
Map<String, String> authProviderConfig) {
final HttpRequest request = client.newHttpRequestBuilder()
.uri(resolveWellKnownUrlForOpenIDIssuer(authProviderConfig)).build();
.uri(resolveWellKnownUrlForOpenIDIssuer(authProviderConfig)).build();
return client.sendAsync(request, String.class).thenApply(response -> {
try {
if (response.isSuccessful() && response.body() != null) {
@@ -151,13 +150,13 @@ private static CompletableFuture<OpenIdConfiguration> getOpenIdConfiguration(Htt
* Issue Token Refresh HTTP Request to OIDC Provider
*/
private static CompletableFuture<OAuthToken> refreshOpenIdToken(
HttpClient httpClient, Map<String, String> authProviderConfig, OpenIdConfiguration openIdConfiguration) {
HttpClient httpClient, Map<String, String> authProviderConfig, OpenIdConfiguration openIdConfiguration) {
if (openIdConfiguration == null || Utils.isNullOrEmpty(openIdConfiguration.tokenEndpoint)) {
LOGGER.warn("oidc: discovery object doesn't contain a valid token endpoint: {}", openIdConfiguration);
return CompletableFuture.completedFuture(null);
}
final HttpRequest request = initTokenRefreshHttpRequest(httpClient, authProviderConfig,
openIdConfiguration.tokenEndpoint);
openIdConfiguration.tokenEndpoint);
return httpClient.sendAsync(request, String.class).thenApply(r -> {
String body = r.body();
if (body != null) {
@@ -202,7 +201,7 @@ private static void persistOAuthTokenToFile(Config currentConfig, String token,
if (currentConfig.getFile() != null && currentConfig.getCurrentContext() != null) {
try {
final String userName = currentConfig.getCurrentContext().getContext().getUser();
KubeConfigFile kubeConfigFile = currentConfig.getFile(userName);
Config.KubeConfigFile kubeConfigFile = currentConfig.getFile(userName);
if (kubeConfigFile == null) {
LOGGER.warn("oidc: failure while persisting new tokens into KUBECONFIG: file for user {} not found", userName);
return;
@@ -217,7 +216,8 @@ private static void persistOAuthTokenToFile(Config currentConfig, String token,
}
}

private static void setAuthProviderAndToken(String token, Map<String, String> authProviderConfig, NamedAuthInfo namedAuthInfo) {
private static void setAuthProviderAndToken(String token, Map<String, String> authProviderConfig,
NamedAuthInfo namedAuthInfo) {
if (namedAuthInfo.getUser() == null) {
namedAuthInfo.setUser(new AuthInfo());
}
@@ -232,19 +232,19 @@ private static void setAuthProviderAndToken(String token, Map<String, String> au

private static NamedAuthInfo getOrCreateNamedAuthInfo(String userName, io.fabric8.kubernetes.api.model.Config kubeConfig) {
return kubeConfig.getUsers().stream()
.filter(n -> n.getName().equals(userName))
.findFirst()
.orElseGet(() -> {
NamedAuthInfo result = new NamedAuthInfo(userName, new AuthInfo());
kubeConfig.getUsers().add(result);
return result;
});
.filter(n -> n.getName().equals(userName))
.findFirst()
.orElseGet(() -> {
NamedAuthInfo result = new NamedAuthInfo(userName, new AuthInfo());
kubeConfig.getUsers().add(result);
return result;
});
}

private static void persistOAuthTokenToFile(AuthProviderConfig config, Map<String, String> authProviderConfig) {
Optional.of(config)
.map(AuthProviderConfig::getConfig)
.ifPresent(c -> c.putAll(authProviderConfig));
.map(AuthProviderConfig::getConfig)
.ifPresent(c -> c.putAll(authProviderConfig));
}

/**
@@ -268,19 +268,19 @@ private static HttpClient initHttpClientWithPemCert(String idpCert, HttpClient.B
clientBuilder.sslContext(keyManagers, trustManagers);
return clientBuilder.build();
} catch (KeyStoreException | InvalidKeySpecException | NoSuchAlgorithmException | IOException | UnrecoverableKeyException
| CertificateException e) {
| CertificateException e) {
throw KubernetesClientException.launderThrowable("Could not import idp certificate", e);
}
}

private static HttpRequest initTokenRefreshHttpRequest(
HttpClient client, Map<String, String> authProviderConfig, String tokenRefreshUrl) {
HttpClient client, Map<String, String> authProviderConfig, String tokenRefreshUrl) {

final String clientId = authProviderConfig.get(CLIENT_ID_KUBECONFIG);
final String clientSecret = authProviderConfig.getOrDefault(CLIENT_SECRET_KUBECONFIG, "");
final HttpRequest.Builder httpRequestBuilder = client.newHttpRequestBuilder().uri(tokenRefreshUrl);
final String credentials = java.util.Base64.getEncoder().encodeToString((clientId + ':' + clientSecret)
.getBytes(StandardCharsets.UTF_8));
.getBytes(StandardCharsets.UTF_8));
httpRequestBuilder.header("Authorization", "Basic " + credentials);

final Map<String, String> requestBody = new LinkedHashMap<>();
@@ -305,8 +305,8 @@ public static boolean idTokenExpired(Config config) {
Map<String, Object> jwtPayloadMap = Serialization.unmarshal(jwtPayloadDecoded, Map.class);
int expiryTimestampInSeconds = (Integer) jwtPayloadMap.get(JWT_TOKEN_EXPIRY_TIMESTAMP_KEY);
return Instant.ofEpochSecond(expiryTimestampInSeconds)
.minusSeconds(TOKEN_EXPIRY_DELTA)
.isBefore(Instant.now());
.minusSeconds(TOKEN_EXPIRY_DELTA)
.isBefore(Instant.now());
} catch (Exception e) {
return true;
}
Original file line number Diff line number Diff line change
@@ -1215,11 +1215,11 @@ void refresh_whenOAuthTokenSourceSetToUser_thenConfigUnchanged() {
}

@Test
void givenEmptyKubeConfig_whenConfigCreated_thenShouldNotProduceNPE() throws URISyntaxException {
void build_givenEmptyKubeConfig_whenConfigCreated_thenShouldNotProduceNPE() throws URISyntaxException {
try {
// Given
System.setProperty("kubeconfig",
new File(Objects.requireNonNull(getClass().getResource("/test-empty-kubeconfig")).toURI()).getAbsolutePath());
getResourceAbsolutePath("/test-empty-kubeconfig"));

// When
Config config = new ConfigBuilder().build();
@@ -1230,4 +1230,118 @@ void givenEmptyKubeConfig_whenConfigCreated_thenShouldNotProduceNPE() throws URI
System.clearProperty("kubeconfig");
}
}

@Test
void build_givenSeveralKubeConfigs_whenAutoconfigured_and_currentContextInFirstFile_then_shouldUseCurrentContextInFirstFile()
throws URISyntaxException {
try {
// Given
System.setProperty(Config.KUBERNETES_KUBECONFIG_FILES,
// only has current-context
getResourceAbsolutePath("/test-kubeconfig-onlycurrentcontext") + File.pathSeparator +
// has all contexts, clusters, users
getResourceAbsolutePath("/test-kubeconfig")
);

// When
Config config = new ConfigBuilder().build();

// Then
assertThat(config.getCurrentContext()).isNotNull();
// current-context set by 1st file, current context in 2nd file is ignored
assertThat(config.getCurrentContext().getName()).isEqualTo("production/172-28-128-4:8443/root");
} finally {
System.clearProperty(Config.KUBERNETES_KUBECONFIG_FILES);
}
}

@Test
void build_givenSeveralKubeConfigs_whenAutoconfigured_and_currentContextInSecondFile_then_shouldUseCurrentContextInSecondFile()
throws URISyntaxException {
try {
// Given
System.setProperty(Config.KUBERNETES_KUBECONFIG_FILES,
// only has current-context
getResourceAbsolutePath("/test-kubeconfig-empty") + File.pathSeparator +
// has all contexts, clusters, users
getResourceAbsolutePath("/test-kubeconfig"));

// When
Config config = new ConfigBuilder().build();

// Then
assertThat(config.getCurrentContext()).isNotNull();
// current-context set by 1st file, current context in 2nd file is ignored
assertThat(config.getCurrentContext().getName()).isEqualTo("testns/172-28-128-4:8443/user");
} finally {
System.clearProperty(Config.KUBERNETES_KUBECONFIG_FILES);
}
}

@Test
void getKubeconfigFilenames_given_filenames_defined_in_KUBECONFIG_then_returns_all_filenames() throws URISyntaxException {
try {
// Given
// only has current-context
String file1 = getResourceAbsolutePath("/test-kubeconfig-empty");
// has all contexts, clusters, users
String file2 = getResourceAbsolutePath("/test-kubeconfig");
System.setProperty(Config.KUBERNETES_KUBECONFIG_FILES, file1 + File.pathSeparator + file2);

// When
String[] filenames = Config.getKubeconfigFilenames();

// Then
assertThat(filenames)
.isNotNull()
.hasSize(2)
.containsExactly(file1, file2);
} finally {
System.clearProperty(Config.KUBERNETES_KUBECONFIG_FILES);
}
}

@Test
void getKubeconfigFilenames_given_directory_defined_in_KUBECONFIG_then_does_not_return_it() throws URISyntaxException {
try {
// Given
// only has current-context
String file1 = new File("/dev/null").getAbsolutePath();
// has all contexts, clusters, users
String file2 = getResourceAbsolutePath("/test-kubeconfig");
System.setProperty(Config.KUBERNETES_KUBECONFIG_FILES, file1 + File.pathSeparator + file2);

// When
String[] filenames = Config.getKubeconfigFilenames();

// Then
assertThat(filenames)
.isNotNull()
.hasSize(1)
.containsExactly(file2);
} finally {
System.clearProperty(Config.KUBERNETES_KUBECONFIG_FILES);
}
}

@Test
void getKubeconfigFilenames_given_KUBECONF_not_set_then_returns_default() {
// Given
// only has current-context
System.clearProperty(Config.KUBERNETES_KUBECONFIG_FILES);

// When
String[] filenames = Config.getKubeconfigFilenames();

// Then
assertThat(filenames)
.isNotNull()
.hasSize(1)
.containsExactly(Config.DEFAULT_KUBECONFIG_FILE.getAbsolutePath());
}

private String getResourceAbsolutePath(String filename) throws URISyntaxException {
return new File(Objects.requireNonNull(getClass().getResource(filename)).toURI()).getAbsolutePath();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: v1
kind: Config
clusters: null
contexts: null
current-context: null
preferences: {}
users: null
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: v1
kind: Config
clusters: null
contexts: null
current-context: production/172-28-128-4:8443/root
preferences: {}
users: null

0 comments on commit 20ad89a

Please sign in to comment.