diff --git a/pom.xml b/pom.xml
index c3f20ef11d..87f458fc7b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -228,6 +228,12 @@
jsr305
${jsr305.version}
+
+ com.google.auth
+ google-auth-library-oauth2-http
+ 1.3.0
+ true
+
diff --git a/util/pom.xml b/util/pom.xml
index 002d42dfcc..2e4c7bdeaf 100644
--- a/util/pom.xml
+++ b/util/pom.xml
@@ -84,6 +84,11 @@
org.bitbucket.b_c
jose4j
+
+ com.google.auth
+ google-auth-library-oauth2-http
+ true
+
junit
diff --git a/util/src/main/java/io/kubernetes/client/util/authenticators/GCPAuthenticator.java b/util/src/main/java/io/kubernetes/client/util/authenticators/GCPAuthenticator.java
index 14438e0bed..5f5599f578 100644
--- a/util/src/main/java/io/kubernetes/client/util/authenticators/GCPAuthenticator.java
+++ b/util/src/main/java/io/kubernetes/client/util/authenticators/GCPAuthenticator.java
@@ -12,6 +12,8 @@
*/
package io.kubernetes.client.util.authenticators;
+import com.google.auth.oauth2.AccessToken;
+import com.google.auth.oauth2.GoogleCredentials;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import io.kubernetes.client.util.KubeConfig;
@@ -40,17 +42,25 @@ public class GCPAuthenticator implements Authenticator {
static final String EXPIRY = "expiry";
static final String CMD_ARGS = "cmd-args";
static final String CMD_PATH = "cmd-path";
+ static final String SCOPES = "scopes";
+ static final String[] DEFAULT_SCOPES =
+ new String[] {
+ "https://www.googleapis.com/auth/cloud-platform",
+ "https://www.googleapis.com/auth/userinfo.email"
+ };
private static final Logger log = LoggerFactory.getLogger(GCPAuthenticator.class);
private final ProcessBuilder pb;
+ private GoogleCredentials gc;
public GCPAuthenticator() {
- this(new ProcessBuilder());
+ this(new ProcessBuilder(), null);
}
- public GCPAuthenticator(ProcessBuilder pb) {
+ public GCPAuthenticator(ProcessBuilder pb, GoogleCredentials gc) {
this.pb = pb;
+ this.gc = gc;
}
@Override
@@ -81,8 +91,39 @@ public boolean isExpired(Map config) {
@Override
public Map refresh(Map config) {
- if (!config.containsKey(CMD_ARGS) || !config.containsKey(CMD_PATH))
- throw new RuntimeException("Could not refresh token");
+ if (isCmd(config)) {
+ return refreshCmd(config);
+ }
+ // Google Application Credentials-based refresh
+ // https://cloud.google.com/kubernetes-engine/docs/how-to/api-server-authentication#environments-without-gcloud
+ String[] scopes = parseScopes(config);
+ try {
+ if (this.gc == null) this.gc = GoogleCredentials.getApplicationDefault().createScoped(scopes);
+ AccessToken accessToken = gc.getAccessToken();
+ config.put(ACCESS_TOKEN, accessToken.getTokenValue());
+ config.put(EXPIRY, accessToken.getExpirationTime());
+ return config;
+ } catch (IOException e) {
+ throw new RuntimeException("The Application Default Credentials are not available.", e);
+ }
+ }
+
+ public String[] parseScopes(Map config) {
+ String scopes = (String) config.get(SCOPES);
+ if (scopes == null) {
+ return DEFAULT_SCOPES;
+ }
+ if (scopes.isEmpty()) {
+ return new String[] {};
+ }
+ return scopes.split(",");
+ }
+
+ private boolean isCmd(Map config) {
+ return config.containsKey(CMD_ARGS) && config.containsKey(CMD_PATH);
+ }
+
+ private Map refreshCmd(Map config) {
String cmdPath = (String) config.get(CMD_PATH);
String cmdArgs = (String) config.get(CMD_ARGS);
List fullCmd =
diff --git a/util/src/test/java/io/kubernetes/client/util/KubeConfigTest.java b/util/src/test/java/io/kubernetes/client/util/KubeConfigTest.java
index 1dbf180bd8..861baa6551 100644
--- a/util/src/test/java/io/kubernetes/client/util/KubeConfigTest.java
+++ b/util/src/test/java/io/kubernetes/client/util/KubeConfigTest.java
@@ -14,6 +14,8 @@
import static org.junit.Assert.*;
+import com.google.auth.oauth2.AccessToken;
+import com.google.auth.oauth2.GoogleCredentials;
import io.kubernetes.client.util.authenticators.Authenticator;
import io.kubernetes.client.util.authenticators.AzureActiveDirectoryAuthenticator;
import io.kubernetes.client.util.authenticators.GCPAuthenticator;
@@ -25,6 +27,8 @@
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
+import java.sql.Date;
+import java.time.Instant;
import java.util.Map;
import org.junit.Rule;
import org.junit.Test;
@@ -151,7 +155,7 @@ public void testGCPAuthProviderStringDate() {
}
@Test
- public void testGCPAuthProviderExpiredToken() {
+ public void testGCPAuthProviderExpiredTokenWithinGCloud() {
String gcpConfigExpiredToken =
"apiVersion: v1\n"
+ "contexts:\n"
@@ -191,7 +195,7 @@ public void testGCPAuthProviderExpiredToken() {
fail("Unexpected exception: " + ex);
}
- KubeConfig.registerAuthenticator(new GCPAuthenticator(mockPB));
+ KubeConfig.registerAuthenticator(new GCPAuthenticator(mockPB, null));
try {
KubeConfig kc = KubeConfig.loadKubeConfig(new StringReader(gcpConfigExpiredToken));
assertEquals("new-fake-token", kc.getAccessToken());
@@ -201,6 +205,41 @@ public void testGCPAuthProviderExpiredToken() {
}
}
+ @Test
+ public void testGCPAuthProviderExpiredTokenWithoutGCloud() {
+ String gcpConfigExpiredToken =
+ "apiVersion: v1\n"
+ + "contexts:\n"
+ + "- context:\n"
+ + " user: gke-cluster\n"
+ + " name: foo-context\n"
+ + "current-context: foo-context\n"
+ + "users:\n"
+ + "- name: gke-cluster\n"
+ + " user:\n"
+ + " auth-provider:\n"
+ + " config:\n"
+ + " access-token: fake-token\n"
+ + " expiry: 1970-01-01T00:00:00Z\n"
+ + " name: gcp";
+
+ String fakeToken = "new-fake-token";
+ String fakeTokenExpiry = "2121-08-05T02:30:24Z";
+
+ GoogleCredentials mockGC = Mockito.mock(GoogleCredentials.class);
+ Mockito.when(mockGC.getAccessToken())
+ .thenReturn(new AccessToken(fakeToken, Date.from(Instant.parse(fakeTokenExpiry))));
+
+ KubeConfig.registerAuthenticator(new GCPAuthenticator(null, mockGC));
+ try {
+ KubeConfig kc = KubeConfig.loadKubeConfig(new StringReader(gcpConfigExpiredToken));
+ assertEquals(fakeToken, kc.getAccessToken());
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ fail("Unexpected exception: " + ex);
+ }
+ }
+
@Test
public void testAzureAuthProvider() {
KubeConfig.registerAuthenticator(new AzureActiveDirectoryAuthenticator());
diff --git a/util/src/test/java/io/kubernetes/client/util/authenticators/GCPAuthenticatorTest.java b/util/src/test/java/io/kubernetes/client/util/authenticators/GCPAuthenticatorTest.java
index b29467f865..eabdd771e3 100644
--- a/util/src/test/java/io/kubernetes/client/util/authenticators/GCPAuthenticatorTest.java
+++ b/util/src/test/java/io/kubernetes/client/util/authenticators/GCPAuthenticatorTest.java
@@ -14,12 +14,17 @@
import static org.assertj.core.api.Fail.fail;
import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import com.google.auth.oauth2.AccessToken;
+import com.google.auth.oauth2.GoogleCredentials;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
+import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -43,18 +48,24 @@ public class GCPAuthenticatorTest {
add(cmdArgsSplit[2]);
}
});
-
+ private static final String fakeToken = "new-fake-token";
+ private static final String fakeTokenExpiry = "2121-08-05T02:30:24Z";
private static final String fakeExecResult =
"{\n"
+ " \"credential\": {\n"
- + " \"access_token\": \"new-fake-token\",\n"
+ + " \"access_token\": \""
+ + fakeToken
+ + "\",\n"
+ " \"id_token\": \"id-fake-token\",\n"
- + " \"token_expiry\": \"2121-08-05T02:30:24Z\"\n"
+ + " \"token_expiry\": \""
+ + fakeTokenExpiry
+ + "\"\n"
+ " }\n"
+ "}";
private final ProcessBuilder mockPB = Mockito.mock(ProcessBuilder.class);
- private final GCPAuthenticator gcpAuthenticator = new GCPAuthenticator(mockPB);
+ private final GoogleCredentials mockGC = Mockito.mock(GoogleCredentials.class);
+ private final GCPAuthenticator gcpAuthenticator = new GCPAuthenticator(mockPB, mockGC);
@Before
public void setup() {
@@ -183,4 +194,15 @@ public void testRefreshLeadingWhitespaceInPathAndArgs() {
List executedCommand = mockPB.command();
MatcherAssert.assertThat(executedCommand, is(expectedCommand));
}
+
+ @Test
+ public void testRefreshApplicationDefaultCredentials() {
+ Date fakeTokenExpiryDate = Date.from(Instant.parse(fakeTokenExpiry));
+ Mockito.when(mockGC.getAccessToken())
+ .thenReturn(new AccessToken(fakeToken, fakeTokenExpiryDate));
+ final Map config = new HashMap() {};
+ final Map result = gcpAuthenticator.refresh(config);
+ assertEquals(fakeToken, result.get(GCPAuthenticator.ACCESS_TOKEN));
+ assertEquals(fakeTokenExpiryDate, result.get(GCPAuthenticator.EXPIRY));
+ }
}