Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Add EndpointContext #2275

Merged
merged 13 commits into from
Dec 8, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
import com.google.api.gax.core.ExecutorAsBackgroundResource;
import com.google.api.gax.core.ExecutorProvider;
import com.google.api.gax.rpc.internal.QuotaProjectIdHidingCredentials;
import com.google.api.gax.rpc.mtls.MtlsProvider;
import com.google.api.gax.tracing.ApiTracerFactory;
import com.google.api.gax.tracing.BaseApiTracerFactory;
import com.google.auth.Credentials;
Expand Down Expand Up @@ -143,29 +142,6 @@ public static ClientContext create(ClientSettings settings) throws IOException {
return create(settings.getStubSettings());
}

/** Returns the endpoint that should be used. See https://google.aip.dev/auth/4114. */
static String getEndpoint(
String endpoint,
String mtlsEndpoint,
boolean switchToMtlsEndpointAllowed,
MtlsProvider mtlsProvider)
throws IOException {
if (switchToMtlsEndpointAllowed) {
switch (mtlsProvider.getMtlsEndpointUsagePolicy()) {
case ALWAYS:
return mtlsEndpoint;
case NEVER:
return endpoint;
default:
if (mtlsProvider.useMtlsClientCertificate() && mtlsProvider.getKeyStore() != null) {
return mtlsEndpoint;
}
return endpoint;
}
}
return endpoint;
}

