34
34
import com .google .api .gax .tracing .ApiTracerFactory ;
35
35
import com .google .api .gax .tracing .BaseApiTracerFactory ;
36
36
import com .google .api .gax .tracing .OpencensusTracerFactory ;
37
+ import com .google .auth .oauth2 .AccessToken ;
38
+ import com .google .auth .oauth2 .GoogleCredentials ;
37
39
import com .google .cloud .NoCredentials ;
38
40
import com .google .cloud .ServiceDefaults ;
39
41
import com .google .cloud .ServiceOptions ;
56
58
import com .google .common .annotations .VisibleForTesting ;
57
59
import com .google .common .base .MoreObjects ;
58
60
import com .google .common .base .Preconditions ;
61
+ import com .google .common .base .Strings ;
59
62
import com .google .common .collect .ImmutableMap ;
60
63
import com .google .common .collect .ImmutableSet ;
61
64
import com .google .common .util .concurrent .ThreadFactoryBuilder ;
79
82
import java .io .IOException ;
80
83
import java .net .MalformedURLException ;
81
84
import java .net .URL ;
85
+ import java .nio .file .Files ;
86
+ import java .nio .file .Paths ;
82
87
import java .time .Duration ;
83
88
import java .util .ArrayList ;
89
+ import java .util .Base64 ;
84
90
import java .util .HashMap ;
85
91
import java .util .List ;
86
92
import java .util .Map ;
92
98
import java .util .concurrent .ThreadFactory ;
93
99
import java .util .concurrent .TimeUnit ;
94
100
import java .util .concurrent .atomic .AtomicInteger ;
101
+ import java .util .regex .Pattern ;
95
102
import javax .annotation .Nonnull ;
96
103
import javax .annotation .Nullable ;
97
104
import javax .annotation .concurrent .GuardedBy ;
@@ -110,6 +117,11 @@ public class SpannerOptions extends ServiceOptions<Spanner, SpannerOptions> {
110
117
111
118
private static final String API_SHORT_NAME = "Spanner" ;
112
119
private static final String DEFAULT_HOST = "https://spanner.googleapis.com" ;
120
+ private static final String CLOUD_SPANNER_HOST_FORMAT = ".*\\ .googleapis\\ .com.*" ;
121
+
122
+ @ VisibleForTesting
123
+ static final Pattern CLOUD_SPANNER_HOST_PATTERN = Pattern .compile (CLOUD_SPANNER_HOST_FORMAT );
124
+
113
125
private static final ImmutableSet <String > SCOPES =
114
126
ImmutableSet .of (
115
127
"https://www.googleapis.com/auth/spanner.admin" ,
@@ -843,8 +855,15 @@ default boolean isEnableEndToEndTracing() {
843
855
default String getMonitoringHost () {
844
856
return null ;
845
857
}
858
+
859
+ default GoogleCredentials getDefaultExternalHostCredentials () {
860
+ return null ;
861
+ }
846
862
}
847
863
864
+ static final String DEFAULT_SPANNER_EXTERNAL_HOST_CREDENTIALS =
865
+ "SPANNER_EXTERNAL_HOST_AUTH_TOKEN" ;
866
+
848
867
/**
849
868
* Default implementation of {@link SpannerEnvironment}. Reads all configuration from environment
850
869
* variables.
@@ -900,6 +919,11 @@ public boolean isEnableEndToEndTracing() {
900
919
public String getMonitoringHost () {
901
920
return System .getenv (SPANNER_MONITORING_HOST );
902
921
}
922
+
923
+ @ Override
924
+ public GoogleCredentials getDefaultExternalHostCredentials () {
925
+ return getOAuthTokenFromFile (System .getenv (DEFAULT_SPANNER_EXTERNAL_HOST_CREDENTIALS ));
926
+ }
903
927
}
904
928
905
929
/** Builder for {@link SpannerOptions} instances. */
@@ -967,6 +991,7 @@ public static class Builder
967
991
private boolean enableBuiltInMetrics = SpannerOptions .environment .isEnableBuiltInMetrics ();
968
992
private String monitoringHost = SpannerOptions .environment .getMonitoringHost ();
969
993
private SslContext mTLSContext = null ;
994
+ private boolean isExternalHost = false ;
970
995
971
996
private static String createCustomClientLibToken (String token ) {
972
997
return token + " " + ServiceOptions .getGoogApiClientLibName ();
@@ -1459,6 +1484,9 @@ public Builder setDecodeMode(DecodeMode decodeMode) {
1459
1484
@ Override
1460
1485
public Builder setHost (String host ) {
1461
1486
super .setHost (host );
1487
+ if (!CLOUD_SPANNER_HOST_PATTERN .matcher (host ).matches ()) {
1488
+ this .isExternalHost = true ;
1489
+ }
1462
1490
// Setting a host should override any SPANNER_EMULATOR_HOST setting.
1463
1491
setEmulatorHost (null );
1464
1492
return this ;
@@ -1629,6 +1657,8 @@ public SpannerOptions build() {
1629
1657
this .setChannelConfigurator (ManagedChannelBuilder ::usePlaintext );
1630
1658
// As we are using plain text, we should never send any credentials.
1631
1659
this .setCredentials (NoCredentials .getInstance ());
1660
+ } else if (isExternalHost && credentials == null ) {
1661
+ credentials = environment .getDefaultExternalHostCredentials ();
1632
1662
}
1633
1663
if (this .numChannels == null ) {
1634
1664
this .numChannels =
@@ -1669,6 +1699,24 @@ public static void useDefaultEnvironment() {
1669
1699
SpannerOptions .environment = SpannerEnvironmentImpl .INSTANCE ;
1670
1700
}
1671
1701
1702
+ @ InternalApi
1703
+ public static GoogleCredentials getDefaultExternalHostCredentialsFromSysEnv () {
1704
+ return getOAuthTokenFromFile (System .getenv (DEFAULT_SPANNER_EXTERNAL_HOST_CREDENTIALS ));
1705
+ }
1706
+
1707
+ private static @ Nullable GoogleCredentials getOAuthTokenFromFile (@ Nullable String file ) {
1708
+ if (!Strings .isNullOrEmpty (file )) {
1709
+ String token ;
1710
+ try {
1711
+ token = Base64 .getEncoder ().encodeToString (Files .readAllBytes (Paths .get (file )));
1712
+ } catch (IOException e ) {
1713
+ throw SpannerExceptionFactory .newSpannerException (e );
1714
+ }
1715
+ return GoogleCredentials .create (new AccessToken (token , null ));
1716
+ }
1717
+ return null ;
1718
+ }
1719
+
1672
1720
/**
1673
1721
* Enables OpenTelemetry traces. Enabling OpenTelemetry traces will disable OpenCensus traces. By
1674
1722
* default, OpenCensus traces are enabled.
0 commit comments