Skip to content

Commit 87779af

Browse files
committed
Merge branch 'sauravzg-feat-cached-channel' into ext-proc
2 parents 234852b + d4a7759 commit 87779af

29 files changed

+2652
-9
lines changed

xds/src/main/java/io/grpc/xds/GrpcBootstrapperImpl.java

Lines changed: 96 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,19 @@
1919
import com.google.common.annotations.VisibleForTesting;
2020
import com.google.common.collect.ImmutableMap;
2121
import com.google.errorprone.annotations.concurrent.GuardedBy;
22+
import io.grpc.CallCredentials;
2223
import io.grpc.ChannelCredentials;
2324
import io.grpc.internal.JsonUtil;
2425
import io.grpc.xds.client.BootstrapperImpl;
2526
import io.grpc.xds.client.XdsInitializationException;
2627
import io.grpc.xds.client.XdsLogger;
28+
import io.grpc.xds.internal.grpcservice.ChannelCredsConfig;
29+
import io.grpc.xds.internal.grpcservice.ConfiguredChannelCredentials;
30+
import io.grpc.xds.internal.grpcservice.GrpcServiceXdsContext;
2731
import java.io.IOException;
2832
import java.util.List;
2933
import java.util.Map;
34+
import java.util.Optional;
3035
import javax.annotation.Nullable;
3136

3237
class GrpcBootstrapperImpl extends BootstrapperImpl {
@@ -97,7 +102,8 @@ protected String getJsonContent() throws XdsInitializationException, IOException
97102
@Override
98103
protected Object getImplSpecificConfig(Map<String, ?> serverConfig, String serverUri)
99104
throws XdsInitializationException {
100-
return getChannelCredentials(serverConfig, serverUri);
105+
ConfiguredChannelCredentials configuredChannel = getChannelCredentials(serverConfig, serverUri);
106+
return configuredChannel != null ? configuredChannel.channelCredentials() : null;
101107
}
102108

103109
@GuardedBy("GrpcBootstrapperImpl.class")
@@ -120,26 +126,26 @@ static synchronized BootstrapInfo defaultBootstrap() throws XdsInitializationExc
120126
return defaultBootstrap;
121127
}
122128

123-
private static ChannelCredentials getChannelCredentials(Map<String, ?> serverConfig,
124-
String serverUri)
129+
private static ConfiguredChannelCredentials getChannelCredentials(Map<String, ?> serverConfig,
130+
String serverUri)
125131
throws XdsInitializationException {
126132
List<?> rawChannelCredsList = JsonUtil.getList(serverConfig, "channel_creds");
127133
if (rawChannelCredsList == null || rawChannelCredsList.isEmpty()) {
128134
throw new XdsInitializationException(
129135
"Invalid bootstrap: server " + serverUri + " 'channel_creds' required");
130136
}
131-
ChannelCredentials channelCredentials =
137+
ConfiguredChannelCredentials credentials =
132138
parseChannelCredentials(JsonUtil.checkObjectList(rawChannelCredsList), serverUri);
133-
if (channelCredentials == null) {
139+
if (credentials == null) {
134140
throw new XdsInitializationException(
135141
"Server " + serverUri + ": no supported channel credentials found");
136142
}
137-
return channelCredentials;
143+
return credentials;
138144
}
139145

140146
@Nullable
141-
private static ChannelCredentials parseChannelCredentials(List<Map<String, ?>> jsonList,
142-
String serverUri)
147+
private static ConfiguredChannelCredentials parseChannelCredentials(List<Map<String, ?>> jsonList,
148+
String serverUri)
143149
throws XdsInitializationException {
144150
for (Map<String, ?> channelCreds : jsonList) {
145151
String type = JsonUtil.getString(channelCreds, "type");
@@ -155,9 +161,90 @@ private static ChannelCredentials parseChannelCredentials(List<Map<String, ?>> j
155161
config = ImmutableMap.of();
156162
}
157163

158-
return provider.newChannelCredentials(config);
164+
ChannelCredentials creds = provider.newChannelCredentials(config);
165+
if (creds == null) {
166+
continue;
167+
}
168+
return ConfiguredChannelCredentials.create(creds, new JsonChannelCredsConfig(type, config));
159169
}
160170
}
161171
return null;
162172
}
173+
174+
@Override
175+
protected Optional<Object> parseAllowedGrpcServices(
176+
Map<String, ?> rawAllowedGrpcServices)
177+
throws XdsInitializationException {
178+
ImmutableMap.Builder<String, GrpcServiceXdsContext.AllowedGrpcService> builder =
179+
ImmutableMap.builder();
180+
for (String targetUri : rawAllowedGrpcServices.keySet()) {
181+
Map<String, ?> serviceConfig = JsonUtil.getObject(rawAllowedGrpcServices, targetUri);
182+
if (serviceConfig == null) {
183+
throw new XdsInitializationException(
184+
"Invalid allowed_grpc_services config for " + targetUri);
185+
}
186+
ConfiguredChannelCredentials configuredChannel =
187+
getChannelCredentials(serviceConfig, targetUri);
188+
189+
Optional<CallCredentials> callCredentials = Optional.empty();
190+
List<?> rawCallCredsList = JsonUtil.getList(serviceConfig, "call_creds");
191+
if (rawCallCredsList != null && !rawCallCredsList.isEmpty()) {
192+
callCredentials =
193+
parseCallCredentials(JsonUtil.checkObjectList(rawCallCredsList), targetUri);
194+
}
195+
196+
GrpcServiceXdsContext.AllowedGrpcService.Builder b = GrpcServiceXdsContext.AllowedGrpcService
197+
.builder().configuredChannelCredentials(configuredChannel);
198+
callCredentials.ifPresent(b::callCredentials);
199+
builder.put(targetUri, b.build());
200+
}
201+
ImmutableMap<String, GrpcServiceXdsContext.AllowedGrpcService> parsed = builder.buildOrThrow();
202+
return parsed.isEmpty() ? Optional.empty() : Optional.of(parsed);
203+
}
204+
205+
@SuppressWarnings("unused")
206+
private static Optional<CallCredentials> parseCallCredentials(List<Map<String, ?>> jsonList,
207+
String targetUri)
208+
throws XdsInitializationException {
209+
// TODO(sauravzg): Currently no xDS call credentials providers are implemented (no
210+
// XdsCallCredentialsRegistry).
211+
// As per A102/A97, we should just ignore unsupported call credentials types
212+
// without throwing an exception.
213+
return Optional.empty();
214+
}
215+
216+
private static final class JsonChannelCredsConfig implements ChannelCredsConfig {
217+
private final String type;
218+
private final Map<String, ?> config;
219+
220+
JsonChannelCredsConfig(String type, Map<String, ?> config) {
221+
this.type = type;
222+
this.config = config;
223+
}
224+
225+
@Override
226+
public String type() {
227+
return type;
228+
}
229+
230+
@Override
231+
public boolean equals(Object o) {
232+
if (this == o) {
233+
return true;
234+
}
235+
if (o == null || getClass() != o.getClass()) {
236+
return false;
237+
}
238+
JsonChannelCredsConfig that = (JsonChannelCredsConfig) o;
239+
return java.util.Objects.equals(type, that.type)
240+
&& java.util.Objects.equals(config, that.config);
241+
}
242+
243+
@Override
244+
public int hashCode() {
245+
return java.util.Objects.hash(type, config);
246+
}
247+
}
248+
163249
}
250+

xds/src/main/java/io/grpc/xds/client/Bootstrapper.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import io.grpc.xds.client.EnvoyProtoData.Node;
2727
import java.util.List;
2828
import java.util.Map;
29+
import java.util.Optional;
2930
import javax.annotation.Nullable;
3031

3132
/**
@@ -205,6 +206,12 @@ public abstract static class BootstrapInfo {
205206
*/
206207
public abstract ImmutableMap<String, AuthorityInfo> authorities();
207208

209+
/**
210+
* Parsed allowed_grpc_services configuration.
211+
* Returns an opaque object containing the parsed configuration.
212+
*/
213+
public abstract Optional<Object> allowedGrpcServices();
214+
208215
@VisibleForTesting
209216
public static Builder builder() {
210217
return new AutoValue_Bootstrapper_BootstrapInfo.Builder()
@@ -231,7 +238,10 @@ public abstract Builder clientDefaultListenerResourceNameTemplate(
231238

232239
public abstract Builder authorities(Map<String, AuthorityInfo> authorities);
233240

241+
public abstract Builder allowedGrpcServices(Optional<Object> allowedGrpcServices);
242+
234243
public abstract BootstrapInfo build();
235244
}
236245
}
246+
237247
}

xds/src/main/java/io/grpc/xds/client/BootstrapperImpl.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,9 +239,20 @@ protected BootstrapInfo.Builder bootstrapBuilder(Map<String, ?> rawData)
239239
builder.authorities(authorityInfoMapBuilder.buildOrThrow());
240240
}
241241

242+
Map<String, ?> rawAllowedGrpcServices = JsonUtil.getObject(rawData, "allowed_grpc_services");
243+
if (rawAllowedGrpcServices != null) {
244+
builder.allowedGrpcServices(parseAllowedGrpcServices(rawAllowedGrpcServices));
245+
}
246+
242247
return builder;
243248
}
244249

