2121import com .google .errorprone .annotations .concurrent .GuardedBy ;
2222import io .grpc .CallCredentials ;
2323import io .grpc .ChannelCredentials ;
24+ import io .grpc .CompositeCallCredentials ;
25+ import io .grpc .internal .GrpcUtil ;
2426import io .grpc .internal .JsonUtil ;
2527import io .grpc .xds .client .AllowedGrpcServices ;
2628import io .grpc .xds .client .AllowedGrpcServices .AllowedGrpcService ;
3032import io .grpc .xds .client .XdsInitializationException ;
3133import io .grpc .xds .client .XdsLogger ;
3234import java .io .IOException ;
35+ import java .util .ArrayList ;
3336import java .util .List ;
3437import java .util .Map ;
3538import java .util .Optional ;
3639import javax .annotation .Nullable ;
3740
3841class GrpcBootstrapperImpl extends BootstrapperImpl {
42+ @ VisibleForTesting
43+ public static boolean enableXdsBootstrapCallCreds = GrpcUtil .getFlag (
44+ "GRPC_EXPERIMENTAL_XDS_BOOTSTRAP_CALL_CREDS" , false );
45+
3946 private static final String BOOTSTRAP_PATH_SYS_ENV_VAR = "GRPC_XDS_BOOTSTRAP" ;
4047 private static final String BOOTSTRAP_PATH_SYS_PROPERTY = "io.grpc.xds.bootstrap" ;
4148 private static final String BOOTSTRAP_CONFIG_SYS_ENV_VAR = "GRPC_XDS_BOOTSTRAP_CONFIG" ;
@@ -104,7 +111,62 @@ protected String getJsonContent() throws XdsInitializationException, IOException
104111 protected Object getImplSpecificConfig (Map <String , ?> serverConfig , String serverUri )
105112 throws XdsInitializationException {
106113 ConfiguredChannelCredentials configuredChannel = getChannelCredentials (serverConfig , serverUri );
107- return configuredChannel != null ? configuredChannel .channelCredentials () : null ;
114+ ChannelCredentials channelCredentials = configuredChannel != null
115+ ? configuredChannel .channelCredentials () : null ;
116+
117+ CallCredentials callCredentials = null ;
118+ List <?> rawCallCreds = JsonUtil .getList (serverConfig , "call_creds" );
119+ if (enableXdsBootstrapCallCreds && rawCallCreds != null ) {
120+ List <Map <String , ?>> callCredsList = JsonUtil .checkObjectList (rawCallCreds );
121+ callCredentials = parseCallCredentials (callCredsList , serverUri );
122+ }
123+
124+ ImmutableMap .Builder <String , Object > builder = ImmutableMap .builder ();
125+ if (channelCredentials != null ) {
126+ builder .put ("grpc.channel_credentials" , channelCredentials );
127+ }
128+ if (callCredentials != null ) {
129+ builder .put ("grpc.call_credentials" , callCredentials );
130+ }
131+ return builder .buildOrThrow ();
132+ }
133+
134+ @ Nullable
135+ private CallCredentials parseCallCredentials (List <Map <String , ?>> jsonList , String serverUri )
136+ throws XdsInitializationException {
137+ List <CallCredentials > parsedCreds = new ArrayList <>();
138+ for (Map <String , ?> credJson : jsonList ) {
139+ String type = JsonUtil .getString (credJson , "type" );
140+ if (type == null ) {
141+ throw new XdsInitializationException (
142+ "Invalid bootstrap: server " + serverUri + " with 'call_creds' type unspecified" );
143+ }
144+ if ("jwt_token_file" .equals (type )) {
145+ Map <String , ?> config = JsonUtil .getObject (credJson , "config" );
146+ if (config == null ) {
147+ throw new XdsInitializationException (
148+ "Invalid bootstrap: server " + serverUri + " with 'jwt_token_file' config missing" );
149+ }
150+ String jwtTokenFile = JsonUtil .getString (config , "jwt_token_file" );
151+ if (jwtTokenFile == null || jwtTokenFile .isEmpty ()) {
152+ throw new XdsInitializationException (
153+ "Invalid bootstrap: server " + serverUri
154+ + " with 'jwt_token_file' jwt_token_file missing or empty" );
155+ }
156+ parsedCreds .add (new JwtTokenFileCallCredentials (jwtTokenFile ));
157+ } else {
158+ logger .log (XdsLogger .XdsLogLevel .INFO ,
159+ "Skipping unsupported call credential type: {0}" , type );
160+ }
161+ }
162+ if (parsedCreds .isEmpty ()) {
163+ return null ;
164+ }
165+ CallCredentials combined = parsedCreds .get (0 );
166+ for (int i = 1 ; i < parsedCreds .size (); i ++) {
167+ combined = new CompositeCallCredentials (combined , parsedCreds .get (i ));
168+ }
169+ return combined ;
108170 }
109171
110172 @ GuardedBy ("GrpcBootstrapperImpl.class" )
@@ -194,8 +256,8 @@ protected Optional<Object> parseImplSpecificObject(
194256 Optional <CallCredentials > callCredentials = Optional .empty ();
195257 List <?> rawCallCredsList = JsonUtil .getList (serviceConfig , "call_creds" );
196258 if (rawCallCredsList != null && !rawCallCredsList .isEmpty ()) {
197- callCredentials =
198- parseCallCredentials (JsonUtil .checkObjectList (rawCallCredsList ), targetUri );
259+ callCredentials = Optional . ofNullable (
260+ parseCallCredentials (JsonUtil .checkObjectList (rawCallCredsList ), targetUri )) ;
199261 }
200262
201263 AllowedGrpcService .Builder b = AllowedGrpcService .builder ()
@@ -208,16 +270,7 @@ protected Optional<Object> parseImplSpecificObject(
208270 return Optional .of (customConfig );
209271 }
210272
211- @ SuppressWarnings ("unused" )
212- private static Optional <CallCredentials > parseCallCredentials (List <Map <String , ?>> jsonList ,
213- String targetUri )
214- throws XdsInitializationException {
215- // TODO(sauravzg): Currently no xDS call credentials providers are implemented (no
216- // XdsCallCredentialsRegistry).
217- // As per A102/A97, we should just ignore unsupported call credentials types
218- // without throwing an exception.
219- return Optional .empty ();
220- }
273+
221274
222275 private static final class JsonChannelCredsConfig implements ChannelCredsConfig {
223276 private final String type ;
0 commit comments