Skip to content

Commit 1165ffe

Browse files
authored
Merge pull request #2007 from jhbae200/master
GCPAuthenticator support without gcloud
2 parents 1c616f3 + 817a54e commit 1165ffe

File tree

5 files changed

+123
-10
lines changed

5 files changed

+123
-10
lines changed

pom.xml

+6
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,12 @@
228228
<artifactId>jsr305</artifactId>
229229
<version>${jsr305.version}</version>
230230
</dependency>
231+
<dependency>
232+
<groupId>com.google.auth</groupId>
233+
<artifactId>google-auth-library-oauth2-http</artifactId>
234+
<version>1.3.0</version>
235+
<optional>true</optional>
236+
</dependency>
231237

232238

233239
<!-- tests -->

util/pom.xml

+5
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@
8484
<groupId>org.bitbucket.b_c</groupId>
8585
<artifactId>jose4j</artifactId>
8686
</dependency>
87+
<dependency>
88+
<groupId>com.google.auth</groupId>
89+
<artifactId>google-auth-library-oauth2-http</artifactId>
90+
<optional>true</optional>
91+
</dependency>
8792
<!-- test dependencies -->
8893
<dependency>
8994
<groupId>junit</groupId>

util/src/main/java/io/kubernetes/client/util/authenticators/GCPAuthenticator.java

+45-4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
*/
1313
package io.kubernetes.client.util.authenticators;
1414

