Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use kubeconfigs listed in KUBECONFIG env var #6295

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* Fix #6281: use GitHub binary repo for Kube API Tests
* Fix #6282: Allow annotated types with Pattern, Min, and Max with Lists and Maps and CRD generation
* Fix #5480: Move `io.fabric8:zjsonpatch` to KubernetesClient project
* Fix #6240: Use kubeconfig files listed in the KUBECONFIG env var

#### Dependency Upgrade
* Fix #2632: Bumped OkHttp from 3.12.12 to 4.12.0
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +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);
Optional.ofNullable(fluent.getAutoConfigure()).orElse(!disableAutoConfig()), true, fluent.getKubeConfigFiles());
buildable.setAuthProvider(fluent.getAuthProvider());
buildable.setFile(fluent.getFile());
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.withFile(instance.getFile());
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
@@ -0,0 +1,87 @@
/*
* 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 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;

public class KubeConfigFile {

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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,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<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);
uploadRequestTimeout, onlyHttpWatches, currentContext, contexts, autoConfigure, true, kubeConfigFiles);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +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 io.fabric8.kubernetes.client.utils.Utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.util.List;

/**
Expand All @@ -40,7 +44,7 @@ private KubeConfigUtils() {
}

public static Config parseConfig(File file) throws IOException {
return Serialization.unmarshal(new FileInputStream(file), Config.class);
return Serialization.unmarshal(Files.newInputStream(file.toPath()), Config.class);
}

public static Config parseConfigFromString(String contents) {
Expand All @@ -56,16 +60,31 @@ public static Config parseConfigFromString(String contents) {
public static NamedContext getCurrentContext(Config config) {
String contextName = config.getCurrentContext();
if (contextName != null) {
return getContext(config, contextName);
}
return null;
}

/**
* Returns the {@link NamedContext} with the given name.
* Returns {@code null} otherwise
*
* @param config the config to search
* @param name the context name to match
* @return the context with the the given name
*/
public static NamedContext getContext(Config config, String name) {
NamedContext context = null;
if (config != null && name != null) {
List<NamedContext> contexts = config.getContexts();
if (contexts != null) {
for (NamedContext context : contexts) {
if (contextName.equals(context.getName())) {
return context;
}
}
context = contexts.stream()
.filter(toInspect -> name.equals(toInspect.getName()))
.findAny()
.orElse(null);
}
}
return null;
return context;
}

/**
Expand All @@ -91,23 +110,49 @@ public static String getUserToken(Config config, Context context) {
* @return {@link AuthInfo} for current context
*/
public static AuthInfo getUserAuthInfo(Config config, Context context) {
AuthInfo authInfo = null;
if (config != null && context != null) {
String user = context.getUser();
if (user != null) {
List<NamedAuthInfo> users = config.getUsers();
if (users != null) {
authInfo = users.stream()
.filter(u -> u.getName().equals(user))
.findAny()
.map(NamedAuthInfo::getUser)
.orElse(null);
}
NamedAuthInfo namedAuthInfo = getAuthInfo(config, context.getUser());
return (namedAuthInfo != null) ? namedAuthInfo.getUser() : null;
}

/**
* Returns the {@link NamedAuthInfo} with the given name.
* Returns {@code null} otherwise
*
* @param config the config to search
* @param name
* @return
*/
public static NamedAuthInfo getAuthInfo(Config config, String name) {
NamedAuthInfo authInfo = null;
if (config != null && name != null) {
List<NamedAuthInfo> users = config.getUsers();
if (users != null) {
authInfo = users.stream()
.filter(toInspect -> name.equals(toInspect.getName()))
.findAny()
.orElse(null);
}
}
return authInfo;
}

/**
* Returns {@code true} if the given {@link Config} has a {@link NamedAuthInfo} with the given name.
* Returns {@code false} otherwise.
*
* @param name the name of the NamedAuthInfo that we are looking for
* @param config the Config to search
* @return true if it contains a NamedAuthInfo with the given name
*/
public static boolean hasAuthInfoNamed(Config config, String name) {
if (Utils.isNullOrEmpty(name)
|| config == null
|| config.getUsers() == null) {
return false;
}
return getAuthInfo(config, name) != null;
}

/**
* Returns the current {@link Cluster} for the current context
*
Expand Down Expand Up @@ -161,4 +206,39 @@ public static void persistKubeConfigIntoFile(Config kubeConfig, String kubeConfi
writer.write(Serialization.asYaml(kubeConfig));
}
}

public static Config merge(Config thisConfig, Config thatConfig) {
if (thisConfig == null) {
return thatConfig;
}
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());
}
if (thisConfig.getExtensions() != null) {
builder.addAllToExtensions(thisConfig.getExtensions());
}
if (!builder.hasCurrentContext()
&& Utils.isNotNullOrEmpty(thisConfig.getCurrentContext())) {
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());
builder.addToExtensions(source.getExtensions().toArray(new NamedExtension[] {}));
destination.setPreferences(builder.build());
}
}
}
Loading
Loading