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

grpc / grpc-java / #20232

02 Apr 2026 11:39AM UTC coverage: 88.784% (+0.03%) from 88.755%
#20232

push

github

web-flow
xds: Add configuration objects for ExtAuthz, GrpcService and Bootstrap changes for GrpcService (#12492)

This commit introduces configuration objects for the external
authorization (ExtAuthz) filter and the gRPC service and corresponding
translations from XDS proto and Bootstrap. These classes provide a
structured, immutable representation of the subset of the configuration
defined in the xDS protobuf messages.

This PR should mostly now (hopefully ) be compliant with
https://github.com/grpc/proposal/pull/510 but without
- CallCredentials (since I don't see A97) being implemented yet and
would prefer to do it in a followup , we return empty optional)
- TlsCredentials( since it's non trivial to construct a TLS credentials
object, we throw an exception)
- LocalCredentials(Java does't support these, we throw an exception)

The main new classes are:
- `ExtAuthzConfig`: Represents the configuration for the `ExtAuthz`
filter, including settings for the gRPC service, header mutation rules,
and other filter behaviors.
- `GrpcServiceConfig`: Represents the configuration for a gRPC service,
including the target URI, credentials, and other settings.
- `HeaderMutationRulesConfig`: Represents the configuration for header
mutation rules.
- `ChannelCredsConfig` and friends: To allow comparison between
credential configuration , to allow caching based on creds which'll be
needed in followup PRs for authz and proc.

The relevant sections of the spec are 
- GrpcService: https://github.com/grpc/proposal/pull/510
- ExtAuthz:
https://github.com/grpc/proposal/pull/481/files#diff-6bb76a24ad2fd8849f164244e68cd54eaR106-R190

This commit also includes parsers to create these configuration objects
from the corresponding protobuf messages, as well as unit tests for the
new classes.

35968 of 40512 relevant lines covered (88.78%)

0.89 hits per line

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

79.57
/../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.CallCredentials;
23
import io.grpc.ChannelCredentials;
24
import io.grpc.internal.JsonUtil;
25
import io.grpc.xds.client.AllowedGrpcServices;
26
import io.grpc.xds.client.AllowedGrpcServices.AllowedGrpcService;
27
import io.grpc.xds.client.BootstrapperImpl;
28
import io.grpc.xds.client.ConfiguredChannelCredentials;
29
import io.grpc.xds.client.ConfiguredChannelCredentials.ChannelCredsConfig;
30
import io.grpc.xds.client.XdsInitializationException;
31
import io.grpc.xds.client.XdsLogger;
32
import java.io.IOException;
33
import java.util.List;
34
import java.util.Map;
35
import java.util.Optional;
36
import javax.annotation.Nullable;
37

38
class GrpcBootstrapperImpl extends BootstrapperImpl {
39
  private static final String BOOTSTRAP_PATH_SYS_ENV_VAR = "GRPC_XDS_BOOTSTRAP";
40
  private static final String BOOTSTRAP_PATH_SYS_PROPERTY = "io.grpc.xds.bootstrap";
41
  private static final String BOOTSTRAP_CONFIG_SYS_ENV_VAR = "GRPC_XDS_BOOTSTRAP_CONFIG";
42
  private static final String BOOTSTRAP_CONFIG_SYS_PROPERTY = "io.grpc.xds.bootstrapConfig";
43
  @VisibleForTesting
1✔
44
  String bootstrapPathFromEnvVar = System.getenv(BOOTSTRAP_PATH_SYS_ENV_VAR);
1✔
45
  @VisibleForTesting
1✔
46
  String bootstrapPathFromSysProp = System.getProperty(BOOTSTRAP_PATH_SYS_PROPERTY);
1✔
47
  @VisibleForTesting
1✔
48
  String bootstrapConfigFromEnvVar = System.getenv(BOOTSTRAP_CONFIG_SYS_ENV_VAR);
1✔
49
  @VisibleForTesting
1✔
50
  String bootstrapConfigFromSysProp = System.getProperty(BOOTSTRAP_CONFIG_SYS_PROPERTY);
1✔
51

52
  GrpcBootstrapperImpl() {
53
    super();
1✔
54
  }
1✔
55

56
  @Override
57
  public BootstrapInfo bootstrap(Map<String, ?> rawData) throws XdsInitializationException {
58
    BootstrapInfo info = super.bootstrap(rawData);
1✔
59
    if (info.servers().isEmpty()) {
1✔
60
      throw new XdsInitializationException("Invalid bootstrap: 'xds_servers' is empty");
1✔
61
    }
62
    return info;
1✔
63
  }
64

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

100
    return jsonContent;
1✔
101
  }
102

103
  @Override
104
  protected Object getImplSpecificConfig(Map<String, ?> serverConfig, String serverUri)
105
      throws XdsInitializationException {
106
    ConfiguredChannelCredentials configuredChannel = getChannelCredentials(serverConfig, serverUri);
1✔
107
    return configuredChannel != null ? configuredChannel.channelCredentials() : null;
1✔
108
  }
109

110
  @GuardedBy("GrpcBootstrapperImpl.class")
111
  private static Map<String, ?> defaultBootstrapOverride;
112
  @GuardedBy("GrpcBootstrapperImpl.class")
113
  private static BootstrapInfo defaultBootstrap;
114

115
  static synchronized void setDefaultBootstrapOverride(Map<String, ?> rawBootstrap) {
116
    defaultBootstrapOverride = rawBootstrap;
×
117
  }
×
118

119
  static synchronized BootstrapInfo defaultBootstrap() throws XdsInitializationException {
120
    if (defaultBootstrap == null) {
×
121
      if (defaultBootstrapOverride == null) {
×
122
        defaultBootstrap = new GrpcBootstrapperImpl().bootstrap();
×
123
      } else {
124
        defaultBootstrap = new GrpcBootstrapperImpl().bootstrap(defaultBootstrapOverride);
×
125
      }
126
    }
127
    return defaultBootstrap;
×
128
  }
129

130
  private static ConfiguredChannelCredentials getChannelCredentials(Map<String, ?> serverConfig,
131
                                                                  String serverUri)
132
      throws XdsInitializationException {
133
    List<?> rawChannelCredsList = JsonUtil.getList(serverConfig, "channel_creds");
1✔
134
    if (rawChannelCredsList == null || rawChannelCredsList.isEmpty()) {
1✔
135
      throw new XdsInitializationException(
1✔
136
          "Invalid bootstrap: server " + serverUri + " 'channel_creds' required");
137
    }
138
    ConfiguredChannelCredentials credentials =
1✔
139
        parseChannelCredentials(JsonUtil.checkObjectList(rawChannelCredsList), serverUri);
1✔
140
    if (credentials == null) {
1✔
141
      throw new XdsInitializationException(
1✔
142
          "Server " + serverUri + ": no supported channel credentials found");
143
    }
144
    return credentials;
1✔
145
  }
146

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

165
        ChannelCredentials creds = provider.newChannelCredentials(config);
1✔
166
        if (creds == null) {
1✔
167
          return null;
×
168
        }
169
        return ConfiguredChannelCredentials.create(creds, new JsonChannelCredsConfig(type, config));
1✔
170
      }
171
    }
1✔
172
    return null;
1✔
173
  }
174

175
  @Override
176
  protected Optional<Object> parseImplSpecificObject(
177
      @Nullable Map<String, ?> rawAllowedGrpcServices)
178
      throws XdsInitializationException {
179
    if (rawAllowedGrpcServices == null || rawAllowedGrpcServices.isEmpty()) {
1✔
180
      return Optional.of(GrpcBootstrapImplConfig.create(AllowedGrpcServices.empty()));
1✔
181
    }
182

183
    ImmutableMap.Builder<String, AllowedGrpcService> builder =
184
        ImmutableMap.builder();
1✔
185
    for (String targetUri : rawAllowedGrpcServices.keySet()) {
1✔
186
      Map<String, ?> serviceConfig = JsonUtil.getObject(rawAllowedGrpcServices, targetUri);
1✔
187
      if (serviceConfig == null) {
1✔
188
        throw new XdsInitializationException(
×
189
            "Invalid allowed_grpc_services config for " + targetUri);
190
      }
191
      ConfiguredChannelCredentials configuredChannel =
1✔
192
          getChannelCredentials(serviceConfig, targetUri);
1✔
193

194
      Optional<CallCredentials> callCredentials = Optional.empty();
1✔
195
      List<?> rawCallCredsList = JsonUtil.getList(serviceConfig, "call_creds");
1✔
196
      if (rawCallCredsList != null && !rawCallCredsList.isEmpty()) {
1✔
197
        callCredentials =
1✔
198
            parseCallCredentials(JsonUtil.checkObjectList(rawCallCredsList), targetUri);
1✔
199
      }
200

201
      AllowedGrpcService.Builder b = AllowedGrpcService.builder()
1✔
202
          .configuredChannelCredentials(configuredChannel);
1✔
203
      callCredentials.ifPresent(b::callCredentials);
1✔
204
      builder.put(targetUri, b.build());
1✔
205
    }
1✔
206
    GrpcBootstrapImplConfig customConfig =
1✔
207
        GrpcBootstrapImplConfig.create(AllowedGrpcServices.create(builder.build()));
1✔
208
    return Optional.of(customConfig);
1✔
209
  }
210

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();
1✔
220
  }
221

222
  private static final class JsonChannelCredsConfig implements ChannelCredsConfig {
223
    private final String type;
224
    private final Map<String, ?> config;
225

226
    JsonChannelCredsConfig(String type, Map<String, ?> config) {
1✔
227
      this.type = type;
1✔
228
      this.config = config;
1✔
229
    }
1✔
230

231
    @Override
232
    public String type() {
233
      return type;
×
234
    }
235

236
    @Override
237
    public boolean equals(Object o) {
238
      if (this == o) {
×
239
        return true;
×
240
      }
241
      if (o == null || getClass() != o.getClass()) {
×
242
        return false;
×
243
      }
244
      JsonChannelCredsConfig that = (JsonChannelCredsConfig) o;
×
245
      return java.util.Objects.equals(type, that.type)
×
246
          && java.util.Objects.equals(config, that.config);
×
247
    }
248

249
    @Override
250
    public int hashCode() {
251
      return java.util.Objects.hash(type, config);
×
252
    }
253
  }
254

255
}
256

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