GCP Auth extension - allow users to specify quota project ID (#1647)

This commit is contained in:
Pranav Sharma 2025-01-16 17:04:05 -05:00 committed by GitHub
parent c00f094556
commit 72dc156391
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 273 additions and 40 deletions

View File

@ -36,7 +36,10 @@ Here is a list of configurable options for the extension:
- `GOOGLE_CLOUD_PROJECT`: Environment variable that represents the Google Cloud Project ID to which the telemetry needs to be exported.
- Can also be configured using `google.cloud.project` system property.
- If this option is not configured, the extension would infer GCP Project ID from the application default credentials. For more information on application default credentials, see [here](https://cloud.google.com/docs/authentication/application-default-credentials).
- This is a required option, the agent configuration will fail if this option is not set.
- `GOOGLE_CLOUD_QUOTA_PROJECT`: Environment variable that represents the Google Cloud Quota Project ID which will be charged for the GCP API usage. To learn more about a *quota project*, see [here](https://cloud.google.com/docs/quotas/quota-project).
- Can also be configured using `google.cloud.quota.project` system property.
- If this option is not configured, the extension will use the Quota Project ID found in the Application Default Credentials (ADC), if available. For more information on application default credentials, see [here](https://cloud.google.com/docs/authentication/application-default-credentials).
## Usage

View File

@ -29,6 +29,7 @@ dependencies {
testCompileOnly("com.google.auto.service:auto-service-annotations")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
testImplementation("org.junit.jupiter:junit-jupiter-api")
testCompileOnly("org.junit.jupiter:junit-jupiter-params")
testImplementation("io.opentelemetry:opentelemetry-api")
testImplementation("io.opentelemetry:opentelemetry-exporter-otlp")
@ -45,6 +46,9 @@ dependencies {
testImplementation("org.springframework.boot:spring-boot-starter:2.7.18")
testImplementation("org.springframework.boot:spring-boot-starter-test:2.7.18")
testAnnotationProcessor("com.google.auto.value:auto-value")
testCompileOnly("com.google.auto.value:auto-value-annotations")
agent("io.opentelemetry.javaagent:opentelemetry-javaagent")
}

View File

@ -18,7 +18,18 @@ public enum ConfigurableOption {
* Represents the Google Cloud Project ID option. Can be configured using the environment variable
* `GOOGLE_CLOUD_PROJECT` or the system property `google.cloud.project`.
*/
GOOGLE_CLOUD_PROJECT("Google Cloud Project ID");
GOOGLE_CLOUD_PROJECT("Google Cloud Project ID"),
/**
* Represents the Google Cloud Quota Project ID option. Can be configured using the environment
* variable `GOOGLE_CLOUD_QUOTA_PROJECT` or the system property `google.cloud.quota.project`. The
* quota project is the project that is used for quota management and billing for the API usage.
*
* <p>The environment variable name is selected to be consistent with the <a
* href="https://cloud.google.com/docs/quotas/set-quota-project">official GCP client
* libraries</a>.
*/
GOOGLE_CLOUD_QUOTA_PROJECT("Google Cloud Quota Project ID");
private final String userReadableName;
private final String environmentVariableName;

View File

@ -59,15 +59,16 @@ public class GcpAuthAutoConfigurationCustomizerProvider
*/
@Override
public void customize(AutoConfigurationCustomizer autoConfiguration) {
GoogleCredentials credentials;
try {
GoogleCredentials credentials = GoogleCredentials.getApplicationDefault();
autoConfiguration
.addSpanExporterCustomizer(
(exporter, configProperties) -> addAuthorizationHeaders(exporter, credentials))
.addResourceCustomizer(GcpAuthAutoConfigurationCustomizerProvider::customizeResource);
credentials = GoogleCredentials.getApplicationDefault();
} catch (IOException e) {
throw new GoogleAuthException(Reason.FAILED_ADC_RETRIEVAL, e);
}
autoConfiguration
.addSpanExporterCustomizer(
(exporter, configProperties) -> addAuthorizationHeaders(exporter, credentials))
.addResourceCustomizer(GcpAuthAutoConfigurationCustomizerProvider::customizeResource);
}
@Override
@ -100,24 +101,19 @@ public class GcpAuthAutoConfigurationCustomizerProvider
} catch (IOException e) {
throw new GoogleAuthException(Reason.FAILED_ADC_REFRESH, e);
}
gcpHeaders.put(QUOTA_USER_PROJECT_HEADER, credentials.getQuotaProjectId());
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);
}
return gcpHeaders;
}
// Updates the current resource with the attributes required for ingesting OTLP data on GCP.
private static Resource customizeResource(Resource resource, ConfigProperties configProperties) {
String gcpProjectId =
ConfigurableOption.GOOGLE_CLOUD_PROJECT.getConfiguredValueWithFallback(
() -> {
try {
GoogleCredentials googleCredentials = GoogleCredentials.getApplicationDefault();
return googleCredentials.getQuotaProjectId();
} catch (IOException e) {
throw new GoogleAuthException(Reason.FAILED_ADC_RETRIEVAL, e);
}
});
String gcpProjectId = ConfigurableOption.GOOGLE_CLOUD_PROJECT.getConfiguredValue();
Resource res =
Resource.create(
Attributes.of(AttributeKey.stringKey(GCP_USER_PROJECT_ID_KEY), gcpProjectId));

View File

@ -9,10 +9,12 @@ import static io.opentelemetry.contrib.gcp.auth.GcpAuthAutoConfigurationCustomiz
import static io.opentelemetry.contrib.gcp.auth.GcpAuthAutoConfigurationCustomizerProvider.QUOTA_USER_PROJECT_HEADER;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.google.auth.oauth2.AccessToken;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableMap;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.trace.Span;
@ -28,6 +30,7 @@ import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil;
import io.opentelemetry.sdk.autoconfigure.internal.ComponentLoader;
import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider;
import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.trace.data.SpanData;
@ -44,9 +47,14 @@ import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.stream.Stream;
import javax.annotation.Nullable;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
@ -58,6 +66,9 @@ import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class GcpAuthAutoConfigurationCustomizerProviderTest {
private static final String DUMMY_GCP_RESOURCE_PROJECT_ID = "my-gcp-resource-project-id";
private static final String DUMMY_GCP_QUOTA_PROJECT_ID = "my-gcp-quota-project-id";
@Mock private GoogleCredentials mockedGoogleCredentials;
@Captor private ArgumentCaptor<Supplier<Map<String, String>>> headerSupplierCaptor;
@ -74,16 +85,17 @@ class GcpAuthAutoConfigurationCustomizerProviderTest {
"foo=bar");
@BeforeEach
@SuppressWarnings("CannotMockMethod")
public void setup() {
MockitoAnnotations.openMocks(this);
Mockito.when(mockedGoogleCredentials.getQuotaProjectId()).thenReturn("test-project");
Mockito.when(mockedGoogleCredentials.getAccessToken())
.thenReturn(new AccessToken("fake", Date.from(Instant.now())));
}
@Test
public void testCustomizerOtlpHttp() {
// Set resource project system property
System.setProperty(
ConfigurableOption.GOOGLE_CLOUD_PROJECT.getSystemProperty(), DUMMY_GCP_RESOURCE_PROJECT_ID);
// Prepare mocks
prepareMockBehaviorForGoogleCredentials();
OtlpHttpSpanExporter mockOtlpHttpSpanExporter = Mockito.mock(OtlpHttpSpanExporter.class);
OtlpHttpSpanExporterBuilder otlpSpanExporterBuilder = OtlpHttpSpanExporter.builder();
OtlpHttpSpanExporterBuilder spyOtlpHttpSpanExporterBuilder =
@ -116,7 +128,7 @@ class GcpAuthAutoConfigurationCustomizerProviderTest {
Mockito.verify(spyOtlpHttpSpanExporterBuilder, Mockito.times(1))
.setHeaders(headerSupplierCaptor.capture());
assertEquals(2, headerSupplierCaptor.getValue().get().size());
assertThat(verifyAuthHeaders(headerSupplierCaptor.getValue().get())).isTrue();
assertThat(authHeadersQuotaProjectIsPresent(headerSupplierCaptor.getValue().get())).isTrue();
Mockito.verify(mockOtlpHttpSpanExporter, Mockito.atLeast(1)).export(Mockito.anyCollection());
@ -125,7 +137,9 @@ class GcpAuthAutoConfigurationCustomizerProviderTest {
.allSatisfy(
spanData -> {
assertThat(spanData.getResource().getAttributes().asMap())
.containsEntry(AttributeKey.stringKey(GCP_USER_PROJECT_ID_KEY), "test-project")
.containsEntry(
AttributeKey.stringKey(GCP_USER_PROJECT_ID_KEY),
DUMMY_GCP_RESOURCE_PROJECT_ID)
.containsEntry(AttributeKey.stringKey("foo"), "bar");
assertThat(spanData.getAttributes().asMap())
.containsKey(AttributeKey.longKey("work_loop"));
@ -135,20 +149,17 @@ class GcpAuthAutoConfigurationCustomizerProviderTest {
@Test
public void testCustomizerOtlpGrpc() {
// Set resource project system property
System.setProperty(
ConfigurableOption.GOOGLE_CLOUD_PROJECT.getSystemProperty(), DUMMY_GCP_RESOURCE_PROJECT_ID);
// Prepare mocks
prepareMockBehaviorForGoogleCredentials();
OtlpGrpcSpanExporter mockOtlpGrpcSpanExporter = Mockito.mock(OtlpGrpcSpanExporter.class);
OtlpGrpcSpanExporterBuilder otlpSpanExporterBuilder = OtlpGrpcSpanExporter.builder();
OtlpGrpcSpanExporterBuilder spyOtlpGrpcSpanExporterBuilder =
Mockito.spy(otlpSpanExporterBuilder);
Mockito.when(spyOtlpGrpcSpanExporterBuilder.build()).thenReturn(mockOtlpGrpcSpanExporter);
Mockito.when(mockOtlpGrpcSpanExporter.shutdown()).thenReturn(CompletableResultCode.ofSuccess());
Mockito.spy(OtlpGrpcSpanExporter.builder());
List<SpanData> exportedSpans = new ArrayList<>();
Mockito.when(mockOtlpGrpcSpanExporter.export(Mockito.anyCollection()))
.thenAnswer(
invocationOnMock -> {
exportedSpans.addAll(invocationOnMock.getArgument(0));
return CompletableResultCode.ofSuccess();
});
Mockito.when(mockOtlpGrpcSpanExporter.toBuilder()).thenReturn(spyOtlpGrpcSpanExporterBuilder);
configureGrpcMockExporters(
mockOtlpGrpcSpanExporter, spyOtlpGrpcSpanExporterBuilder, exportedSpans);
try (MockedStatic<GoogleCredentials> googleCredentialsMockedStatic =
Mockito.mockStatic(GoogleCredentials.class)) {
@ -166,7 +177,7 @@ class GcpAuthAutoConfigurationCustomizerProviderTest {
Mockito.verify(spyOtlpGrpcSpanExporterBuilder, Mockito.times(1))
.setHeaders(headerSupplierCaptor.capture());
assertEquals(2, headerSupplierCaptor.getValue().get().size());
verifyAuthHeaders(headerSupplierCaptor.getValue().get());
assertThat(authHeadersQuotaProjectIsPresent(headerSupplierCaptor.getValue().get())).isTrue();
Mockito.verify(mockOtlpGrpcSpanExporter, Mockito.atLeast(1)).export(Mockito.anyCollection());
@ -175,7 +186,9 @@ class GcpAuthAutoConfigurationCustomizerProviderTest {
.allSatisfy(
spanData -> {
assertThat(spanData.getResource().getAttributes().asMap())
.containsEntry(AttributeKey.stringKey(GCP_USER_PROJECT_ID_KEY), "test-project")
.containsEntry(
AttributeKey.stringKey(GCP_USER_PROJECT_ID_KEY),
DUMMY_GCP_RESOURCE_PROJECT_ID)
.containsEntry(AttributeKey.stringKey("foo"), "bar");
assertThat(spanData.getAttributes().asMap())
.containsKey(AttributeKey.longKey("work_loop"));
@ -183,6 +196,209 @@ class GcpAuthAutoConfigurationCustomizerProviderTest {
}
}
@Test
public void testCustomizerFailWithMissingResourceProject() {
OtlpGrpcSpanExporter mockOtlpGrpcSpanExporter = Mockito.mock(OtlpGrpcSpanExporter.class);
try (MockedStatic<GoogleCredentials> googleCredentialsMockedStatic =
Mockito.mockStatic(GoogleCredentials.class)) {
googleCredentialsMockedStatic
.when(GoogleCredentials::getApplicationDefault)
.thenReturn(mockedGoogleCredentials);
assertThrows(
ConfigurationException.class,
() -> buildOpenTelemetrySdkWithExporter(mockOtlpGrpcSpanExporter));
}
}
@ParameterizedTest
@MethodSource("provideQuotaBehaviorTestCases")
@SuppressWarnings("CannotMockMethod")
public void testQuotaProjectBehavior(QuotaProjectIdTestBehavior testCase) {
// 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);
}
// configure environment according to test case
String quotaProjectId = testCase.getUserSpecifiedQuotaProjectId(); // maybe empty string
if (testCase.getUserSpecifiedQuotaProjectId() != null) {
// user specified a quota project id
System.setProperty(
ConfigurableOption.GOOGLE_CLOUD_QUOTA_PROJECT.getSystemProperty(), quotaProjectId);
}
// prepare mock exporter
OtlpGrpcSpanExporter mockOtlpGrpcSpanExporter = Mockito.mock(OtlpGrpcSpanExporter.class);
OtlpGrpcSpanExporterBuilder spyOtlpGrpcSpanExporterBuilder =
Mockito.spy(OtlpGrpcSpanExporter.builder());
List<SpanData> exportedSpans = new ArrayList<>();
configureGrpcMockExporters(
mockOtlpGrpcSpanExporter, spyOtlpGrpcSpanExporterBuilder, exportedSpans);
try (MockedStatic<GoogleCredentials> googleCredentialsMockedStatic =
Mockito.mockStatic(GoogleCredentials.class)) {
googleCredentialsMockedStatic
.when(GoogleCredentials::getApplicationDefault)
.thenReturn(mockedGoogleCredentials);
// Export telemetry to capture headers in the export calls
OpenTelemetrySdk sdk = buildOpenTelemetrySdkWithExporter(mockOtlpGrpcSpanExporter);
generateTestSpan(sdk);
CompletableResultCode code = sdk.shutdown();
CompletableResultCode joinResult = code.join(10, TimeUnit.SECONDS);
assertTrue(joinResult.isSuccess());
Mockito.verify(spyOtlpGrpcSpanExporterBuilder, Mockito.times(1))
.setHeaders(headerSupplierCaptor.capture());
// assert that the Authorization bearer token header is present
Map<String, String> exportHeaders = headerSupplierCaptor.getValue().get();
assertThat(exportHeaders).containsEntry("Authorization", "Bearer fake");
if (testCase.getExpectedQuotaProjectInHeader() == null) {
// there should be no user quota project header
assertThat(exportHeaders).doesNotContainKey(QUOTA_USER_PROJECT_HEADER);
} else {
// there should be user quota project header with expected value
assertThat(exportHeaders)
.containsEntry(QUOTA_USER_PROJECT_HEADER, testCase.getExpectedQuotaProjectInHeader());
}
}
}
/**
* Test cases specifying expected value for the user quota project header given the user input and
* the current credentials state.
*
* <p>{@code null} for {@link QuotaProjectIdTestBehavior#getUserSpecifiedQuotaProjectId()}
* indicates the case of user not specifying the quota project ID.
*
* <p>{@code null} value for {@link QuotaProjectIdTestBehavior#getExpectedQuotaProjectInHeader()}
* indicates the expectation that the QUOTA_USER_PROJECT_HEADER should not be present in the
* export headers.
*
* <p>{@code true} for {@link QuotaProjectIdTestBehavior#getIsQuotaProjectPresentInCredentials()}
* indicates that the mocked credentials are configured to provide DUMMY_GCP_QUOTA_PROJECT_ID as
* the quota project ID.
*/
private static Stream<Arguments> provideQuotaBehaviorTestCases() {
return Stream.of(
Arguments.of(
QuotaProjectIdTestBehavior.builder()
.setUserSpecifiedQuotaProjectId(DUMMY_GCP_QUOTA_PROJECT_ID)
.setIsQuotaProjectPresentInCredentials(true)
.setExpectedQuotaProjectInHeader(DUMMY_GCP_QUOTA_PROJECT_ID)
.build()),
Arguments.of(
QuotaProjectIdTestBehavior.builder()
.setUserSpecifiedQuotaProjectId(DUMMY_GCP_QUOTA_PROJECT_ID)
.setIsQuotaProjectPresentInCredentials(false)
.setExpectedQuotaProjectInHeader(DUMMY_GCP_QUOTA_PROJECT_ID)
.build()),
Arguments.of(
QuotaProjectIdTestBehavior.builder()
.setUserSpecifiedQuotaProjectId("my-custom-quota-project-id")
.setIsQuotaProjectPresentInCredentials(true)
.setExpectedQuotaProjectInHeader("my-custom-quota-project-id")
.build()),
Arguments.of(
QuotaProjectIdTestBehavior.builder()
.setUserSpecifiedQuotaProjectId("my-custom-quota-project-id")
.setIsQuotaProjectPresentInCredentials(false)
.setExpectedQuotaProjectInHeader("my-custom-quota-project-id")
.build()),
Arguments.of(
QuotaProjectIdTestBehavior.builder()
.setUserSpecifiedQuotaProjectId("") // user explicitly specifies empty
.setIsQuotaProjectPresentInCredentials(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)
.build()),
Arguments.of(
QuotaProjectIdTestBehavior.builder()
.setUserSpecifiedQuotaProjectId("")
.setIsQuotaProjectPresentInCredentials(false)
.setExpectedQuotaProjectInHeader(null)
.build()),
Arguments.of(
QuotaProjectIdTestBehavior.builder()
.setUserSpecifiedQuotaProjectId(null)
.setIsQuotaProjectPresentInCredentials(false)
.setExpectedQuotaProjectInHeader(null)
.build()));
}
// Configure necessary behavior on the Grpc mock exporters to work
// TODO: Potential improvement - make this work for Http exporter as well.
private static void configureGrpcMockExporters(
OtlpGrpcSpanExporter mockGrpcExporter,
OtlpGrpcSpanExporterBuilder spyGrpcExporterBuilder,
List<SpanData> exportedSpanContainer) {
Mockito.when(spyGrpcExporterBuilder.build()).thenReturn(mockGrpcExporter);
Mockito.when(mockGrpcExporter.shutdown()).thenReturn(CompletableResultCode.ofSuccess());
Mockito.when(mockGrpcExporter.toBuilder()).thenReturn(spyGrpcExporterBuilder);
Mockito.when(mockGrpcExporter.export(Mockito.anyCollection()))
.thenAnswer(
invocationOnMock -> {
exportedSpanContainer.addAll(invocationOnMock.getArgument(0));
return CompletableResultCode.ofSuccess();
});
}
@AutoValue
abstract static class QuotaProjectIdTestBehavior {
// A null user specified quota represents the use case where user omits specifying quota
@Nullable
abstract String getUserSpecifiedQuotaProjectId();
abstract boolean getIsQuotaProjectPresentInCredentials();
// If expected quota project in header is null, the header entry should not be present in export
@Nullable
abstract String getExpectedQuotaProjectInHeader();
static Builder builder() {
return new AutoValue_GcpAuthAutoConfigurationCustomizerProviderTest_QuotaProjectIdTestBehavior
.Builder();
}
@AutoValue.Builder
abstract static class Builder {
abstract Builder setUserSpecifiedQuotaProjectId(String quotaProjectId);
abstract Builder setIsQuotaProjectPresentInCredentials(
boolean quotaProjectPresentInCredentials);
abstract Builder setExpectedQuotaProjectInHeader(String expectedQuotaProjectInHeader);
abstract QuotaProjectIdTestBehavior build();
}
}
@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())));
}
private OpenTelemetrySdk buildOpenTelemetrySdkWithExporter(SpanExporter spanExporter) {
SpiHelper spiHelper =
SpiHelper.create(GcpAuthAutoConfigurationCustomizerProviderTest.class.getClassLoader());
@ -215,9 +431,12 @@ class GcpAuthAutoConfigurationCustomizerProviderTest {
return builder.build().getOpenTelemetrySdk();
}
private static boolean verifyAuthHeaders(Map<String, String> headers) {
private static boolean authHeadersQuotaProjectIsPresent(Map<String, String> headers) {
Set<Entry<String, String>> headerEntrySet = headers.entrySet();
return headerEntrySet.contains(new SimpleEntry<>(QUOTA_USER_PROJECT_HEADER, "test-project"))
return headerEntrySet.contains(
new SimpleEntry<>(
QUOTA_USER_PROJECT_HEADER,
GcpAuthAutoConfigurationCustomizerProviderTest.DUMMY_GCP_QUOTA_PROJECT_ID))
&& headerEntrySet.contains(new SimpleEntry<>("Authorization", "Bearer fake"));
}