15+
import com.google.auth.oauth2.AccessToken;
16+
import com.google.auth.oauth2.GoogleCredentials;
1517
import com.google.gson.JsonObject;
1618
import com.google.gson.JsonParser;
1719
import io.kubernetes.client.util.KubeConfig;
@@ -40,17 +42,25 @@ public class GCPAuthenticator implements Authenticator {
4042
static final String EXPIRY = "expiry";
4143
static final String CMD_ARGS = "cmd-args";
4244
static final String CMD_PATH = "cmd-path";
45+
static final String SCOPES = "scopes";
46+
static final String[] DEFAULT_SCOPES =
47+
new String[] {
48+
"https://www.googleapis.com/auth/cloud-platform",
49+
"https://www.googleapis.com/auth/userinfo.email"
50+
};
4351

4452
private static final Logger log = LoggerFactory.getLogger(GCPAuthenticator.class);
4553

4654
private final ProcessBuilder pb;
55+
private GoogleCredentials gc;
4756

4857
public GCPAuthenticator() {
49-
this(new ProcessBuilder());
58+
this(new ProcessBuilder(), null);
5059
}
5160

52-
public GCPAuthenticator(ProcessBuilder pb) {
61+
public GCPAuthenticator(ProcessBuilder pb, GoogleCredentials gc) {
5362
this.pb = pb;
63+
this.gc = gc;
5464
}
5565

5666
@Override
@@ -81,8 +91,39 @@ public boolean isExpired(Map<String, Object> config) {
8191

8292
@Override
8393
public Map<String, Object> refresh(Map<String, Object> config) {
84-
if (!config.containsKey(CMD_ARGS) || !config.containsKey(CMD_PATH))
85-
throw new RuntimeException("Could not refresh token");
94+
if (isCmd(config)) {
95+
return refreshCmd(config);
96+
}
97+
// Google Application Credentials-based refresh
98+
// https://cloud.google.com/kubernetes-engine/docs/how-to/api-server-authentication#environments-without-gcloud
99+
String[] scopes = parseScopes(config);
100+
try {
101+
if (this.gc == null) this.gc = GoogleCredentials.getApplicationDefault().createScoped(scopes);
102+
AccessToken accessToken = gc.getAccessToken();
103+
config.put(ACCESS_TOKEN, accessToken.getTokenValue());
104+
config.put(EXPIRY, accessToken.getExpirationTime());
105+
return config;
106+
} catch (IOException e) {
107+
throw new RuntimeException("The Application Default Credentials are not available.", e);
108+
}
109+
}
110+
111+
public String[] parseScopes(Map<String, Object> config) {
112+
String scopes = (String) config.get(SCOPES);
113+
if (scopes == null) {
114+
return DEFAULT_SCOPES;
115+
}
116+
if (scopes.isEmpty()) {
117+
return new String[] {};
118+
}
119+
return scopes.split(",");
120+
}
121+
122+
private boolean isCmd(Map<String, Object> config) {
123+
return config.containsKey(CMD_ARGS) && config.containsKey(CMD_PATH);
124+
}
125+
126+
private Map<String, Object> refreshCmd(Map<String, Object> config) {
86127
String cmdPath = (String) config.get(CMD_PATH);
87128
String cmdArgs = (String) config.get(CMD_ARGS);
88129
List<String> fullCmd =

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

+41-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
import static org.junit.Assert.*;
1616

17+
import com.google.auth.oauth2.AccessToken;
18+
import com.google.auth.oauth2.GoogleCredentials;
1719
import io.kubernetes.client.util.authenticators.Authenticator;
1820
import io.kubernetes.client.util.authenticators.AzureActiveDirectoryAuthenticator;
1921
import io.kubernetes.client.util.authenticators.GCPAuthenticator;
@@ -25,6 +27,8 @@
2527
import java.io.StringReader;
2628
import java.nio.charset.StandardCharsets;
2729
import java.nio.file.Files;
30+
import java.sql.Date;
31+
import java.time.Instant;
2832
import java.util.Map;
2933
import org.junit.Rule;
3034
import org.junit.Test;
@@ -151,7 +155,7 @@ public void testGCPAuthProviderStringDate() {
151155
}
152156

153157
@Test
154-
public void testGCPAuthProviderExpiredToken() {
158+
public void testGCPAuthProviderExpiredTokenWithinGCloud() {
155159
String gcpConfigExpiredToken =
156160
"apiVersion: v1\n"
157161
+ "contexts:\n"
@@ -191,7 +195,7 @@ public void testGCPAuthProviderExpiredToken() {
191195
fail("Unexpected exception: " + ex);
192196
}
193197

194-
KubeConfig.registerAuthenticator(new GCPAuthenticator(mockPB));
198+
KubeConfig.registerAuthenticator(new GCPAuthenticator(mockPB, null));
195199
try {
196200
KubeConfig kc = KubeConfig.loadKubeConfig(new StringReader(gcpConfigExpiredToken));
197201
assertEquals("new-fake-token", kc.getAccessToken());
@@ -201,6 +205,41 @@ public void testGCPAuthProviderExpiredToken() {
201205
}
202206
}
203207

208+
@Test
209+
public void testGCPAuthProviderExpiredTokenWithoutGCloud() {
210+
String gcpConfigExpiredToken =
211+
"apiVersion: v1\n"
212+
+ "contexts:\n"
213+
+ "- context:\n"
214+
+ " user: gke-cluster\n"
215+
+ " name: foo-context\n"
216+
+ "current-context: foo-context\n"
217+
+ "users:\n"
218+
+ "- name: gke-cluster\n"
219+
+ " user:\n"
220+
+ " auth-provider:\n"
221+
+ " config:\n"
222+
+ " access-token: fake-token\n"
223+
+ " expiry: 1970-01-01T00:00:00Z\n"
224+
+ " name: gcp";
225+
226+
String fakeToken = "new-fake-token";
227+
String fakeTokenExpiry = "2121-08-05T02:30:24Z";
228+
229+
GoogleCredentials mockGC = Mockito.mock(GoogleCredentials.class);
230+
Mockito.when(mockGC.getAccessToken())
231+
.thenReturn(new AccessToken(fakeToken, Date.from(Instant.parse(fakeTokenExpiry))));
232+
233+
KubeConfig.registerAuthenticator(new GCPAuthenticator(null, mockGC));
234+
try {
235+
KubeConfig kc = KubeConfig.loadKubeConfig(new StringReader(gcpConfigExpiredToken));
236+
assertEquals(fakeToken, kc.getAccessToken());
237+
} catch (Exception ex) {
238+
ex.printStackTrace();
239+
fail("Unexpected exception: " + ex);
240+
}
241+
}
242+
204243
@Test
205244
public void testAzureAuthProvider() {
206245
KubeConfig.registerAuthenticator(new AzureActiveDirectoryAuthenticator());

util/src/test/java/io/kubernetes/client/util/authenticators/GCPAuthenticatorTest.java

+26-4
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,17 @@
1414

1515
import static org.assertj.core.api.Fail.fail;
1616
import static org.hamcrest.Matchers.is;
17+
import static org.junit.Assert.assertEquals;
1718

19+
import com.google.auth.oauth2.AccessToken;
20+
import com.google.auth.oauth2.GoogleCredentials;
1821
import java.io.ByteArrayInputStream;
1922
import java.io.IOException;
2023
import java.nio.charset.StandardCharsets;
24+
import java.time.Instant;
2125
import java.util.ArrayList;
2226
import java.util.Collections;
27+
import java.util.Date;
2328
import java.util.HashMap;
2429
import java.util.List;
2530
import java.util.Map;
@@ -43,18 +48,24 @@ public class GCPAuthenticatorTest {
4348
add(cmdArgsSplit[2]);
4449
}
4550
});
46-
51+
private static final String fakeToken = "new-fake-token";
52+
private static final String fakeTokenExpiry = "2121-08-05T02:30:24Z";
4753
private static final String fakeExecResult =
4854
"{\n"
4955
+ " \"credential\": {\n"
50-
+ " \"access_token\": \"new-fake-token\",\n"
56+
+ " \"access_token\": \""
57+
+ fakeToken
58+
+ "\",\n"
5159
+ " \"id_token\": \"id-fake-token\",\n"
52-
+ " \"token_expiry\": \"2121-08-05T02:30:24Z\"\n"
60+
+ " \"token_expiry\": \""
61+
+ fakeTokenExpiry
62+
+ "\"\n"
5363
+ " }\n"
5464
+ "}";
5565

5666
private final ProcessBuilder mockPB = Mockito.mock(ProcessBuilder.class);
57-
private final GCPAuthenticator gcpAuthenticator = new GCPAuthenticator(mockPB);
67+
private final GoogleCredentials mockGC = Mockito.mock(GoogleCredentials.class);
68+
private final GCPAuthenticator gcpAuthenticator = new GCPAuthenticator(mockPB, mockGC);
5869

5970
@Before
6071
public void setup() {
@@ -183,4 +194,15 @@ public void testRefreshLeadingWhitespaceInPathAndArgs() {
183194
List<String> executedCommand = mockPB.command();
184195
MatcherAssert.assertThat(executedCommand, is(expectedCommand));
185196
}
197+
198+
@Test
199+
public void testRefreshApplicationDefaultCredentials() {
200+
Date fakeTokenExpiryDate = Date.from(Instant.parse(fakeTokenExpiry));
201+
Mockito.when(mockGC.getAccessToken())
202+
.thenReturn(new AccessToken(fakeToken, fakeTokenExpiryDate));
203+
final Map<String, Object> config = new HashMap<String, Object>() {};
204+
final Map<String, Object> result = gcpAuthenticator.refresh(config);
205+
assertEquals(fakeToken, result.get(GCPAuthenticator.ACCESS_TOKEN));
206+
assertEquals(fakeTokenExpiryDate, result.get(GCPAuthenticator.EXPIRY));
207+
}
186208
}

0 commit comments

Comments
 (0)