250+
protected java.util.Optional<Object> parseAllowedGrpcServices(
251+
Map<String, ?> rawAllowedGrpcServices)
252+
throws XdsInitializationException {
253+
return java.util.Optional.empty();
254+
}
255+
245256
private List<ServerInfo> parseServerInfos(List<?> rawServerConfigs, XdsLogger logger)
246257
throws XdsInitializationException {
247258
logger.log(XdsLogLevel.INFO, "Configured with {0} xDS servers", rawServerConfigs.size());

xds/src/main/java/io/grpc/xds/internal/MatcherParser.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,25 @@ public static Matchers.StringMatcher parseStringMatcher(
9797
"Unknown StringMatcher match pattern: " + proto.getMatchPatternCase());
9898
}
9999
}
100+
101+
/** Translates envoy proto FractionalPercent to internal FractionMatcher. */
102+
public static Matchers.FractionMatcher parseFractionMatcher(
103+
io.envoyproxy.envoy.type.v3.FractionalPercent proto) {
104+
int denominator;
105+
switch (proto.getDenominator()) {
106+
case HUNDRED:
107+
denominator = 100;
108+
break;
109+
case TEN_THOUSAND:
110+
denominator = 10_000;
111+
break;
112+
case MILLION:
113+
denominator = 1_000_000;
114+
break;
115+
case UNRECOGNIZED:
116+
default:
117+
throw new IllegalArgumentException("Unknown denominator type: " + proto.getDenominator());
118+
}
119+
return Matchers.FractionMatcher.create(proto.getNumerator(), denominator);
120+
}
100121
}
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/*
2+
* Copyright 2025 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.internal.extauthz;
18+
19+
import com.google.auto.value.AutoValue;
20+
import com.google.common.collect.ImmutableList;
21+
import io.grpc.Status;
22+
import io.grpc.xds.internal.Matchers;
23+
import io.grpc.xds.internal.grpcservice.GrpcServiceConfig;
24+
import io.grpc.xds.internal.headermutations.HeaderMutationRulesConfig;
25+
import java.util.Optional;
26+
27+
/**
28+
* Represents the configuration for the external authorization (ext_authz) filter. This class
29+
* encapsulates the settings defined in the
30+
* {@link io.envoyproxy.envoy.extensions.filters.http.ext_authz.v3.ExtAuthz} proto, providing a
31+
* structured, immutable representation for use within gRPC. It includes configurations for the gRPC
32+
* service used for authorization, header mutation rules, and other filter behaviors.
33+
*/
34+
@AutoValue
35+
public abstract class ExtAuthzConfig {
36+
37+
/** Creates a new builder for creating {@link ExtAuthzConfig} instances. */
38+
public static Builder newBuilder() {
39+
return new AutoValue_ExtAuthzConfig.Builder().allowedHeaders(ImmutableList.of())
40+
.disallowedHeaders(ImmutableList.of()).statusOnError(Status.PERMISSION_DENIED)
41+
.filterEnabled(Matchers.FractionMatcher.create(100, 100));
42+
}
43+
44+
/**
45+
* The gRPC service configuration for the external authorization service. This is a required
46+
* field.
47+
*
48+
* @see ExtAuthz#getGrpcService()
49+
*/
50+
public abstract GrpcServiceConfig grpcService();
51+
52+
/**
53+
* Changes the filter's behavior on errors from the authorization service. If {@code true}, the
54+
* filter will accept the request even if the authorization service fails or returns an error.
55+
*
56+
* @see ExtAuthz#getFailureModeAllow()
57+
*/
58+
public abstract boolean failureModeAllow();
59+
60+
/**
61+
* Determines if the {@code x-envoy-auth-failure-mode-allowed} header is added to the request when
62+
* {@link #failureModeAllow()} is true.
63+
*
64+
* @see ExtAuthz#getFailureModeAllowHeaderAdd()
65+
*/
66+
public abstract boolean failureModeAllowHeaderAdd();
67+
68+
/**
69+
* Specifies if the peer certificate is sent to the external authorization service.
70+
*
71+
* @see ExtAuthz#getIncludePeerCertificate()
72+
*/
73+
public abstract boolean includePeerCertificate();
74+
75+
/**
76+
* The gRPC status returned to the client when the authorization server returns an error or is
77+
* unreachable. Defaults to {@code PERMISSION_DENIED}.
78+
*
79+
* @see io.envoyproxy.envoy.extensions.filters.http.ext_authz.v3.ExtAuthz#getStatusOnError()
80+
*/
81+
public abstract Status statusOnError();
82+
83+
/**
84+
* Specifies whether to deny requests when the filter is disabled. Defaults to {@code false}.
85+
*
86+
* @see ExtAuthz#getDenyAtDisable()
87+
*/
88+
public abstract boolean denyAtDisable();
89+
90+
/**
91+
* The fraction of requests that will be checked by the authorization service. Defaults to all
92+
* requests.
93+
*
94+
* @see ExtAuthz#getFilterEnabled()
95+
*/
96+
public abstract Matchers.FractionMatcher filterEnabled();
97+
98+
/**
99+
* Specifies which request headers are sent to the authorization service. If empty, all headers
100+
* are sent.
101+
*
102+
* @see ExtAuthz#getAllowedHeaders()
103+
*/
104+
public abstract ImmutableList<Matchers.StringMatcher> allowedHeaders();
105+
106+
/**
107+
* Specifies which request headers are not sent to the authorization service. This overrides
108+
* {@link #allowedHeaders()}.
109+
*
110+
* @see ExtAuthz#getDisallowedHeaders()
111+
*/
112+
public abstract ImmutableList<Matchers.StringMatcher> disallowedHeaders();
113+
114+
/**
115+
* Rules for what modifications an ext_authz server may make to request headers.
116+
*
117+
* @see ExtAuthz#getDecoderHeaderMutationRules()
118+
*/
119+
public abstract Optional<HeaderMutationRulesConfig> decoderHeaderMutationRules();
120+
121+
@AutoValue.Builder
122+
public abstract static class Builder {
123+
public abstract Builder grpcService(GrpcServiceConfig grpcService);
124+
125+
public abstract Builder failureModeAllow(boolean failureModeAllow);
126+
127+
public abstract Builder failureModeAllowHeaderAdd(boolean failureModeAllowHeaderAdd);
128+
129+
public abstract Builder includePeerCertificate(boolean includePeerCertificate);
130+
131+
public abstract Builder statusOnError(Status statusOnError);
132+
133+
public abstract Builder denyAtDisable(boolean denyAtDisable);
134+
135+
public abstract Builder filterEnabled(Matchers.FractionMatcher filterEnabled);
136+
137+
public abstract Builder allowedHeaders(Iterable<Matchers.StringMatcher> allowedHeaders);
138+
139+
public abstract Builder disallowedHeaders(Iterable<Matchers.StringMatcher> disallowedHeaders);
140+
141+
public abstract Builder decoderHeaderMutationRules(HeaderMutationRulesConfig rules);
142+
143+
public abstract ExtAuthzConfig build();
144+
}
145+
}

0 commit comments

Comments
 (0)