From 1fda848f91f7452ad7d8e1b154bcb66fe9c5bd62 Mon Sep 17 00:00:00 2001 From: Pranav Sharma Date: Wed, 16 Apr 2025 22:54:45 +0000 Subject: [PATCH 1/6] Update quota project id header retrieval --- .../contrib/gcp/auth/ConfigurableOption.java | 17 ++++ ...thAutoConfigurationCustomizerProvider.java | 39 ++++++-- ...toConfigurationCustomizerProviderTest.java | 98 ++++++++++++------- 3 files changed, 108 insertions(+), 46 deletions(-) diff --git a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/ConfigurableOption.java b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/ConfigurableOption.java index 1bf90e48f3..7928f9ab4e 100644 --- a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/ConfigurableOption.java +++ b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/ConfigurableOption.java @@ -7,6 +7,7 @@ import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import java.util.Locale; +import java.util.Optional; import java.util.function.Supplier; /** @@ -101,4 +102,20 @@ String getConfiguredValueWithFallback(Supplier fallback) { return fallback.get(); } } + + /** + * Retrieves the value for this option, prioritizing environment variables before system + * properties. If neither an environment variable nor a system property is set for this option, + * then an empty {@link Optional} is returned. + * + * @return The configured value for the option, if set, obtained from the environment variable, + * system property, or empty {@link Optional}, in that order of precedence. + */ + Optional getConfiguredValueAsOptional() { + try { + return Optional.of(this.getConfiguredValue()); + } catch (ConfigurationException e) { + return Optional.empty(); + } + } } diff --git a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java index 70e9bdd3be..db97471c0e 100644 --- a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java +++ b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java @@ -20,8 +20,11 @@ import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.export.SpanExporter; import java.io.IOException; -import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; /** * An AutoConfigurationCustomizerProvider for Google Cloud Platform (GCP) OpenTelemetry (OTLP) @@ -40,7 +43,7 @@ public class GcpAuthAutoConfigurationCustomizerProvider implements AutoConfigurationCustomizerProvider { - static final String QUOTA_USER_PROJECT_HEADER = "X-Goog-User-Project"; + static final String QUOTA_USER_PROJECT_HEADER = "x-goog-user-project"; static final String GCP_USER_PROJECT_ID_KEY = "gcp.project_id"; /** @@ -95,20 +98,36 @@ private static SpanExporter addAuthorizationHeaders( } private static Map getRequiredHeaderMap(GoogleCredentials credentials) { - Map gcpHeaders = new HashMap<>(); + Map> gcpHeaders; try { credentials.refreshIfExpired(); + gcpHeaders = credentials.getRequestMetadata(); } catch (IOException e) { throw new GoogleAuthException(Reason.FAILED_ADC_REFRESH, e); } - gcpHeaders.put("Authorization", "Bearer " + credentials.getAccessToken().getTokenValue()); - String configuredQuotaProjectId = - ConfigurableOption.GOOGLE_CLOUD_QUOTA_PROJECT.getConfiguredValueWithFallback( - credentials::getQuotaProjectId); - if (configuredQuotaProjectId != null && !configuredQuotaProjectId.isEmpty()) { - gcpHeaders.put(QUOTA_USER_PROJECT_HEADER, configuredQuotaProjectId); + // flatten list + Map flattenedHeaders = + gcpHeaders.entrySet().stream() + .collect( + Collectors.toMap( + Map.Entry::getKey, + entry -> + entry.getValue().stream() + .filter(Objects::nonNull) // Filter nulls + .filter(s -> !s.isEmpty()) // Filter empty strings + .collect(Collectors.joining(",")), + (v1, v2) -> v2 // Merge function - take the last seen value + )); + // Add quota user project header if not detected by the auth library and user provided it via + // system properties. + if (!flattenedHeaders.containsKey(QUOTA_USER_PROJECT_HEADER)) { + Optional maybeConfiguredQuotaProjectId = + ConfigurableOption.GOOGLE_CLOUD_QUOTA_PROJECT.getConfiguredValueAsOptional(); + maybeConfiguredQuotaProjectId.ifPresent( + configuredQuotaProjectId -> + flattenedHeaders.put(QUOTA_USER_PROJECT_HEADER, configuredQuotaProjectId)); } - return gcpHeaders; + return flattenedHeaders; } // Updates the current resource with the attributes required for ingesting OTLP data on GCP. diff --git a/gcp-auth-extension/src/test/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProviderTest.java b/gcp-auth-extension/src/test/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProviderTest.java index 39f626e617..5cbee08900 100644 --- a/gcp-auth-extension/src/test/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProviderTest.java +++ b/gcp-auth-extension/src/test/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProviderTest.java @@ -35,6 +35,7 @@ import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.trace.data.SpanData; import io.opentelemetry.sdk.trace.export.SpanExporter; +import java.io.IOException; import java.time.Duration; import java.time.Instant; import java.util.AbstractMap.SimpleEntry; @@ -214,26 +215,33 @@ public void testCustomizerFailWithMissingResourceProject() { @ParameterizedTest @MethodSource("provideQuotaBehaviorTestCases") @SuppressWarnings("CannotMockMethod") - public void testQuotaProjectBehavior(QuotaProjectIdTestBehavior testCase) { + public void testQuotaProjectBehavior(QuotaProjectIdTestBehavior testCase) throws IOException { // Set resource project system property System.setProperty( ConfigurableOption.GOOGLE_CLOUD_PROJECT.getSystemProperty(), DUMMY_GCP_RESOURCE_PROJECT_ID); - // Configure mock credentials to return fake access token - Mockito.when(mockedGoogleCredentials.getAccessToken()) - .thenReturn(new AccessToken("fake", Date.from(Instant.now()))); - - // To prevent unncecessary stubbings, mock getQuotaProjectId only when necessary - if (testCase.getUserSpecifiedQuotaProjectId() == null - || testCase.getUserSpecifiedQuotaProjectId().isEmpty()) { - String quotaProjectFromCredential = - testCase.getIsQuotaProjectPresentInCredentials() ? DUMMY_GCP_QUOTA_PROJECT_ID : null; - Mockito.when(mockedGoogleCredentials.getQuotaProjectId()) - .thenReturn(quotaProjectFromCredential); + + // Prepare request metadata + AccessToken fakeAccessToken = new AccessToken("fake", Date.from(Instant.now())); + ImmutableMap> mockedRequestMetadata; + if (testCase.getIsQuotaProjectPresentInMetadata()) { + mockedRequestMetadata = + ImmutableMap.of( + "Authorization", + Collections.singletonList("Bearer " + fakeAccessToken.getTokenValue()), + QUOTA_USER_PROJECT_HEADER, + Collections.singletonList(DUMMY_GCP_QUOTA_PROJECT_ID)); + } else { + mockedRequestMetadata = + ImmutableMap.of( + "Authorization", + Collections.singletonList("Bearer " + fakeAccessToken.getTokenValue())); } + // mock credentials to return the prepared request metadata + Mockito.when(mockedGoogleCredentials.getRequestMetadata()).thenReturn(mockedRequestMetadata); // configure environment according to test case String quotaProjectId = testCase.getUserSpecifiedQuotaProjectId(); // maybe empty string - if (testCase.getUserSpecifiedQuotaProjectId() != null) { + if (quotaProjectId != null) { // user specified a quota project id System.setProperty( ConfigurableOption.GOOGLE_CLOUD_QUOTA_PROJECT.getSystemProperty(), quotaProjectId); @@ -288,58 +296,62 @@ public void testQuotaProjectBehavior(QuotaProjectIdTestBehavior testCase) { * indicates the expectation that the QUOTA_USER_PROJECT_HEADER should not be present in the * export headers. * - *

{@code true} for {@link QuotaProjectIdTestBehavior#getIsQuotaProjectPresentInCredentials()} + *

{@code true} for {@link QuotaProjectIdTestBehavior#getIsQuotaProjectPresentInMetadata()} * indicates that the mocked credentials are configured to provide DUMMY_GCP_QUOTA_PROJECT_ID as * the quota project ID. */ private static Stream provideQuotaBehaviorTestCases() { return Stream.of( + // If quota project present in metadata, it will be used Arguments.of( QuotaProjectIdTestBehavior.builder() .setUserSpecifiedQuotaProjectId(DUMMY_GCP_QUOTA_PROJECT_ID) - .setIsQuotaProjectPresentInCredentials(true) + .setIsQuotaProjectPresentInMetadata(true) .setExpectedQuotaProjectInHeader(DUMMY_GCP_QUOTA_PROJECT_ID) .build()), Arguments.of( QuotaProjectIdTestBehavior.builder() - .setUserSpecifiedQuotaProjectId(DUMMY_GCP_QUOTA_PROJECT_ID) - .setIsQuotaProjectPresentInCredentials(false) + .setUserSpecifiedQuotaProjectId("my-custom-quota-project-id") + .setIsQuotaProjectPresentInMetadata(true) .setExpectedQuotaProjectInHeader(DUMMY_GCP_QUOTA_PROJECT_ID) .build()), + // If quota project not present in request metadata, then user specified project is used Arguments.of( QuotaProjectIdTestBehavior.builder() - .setUserSpecifiedQuotaProjectId("my-custom-quota-project-id") - .setIsQuotaProjectPresentInCredentials(true) - .setExpectedQuotaProjectInHeader("my-custom-quota-project-id") + .setUserSpecifiedQuotaProjectId(DUMMY_GCP_QUOTA_PROJECT_ID) + .setIsQuotaProjectPresentInMetadata(false) + .setExpectedQuotaProjectInHeader(DUMMY_GCP_QUOTA_PROJECT_ID) .build()), Arguments.of( QuotaProjectIdTestBehavior.builder() .setUserSpecifiedQuotaProjectId("my-custom-quota-project-id") - .setIsQuotaProjectPresentInCredentials(false) + .setIsQuotaProjectPresentInMetadata(false) .setExpectedQuotaProjectInHeader("my-custom-quota-project-id") .build()), + // Testing for special edge case inputs + // user-specified quota project is empty Arguments.of( QuotaProjectIdTestBehavior.builder() .setUserSpecifiedQuotaProjectId("") // user explicitly specifies empty - .setIsQuotaProjectPresentInCredentials(true) + .setIsQuotaProjectPresentInMetadata(true) .setExpectedQuotaProjectInHeader(DUMMY_GCP_QUOTA_PROJECT_ID) .build()), Arguments.of( QuotaProjectIdTestBehavior.builder() - .setUserSpecifiedQuotaProjectId(null) // user omits specifying quota project - .setIsQuotaProjectPresentInCredentials(true) - .setExpectedQuotaProjectInHeader(DUMMY_GCP_QUOTA_PROJECT_ID) + .setUserSpecifiedQuotaProjectId("") + .setIsQuotaProjectPresentInMetadata(false) + .setExpectedQuotaProjectInHeader(null) .build()), Arguments.of( QuotaProjectIdTestBehavior.builder() - .setUserSpecifiedQuotaProjectId("") - .setIsQuotaProjectPresentInCredentials(false) - .setExpectedQuotaProjectInHeader(null) + .setUserSpecifiedQuotaProjectId(null) // user omits specifying quota project + .setIsQuotaProjectPresentInMetadata(true) + .setExpectedQuotaProjectInHeader(DUMMY_GCP_QUOTA_PROJECT_ID) .build()), Arguments.of( QuotaProjectIdTestBehavior.builder() .setUserSpecifiedQuotaProjectId(null) - .setIsQuotaProjectPresentInCredentials(false) + .setIsQuotaProjectPresentInMetadata(false) .setExpectedQuotaProjectInHeader(null) .build())); } @@ -367,7 +379,7 @@ abstract static class QuotaProjectIdTestBehavior { @Nullable abstract String getUserSpecifiedQuotaProjectId(); - abstract boolean getIsQuotaProjectPresentInCredentials(); + abstract boolean getIsQuotaProjectPresentInMetadata(); // If expected quota project in header is null, the header entry should not be present in export @Nullable @@ -382,9 +394,15 @@ static Builder builder() { abstract static class Builder { abstract Builder setUserSpecifiedQuotaProjectId(String quotaProjectId); - abstract Builder setIsQuotaProjectPresentInCredentials( - boolean quotaProjectPresentInCredentials); + abstract Builder setIsQuotaProjectPresentInMetadata(boolean quotaProjectPresentInMetadata); + /** + * Sets the expected quota project header value for the test case. A null value is allowed, + * and it indicates that the header should not be present in the export request. + * + * @param expectedQuotaProjectInHeader the expected header value to match in the export + * headers. + */ abstract Builder setExpectedQuotaProjectInHeader(String expectedQuotaProjectInHeader); abstract QuotaProjectIdTestBehavior build(); @@ -393,10 +411,18 @@ abstract Builder setIsQuotaProjectPresentInCredentials( @SuppressWarnings("CannotMockMethod") private void prepareMockBehaviorForGoogleCredentials() { - Mockito.when(mockedGoogleCredentials.getQuotaProjectId()) - .thenReturn(DUMMY_GCP_QUOTA_PROJECT_ID); - Mockito.when(mockedGoogleCredentials.getAccessToken()) - .thenReturn(new AccessToken("fake", Date.from(Instant.now()))); + AccessToken fakeAccessToken = new AccessToken("fake", Date.from(Instant.now())); + try { + Mockito.when(mockedGoogleCredentials.getRequestMetadata()) + .thenReturn( + ImmutableMap.of( + "Authorization", + Collections.singletonList("Bearer " + fakeAccessToken.getTokenValue()), + QUOTA_USER_PROJECT_HEADER, + Collections.singletonList(DUMMY_GCP_QUOTA_PROJECT_ID))); + } catch (IOException e) { + throw new RuntimeException(e); + } } private OpenTelemetrySdk buildOpenTelemetrySdkWithExporter(SpanExporter spanExporter) { From 840bb960b29113f7bfdfa30e6c50a1c8269edcce Mon Sep 17 00:00:00 2001 From: Pranav Sharma Date: Thu, 17 Apr 2025 18:18:32 +0000 Subject: [PATCH 2/6] Add a separate IntegrationTest to user Service Account Creds --- gcp-auth-extension/build.gradle.kts | 32 +++++++++++++++++-- ...s.json => fake_service_account_creds.json} | 0 2 files changed, 30 insertions(+), 2 deletions(-) rename gcp-auth-extension/src/test/resources/{fakecreds.json => fake_service_account_creds.json} (100%) diff --git a/gcp-auth-extension/build.gradle.kts b/gcp-auth-extension/build.gradle.kts index 112113e664..0b82fe7e5e 100644 --- a/gcp-auth-extension/build.gradle.kts +++ b/gcp-auth-extension/build.gradle.kts @@ -103,7 +103,7 @@ tasks.register("copyAgent") { }) } -tasks.register("IntegrationTest") { +tasks.register("IntegrationTestUserCreds") { dependsOn(tasks.shadowJar) dependsOn(tasks.named("copyAgent")) @@ -111,7 +111,35 @@ tasks.register("IntegrationTest") { // include only the integration test file include("io/opentelemetry/contrib/gcp/auth/GcpAuthExtensionEndToEndTest.class") - val fakeCredsFilePath = project.file("src/test/resources/fakecreds.json").absolutePath + val fakeCredsFilePath = project.file("src/test/resources/fake_user_creds.json").absolutePath + + environment("GOOGLE_CLOUD_QUOTA_PROJECT", "quota-project-id") + environment("GOOGLE_APPLICATION_CREDENTIALS", fakeCredsFilePath) + jvmArgs = listOf( + "-javaagent:$javaAgentJarPath", + "-Dotel.javaagent.extensions=$authExtensionJarPath", + "-Dgoogle.cloud.project=my-gcp-project", + "-Dotel.java.global-autoconfigure.enabled=true", + "-Dotel.exporter.otlp.endpoint=http://localhost:4318", + "-Dotel.resource.providers.gcp.enabled=true", + "-Dotel.traces.exporter=otlp", + "-Dotel.bsp.schedule.delay=2000", + "-Dotel.metrics.exporter=none", + "-Dotel.logs.exporter=none", + "-Dotel.exporter.otlp.protocol=http/protobuf", + "-Dmockserver.logLevel=off" + ) +} + +tasks.register("IntegrationTestServiceAccountCreds") { + dependsOn(tasks.shadowJar) + dependsOn(tasks.named("copyAgent")) + + useJUnitPlatform() + // include only the integration test file + include("io/opentelemetry/contrib/gcp/auth/GcpAuthExtensionEndToEndTest.class") + + val fakeCredsFilePath = project.file("src/test/resources/fake_service_account_creds.json").absolutePath environment("GOOGLE_CLOUD_QUOTA_PROJECT", "quota-project-id") environment("GOOGLE_APPLICATION_CREDENTIALS", fakeCredsFilePath) diff --git a/gcp-auth-extension/src/test/resources/fakecreds.json b/gcp-auth-extension/src/test/resources/fake_service_account_creds.json similarity index 100% rename from gcp-auth-extension/src/test/resources/fakecreds.json rename to gcp-auth-extension/src/test/resources/fake_service_account_creds.json From ab813a65f8a4ad90daa728faf76e921a0f568190 Mon Sep 17 00:00:00 2001 From: Pranav Sharma Date: Thu, 17 Apr 2025 18:19:10 +0000 Subject: [PATCH 3/6] Remove extraneous refresh credentials call --- .../gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java index db97471c0e..2c3ad199a6 100644 --- a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java +++ b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java @@ -100,7 +100,7 @@ private static SpanExporter addAuthorizationHeaders( private static Map getRequiredHeaderMap(GoogleCredentials credentials) { Map> gcpHeaders; try { - credentials.refreshIfExpired(); + // this also refreshes the credentials, if required gcpHeaders = credentials.getRequestMetadata(); } catch (IOException e) { throw new GoogleAuthException(Reason.FAILED_ADC_REFRESH, e); From 221442d9fb0d761666e168220de6aec17b0841c9 Mon Sep 17 00:00:00 2001 From: Pranav Sharma Date: Thu, 17 Apr 2025 18:43:34 +0000 Subject: [PATCH 4/6] Add fake user creds --- gcp-auth-extension/src/test/resources/fake_user_creds.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 gcp-auth-extension/src/test/resources/fake_user_creds.json diff --git a/gcp-auth-extension/src/test/resources/fake_user_creds.json b/gcp-auth-extension/src/test/resources/fake_user_creds.json new file mode 100644 index 0000000000..fd798897fb --- /dev/null +++ b/gcp-auth-extension/src/test/resources/fake_user_creds.json @@ -0,0 +1,7 @@ +{ + "client_id": "....apps.googleusercontent.com", + "client_secret": "...", + "refresh_token": "1//...", + "quota_project_id": "your-configured-quota-project", + "type": "authorized_user" +} From 3fc96239d146dfd538bb59d6e483335ebc170e7f Mon Sep 17 00:00:00 2001 From: Pranav Sharma Date: Fri, 18 Apr 2025 17:54:52 +0000 Subject: [PATCH 5/6] Fix flaky unit tests --- gcp-auth-extension/build.gradle.kts | 34 +++---------------- .../auth/GcpAuthExtensionEndToEndTest.java | 3 +- .../resources/fake_service_account_creds.json | 13 ------- 3 files changed, 6 insertions(+), 44 deletions(-) delete mode 100644 gcp-auth-extension/src/test/resources/fake_service_account_creds.json diff --git a/gcp-auth-extension/build.gradle.kts b/gcp-auth-extension/build.gradle.kts index 0b82fe7e5e..7e1460f676 100644 --- a/gcp-auth-extension/build.gradle.kts +++ b/gcp-auth-extension/build.gradle.kts @@ -55,6 +55,9 @@ dependencies { tasks { test { useJUnitPlatform() + // Unset relevant environment variables to provide a clean state for the tests + environment("GOOGLE_CLOUD_PROJECT", "") + environment("GOOGLE_CLOUD_QUOTA_PROJECT", "") // exclude integration test exclude("io/opentelemetry/contrib/gcp/auth/GcpAuthExtensionEndToEndTest.class") } @@ -127,34 +130,7 @@ tasks.register("IntegrationTestUserCreds") { "-Dotel.metrics.exporter=none", "-Dotel.logs.exporter=none", "-Dotel.exporter.otlp.protocol=http/protobuf", - "-Dmockserver.logLevel=off" - ) -} - -tasks.register("IntegrationTestServiceAccountCreds") { - dependsOn(tasks.shadowJar) - dependsOn(tasks.named("copyAgent")) - - useJUnitPlatform() - // include only the integration test file - include("io/opentelemetry/contrib/gcp/auth/GcpAuthExtensionEndToEndTest.class") - - val fakeCredsFilePath = project.file("src/test/resources/fake_service_account_creds.json").absolutePath - - environment("GOOGLE_CLOUD_QUOTA_PROJECT", "quota-project-id") - environment("GOOGLE_APPLICATION_CREDENTIALS", fakeCredsFilePath) - jvmArgs = listOf( - "-javaagent:$javaAgentJarPath", - "-Dotel.javaagent.extensions=$authExtensionJarPath", - "-Dgoogle.cloud.project=my-gcp-project", - "-Dotel.java.global-autoconfigure.enabled=true", - "-Dotel.exporter.otlp.endpoint=http://localhost:4318", - "-Dotel.resource.providers.gcp.enabled=true", - "-Dotel.traces.exporter=otlp", - "-Dotel.bsp.schedule.delay=2000", - "-Dotel.metrics.exporter=none", - "-Dotel.logs.exporter=none", - "-Dotel.exporter.otlp.protocol=http/protobuf", - "-Dmockserver.logLevel=off" + "-Dotel.javaagent.debug=false", + "-Dmockserver.logLevel=trace" ) } diff --git a/gcp-auth-extension/src/test/java/io/opentelemetry/contrib/gcp/auth/GcpAuthExtensionEndToEndTest.java b/gcp-auth-extension/src/test/java/io/opentelemetry/contrib/gcp/auth/GcpAuthExtensionEndToEndTest.java index 421d4fcec2..e04baed936 100644 --- a/gcp-auth-extension/src/test/java/io/opentelemetry/contrib/gcp/auth/GcpAuthExtensionEndToEndTest.java +++ b/gcp-auth-extension/src/test/java/io/opentelemetry/contrib/gcp/auth/GcpAuthExtensionEndToEndTest.java @@ -89,10 +89,9 @@ public static void setup() throws NoSuchAlgorithmException, KeyManagementExcepti // Set up mock OTLP backend server to which traces will be exported backendServer = ClientAndServer.startClientAndServer(EXPORTER_ENDPOINT_PORT); backendServer.when(request()).respond(response().withStatusCode(200)); - - // Set up the mock gcp metadata server to provide fake credentials String accessTokenResponse = "{\"access_token\": \"fake.access_token\",\"expires_in\": 3600, \"token_type\": \"Bearer\"}"; + mockGcpOAuth2Server = ClientAndServer.startClientAndServer(MOCK_GCP_OAUTH2_PORT); MockServerClient mockServerClient = diff --git a/gcp-auth-extension/src/test/resources/fake_service_account_creds.json b/gcp-auth-extension/src/test/resources/fake_service_account_creds.json deleted file mode 100644 index 1000f70db0..0000000000 --- a/gcp-auth-extension/src/test/resources/fake_service_account_creds.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "type": "service_account", - "project_id": "quota-project-id", - "private_key_id": "aljmafmlamlmmasma", - "private_key": "-----BEGIN PRIVATE KEY-----\nMIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALX0PQoe1igW12ikv1bN/r9lN749y2ijmbc/mFHPyS3hNTyOCjDvBbXYbDhQJzWVUikh4mvGBA07qTj79Xc3yBDfKP2IeyYQIFe0t0zkd7R9Zdn98Y2rIQC47aAbDfubtkU1U72t4zL11kHvoa0/RuFZjncvlr42X7be7lYh4p3NAgMBAAECgYASk5wDw4Az2ZkmeuN6Fk/y9H+Lcb2pskJIXjrL533vrDWGOC48LrsThMQPv8cxBky8HFSEklPpkfTF95tpD43iVwJRB/GrCtGTw65IfJ4/tI09h6zGc4yqvIo1cHX/LQ+SxKLGyir/dQM925rGt/VojxY5ryJR7GLbCzxPnJm/oQJBANwOCO6D2hy1LQYJhXh7O+RLtA/tSnT1xyMQsGT+uUCMiKS2bSKx2wxo9k7h3OegNJIu1q6nZ6AbxDK8H3+d0dUCQQDTrPSXagBxzp8PecbaCHjzNRSQE2in81qYnrAFNB4o3DpHyMMY6s5ALLeHKscEWnqP8Ur6X4PvzZecCWU9BKAZAkAutLPknAuxSCsUOvUfS1i87ex77Ot+w6POp34pEX+UWb+u5iFn2cQacDTHLV1LtE80L8jVLSbrbrlH43H0DjU5AkEAgidhycxS86dxpEljnOMCw8CKoUBd5I880IUahEiUltk7OLJYS/Ts1wbn3kPOVX3wyJs8WBDtBkFrDHW2ezth2QJADj3e1YhMVdjJW5jqwlD/VNddGjgzyunmiZg0uOXsHXbytYmsA545S8KRQFaJKFXYYFo2kOjqOiC1T2cAzMDjCQ==\n-----END PRIVATE KEY-----\n", - "client_email": "sample@appspot.gserviceaccount.com", - "client_id": "100000000000000000221", - "auth_uri": "https://accounts.google.com/o/oauth2/auth", - "token_uri": "https://oauth2.googleapis.com/token", - "auth_provider_x509_cert_url": "", - "client_x509_cert_url": "", - "universe_domain": "googleapis.com" -} From 59aa046449c78077e3bf1e5783c2f593be28e8f7 Mon Sep 17 00:00:00 2001 From: Pranav Sharma Date: Tue, 22 Apr 2025 16:15:02 +0000 Subject: [PATCH 6/6] Remove extraneous merge function --- .../gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java index 2c3ad199a6..053aeef7de 100644 --- a/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java +++ b/gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/GcpAuthAutoConfigurationCustomizerProvider.java @@ -115,9 +115,7 @@ private static Map getRequiredHeaderMap(GoogleCredentials creden entry.getValue().stream() .filter(Objects::nonNull) // Filter nulls .filter(s -> !s.isEmpty()) // Filter empty strings - .collect(Collectors.joining(",")), - (v1, v2) -> v2 // Merge function - take the last seen value - )); + .collect(Collectors.joining(",")))); // Add quota user project header if not detected by the auth library and user provided it via // system properties. if (!flattenedHeaders.containsKey(QUOTA_USER_PROJECT_HEADER)) {