• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

grpc / grpc-java / #20033

29 Oct 2025 04:43PM UTC coverage: 88.533% (-0.03%) from 88.561%
#20033

push

github

web-flow
xds,googleapis: Allow wrapping NameResolver to inject XdsClient (#12450)

Since there is no longer a single global XdsClient, it makes more sense
to allow things like the c2p name resolver to inject its own bootstrap
even if there is one defined in an environment variable.
GoogleCloudToProdNameResolver can now pass an XdsClient instance to
XdsNameResolver, and SharedXdsClientPoolProvider allows
GoogleCloudToProdNameResolver to choose the bootstrap for that one
specific target.

Since XdsNameResolver is no longer in control of the XdsClient pool the
XdsClient instance is now passed to ClusterImplLb. A channel will now
only access the global XdsClient pool exactly once: in the name
resolver.

BootstrapInfo is purposefully being shared across channels, as we really
want to share things like credentials which can have significant memory
use and may have caches which reduce I/O when shared. That is why
SharedXdsClientPoolProvider receives BootstrapInfo instead of
Map<String,?>.

Verifying BootstrapInfo.server() is not empty was moved from
SharedXdsClientPoolProvider to GrpcBootstrapperImpl so avoid
getOrCreate() throwing an exception in only that one case. It might make
sense to move that to BootstrapperImpl, but that will need more
investigation.

A lot of tests needed updating because XdsClientPoolProvider is no
longer responsible for parsing the bootstrap, so we now need bootstraps
even if XdsClientPoolProvider will ignore it.

This also fixes a bug in GoogleCloudToProdNameResolver where it would
initialize the delegate even when it failed to create the bootstrap.
That would certainly cause all RPCs on the channel to fail because of
the missing bootstrap and it defeated the point of `succeeded == false`
and `refresh()` which was supposed to retry contacting the metadata
server.

The server tests were enhanced to give a useful error when
server.start() throws an exception, as otherwise the real error is lost.

b/442819521

34966 of 39495 relevant lines covered (88.53%)

0.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.84
/../xds/src/main/java/io/grpc/xds/GrpcBootstrapperImpl.java
1
/*
2
 * Copyright 2024 The gRPC Authors
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16

17
package io.grpc.xds;
18

19
import com.google.common.annotations.VisibleForTesting;
20
import com.google.common.collect.ImmutableMap;
21
import com.google.errorprone.annotations.concurrent.GuardedBy;
22
import io.grpc.ChannelCredentials;
23
import io.grpc.internal.JsonUtil;
24
import io.grpc.xds.client.BootstrapperImpl;
25
import io.grpc.xds.client.XdsInitializationException;
26
import io.grpc.xds.client.XdsLogger;
27
import java.io.IOException;
28
import java.util.List;
29
import java.util.Map;
30
import javax.annotation.Nullable;
31

32
class GrpcBootstrapperImpl extends BootstrapperImpl {
33
  private static final String BOOTSTRAP_PATH_SYS_ENV_VAR = "GRPC_XDS_BOOTSTRAP";
34
  private static final String BOOTSTRAP_PATH_SYS_PROPERTY = "io.grpc.xds.bootstrap";
35
  private static final String BOOTSTRAP_CONFIG_SYS_ENV_VAR = "GRPC_XDS_BOOTSTRAP_CONFIG";
36
  private static final String BOOTSTRAP_CONFIG_SYS_PROPERTY = "io.grpc.xds.bootstrapConfig";
37
  @VisibleForTesting
1✔
38
  String bootstrapPathFromEnvVar = System.getenv(BOOTSTRAP_PATH_SYS_ENV_VAR);
1✔
39
  @VisibleForTesting
1✔
40
  String bootstrapPathFromSysProp = System.getProperty(BOOTSTRAP_PATH_SYS_PROPERTY);
1✔
41
  @VisibleForTesting
1✔
42
  String bootstrapConfigFromEnvVar = System.getenv(BOOTSTRAP_CONFIG_SYS_ENV_VAR);
1✔
43
  @VisibleForTesting
1✔
44
  String bootstrapConfigFromSysProp = System.getProperty(BOOTSTRAP_CONFIG_SYS_PROPERTY);
1✔
45

46
  GrpcBootstrapperImpl() {
47
    super();
1✔
48
  }
1✔
49

50
  @Override
51
  public BootstrapInfo bootstrap(Map<String, ?> rawData) throws XdsInitializationException {
52
    BootstrapInfo info = super.bootstrap(rawData);
1✔
53
    if (info.servers().isEmpty()) {
1✔
54
      throw new XdsInitializationException("Invalid bootstrap: 'xds_servers' is empty");
1✔
55
    }
56
    return info;
1✔
57
  }
58

59
  /**
60
   * Gets the bootstrap config as JSON. Searches the config (or file of config) with the
61
   * following order:
62
   *
63
   * <ol>
64
   *   <li>A filesystem path defined by environment variable "GRPC_XDS_BOOTSTRAP"</li>
65
   *   <li>A filesystem path defined by Java System Property "io.grpc.xds.bootstrap"</li>
66
   *   <li>Environment variable value of "GRPC_XDS_BOOTSTRAP_CONFIG"</li>
67
   *   <li>Java System Property value of "io.grpc.xds.bootstrapConfig"</li>
68
   * </ol>
69
   */
70
  @Override
71
  protected String getJsonContent() throws XdsInitializationException, IOException {
72
    String jsonContent;
73
    String filePath =
74
        bootstrapPathFromEnvVar != null ? bootstrapPathFromEnvVar : bootstrapPathFromSysProp;
1✔
75
    if (filePath != null) {
1✔
76
      logger.log(XdsLogger.XdsLogLevel.INFO, "Reading bootstrap file from {0}", filePath);
1✔
77
      jsonContent = reader.readFile(filePath);
1✔
78
      logger.log(XdsLogger.XdsLogLevel.INFO, "Reading bootstrap from " + filePath);
1✔
79
    } else {
80
      jsonContent = bootstrapConfigFromEnvVar != null
1✔
81
          ? bootstrapConfigFromEnvVar : bootstrapConfigFromSysProp;
1✔
82
    }
83
    if (jsonContent == null) {
1✔
84
      throw new XdsInitializationException(
1✔
85
          "Cannot find bootstrap configuration\n"
86
              + "Environment variables searched:\n"
87
              + "- " + BOOTSTRAP_PATH_SYS_ENV_VAR + "\n"
88
              + "- " + BOOTSTRAP_CONFIG_SYS_ENV_VAR + "\n\n"
89
              + "Java System Properties searched:\n"
90
              + "- " + BOOTSTRAP_PATH_SYS_PROPERTY + "\n"
91
              + "- " + BOOTSTRAP_CONFIG_SYS_PROPERTY + "\n\n");
92
    }
93

94
    return jsonContent;
1✔
95
  }
96

97
  @Override
98
  protected Object getImplSpecificConfig(Map<String, ?> serverConfig, String serverUri)
99
      throws XdsInitializationException {
100
    return getChannelCredentials(serverConfig, serverUri);
1✔
101
  }
102

103
  @GuardedBy("GrpcBootstrapperImpl.class")
104
  private static BootstrapInfo defaultBootstrap;
105

106
  static synchronized BootstrapInfo defaultBootstrap() throws XdsInitializationException {
107
    if (defaultBootstrap == null) {
×
108
      defaultBootstrap = new GrpcBootstrapperImpl().bootstrap();
×
109
    }
110
    return defaultBootstrap;
×
111
  }
112

113
  private static ChannelCredentials getChannelCredentials(Map<String, ?> serverConfig,
114
                                                          String serverUri)
115
      throws XdsInitializationException {
116
    List<?> rawChannelCredsList = JsonUtil.getList(serverConfig, "channel_creds");
1✔
117
    if (rawChannelCredsList == null || rawChannelCredsList.isEmpty()) {
1✔
118
      throw new XdsInitializationException(
1✔
119
          "Invalid bootstrap: server " + serverUri + " 'channel_creds' required");
120
    }
121
    ChannelCredentials channelCredentials =
1✔
122
        parseChannelCredentials(JsonUtil.checkObjectList(rawChannelCredsList), serverUri);
1✔
123
    if (channelCredentials == null) {
1✔
124
      throw new XdsInitializationException(
1✔
125
          "Server " + serverUri + ": no supported channel credentials found");
126
    }
127
    return channelCredentials;
1✔
128
  }
129

130
  @Nullable
131
  private static ChannelCredentials parseChannelCredentials(List<Map<String, ?>> jsonList,
132
                                                            String serverUri)
133
      throws XdsInitializationException {
134
    for (Map<String, ?> channelCreds : jsonList) {
1✔
135
      String type = JsonUtil.getString(channelCreds, "type");
1✔
136
      if (type == null) {
1✔
137
        throw new XdsInitializationException(
×
138
            "Invalid bootstrap: server " + serverUri + " with 'channel_creds' type unspecified");
139
      }
140
      XdsCredentialsProvider provider =  XdsCredentialsRegistry.getDefaultRegistry()
1✔
141
          .getProvider(type);
1✔
142
      if (provider != null) {
1✔
143
        Map<String, ?> config = JsonUtil.getObject(channelCreds, "config");
1✔
144
        if (config == null) {
1✔
145
          config = ImmutableMap.of();
1✔
146
        }
147

148
        return provider.newChannelCredentials(config);
1✔
149
      }
150
    }
1✔
151
    return null;
1✔
152
  }
153
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc