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

grpc / grpc-java / #20046

03 Nov 2025 09:31PM UTC coverage: 88.53% (+0.008%) from 88.522%
#20046

push

github

ejona86
xds: Avoid default bootstrap when global override in XdsNameResolver

This fixes a regression with an internal API from 27d150890 where
overridding the global bootstrap didn't impact parsing the default
bootstrap. So if no global bootstrap was available XdsNameResolver would
fail to start even though an override was in place in
SharedXdsClientPoolProvider. Instead of dealing with the override in
SharedXdsClientPoolProvider, do it in GrpcBootstrapperImpl so
XdsNameResolver is ignorant of the source of the default bootstrap.

We want all of this to go away in favor of XDS_CLIENT_SUPPLIER
injection, but there needs to be some overlap for migration.

cl/826085025

34963 of 39493 relevant lines covered (88.53%)

0.89 hits per line

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

84.91
/../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 Map<String, ?> defaultBootstrapOverride;
105
  @GuardedBy("GrpcBootstrapperImpl.class")
106
  private static BootstrapInfo defaultBootstrap;
107

108
  static synchronized void setDefaultBootstrapOverride(Map<String, ?> rawBootstrap) {
109
    defaultBootstrapOverride = rawBootstrap;
×
110
  }
×
111

112
  static synchronized BootstrapInfo defaultBootstrap() throws XdsInitializationException {
113
    if (defaultBootstrap == null) {
×
114
      if (defaultBootstrapOverride == null) {
×
115
        defaultBootstrap = new GrpcBootstrapperImpl().bootstrap();
×
116
      } else {
117
        defaultBootstrap = new GrpcBootstrapperImpl().bootstrap(defaultBootstrapOverride);
×
118
      }
119
    }
120
    return defaultBootstrap;
×
121
  }
122

123
  private static ChannelCredentials getChannelCredentials(Map<String, ?> serverConfig,
124
                                                          String serverUri)
125
      throws XdsInitializationException {
126
    List<?> rawChannelCredsList = JsonUtil.getList(serverConfig, "channel_creds");
1✔
127
    if (rawChannelCredsList == null || rawChannelCredsList.isEmpty()) {
1✔
128
      throw new XdsInitializationException(
1✔
129
          "Invalid bootstrap: server " + serverUri + " 'channel_creds' required");
130
    }
131
    ChannelCredentials channelCredentials =
1✔
132
        parseChannelCredentials(JsonUtil.checkObjectList(rawChannelCredsList), serverUri);
1✔
133
    if (channelCredentials == null) {
1✔
134
      throw new XdsInitializationException(
1✔
135
          "Server " + serverUri + ": no supported channel credentials found");
136
    }
137
    return channelCredentials;
1✔
138
  }
139

140
  @Nullable
141
  private static ChannelCredentials parseChannelCredentials(List<Map<String, ?>> jsonList,
142
                                                            String serverUri)
143
      throws XdsInitializationException {
144
    for (Map<String, ?> channelCreds : jsonList) {
1✔
145
      String type = JsonUtil.getString(channelCreds, "type");
1✔
146
      if (type == null) {
1✔
147
        throw new XdsInitializationException(
×
148
            "Invalid bootstrap: server " + serverUri + " with 'channel_creds' type unspecified");
149
      }
150
      XdsCredentialsProvider provider =  XdsCredentialsRegistry.getDefaultRegistry()
1✔
151
          .getProvider(type);
1✔
152
      if (provider != null) {
1✔
153
        Map<String, ?> config = JsonUtil.getObject(channelCreds, "config");
1✔
154
        if (config == null) {
1✔
155
          config = ImmutableMap.of();
1✔
156
        }
157

158
        return provider.newChannelCredentials(config);
1✔
159
      }
160
    }
1✔
161
    return null;
1✔
162
  }
163
}
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