Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -800,8 +800,15 @@ public String getAccount() {
@InternalApi
@Override
public String getRegionalAccessBoundaryUrl() throws IOException {
String account = getAccount();
// In GKE environments, the default service account might return a non-email placeholder.
// Since RAB lookup requires a valid email-based service account, we skip RAB lookup
// in non-email scenarios by returning null.
if (account == null || !account.contains("@")) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know the document may have changed since this PR was raised, but I think the the standard email pattern is a regex and we will need to confirm to that.

return null;
}
Comment on lines +807 to +809
Copy link
Copy Markdown
Member

@lqiu96 lqiu96 Jun 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we will also need to follow the recommendation for a one-time log message that the RAB instance is skipped (maybe here, maybe in the RABManager)

return String.format(
OAuth2Utils.IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_SERVICE_ACCOUNT, getAccount());
OAuth2Utils.IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_SERVICE_ACCOUNT, account);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,10 @@ void triggerAsyncRefresh(
() -> {
try {
String url = provider.getRegionalAccessBoundaryUrl();
if (url == null) {
future.set(null);
return;
}
Comment on lines +199 to +202
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

qq, can you remind me again why we need to do future.set(null);?

RegionalAccessBoundary newRAB =
RegionalAccessBoundary.refresh(
transportFactory, url, accessToken, clock, maxRetryElapsedTimeMillis);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1242,6 +1242,37 @@ void refresh_regionalAccessBoundarySuccess() throws IOException, InterruptedExce
Arrays.asList(TestUtils.REGIONAL_ACCESS_BOUNDARY_ENCODED_LOCATION));
}

@org.junit.jupiter.api.Test
void refresh_regionalAccessBoundaryNonEmail() throws IOException, InterruptedException {

String nonEmailAccount = "non-email-account-value";
MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory();
RegionalAccessBoundary regionalAccessBoundary =
new RegionalAccessBoundary(
TestUtils.REGIONAL_ACCESS_BOUNDARY_ENCODED_LOCATION,
TestUtils.REGIONAL_ACCESS_BOUNDARY_LOCATIONS,
null);
transportFactory.transport.setRegionalAccessBoundary(regionalAccessBoundary);
transportFactory.transport.setServiceAccountEmail(nonEmailAccount);

ComputeEngineCredentials credentials =
ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build();

// First call: should NOT initiate async refresh (or should skip it gracefully).
Map<String, List<String>> headers = credentials.getRequestMetadata();
assertNull(headers.get(X_ALLOWED_LOCATIONS_HEADER_KEY));

// Wait a brief time to make sure no background tasks execute and set it.
Thread.sleep(500);

// It should still be null.
assertNull(credentials.getRegionalAccessBoundary());

// And header should still be missing on second call.
headers = credentials.getRequestMetadata();
assertNull(headers.get(X_ALLOWED_LOCATIONS_HEADER_KEY));
}

private void waitForRegionalAccessBoundary(GoogleCredentials credentials)
throws InterruptedException {
long deadline = System.currentTimeMillis() + 5000;
Expand Down
Loading