Skip to content

Commit 791ba1c

Browse files
committed
Add support for kubeconfig based client creation.
1 parent b8d40b4 commit 791ba1c

File tree

7 files changed

+475
-7
lines changed

7 files changed

+475
-7
lines changed

examples/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,10 @@
1414
<artifactId>client-java</artifactId>
1515
<version>1.0-SNAPSHOT</version>
1616
</dependency>
17+
<dependency>
18+
<groupId>io.kubernetes</groupId>
19+
<artifactId>client-java-util</artifactId>
20+
<version>1.0-SNAPSHOT</version>
21+
</dependency>
1722
</dependencies>
1823
</project>

examples/src/main/java/io/kubernetes/client/examples/Example.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
import io.kubernetes.client.apis.CoreV1Api;
77
import io.kubernetes.client.models.V1Pod;
88
import io.kubernetes.client.models.V1PodList;
9+
import io.kubernetes.client.util.Config;
10+
11+
import java.io.IOException;
912

1013
/**
1114
* A simple example of how to use the Java API
@@ -18,9 +21,14 @@
1821
* From inside $REPO_DIR/kubernetes
1922
*/
2023
public class Example {
21-
public static void main(String[] args) throws ApiException{
22-
ApiClient client = new ApiClient();
23-
client.setBasePath("http://localhost:8001");
24+
public static void main(String[] args) throws IOException, ApiException{
25+
ApiClient client;
26+
if (args.length == 0) {
27+
client = new ApiClient();
28+
client.setBasePath("http://localhost:8001");
29+
} else {
30+
client = Config.fromConfig(args[0]);
31+
}
2432
Configuration.setDefaultApiClient(client);
2533

2634
CoreV1Api api = new CoreV1Api();

kubernetes/.swagger-codegen-ignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
.gitignore
22
git_push.sh
3+
# Remove this once https://github.com/swagger-api/swagger-codegen/pull/5629
4+
# is merged.
5+
src/main/java/io/kubernetes/client/ApiClient.java
36

kubernetes/src/main/java/io/kubernetes/client/ApiClient.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@
6464

6565
import javax.net.ssl.HostnameVerifier;
6666
import javax.net.ssl.KeyManager;
67-
import javax.net.ssl.KeyManagerFactory;
6867
import javax.net.ssl.SSLContext;
6968
import javax.net.ssl.SSLSession;
7069
import javax.net.ssl.TrustManager;
@@ -127,6 +126,7 @@ public class ApiClient {
127126

128127
private InputStream sslCaCert;
129128
private boolean verifyingSsl;
129+
private KeyManager[] keyManagers;
130130

131131
private OkHttpClient httpClient;
132132
private JSON json;
@@ -271,6 +271,12 @@ public ApiClient setSslCaCert(InputStream sslCaCert) {
271271
return this;
272272
}
273273

274+
public ApiClient setKeyManagers(KeyManager[] managers) {
275+
this.keyManagers = managers;
276+
applySslSettings();
277+
return this;
278+
}
279+
274280
public DateFormat getDateFormat() {
275281
return dateFormat;
276282
}
@@ -1252,7 +1258,6 @@ private void initDatetimeFormat() {
12521258
*/
12531259
private void applySslSettings() {
12541260
try {
1255-
KeyManager[] keyManagers = null;
12561261
TrustManager[] trustManagers = null;
12571262
HostnameVerifier hostnameVerifier = null;
12581263
if (!verifyingSsl) {

util/pom.xml

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,28 @@
1414
<artifactId>client-java</artifactId>
1515
<version>1.0-SNAPSHOT</version>
1616
</dependency>
17+
<dependency>
18+
<groupId>org.yaml</groupId>
19+
<artifactId>snakeyaml</artifactId>
20+
<version>1.18</version>
21+
</dependency>
22+
<dependency>
23+
<groupId>commons-codec</groupId>
24+
<artifactId>commons-codec</artifactId>
25+
<version>1.10</version>
26+
</dependency>
1727
</dependencies>
18-
</project>
28+
<build>
29+
<plugins>
30+
<plugin>
31+
<groupId>org.apache.maven.plugins</groupId>
32+
<artifactId>maven-compiler-plugin</artifactId>
33+
<version>3.1</version>
34+
<configuration>
35+
<source>1.7</source>
36+
<target>1.7</target>
37+
</configuration>
38+
</plugin>
39+
</plugins>
40+
</build>
41+
</project>

util/src/main/java/io/kubernetes/client/util/Config.java

Lines changed: 131 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,35 @@
11
package io.kubernetes.client.util;
22

33
import io.kubernetes.client.ApiClient;
4+
import io.kubernetes.client.ApiException;
5+
import io.kubernetes.client.Configuration;
6+
import io.kubernetes.client.apis.CoreV1Api;
7+
import io.kubernetes.client.models.V1Pod;
8+
import io.kubernetes.client.models.V1PodList;
49

510
import java.io.BufferedReader;
11+
import java.io.ByteArrayInputStream;
612
import java.io.FileReader;
713
import java.io.FileInputStream;
14+
import java.io.FileNotFoundException;
15+
import java.io.InputStream;
16+
import java.io.InputStreamReader;
817
import java.io.IOException;
18+
import java.io.Reader;
19+
import java.io.UnsupportedEncodingException;
20+
import java.security.KeyStoreException;
21+
import java.security.NoSuchAlgorithmException;
22+
import java.security.UnrecoverableKeyException;
23+
import java.security.cert.CertificateException;
24+
import java.util.ArrayList;
25+
import java.util.List;
26+
import java.util.Map;
27+
28+
import javax.net.ssl.KeyManager;
29+
import javax.net.ssl.KeyManagerFactory;
30+
31+
import org.yaml.snakeyaml.Yaml;
32+
import org.yaml.snakeyaml.constructor.SafeConstructor;
933

1034
public class Config {
1135
private static final String SERVICEACCOUNT_ROOT =
@@ -63,4 +87,110 @@ public static ApiClient fromToken(String url, String token, boolean validateSSL)
6387
client.setAccessToken(token);
6488
return client;
6589
}
66-
}
90+
91+
public static ApiClient fromConfig(String fileName) throws IOException {
92+
return fromConfig(new FileReader(fileName));
93+
}
94+
95+
public static ApiClient fromConfig(InputStream stream) {
96+
return fromConfig(new InputStreamReader(stream));
97+
}
98+
99+
public static ApiClient fromConfig(Reader input) {
100+
// Note to the reader: I considered creating a Config object
101+
// and parsing into that instead of using Maps, but honestly
102+
// this seemed cleaner than a bunch of boilerplate classes
103+
Yaml yaml = new Yaml(new SafeConstructor());
104+
Object config = yaml.load(input);
105+
Map<String, Object> configMap = (Map<String, Object>)config;
106+
107+
ArrayList<Object> clusters = (ArrayList<Object>)configMap.get("clusters");
108+
ArrayList<Object> contexts = (ArrayList<Object>)configMap.get("contexts");
109+
ArrayList<Object> users = (ArrayList<Object>)configMap.get("users");
110+
String currentContext = (String)configMap.get("current-context");
111+
112+
Map<String, Object> contextMap = findObject(contexts, currentContext);
113+
if (contextMap == null) {
114+
return null;
115+
}
116+
contextMap = (Map<String, Object>)contextMap.get("context");
117+
118+
String user = (String)contextMap.get("user");
119+
String cluster = (String)contextMap.get("cluster");
120+
121+
Map<String, Object> clusterMap = findObject(clusters, cluster);
122+
if (clusterMap == null) {
123+
return null;
124+
}
125+
clusterMap = (Map<String, Object>)clusterMap.get("cluster");
126+
127+
Map<String, Object> userMap = findObject(users, user);
128+
if (user == null) {
129+
return null;
130+
}
131+
userMap = (Map<String, Object>)userMap.get("user");
132+
133+
String server = (String) clusterMap.get("server");
134+
String caCert = (String) clusterMap.get("certificate-authority-data");
135+
String caCertFile = (String) clusterMap.get("certificate-authority");
136+
137+
ApiClient client = new ApiClient();
138+
client.setBasePath(server);
139+
140+
String clientCertificate = (String) userMap.get("client-certificate");
141+
String clientCertificateData = (String) userMap.get("client-certificate-data");
142+
String clientKey = (String) userMap.get("client-key");
143+
String clientKeyData = (String) userMap.get("client-key-data");
144+
145+
try {
146+
KeyManager[] mgrs = SSLUtils.keyManagers(
147+
clientCertificateData, clientCertificate,
148+
clientKeyData, clientKey,
149+
"RSA", "",
150+
null, null);
151+
client.setKeyManagers(mgrs);
152+
} catch (Exception ex) {
153+
ex.printStackTrace();
154+
}
155+
156+
// It's silly to have to do it in this order, but each SSL setup
157+
// consumes the CA cert, so if we do this before the client certs
158+
// are injected the cert input stream is exhausted and things get
159+
// grumpy'
160+
if (caCert != null) {
161+
try {
162+
client.setSslCaCert(new ByteArrayInputStream(caCert.getBytes("UTF-8")));
163+
} catch (UnsupportedEncodingException ex) {
164+
ex.printStackTrace();
165+
}
166+
} else if (caCertFile != null) {
167+
try {
168+
client.setSslCaCert(new FileInputStream(caCertFile));
169+
} catch (FileNotFoundException ex) {
170+
ex.printStackTrace();
171+
}
172+
}
173+
174+
Object authProvider = userMap.get("auth-provider");
175+
if (authProvider != null) {
176+
Map<String, Object> authProviderMap = (Map<String, Object>) authProvider;
177+
Map<String, Object> authConfig = (Map<String, Object>) authProviderMap.get("config");
178+
if (authConfig != null) {
179+
String token = (String) authConfig.get("access-token");
180+
client.setAccessToken(token);
181+
}
182+
}
183+
184+
return client;
185+
}
186+
187+
private static Map<String, Object> findObject(ArrayList<Object> list, String name) {
188+
for (Object obj : list) {
189+
Map<String, Object> map = (Map<String, Object>)obj;
190+
if (name.equals((String)map.get("name"))) {
191+
return map;
192+
}
193+
}
194+
return null;
195+
}
196+
}

0 commit comments

Comments
 (0)