xref: /aosp_15_r20/external/grpc-grpc-java/xds/src/main/java/io/grpc/xds/EnvoyServerProtoData.java (revision e07d83d3ffcef9ecfc9f7f475418ec639ff0e5fe)
1 /*
2  * Copyright 2020 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.auto.value.AutoValue;
20 import com.google.common.annotations.VisibleForTesting;
21 import com.google.common.collect.ImmutableList;
22 import com.google.protobuf.util.Durations;
23 import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.CommonTlsContext;
24 import io.grpc.Internal;
25 import io.grpc.xds.internal.security.SslContextProviderSupplier;
26 import java.net.InetAddress;
27 import java.net.UnknownHostException;
28 import java.util.Objects;
29 import javax.annotation.Nullable;
30 
31 /**
32  * Defines gRPC data types for Envoy protobuf messages used in xDS protocol on the server side,
33  * similar to how {@link EnvoyProtoData} defines it for the client side.
34  */
35 @Internal
36 public final class EnvoyServerProtoData {
37 
38   // Prevent instantiation.
EnvoyServerProtoData()39   private EnvoyServerProtoData() {
40   }
41 
42   public abstract static class BaseTlsContext {
43     @Nullable protected final CommonTlsContext commonTlsContext;
44 
BaseTlsContext(@ullable CommonTlsContext commonTlsContext)45     protected BaseTlsContext(@Nullable CommonTlsContext commonTlsContext) {
46       this.commonTlsContext = commonTlsContext;
47     }
48 
getCommonTlsContext()49     @Nullable public CommonTlsContext getCommonTlsContext() {
50       return commonTlsContext;
51     }
52 
53     @Override
equals(Object o)54     public boolean equals(Object o) {
55       if (this == o) {
56         return true;
57       }
58       if (!(o instanceof BaseTlsContext)) {
59         return false;
60       }
61       BaseTlsContext that = (BaseTlsContext) o;
62       return Objects.equals(commonTlsContext, that.commonTlsContext);
63     }
64 
65     @Override
hashCode()66     public int hashCode() {
67       return Objects.hashCode(commonTlsContext);
68     }
69   }
70 
71   public static final class UpstreamTlsContext extends BaseTlsContext {
72 
73     @VisibleForTesting
UpstreamTlsContext(CommonTlsContext commonTlsContext)74     public UpstreamTlsContext(CommonTlsContext commonTlsContext) {
75       super(commonTlsContext);
76     }
77 
fromEnvoyProtoUpstreamTlsContext( io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext upstreamTlsContext)78     public static UpstreamTlsContext fromEnvoyProtoUpstreamTlsContext(
79         io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
80             upstreamTlsContext) {
81       return new UpstreamTlsContext(upstreamTlsContext.getCommonTlsContext());
82     }
83 
84     @Override
toString()85     public String toString() {
86       return "UpstreamTlsContext{" + "commonTlsContext=" + commonTlsContext + '}';
87     }
88   }
89 
90   public static final class DownstreamTlsContext extends BaseTlsContext {
91 
92     private final boolean requireClientCertificate;
93 
94     @VisibleForTesting
DownstreamTlsContext( CommonTlsContext commonTlsContext, boolean requireClientCertificate)95     public DownstreamTlsContext(
96         CommonTlsContext commonTlsContext, boolean requireClientCertificate) {
97       super(commonTlsContext);
98       this.requireClientCertificate = requireClientCertificate;
99     }
100 
fromEnvoyProtoDownstreamTlsContext( io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext downstreamTlsContext)101     public static DownstreamTlsContext fromEnvoyProtoDownstreamTlsContext(
102         io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
103             downstreamTlsContext) {
104       return new DownstreamTlsContext(downstreamTlsContext.getCommonTlsContext(),
105         downstreamTlsContext.hasRequireClientCertificate());
106     }
107 
isRequireClientCertificate()108     public boolean isRequireClientCertificate() {
109       return requireClientCertificate;
110     }
111 
112     @Override
toString()113     public String toString() {
114       return "DownstreamTlsContext{"
115           + "commonTlsContext="
116           + commonTlsContext
117           + ", requireClientCertificate="
118           + requireClientCertificate
119           + '}';
120     }
121 
122     @Override
equals(Object o)123     public boolean equals(Object o) {
124       if (this == o) {
125         return true;
126       }
127       if (o == null || getClass() != o.getClass()) {
128         return false;
129       }
130       if (!super.equals(o)) {
131         return false;
132       }
133       DownstreamTlsContext that = (DownstreamTlsContext) o;
134       return requireClientCertificate == that.requireClientCertificate;
135     }
136 
137     @Override
hashCode()138     public int hashCode() {
139       return Objects.hash(super.hashCode(), requireClientCertificate);
140     }
141   }
142 
143   @AutoValue
144   abstract static class CidrRange {
145 
addressPrefix()146     abstract InetAddress addressPrefix();
147 
prefixLen()148     abstract int prefixLen();
149 
create(String addressPrefix, int prefixLen)150     static CidrRange create(String addressPrefix, int prefixLen) throws UnknownHostException {
151       return new AutoValue_EnvoyServerProtoData_CidrRange(
152           InetAddress.getByName(addressPrefix), prefixLen);
153     }
154   }
155 
156   enum ConnectionSourceType {
157     // Any connection source matches.
158     ANY,
159 
160     // Match a connection originating from the same host.
161     SAME_IP_OR_LOOPBACK,
162 
163     // Match a connection originating from a different host.
164     EXTERNAL
165   }
166 
167   /**
168    * Corresponds to Envoy proto message
169    * {@link io.envoyproxy.envoy.config.listener.v3.FilterChainMatch}.
170    */
171   @AutoValue
172   abstract static class FilterChainMatch {
173 
destinationPort()174     abstract int destinationPort();
175 
prefixRanges()176     abstract ImmutableList<CidrRange> prefixRanges();
177 
applicationProtocols()178     abstract ImmutableList<String> applicationProtocols();
179 
sourcePrefixRanges()180     abstract ImmutableList<CidrRange> sourcePrefixRanges();
181 
connectionSourceType()182     abstract ConnectionSourceType connectionSourceType();
183 
sourcePorts()184     abstract ImmutableList<Integer> sourcePorts();
185 
serverNames()186     abstract ImmutableList<String> serverNames();
187 
transportProtocol()188     abstract String transportProtocol();
189 
create(int destinationPort, ImmutableList<CidrRange> prefixRanges, ImmutableList<String> applicationProtocols, ImmutableList<CidrRange> sourcePrefixRanges, ConnectionSourceType connectionSourceType, ImmutableList<Integer> sourcePorts, ImmutableList<String> serverNames, String transportProtocol)190     public static FilterChainMatch create(int destinationPort,
191         ImmutableList<CidrRange> prefixRanges,
192         ImmutableList<String> applicationProtocols, ImmutableList<CidrRange> sourcePrefixRanges,
193         ConnectionSourceType connectionSourceType, ImmutableList<Integer> sourcePorts,
194         ImmutableList<String> serverNames, String transportProtocol) {
195       return new AutoValue_EnvoyServerProtoData_FilterChainMatch(
196           destinationPort, prefixRanges, applicationProtocols, sourcePrefixRanges,
197           connectionSourceType, sourcePorts, serverNames, transportProtocol);
198     }
199   }
200 
201   /**
202    * Corresponds to Envoy proto message {@link io.envoyproxy.envoy.config.listener.v3.FilterChain}.
203    */
204   @AutoValue
205   abstract static class FilterChain {
206 
207     // possibly empty
name()208     abstract String name();
209 
210     // TODO(sanjaypujare): flatten structure by moving FilterChainMatch class members here.
filterChainMatch()211     abstract FilterChainMatch filterChainMatch();
212 
httpConnectionManager()213     abstract HttpConnectionManager httpConnectionManager();
214 
215     @Nullable
sslContextProviderSupplier()216     abstract SslContextProviderSupplier sslContextProviderSupplier();
217 
create( String name, FilterChainMatch filterChainMatch, HttpConnectionManager httpConnectionManager, @Nullable DownstreamTlsContext downstreamTlsContext, TlsContextManager tlsContextManager)218     static FilterChain create(
219         String name,
220         FilterChainMatch filterChainMatch,
221         HttpConnectionManager httpConnectionManager,
222         @Nullable DownstreamTlsContext downstreamTlsContext,
223         TlsContextManager tlsContextManager) {
224       SslContextProviderSupplier sslContextProviderSupplier =
225           downstreamTlsContext == null
226               ? null : new SslContextProviderSupplier(downstreamTlsContext, tlsContextManager);
227       return new AutoValue_EnvoyServerProtoData_FilterChain(
228           name, filterChainMatch, httpConnectionManager, sslContextProviderSupplier);
229     }
230   }
231 
232   /**
233    * Corresponds to Envoy proto message {@link io.envoyproxy.envoy.config.listener.v3.Listener} and
234    * related classes.
235    */
236   @AutoValue
237   abstract static class Listener {
238 
name()239     abstract String name();
240 
241     @Nullable
address()242     abstract String address();
243 
filterChains()244     abstract ImmutableList<FilterChain> filterChains();
245 
246     @Nullable
defaultFilterChain()247     abstract FilterChain defaultFilterChain();
248 
create( String name, @Nullable String address, ImmutableList<FilterChain> filterChains, @Nullable FilterChain defaultFilterChain)249     static Listener create(
250         String name,
251         @Nullable String address,
252         ImmutableList<FilterChain> filterChains,
253         @Nullable FilterChain defaultFilterChain) {
254       return new AutoValue_EnvoyServerProtoData_Listener(name, address, filterChains,
255           defaultFilterChain);
256     }
257   }
258 
259   /**
260    * Corresponds to Envoy proto message {@link
261    * io.envoyproxy.envoy.config.cluster.v3.OutlierDetection}. Only the fields supported by gRPC are
262    * included.
263    *
264    * <p>Protobuf Duration fields are represented in their string format (e.g. "10s").
265    */
266   @AutoValue
267   abstract static class OutlierDetection {
268 
269     @Nullable
intervalNanos()270     abstract Long intervalNanos();
271 
272     @Nullable
baseEjectionTimeNanos()273     abstract Long baseEjectionTimeNanos();
274 
275     @Nullable
maxEjectionTimeNanos()276     abstract Long maxEjectionTimeNanos();
277 
278     @Nullable
maxEjectionPercent()279     abstract Integer maxEjectionPercent();
280 
281     @Nullable
successRateEjection()282     abstract SuccessRateEjection successRateEjection();
283 
284     @Nullable
failurePercentageEjection()285     abstract FailurePercentageEjection failurePercentageEjection();
286 
create( @ullable Long intervalNanos, @Nullable Long baseEjectionTimeNanos, @Nullable Long maxEjectionTimeNanos, @Nullable Integer maxEjectionPercentage, @Nullable SuccessRateEjection successRateEjection, @Nullable FailurePercentageEjection failurePercentageEjection)287     static OutlierDetection create(
288         @Nullable Long intervalNanos,
289         @Nullable Long baseEjectionTimeNanos,
290         @Nullable Long maxEjectionTimeNanos,
291         @Nullable Integer maxEjectionPercentage,
292         @Nullable SuccessRateEjection successRateEjection,
293         @Nullable FailurePercentageEjection failurePercentageEjection) {
294       return new AutoValue_EnvoyServerProtoData_OutlierDetection(intervalNanos,
295           baseEjectionTimeNanos, maxEjectionTimeNanos, maxEjectionPercentage, successRateEjection,
296           failurePercentageEjection);
297     }
298 
fromEnvoyOutlierDetection( io.envoyproxy.envoy.config.cluster.v3.OutlierDetection envoyOutlierDetection)299     static OutlierDetection fromEnvoyOutlierDetection(
300         io.envoyproxy.envoy.config.cluster.v3.OutlierDetection envoyOutlierDetection) {
301 
302       Long intervalNanos = envoyOutlierDetection.hasInterval()
303           ? Durations.toNanos(envoyOutlierDetection.getInterval()) : null;
304       Long baseEjectionTimeNanos = envoyOutlierDetection.hasBaseEjectionTime()
305           ? Durations.toNanos(envoyOutlierDetection.getBaseEjectionTime()) : null;
306       Long maxEjectionTimeNanos = envoyOutlierDetection.hasMaxEjectionTime()
307           ? Durations.toNanos(envoyOutlierDetection.getMaxEjectionTime()) : null;
308       Integer maxEjectionPercentage = envoyOutlierDetection.hasMaxEjectionPercent()
309           ? envoyOutlierDetection.getMaxEjectionPercent().getValue() : null;
310 
311       SuccessRateEjection successRateEjection;
312       // If success rate enforcement has been turned completely off, don't configure this ejection.
313       if (envoyOutlierDetection.hasEnforcingSuccessRate()
314           && envoyOutlierDetection.getEnforcingSuccessRate().getValue() == 0) {
315         successRateEjection = null;
316       } else {
317         Integer stdevFactor = envoyOutlierDetection.hasSuccessRateStdevFactor()
318             ? envoyOutlierDetection.getSuccessRateStdevFactor().getValue() : null;
319         Integer enforcementPercentage = envoyOutlierDetection.hasEnforcingSuccessRate()
320             ? envoyOutlierDetection.getEnforcingSuccessRate().getValue() : null;
321         Integer minimumHosts = envoyOutlierDetection.hasSuccessRateMinimumHosts()
322             ? envoyOutlierDetection.getSuccessRateMinimumHosts().getValue() : null;
323         Integer requestVolume = envoyOutlierDetection.hasSuccessRateRequestVolume()
324             ? envoyOutlierDetection.getSuccessRateMinimumHosts().getValue() : null;
325 
326         successRateEjection = SuccessRateEjection.create(stdevFactor, enforcementPercentage,
327             minimumHosts, requestVolume);
328       }
329 
330       FailurePercentageEjection failurePercentageEjection;
331       if (envoyOutlierDetection.hasEnforcingFailurePercentage()
332           && envoyOutlierDetection.getEnforcingFailurePercentage().getValue() == 0) {
333         failurePercentageEjection = null;
334       } else {
335         Integer threshold = envoyOutlierDetection.hasFailurePercentageThreshold()
336             ? envoyOutlierDetection.getFailurePercentageThreshold().getValue() : null;
337         Integer enforcementPercentage = envoyOutlierDetection.hasEnforcingFailurePercentage()
338             ? envoyOutlierDetection.getEnforcingFailurePercentage().getValue() : null;
339         Integer minimumHosts = envoyOutlierDetection.hasFailurePercentageMinimumHosts()
340             ? envoyOutlierDetection.getFailurePercentageMinimumHosts().getValue() : null;
341         Integer requestVolume = envoyOutlierDetection.hasFailurePercentageRequestVolume()
342             ? envoyOutlierDetection.getFailurePercentageRequestVolume().getValue() : null;
343 
344         failurePercentageEjection = FailurePercentageEjection.create(threshold,
345             enforcementPercentage, minimumHosts, requestVolume);
346       }
347 
348       return create(intervalNanos, baseEjectionTimeNanos, maxEjectionTimeNanos,
349           maxEjectionPercentage, successRateEjection, failurePercentageEjection);
350     }
351   }
352 
353   @AutoValue
354   abstract static class SuccessRateEjection {
355 
356     @Nullable
stdevFactor()357     abstract Integer stdevFactor();
358 
359     @Nullable
enforcementPercentage()360     abstract Integer enforcementPercentage();
361 
362     @Nullable
minimumHosts()363     abstract Integer minimumHosts();
364 
365     @Nullable
requestVolume()366     abstract Integer requestVolume();
367 
create( @ullable Integer stdevFactor, @Nullable Integer enforcementPercentage, @Nullable Integer minimumHosts, @Nullable Integer requestVolume)368     static SuccessRateEjection create(
369         @Nullable Integer stdevFactor,
370         @Nullable Integer enforcementPercentage,
371         @Nullable Integer minimumHosts,
372         @Nullable Integer requestVolume) {
373       return new AutoValue_EnvoyServerProtoData_SuccessRateEjection(stdevFactor,
374           enforcementPercentage, minimumHosts, requestVolume);
375     }
376   }
377 
378   @AutoValue
379   abstract static class FailurePercentageEjection {
380 
381     @Nullable
threshold()382     abstract Integer threshold();
383 
384     @Nullable
enforcementPercentage()385     abstract Integer enforcementPercentage();
386 
387     @Nullable
minimumHosts()388     abstract Integer minimumHosts();
389 
390     @Nullable
requestVolume()391     abstract Integer requestVolume();
392 
create( @ullable Integer threshold, @Nullable Integer enforcementPercentage, @Nullable Integer minimumHosts, @Nullable Integer requestVolume)393     static FailurePercentageEjection create(
394         @Nullable Integer threshold,
395         @Nullable Integer enforcementPercentage,
396         @Nullable Integer minimumHosts,
397         @Nullable Integer requestVolume) {
398       return new AutoValue_EnvoyServerProtoData_FailurePercentageEjection(threshold,
399           enforcementPercentage, minimumHosts, requestVolume);
400     }
401   }
402 }
403