Skip to content

Commit 913ecb9

Browse files
authored
Merge pull request #967 from zshihang/master
reload service account token after expiry
2 parents 0197ea7 + 41844ea commit 913ecb9

File tree

6 files changed

+153
-2
lines changed

6 files changed

+153
-2
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ public static void main(String[] args) throws IOException, ApiException {
4040
// 4. master endpoints(ip, port) from pre-set environment variables
4141
ApiClient client = ClientBuilder.cluster().build();
4242

43+
// if you prefer not to refresh service account token, please use:
44+
// ApiClient client = ClientBuilder.oldCluster().build();
45+
4346
// set the global default api-client to the in-cluster one from above
4447
Configuration.setDefaultApiClient(client);
4548

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

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import io.kubernetes.client.util.credentials.AccessTokenAuthentication;
2222
import io.kubernetes.client.util.credentials.Authentication;
2323
import io.kubernetes.client.util.credentials.KubeconfigAuthentication;
24+
import io.kubernetes.client.util.credentials.TokenFileAuthentication;
2425
import java.io.BufferedReader;
2526
import java.io.ByteArrayInputStream;
2627
import java.io.File;
@@ -190,12 +191,12 @@ private static File findConfigInHomeDir() {
190191
}
191192

192193
/**
193-
* Creates a builder which is pre-configured from the cluster configuration.
194+
* [DEPRECATED] Creates a builder which is pre-configured from the cluster configuration.
194195
*
195196
* @return <tt>ClientBuilder</tt> configured from the cluster configuration.
196197
* @throws IOException if the Service Account Token Path or CA Path is not readable.
197198
*/
198-
public static ClientBuilder cluster() throws IOException {
199+
public static ClientBuilder oldCluster() throws IOException {
199200
final ClientBuilder builder = new ClientBuilder();
200201

201202
final String host = System.getenv(ENV_SERVICE_HOST);
@@ -211,6 +212,26 @@ public static ClientBuilder cluster() throws IOException {
211212
return builder;
212213
}
213214

215+
/**
216+
* Creates a builder which is pre-configured from the cluster configuration.
217+
*
218+
* @return <tt>ClientBuilder</tt> configured from the cluster configuration where service account
219+
* token will be reloaded.
220+
* @throws IOException if the Service Account Token Path or CA Path is not readable.
221+
*/
222+
public static ClientBuilder cluster() throws IOException {
223+
final ClientBuilder builder = new ClientBuilder();
224+
225+
final String host = System.getenv(ENV_SERVICE_HOST);
226+
final String port = System.getenv(ENV_SERVICE_PORT);
227+
builder.setBasePath(host, port);
228+
229+
builder.setCertificateAuthority(Files.readAllBytes(Paths.get(SERVICEACCOUNT_CA_PATH)));
230+
builder.setAuthentication(new TokenFileAuthentication(SERVICEACCOUNT_TOKEN_PATH));
231+
232+
return builder;
233+
}
234+
214235
protected ClientBuilder setBasePath(String host, String port) {
215236
try {
216237
Integer iPort = Integer.valueOf(port);
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package io.kubernetes.client.util.credentials;
2+
3+
import io.kubernetes.client.openapi.ApiClient;
4+
import java.io.IOException;
5+
import java.nio.charset.Charset;
6+
import java.nio.file.Files;
7+
import java.nio.file.Paths;
8+
import java.time.Instant;
9+
import okhttp3.Interceptor;
10+
import okhttp3.OkHttpClient;
11+
import okhttp3.Request;
12+
import okhttp3.Response;
13+
14+
// TODO: prefer OpenAPI backed Auentication once it is available. see details in
15+
// https://github.com/OpenAPITools/openapi-generator/pull/6036. currently, the
16+
// workaround is to hijack the http request.
17+
public class TokenFileAuthentication implements Authentication, Interceptor {
18+
private String file;
19+
private String token;
20+
private Instant expiry;
21+
22+
public TokenFileAuthentication(String file) {
23+
this.expiry = Instant.MIN;
24+
this.file = file;
25+
}
26+
27+
private String getToken() {
28+
if (Instant.now().isAfter(this.expiry)) {
29+
try {
30+
this.token =
31+
new String(Files.readAllBytes(Paths.get(this.file)), Charset.defaultCharset()).trim();
32+
expiry = Instant.now().plusSeconds(60);
33+
} catch (IOException ie) {
34+
throw new RuntimeException("Cannot read file: " + this.file);
35+
}
36+
}
37+
38+
return this.token;
39+
}
40+
41+
public void setExpiry(Instant expiry) {
42+
this.expiry = expiry;
43+
}
44+
45+
public void setFile(String file) {
46+
this.file = file;
47+
}
48+
49+
@Override
50+
public void provide(ApiClient client) {
51+
OkHttpClient withInterceptor = client.getHttpClient().newBuilder().addInterceptor(this).build();
52+
client.setHttpClient(withInterceptor);
53+
}
54+
55+
@Override
56+
public Response intercept(Interceptor.Chain chain) throws IOException {
57+
Request request = chain.request();
58+
Request newRequest;
59+
newRequest = request.newBuilder().header("Authorization", "Bearer " + getToken()).build();
60+
return chain.proceed(newRequest);
61+
}
62+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package io.kubernetes.client.util.credentials;
2+
3+
import static com.github.tomakehurst.wiremock.client.WireMock.*;
4+
5+
import com.github.tomakehurst.wiremock.client.WireMock;
6+
import com.github.tomakehurst.wiremock.junit.WireMockRule;
7+
import com.google.common.io.Resources;
8+
import io.kubernetes.client.openapi.ApiClient;
9+
import io.kubernetes.client.openapi.ApiException;
10+
import io.kubernetes.client.openapi.Configuration;
11+
import io.kubernetes.client.openapi.apis.CoreV1Api;
12+
import java.io.IOException;
13+
import java.time.Instant;
14+
import org.junit.Before;
15+
import org.junit.Rule;
16+
import org.junit.Test;
17+
18+
public class TokenFileAuthenticationTest {
19+
private static final String SERVICEACCOUNT_TOKEN1_PATH =
20+
Resources.getResource("token1").getPath();
21+
private static final String SERVICEACCOUNT_TOKEN2_PATH =
22+
Resources.getResource("token2").getPath();
23+
private static final int PORT = 8089;
24+
private TokenFileAuthentication auth;
25+
26+
@Rule public WireMockRule wireMockRule = new WireMockRule(PORT);
27+
28+
@Before
29+
public void setup() throws IOException {
30+
final ApiClient client = new ApiClient();
31+
client.setBasePath("http://localhost:" + PORT);
32+
this.auth = new TokenFileAuthentication(SERVICEACCOUNT_TOKEN1_PATH);
33+
this.auth.provide(client);
34+
Configuration.setDefaultApiClient(client);
35+
}
36+
37+
@Test
38+
public void testTokenProvided() throws IOException, ApiException {
39+
stubFor(
40+
get(urlPathEqualTo("/api/v1/pods")).willReturn(okForContentType("application/json", "{}")));
41+
CoreV1Api api = new CoreV1Api();
42+
43+
api.listPodForAllNamespaces(null, null, null, null, null, null, null, null, null);
44+
WireMock.verify(
45+
1,
46+
getRequestedFor(urlPathEqualTo("/api/v1/pods"))
47+
.withHeader("Authorization", equalTo("Bearer token1")));
48+
49+
this.auth.setFile(SERVICEACCOUNT_TOKEN2_PATH);
50+
api.listPodForAllNamespaces(null, null, null, null, null, null, null, null, null);
51+
WireMock.verify(
52+
2,
53+
getRequestedFor(urlPathEqualTo("/api/v1/pods"))
54+
.withHeader("Authorization", equalTo("Bearer token1")));
55+
56+
this.auth.setExpiry(Instant.now().minusSeconds(1));
57+
api.listPodForAllNamespaces(null, null, null, null, null, null, null, null, null);
58+
WireMock.verify(
59+
1,
60+
getRequestedFor(urlPathEqualTo("/api/v1/pods"))
61+
.withHeader("Authorization", equalTo("Bearer token2")));
62+
}
63+
}

util/src/test/resources/token1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
token1

util/src/test/resources/token2

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
token2

0 commit comments

Comments
 (0)