blob: 5015c56ba926a504840e7b264c231772ddbf5d3a [file] [log] [blame]
/*
* Copyright 2020 The gRPC Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.grpc.xds;
import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.protobuf.util.Durations;
import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.CommonTlsContext;
import io.grpc.Internal;
import io.grpc.xds.internal.security.SslContextProviderSupplier;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Objects;
import javax.annotation.Nullable;
/**
* Defines gRPC data types for Envoy protobuf messages used in xDS protocol on the server side,
* similar to how {@link EnvoyProtoData} defines it for the client side.
*/
@Internal
public final class EnvoyServerProtoData {
// Prevent instantiation.
private EnvoyServerProtoData() {
}
public abstract static class BaseTlsContext {
@Nullable protected final CommonTlsContext commonTlsContext;
protected BaseTlsContext(@Nullable CommonTlsContext commonTlsContext) {
this.commonTlsContext = commonTlsContext;
}
@Nullable public CommonTlsContext getCommonTlsContext() {
return commonTlsContext;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof BaseTlsContext)) {
return false;
}
BaseTlsContext that = (BaseTlsContext) o;
return Objects.equals(commonTlsContext, that.commonTlsContext);
}
@Override
public int hashCode() {
return Objects.hashCode(commonTlsContext);
}
}
public static final class UpstreamTlsContext extends BaseTlsContext {
@VisibleForTesting
public UpstreamTlsContext(CommonTlsContext commonTlsContext) {
super(commonTlsContext);
}
public static UpstreamTlsContext fromEnvoyProtoUpstreamTlsContext(
io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
upstreamTlsContext) {
return new UpstreamTlsContext(upstreamTlsContext.getCommonTlsContext());
}
@Override
public String toString() {
return "UpstreamTlsContext{" + "commonTlsContext=" + commonTlsContext + '}';
}
}
public static final class DownstreamTlsContext extends BaseTlsContext {
private final boolean requireClientCertificate;
@VisibleForTesting
public DownstreamTlsContext(
CommonTlsContext commonTlsContext, boolean requireClientCertificate) {
super(commonTlsContext);
this.requireClientCertificate = requireClientCertificate;
}
public static DownstreamTlsContext fromEnvoyProtoDownstreamTlsContext(
io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
downstreamTlsContext) {
return new DownstreamTlsContext(downstreamTlsContext.getCommonTlsContext(),
downstreamTlsContext.hasRequireClientCertificate());
}
public boolean isRequireClientCertificate() {
return requireClientCertificate;
}
@Override
public String toString() {
return "DownstreamTlsContext{"
+ "commonTlsContext="
+ commonTlsContext
+ ", requireClientCertificate="
+ requireClientCertificate
+ '}';
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
DownstreamTlsContext that = (DownstreamTlsContext) o;
return requireClientCertificate == that.requireClientCertificate;
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), requireClientCertificate);
}
}
@AutoValue
abstract static class CidrRange {
abstract InetAddress addressPrefix();
abstract int prefixLen();
static CidrRange create(String addressPrefix, int prefixLen) throws UnknownHostException {
return new AutoValue_EnvoyServerProtoData_CidrRange(
InetAddress.getByName(addressPrefix), prefixLen);
}
}
enum ConnectionSourceType {
// Any connection source matches.
ANY,
// Match a connection originating from the same host.
SAME_IP_OR_LOOPBACK,
// Match a connection originating from a different host.
EXTERNAL
}
/**
* Corresponds to Envoy proto message
* {@link io.envoyproxy.envoy.config.listener.v3.FilterChainMatch}.
*/
@AutoValue
abstract static class FilterChainMatch {
abstract int destinationPort();
abstract ImmutableList<CidrRange> prefixRanges();
abstract ImmutableList<String> applicationProtocols();
abstract ImmutableList<CidrRange> sourcePrefixRanges();
abstract ConnectionSourceType connectionSourceType();
abstract ImmutableList<Integer> sourcePorts();
abstract ImmutableList<String> serverNames();
abstract String transportProtocol();
public static FilterChainMatch create(int destinationPort,
ImmutableList<CidrRange> prefixRanges,
ImmutableList<String> applicationProtocols, ImmutableList<CidrRange> sourcePrefixRanges,
ConnectionSourceType connectionSourceType, ImmutableList<Integer> sourcePorts,
ImmutableList<String> serverNames, String transportProtocol) {
return new AutoValue_EnvoyServerProtoData_FilterChainMatch(
destinationPort, prefixRanges, applicationProtocols, sourcePrefixRanges,
connectionSourceType, sourcePorts, serverNames, transportProtocol);
}
}
/**
* Corresponds to Envoy proto message {@link io.envoyproxy.envoy.config.listener.v3.FilterChain}.
*/
@AutoValue
abstract static class FilterChain {
// possibly empty
abstract String name();
// TODO(sanjaypujare): flatten structure by moving FilterChainMatch class members here.
abstract FilterChainMatch filterChainMatch();
abstract HttpConnectionManager httpConnectionManager();
@Nullable
abstract SslContextProviderSupplier sslContextProviderSupplier();
static FilterChain create(
String name,
FilterChainMatch filterChainMatch,
HttpConnectionManager httpConnectionManager,
@Nullable DownstreamTlsContext downstreamTlsContext,
TlsContextManager tlsContextManager) {
SslContextProviderSupplier sslContextProviderSupplier =
downstreamTlsContext == null
? null : new SslContextProviderSupplier(downstreamTlsContext, tlsContextManager);
return new AutoValue_EnvoyServerProtoData_FilterChain(
name, filterChainMatch, httpConnectionManager, sslContextProviderSupplier);
}
}
/**
* Corresponds to Envoy proto message {@link io.envoyproxy.envoy.config.listener.v3.Listener} and
* related classes.
*/
@AutoValue
abstract static class Listener {
abstract String name();
@Nullable
abstract String address();
abstract ImmutableList<FilterChain> filterChains();
@Nullable
abstract FilterChain defaultFilterChain();
static Listener create(
String name,
@Nullable String address,
ImmutableList<FilterChain> filterChains,
@Nullable FilterChain defaultFilterChain) {
return new AutoValue_EnvoyServerProtoData_Listener(name, address, filterChains,
defaultFilterChain);
}
}
/**
* Corresponds to Envoy proto message {@link
* io.envoyproxy.envoy.config.cluster.v3.OutlierDetection}. Only the fields supported by gRPC are
* included.
*
* <p>Protobuf Duration fields are represented in their string format (e.g. "10s").
*/
@AutoValue
abstract static class OutlierDetection {
@Nullable
abstract Long intervalNanos();
@Nullable
abstract Long baseEjectionTimeNanos();
@Nullable
abstract Long maxEjectionTimeNanos();
@Nullable
abstract Integer maxEjectionPercent();
@Nullable
abstract SuccessRateEjection successRateEjection();
@Nullable
abstract FailurePercentageEjection failurePercentageEjection();
static OutlierDetection create(
@Nullable Long intervalNanos,
@Nullable Long baseEjectionTimeNanos,
@Nullable Long maxEjectionTimeNanos,
@Nullable Integer maxEjectionPercentage,
@Nullable SuccessRateEjection successRateEjection,
@Nullable FailurePercentageEjection failurePercentageEjection) {
return new AutoValue_EnvoyServerProtoData_OutlierDetection(intervalNanos,
baseEjectionTimeNanos, maxEjectionTimeNanos, maxEjectionPercentage, successRateEjection,
failurePercentageEjection);
}
static OutlierDetection fromEnvoyOutlierDetection(
io.envoyproxy.envoy.config.cluster.v3.OutlierDetection envoyOutlierDetection) {
Long intervalNanos = envoyOutlierDetection.hasInterval()
? Durations.toNanos(envoyOutlierDetection.getInterval()) : null;
Long baseEjectionTimeNanos = envoyOutlierDetection.hasBaseEjectionTime()
? Durations.toNanos(envoyOutlierDetection.getBaseEjectionTime()) : null;
Long maxEjectionTimeNanos = envoyOutlierDetection.hasMaxEjectionTime()
? Durations.toNanos(envoyOutlierDetection.getMaxEjectionTime()) : null;
Integer maxEjectionPercentage = envoyOutlierDetection.hasMaxEjectionPercent()
? envoyOutlierDetection.getMaxEjectionPercent().getValue() : null;
SuccessRateEjection successRateEjection;
// If success rate enforcement has been turned completely off, don't configure this ejection.
if (envoyOutlierDetection.hasEnforcingSuccessRate()
&& envoyOutlierDetection.getEnforcingSuccessRate().getValue() == 0) {
successRateEjection = null;
} else {
Integer stdevFactor = envoyOutlierDetection.hasSuccessRateStdevFactor()
? envoyOutlierDetection.getSuccessRateStdevFactor().getValue() : null;
Integer enforcementPercentage = envoyOutlierDetection.hasEnforcingSuccessRate()
? envoyOutlierDetection.getEnforcingSuccessRate().getValue() : null;
Integer minimumHosts = envoyOutlierDetection.hasSuccessRateMinimumHosts()
? envoyOutlierDetection.getSuccessRateMinimumHosts().getValue() : null;
Integer requestVolume = envoyOutlierDetection.hasSuccessRateRequestVolume()
? envoyOutlierDetection.getSuccessRateMinimumHosts().getValue() : null;
successRateEjection = SuccessRateEjection.create(stdevFactor, enforcementPercentage,
minimumHosts, requestVolume);
}
FailurePercentageEjection failurePercentageEjection;
if (envoyOutlierDetection.hasEnforcingFailurePercentage()
&& envoyOutlierDetection.getEnforcingFailurePercentage().getValue() == 0) {
failurePercentageEjection = null;
} else {
Integer threshold = envoyOutlierDetection.hasFailurePercentageThreshold()
? envoyOutlierDetection.getFailurePercentageThreshold().getValue() : null;
Integer enforcementPercentage = envoyOutlierDetection.hasEnforcingFailurePercentage()
? envoyOutlierDetection.getEnforcingFailurePercentage().getValue() : null;
Integer minimumHosts = envoyOutlierDetection.hasFailurePercentageMinimumHosts()
? envoyOutlierDetection.getFailurePercentageMinimumHosts().getValue() : null;
Integer requestVolume = envoyOutlierDetection.hasFailurePercentageRequestVolume()
? envoyOutlierDetection.getFailurePercentageRequestVolume().getValue() : null;
failurePercentageEjection = FailurePercentageEjection.create(threshold,
enforcementPercentage, minimumHosts, requestVolume);
}
return create(intervalNanos, baseEjectionTimeNanos, maxEjectionTimeNanos,
maxEjectionPercentage, successRateEjection, failurePercentageEjection);
}
}
@AutoValue
abstract static class SuccessRateEjection {
@Nullable
abstract Integer stdevFactor();
@Nullable
abstract Integer enforcementPercentage();
@Nullable
abstract Integer minimumHosts();
@Nullable
abstract Integer requestVolume();
static SuccessRateEjection create(
@Nullable Integer stdevFactor,
@Nullable Integer enforcementPercentage,
@Nullable Integer minimumHosts,
@Nullable Integer requestVolume) {
return new AutoValue_EnvoyServerProtoData_SuccessRateEjection(stdevFactor,
enforcementPercentage, minimumHosts, requestVolume);
}
}
@AutoValue
abstract static class FailurePercentageEjection {
@Nullable
abstract Integer threshold();
@Nullable
abstract Integer enforcementPercentage();
@Nullable
abstract Integer minimumHosts();
@Nullable
abstract Integer requestVolume();
static FailurePercentageEjection create(
@Nullable Integer threshold,
@Nullable Integer enforcementPercentage,
@Nullable Integer minimumHosts,
@Nullable Integer requestVolume) {
return new AutoValue_EnvoyServerProtoData_FailurePercentageEjection(threshold,
enforcementPercentage, minimumHosts, requestVolume);
}
}
}