/**
* Instantiates the executor, credentials, and transport context based on the given client
* settings.
Expand Down Expand Up @@ -224,12 +200,13 @@ public static ClientContext create(StubSettings settings) throws IOException {
if (transportChannelProvider.needsCredentials() && credentials != null) {
transportChannelProvider = transportChannelProvider.withCredentials(credentials);
}
String endpoint =
getEndpoint(
settings.getEndpoint(),
settings.getMtlsEndpoint(),
settings.getSwitchToMtlsEndpointAllowed(),
new MtlsProvider());
EndpointContext endpointContext =
EndpointContext.newBuilder()
.setClientSettingsEndpoint(settings.getEndpoint())
.setMtlsEndpoint(settings.getMtlsEndpoint())
.setSwitchToMtlsEndpointAllowed(settings.getSwitchToMtlsEndpointAllowed())
.build();
String endpoint = endpointContext.getResolvedEndpoint();
if (transportChannelProvider.needsEndpoint()) {
transportChannelProvider = transportChannelProvider.withEndpoint(endpoint);
}
Expand Down
128 changes: 128 additions & 0 deletions gax-java/gax/src/main/java/com/google/api/gax/rpc/EndpointContext.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
* Copyright 2023 Google LLC
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google LLC nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.google.api.gax.rpc;

import com.google.api.core.InternalApi;
import com.google.api.gax.rpc.mtls.MtlsProvider;
import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import java.io.IOException;
import javax.annotation.Nullable;

/** Contains the fields required to resolve the endpoint */
@InternalApi
@AutoValue
public abstract class EndpointContext {
/**
* ClientSettingsEndpoint is the endpoint value set via the ClientSettings/StubSettings classes.
*/
@Nullable
public abstract String clientSettingsEndpoint();
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think this can be null as it is coming from StubSettings.endpoint?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I believe for most non-modified GAPICs, this is the case. It should be set via the {Service}StubSettings like: https://github.com/googleapis/google-cloud-java/blob/6227504db7c511b379cad2d67960d63f36eed39b/java-grafeas/src/main/java/io/grafeas/v1/stub/GrafeasStubSettings.java#L635 with a non-null value (as the generator will fail if it can't find it).

I'm accounting for the Grafeas edge case: https://github.com/googleapis/google-cloud-java/blob/6227504db7c511b379cad2d67960d63f36eed39b/java-grafeas/src/main/java/io/grafeas/v1/stub/GrafeasStubSettings.java#L403-L405 which has post-processed changes.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I see, this is good for now then. Let's keep this in mind and come back for it later if we have time. I'm not sure why we removed the default endpoint for grafeas, and how does grafeas create channels without a default endpoint. But ideally this should not be nullable, otherwise gRPC channel creation could fail.


@Nullable
public abstract String mtlsEndpoint();

public abstract boolean switchToMtlsEndpointAllowed();

@Nullable
public abstract MtlsProvider mtlsProvider();

public abstract Builder toBuilder();

private String resolvedEndpoint;

public static Builder newBuilder() {
return new AutoValue_EndpointContext.Builder().setSwitchToMtlsEndpointAllowed(false);
}

@VisibleForTesting
void determineEndpoint() throws IOException {
MtlsProvider mtlsProvider = mtlsProvider() == null ? new MtlsProvider() : mtlsProvider();
resolvedEndpoint =
mtlsEndpointResolver(
clientSettingsEndpoint(), mtlsEndpoint(), switchToMtlsEndpointAllowed(), mtlsProvider);
}

// This takes in parameters because determineEndpoint()'s logic will be updated
// to pass custom values in.
// Follows https://google.aip.dev/auth/4114 for resolving the endpoint
@VisibleForTesting
String mtlsEndpointResolver(
String endpoint,
String mtlsEndpoint,
boolean switchToMtlsEndpointAllowed,
MtlsProvider mtlsProvider)
throws IOException {
if (switchToMtlsEndpointAllowed && mtlsProvider != null) {
switch (mtlsProvider.getMtlsEndpointUsagePolicy()) {
case ALWAYS:
return mtlsEndpoint;
case NEVER:
return endpoint;
default:
if (mtlsProvider.useMtlsClientCertificate() && mtlsProvider.getKeyStore() != null) {
return mtlsEndpoint;
}
return endpoint;
}
}
return endpoint;
}

/**
* The resolved endpoint is the computed endpoint after accounting for the custom endpoints and
* mTLS configurations.
*/
public String getResolvedEndpoint() {
return resolvedEndpoint;
}
lqiu96 marked this conversation as resolved.
Show resolved Hide resolved

@AutoValue.Builder
public abstract static class Builder {
/**
* ClientSettingsEndpoint is the endpoint value set via the ClientSettings/StubSettings classes.
*/
public abstract Builder setClientSettingsEndpoint(String clientSettingsEndpoint);
lqiu96 marked this conversation as resolved.
Show resolved Hide resolved

public abstract Builder setMtlsEndpoint(String mtlsEndpoint);

public abstract Builder setSwitchToMtlsEndpointAllowed(boolean switchToMtlsEndpointAllowed);

public abstract Builder setMtlsProvider(MtlsProvider mtlsProvider);

abstract EndpointContext autoBuild();

public EndpointContext build() throws IOException {
EndpointContext endpointContext = autoBuild();
endpointContext.determineEndpoint();
return endpointContext;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

Expand All @@ -47,11 +46,8 @@
import com.google.api.gax.core.FixedCredentialsProvider;
import com.google.api.gax.core.FixedExecutorProvider;
import com.google.api.gax.core.NoCredentialsProvider;
import com.google.api.gax.rpc.mtls.MtlsProvider;
import com.google.api.gax.rpc.mtls.MtlsProvider.MtlsEndpointUsagePolicy;
import com.google.api.gax.rpc.testing.FakeChannel;
import com.google.api.gax.rpc.testing.FakeClientSettings;
import com.google.api.gax.rpc.testing.FakeMtlsProvider;
import com.google.api.gax.rpc.testing.FakeStubSettings;
import com.google.api.gax.rpc.testing.FakeTransportChannel;
import com.google.auth.Credentials;
Expand Down Expand Up @@ -636,93 +632,6 @@ public void testUserAgentConcat() throws Exception {
private static String endpoint = "https://foo.googleapis.com";
private static String mtlsEndpoint = "https://foo.mtls.googleapis.com";

@Test
public void testAutoUseMtlsEndpoint() throws IOException {
// Test the case client certificate exists and mTLS endpoint is selected.
boolean switchToMtlsEndpointAllowed = true;
MtlsProvider provider =
new FakeMtlsProvider(
true,
MtlsEndpointUsagePolicy.AUTO,
FakeMtlsProvider.createTestMtlsKeyStore(),
"",
false);
String endpointSelected =
ClientContext.getEndpoint(endpoint, mtlsEndpoint, switchToMtlsEndpointAllowed, provider);
assertEquals(mtlsEndpoint, endpointSelected);
}

@Test
public void testEndpointNotOverridable() throws IOException {
// Test the case that switching to mTLS endpoint is not allowed so the original endpoint is
// selected.
boolean switchToMtlsEndpointAllowed = false;
MtlsProvider provider =
new FakeMtlsProvider(
true,
MtlsEndpointUsagePolicy.AUTO,
FakeMtlsProvider.createTestMtlsKeyStore(),
"",
false);
String endpointSelected =
ClientContext.getEndpoint(endpoint, mtlsEndpoint, switchToMtlsEndpointAllowed, provider);
assertEquals(endpoint, endpointSelected);
}

@Test
public void testNoClientCertificate() throws IOException {
// Test the case that client certificates doesn't exists so the original endpoint is selected.
boolean switchToMtlsEndpointAllowed = true;
MtlsProvider provider =
new FakeMtlsProvider(true, MtlsEndpointUsagePolicy.AUTO, null, "", false);
String endpointSelected =
ClientContext.getEndpoint(endpoint, mtlsEndpoint, switchToMtlsEndpointAllowed, provider);
assertEquals(endpoint, endpointSelected);
}

@Test
public void testAlwaysUseMtlsEndpoint() throws IOException {
// Test the case that mTLS endpoint is always used.
boolean switchToMtlsEndpointAllowed = true;
MtlsProvider provider =
new FakeMtlsProvider(false, MtlsEndpointUsagePolicy.ALWAYS, null, "", false);
String endpointSelected =
ClientContext.getEndpoint(endpoint, mtlsEndpoint, switchToMtlsEndpointAllowed, provider);
assertEquals(mtlsEndpoint, endpointSelected);
}

@Test
public void testNeverUseMtlsEndpoint() throws IOException {
// Test the case that mTLS endpoint is never used.
boolean switchToMtlsEndpointAllowed = true;
MtlsProvider provider =
new FakeMtlsProvider(
true,
MtlsEndpointUsagePolicy.NEVER,
FakeMtlsProvider.createTestMtlsKeyStore(),
"",
false);
String endpointSelected =
ClientContext.getEndpoint(endpoint, mtlsEndpoint, switchToMtlsEndpointAllowed, provider);
assertEquals(endpoint, endpointSelected);
}

@Test
public void testGetKeyStoreThrows() throws IOException {
// Test the case that getKeyStore throws exceptions.
try {
boolean switchToMtlsEndpointAllowed = true;
MtlsProvider provider =
new FakeMtlsProvider(true, MtlsEndpointUsagePolicy.AUTO, null, "", true);
ClientContext.getEndpoint(endpoint, mtlsEndpoint, switchToMtlsEndpointAllowed, provider);
fail("should throw an exception");
} catch (IOException e) {
assertTrue(
"expected getKeyStore to throw an exception",
e.getMessage().contains("getKeyStore throws exception"));
}
}

@Test
public void testSwitchToMtlsEndpointAllowed() throws IOException {
StubSettings settings = new FakeStubSettings.Builder().setEndpoint(endpoint).build();
Expand Down
Loading
Loading