Skip to content

Commit ad22d1e

Browse files
Merge pull request #37 from brendandburns/plugins
Add an authenticator interface and implementation.
2 parents 5e2bf35 + e7a637e commit ad22d1e

File tree

4 files changed

+157
-9
lines changed

4 files changed

+157
-9
lines changed

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

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,14 @@
2222
import java.nio.file.FileSystems;
2323
import java.util.ArrayList;
2424
import java.util.Date;
25+
import java.util.HashMap;
2526
import java.util.Map;
2627

2728
import org.yaml.snakeyaml.Yaml;
2829
import org.yaml.snakeyaml.constructor.SafeConstructor;
2930

31+
import io.kubernetes.client.util.authenticators.Authenticator;
32+
3033
/**
3134
* KubeConfig represents a kubernetes client configuration
3235
*/
@@ -35,6 +38,7 @@ public class KubeConfig {
3538
public static final String ENV_HOME = "HOME";
3639
public static final String KUBEDIR = ".kube";
3740
public static final String KUBECONFIG = "config";
41+
private static Map<String, Authenticator> authenticators = new HashMap<>();
3842

3943
// Note to the reader: I considered creating a Config object
4044
// and parsing into that instead of using Maps, but honestly
@@ -47,6 +51,12 @@ public class KubeConfig {
4751
Map<String, Object> currentCluster;
4852
Map<String, Object> currentUser;
4953

54+
public static void registerAuthenticator(Authenticator auth) {
55+
synchronized (authenticators) {
56+
authenticators.put(auth.getName(), auth);
57+
}
58+
}
59+
5060
/**
5161
* Load a Kubernetes config from the default location
5262
*/
@@ -154,19 +164,21 @@ public String getAccessToken() {
154164
if (currentUser == null) {
155165
return null;
156166
}
167+
157168
Object authProvider = currentUser.get("auth-provider");
158-
if (authProvider != null) {
159-
Map<String, Object> authProviderMap = (Map<String, Object>) authProvider;
169+
if (authProvider != null) { Map<String, Object> authProviderMap = (Map<String, Object>) authProvider;
160170
Map<String, Object> authConfig = (Map<String, Object>) authProviderMap.get("config");
161171
if (authConfig != null) {
162-
Date expiry = (Date) authConfig.get("expiry");
163-
if (expiry != null) {
164-
if (expiry.compareTo(new Date()) <= 0) {
165-
// TODO: Generate new token here...
166-
throw new IllegalStateException("Token is expired!");
172+
String name = (String) authProviderMap.get("name");
173+
Authenticator auth = authenticators.get(name);
174+
System.out.println(auth + " for " + name);
175+
if (auth != null) {
176+
if (auth.isExpired(authConfig)) {
177+
authConfig = auth.refresh(authConfig);
178+
// TODO persist things here.
167179
}
180+
return auth.getToken(authConfig);
168181
}
169-
return (String) authConfig.get("access-token");
170182
}
171183
}
172184
if (currentUser.containsKey("token")) {
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
Copyright 2017 The Kubernetes Authors.
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
package io.kubernetes.client.util.authenticators;
14+
15+
import java.util.Map;
16+
17+
/**
18+
* The Authenticator interface represents a plugin that can handle
19+
* a specific type of authentication information (e.g. 'gcp')
20+
*/
21+
public interface Authenticator {
22+
/**
23+
* Return the name of this authenticator, this should be the
24+
* value that is also in a kubeconfig file.
25+
*/
26+
public String getName();
27+
28+
/**
29+
* Get a token from this authenticator.
30+
* @param config The configuration information for this authenticator
31+
* @returns The new token, null of no such token can be found/generated
32+
*/
33+
public String getToken(Map<String, Object> config);
34+
35+
/**
36+
* Determine if this config is expired
37+
*/
38+
public boolean isExpired(Map<String, Object> config);
39+
40+
/**
41+
* Refresh an expired token with a new fresh one.
42+
*/
43+
public Map<String, Object> refresh(Map<String, Object> config);
44+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
Copyright 2017 The Kubernetes Authors.
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
package io.kubernetes.client.util.authenticators;
14+
15+
import java.util.Date;
16+
import java.util.Map;
17+
18+
import io.kubernetes.client.util.KubeConfig;
19+
20+
/**
21+
* The Authenticator interface represents a plugin that can handle
22+
* a specific type of authentication information (e.g. 'gcp')
23+
*/
24+
public class GCPAuthenticator implements Authenticator {
25+
static {
26+
KubeConfig.registerAuthenticator(new GCPAuthenticator());
27+
}
28+
@Override
29+
public String getName() {
30+
return "gcp";
31+
}
32+
33+
@Override
34+
public String getToken(Map<String, Object> config) {
35+
return (String) config.get("access-token");
36+
}
37+
38+
@Override
39+
public boolean isExpired(Map<String, Object> config) {
40+
Date expiry = (Date) config.get("expiry");
41+
if (expiry != null && expiry.compareTo(new Date()) <= 0) {
42+
return true;
43+
}
44+
return false;
45+
}
46+
47+
@Override
48+
public Map<String, Object> refresh(Map<String, Object> config) {
49+
throw new IllegalStateException("Unimplemented");
50+
}
51+
}

util/src/test/java/io/kuberentes/client/util/KubeConfigTest.java

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414

1515
import com.google.common.io.ByteStreams;
1616
import io.kubernetes.client.ApiClient;
17+
import io.kubernetes.client.util.authenticators.GCPAuthenticator;
1718

1819
import java.io.File;
20+
import java.io.FileReader;
1921
import java.io.FileWriter;
2022
import java.io.IOException;
2123
import java.io.StringReader;
@@ -72,4 +74,43 @@ public void testTokenFile() throws IOException {
7274
KubeConfig config = KubeConfig.loadKubeConfig(new StringReader(replace));
7375
assertEquals(config.getAccessToken(), token);
7476
}
75-
}
77+
78+
public static String GCP_CONFIG =
79+
"apiVersion: v1\n" +
80+
"contexts:\n" +
81+
"- context:\n" +
82+
" user: gke-cluster\n" +
83+
" name: foo-context\n" +
84+
"current-context: foo-context\n" +
85+
"users:\n" +
86+
"- name: gke-cluster\n" +
87+
" user:\n" +
88+
" auth-provider:\n" +
89+
" config:\n" +
90+
" access-token: fake-token\n" +
91+
" cmd-args: config config-helper --format=json\n" +
92+
" cmd-path: /usr/lib/google-cloud-sdk/bin/gcloud\n" +
93+
" expiry: 2117-06-22T18:27:02Z\n" +
94+
" expiry-key: '{.credential.token_expiry}'\n" +
95+
" token-key: '{.credential.access_token}'\n" +
96+
" name: gcp\n";
97+
98+
@Test
99+
public void testGCPAuthProvider() {
100+
KubeConfig.registerAuthenticator(new GCPAuthenticator());
101+
102+
try {
103+
File config = folder.newFile("config");
104+
FileWriter writer = new FileWriter(config);
105+
writer.write(GCP_CONFIG);
106+
writer.flush();
107+
writer.close();
108+
109+
KubeConfig kc = KubeConfig.loadKubeConfig(new FileReader(config));
110+
assertEquals("fake-token", kc.getAccessToken());
111+
} catch (Exception ex) {
112+
ex.printStackTrace();
113+
fail("Unexpected exception: " + ex);
114+
}
115+
}
116+
}

0 commit comments

Comments
 (0)