Skip to content

Commit

Permalink
Fix fabric8io#2308: kubeconfig with external authentication command d…
Browse files Browse the repository at this point in the history
…oesn't work
  • Loading branch information
rohanKanojia committed Jul 31, 2020
1 parent 14cae94 commit cf9c24b
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 37 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -3,6 +3,7 @@
### 4.10-SNAPSHOT
#### Bugs
* Fix #2373: Unable to create a Template on OCP3
* Fix #2308: Fix kubernetes client `Config` loading KUBECONFIG with external authentication command
* Fix #2316: Cannot load resource from stream without apiVersion

#### Improvements
Expand Down
Expand Up @@ -32,6 +32,7 @@
import java.util.Locale;
import java.util.Map;

import org.apache.commons.lang.SystemUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -600,41 +601,11 @@ private static boolean loadFromKubeconfig(Config config, String context, String
} else if (config.getOauthTokenProvider() == null) { // https://kubernetes.io/docs/reference/access-authn-authz/authentication/#client-go-credential-plugins
ExecConfig exec = currentAuthInfo.getExec();
if (exec != null) {
String apiVersion = exec.getApiVersion();
if ("client.authentication.k8s.io/v1alpha1".equals(apiVersion) || "client.authentication.k8s.io/v1beta1".equals(apiVersion)) {
List<String> argv = new ArrayList<String>();
String command = exec.getCommand();
if (command.contains("/") && !command.startsWith("/") && kubeconfigPath != null && !kubeconfigPath.isEmpty()) {
// Appears to be a relative path; normalize. Spec is vague about how to detect this situation.
command = Paths.get(kubeconfigPath).resolveSibling(command).normalize().toString();
}
argv.add(command);
List<String> args = exec.getArgs();
if (args != null) {
argv.addAll(args);
}
ProcessBuilder pb = new ProcessBuilder(argv);
List<ExecEnvVar> env = exec.getEnv();
if (env != null) {
Map<String, String> environment = pb.environment();
env.forEach(var -> environment.put(var.getName(), var.getValue()));
}
// TODO check behavior of tty & stdin
Process p = pb.start();
if (p.waitFor() != 0) {
LOGGER.warn(IOHelpers.readFully(p.getErrorStream()));
}
ExecCredential ec = Serialization.unmarshal(p.getInputStream(), ExecCredential.class);
if (!apiVersion.equals(ec.apiVersion)) {
LOGGER.warn("Wrong apiVersion {} vs. {}", ec.apiVersion, apiVersion);
}
if (ec.status != null && ec.status.token != null) {
config.setOauthToken(ec.status.token);
} else {
LOGGER.warn("No token returned");
}
} else { // TODO v1beta1?
LOGGER.warn("Unsupported apiVersion: {}", apiVersion);
ExecCredential ec = getExecCredentialFromExecConfig(exec, kubeconfigPath);
if (ec != null && ec.status != null && ec.status.token != null) {
config.setOauthToken(ec.status.token);
} else {
LOGGER.warn("No token returned");
}
}
}
Expand All @@ -651,6 +622,63 @@ private static boolean loadFromKubeconfig(Config config, String context, String
return false;
}

protected static ExecCredential getExecCredentialFromExecConfig(ExecConfig exec, String kubeconfigPath) throws IOException, InterruptedException {
String apiVersion = exec.getApiVersion();
if ("client.authentication.k8s.io/v1alpha1".equals(apiVersion) || "client.authentication.k8s.io/v1beta1".equals(apiVersion)) {
List<ExecEnvVar> env = exec.getEnv();
// TODO check behavior of tty & stdin
ProcessBuilder pb = new ProcessBuilder(getAuthenticatorCommandFromExecConfig(exec, kubeconfigPath));
if (env != null) {
Map<String, String> environment = pb.environment();
env.forEach(var -> environment.put(var.getName(), var.getValue()));
}
Process p = pb.start();
if (p.waitFor() != 0) {
LOGGER.warn(IOHelpers.readFully(p.getErrorStream()));
}
ExecCredential ec = Serialization.unmarshal(p.getInputStream(), ExecCredential.class);
if (!apiVersion.equals(ec.apiVersion)) {
LOGGER.warn("Wrong apiVersion {} vs. {}", ec.apiVersion, apiVersion);
} else {
return ec;
}
} else { // TODO v1beta1?
LOGGER.warn("Unsupported apiVersion: {}", apiVersion);
}
return null;
}

protected static List<String> getAuthenticatorCommandFromExecConfig(ExecConfig exec, String kubeconfigPath) {
List<String> argv = new ArrayList<>();
String command = exec.getCommand();
if (command.contains("/") && !command.startsWith("/") && kubeconfigPath != null && !kubeconfigPath.isEmpty()) {
// Appears to be a relative path; normalize. Spec is vague about how to detect this situation.
command = Paths.get(kubeconfigPath).resolveSibling(command).normalize().toString();
}
argv.add(getCommandWithShellPrefix(command));
List<String> args = exec.getArgs();
if (args != null) {
argv.addAll(args);
}
return argv;
}

protected static String getCommandWithShellPrefix(String command) {
File commandFile = new File(command);
// No need to add prefix when path is absolute
if (commandFile.isAbsolute()) {
return command;
}

if (SystemUtils.IS_OS_WINDOWS) {
return "cmd /C start " + command;
} else if (SystemUtils.IS_OS_UNIX) {
return "sh -c " + command;
} else {
return command;
}
}

private static Context setCurrentContext(String context, Config config, io.fabric8.kubernetes.api.model.Config kubeConfig) {
if (context != null) {
kubeConfig.setCurrentContext(context);
Expand Down
Expand Up @@ -16,6 +16,8 @@

package io.fabric8.kubernetes.client;

import io.fabric8.kubernetes.api.model.ExecConfig;
import io.fabric8.kubernetes.api.model.ExecConfigBuilder;
import io.fabric8.kubernetes.client.utils.Serialization;
import io.fabric8.kubernetes.client.utils.Utils;
import okhttp3.OkHttpClient;
Expand All @@ -29,12 +31,10 @@
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
Expand Down Expand Up @@ -577,4 +577,28 @@ private void assertConfig(Config config) {
assertEquals("/path/to/keystore", config.getKeyStoreFile());
assertEquals("keystorePassphrase", config.getKeyStorePassphrase());
}

@Test
void testGetAuthenticatorCommandFromExecConfigOnLinux() {
// Given
ExecConfig execConfig = new ExecConfigBuilder()
.withApiVersion("client.authentication.k8s.io/v1alpha1")
.addToArgs("--region", "us-west2", "eks", "get-token", "--cluster-name", "api-eks.example.com")
.withCommand("aws")
.build();

// When
List<String> processBuilderArgs = Config.getAuthenticatorCommandFromExecConfig(execConfig, "~/.kube/config");

// Then
assertNotNull(processBuilderArgs);
assertEquals(7, processBuilderArgs.size());
assertEquals("sh -c aws", processBuilderArgs.get(0));
assertEquals("--region", processBuilderArgs.get(1));
assertEquals("us-west2", processBuilderArgs.get(2));
assertEquals("eks", processBuilderArgs.get(3));
assertEquals("get-token", processBuilderArgs.get(4));
assertEquals("--cluster-name", processBuilderArgs.get(5));
assertEquals("api-eks.example.com", processBuilderArgs.get(6));
}
}

0 comments on commit cf9c24b

Please sign in to comment.