Skip to content

Commit fea04dd

Browse files
committed
feat: Add getProjectId to ComputeEngineCredentials
1 parent 98b62ba commit fea04dd

File tree

1 file changed

+72
-0
lines changed

1 file changed

+72
-0
lines changed

oauth2_http/java/com/google/auth/oauth2/ComputeEngineCredentials.java

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ public class ComputeEngineCredentials extends GoogleCredentials
130130
private transient HttpTransportFactory transportFactory;
131131

132132
private String universeDomainFromMetadata = null;
133+
private String projectId = null;
133134

134135
/**
135136
* Experimental Feature.
@@ -341,6 +342,72 @@ private String getUniverseDomainFromMetadata() throws IOException {
341342
return responseString;
342343
}
343344

345+
/**
346+
* Retrieves the Google Cloud project ID from the Compute Engine (GCE) metadata server.
347+
* <p>
348+
* On its first successful execution, it fetches the project ID and caches it for the lifetime
349+
* of the object. Subsequent calls will return the cached value without making additional network
350+
* requests.
351+
* <p>
352+
* If the request to the metadata server fails (e.g., due to network issues, or if the VM lacks
353+
* the required service account permissions), the method will attempt to fall back to a default
354+
* project ID provider which could be {@code null}.
355+
*
356+
* @return the GCP project ID string, or {@code null} if the metadata server is
357+
* inaccessible and no fallback project ID can be determined.
358+
*/
359+
@Override
360+
public String getProjectId() {
361+
synchronized (this) {
362+
if (this.projectId != null) {
363+
return this.projectId;
364+
}
365+
}
366+
367+
String projectIdFromMetadata = getProjectIdFromMetadata();
368+
synchronized (this) {
369+
this.projectId = projectIdFromMetadata;
370+
}
371+
return projectIdFromMetadata;
372+
}
373+
374+
private String getProjectIdFromMetadata() {
375+
try {
376+
HttpResponse response = getMetadataResponse(getProjectIdUrl(), RequestType.UNTRACKED, false);
377+
int statusCode = response.getStatusCode();
378+
if (statusCode == HttpStatusCodes.STATUS_CODE_NOT_FOUND) {
379+
LoggingUtils.log(LOGGER_PROVIDER, Level.WARNING, Collections.emptyMap(), String.format(
380+
"Error code %s trying to get project ID from"
381+
+ " Compute Engine metadata. This may be because the virtual machine instance"
382+
+ " does not have permission scopes specified.",
383+
statusCode));
384+
return super.getProjectId();
385+
}
386+
if (statusCode != HttpStatusCodes.STATUS_CODE_OK) {
387+
LoggingUtils.log(
388+
LOGGER_PROVIDER,
389+
Level.WARNING,
390+
Collections.emptyMap(),
391+
String.format(
392+
"Unexpected Error code %s trying to get project ID"
393+
+ " from Compute Engine metadata for the default service account: %s",
394+
statusCode, response.parseAsString()));
395+
return super.getProjectId();
396+
}
397+
return response.parseAsString();
398+
} catch (IOException e) {
399+
LoggingUtils.log(
400+
LOGGER_PROVIDER,
401+
Level.WARNING,
402+
Collections.emptyMap(),
403+
String.format(
404+
"Unexpected Error: %s trying to get project ID"
405+
+ " from Compute Engine metadata server. Reason: %s",
406+
e.getMessage(), e.getCause().toString()));
407+
return super.getProjectId();
408+
}
409+
}
410+
344411
/** Refresh the access token by getting it from the GCE metadata server */
345412
@Override
346413
public AccessToken refreshAccessToken() throws IOException {
@@ -642,6 +709,11 @@ public static String getIdentityDocumentUrl() {
642709
+ "/computeMetadata/v1/instance/service-accounts/default/identity";
643710
}
644711

712+
public static String getProjectIdUrl() {
713+
return getMetadataServerUrl(DefaultCredentialsProvider.DEFAULT)
714+
+ "/computeMetadata/v1/project/project-id";
715+
}
716+
645717
@Override
646718
public int hashCode() {
647719
return Objects.hash(transportFactoryClassName);

0 commit comments

Comments
 (0)