![JAR search and dependency download from the Maven repository](/logo.png)
io.grpc.xds.client.EnvoyProtoData Maven / Gradle / Ivy
/*
* Copyright 2019 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.client;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.MoreObjects;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.protobuf.ListValue;
import com.google.protobuf.NullValue;
import com.google.protobuf.Struct;
import com.google.protobuf.Value;
import io.grpc.Internal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import javax.annotation.Nullable;
/**
* Defines gRPC data types for Envoy protobuf messages used in xDS protocol. Each data type has
* the same name as Envoy's corresponding protobuf message, but only with fields used by gRPC.
*
* Each data type should define a {@code fromEnvoyProtoXXX} static method to convert an Envoy
* proto message to an instance of that data type.
*
*
For data types that need to be sent as protobuf messages, a {@code toEnvoyProtoXXX} instance
* method is defined to convert an instance to Envoy proto message.
*
*
Data conversion should follow the invariant: converted data is guaranteed to be valid for
* gRPC. If the protobuf message contains invalid data, the conversion should fail and no object
* should be instantiated.
*/
@Internal
public final class EnvoyProtoData {
// Prevent instantiation.
private EnvoyProtoData() {
}
/**
* See corresponding Envoy proto message {@link io.envoyproxy.envoy.config.core.v3.Node}.
*/
public static final class Node {
private final String id;
private final String cluster;
@Nullable
private final Map metadata;
@Nullable
private final Locality locality;
private final List listeningAddresses;
private final String buildVersion;
private final String userAgentName;
@Nullable
private final String userAgentVersion;
private final List clientFeatures;
private Node(
String id, String cluster, @Nullable Map metadata, @Nullable Locality locality,
List listeningAddresses, String buildVersion, String userAgentName,
@Nullable String userAgentVersion, List clientFeatures) {
this.id = checkNotNull(id, "id");
this.cluster = checkNotNull(cluster, "cluster");
this.metadata = metadata;
this.locality = locality;
this.listeningAddresses = Collections.unmodifiableList(
checkNotNull(listeningAddresses, "listeningAddresses"));
this.buildVersion = checkNotNull(buildVersion, "buildVersion");
this.userAgentName = checkNotNull(userAgentName, "userAgentName");
this.userAgentVersion = userAgentVersion;
this.clientFeatures = Collections.unmodifiableList(
checkNotNull(clientFeatures, "clientFeatures"));
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("id", id)
.add("cluster", cluster)
.add("metadata", metadata)
.add("locality", locality)
.add("listeningAddresses", listeningAddresses)
.add("buildVersion", buildVersion)
.add("userAgentName", userAgentName)
.add("userAgentVersion", userAgentVersion)
.add("clientFeatures", clientFeatures)
.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Node node = (Node) o;
return Objects.equals(id, node.id)
&& Objects.equals(cluster, node.cluster)
&& Objects.equals(metadata, node.metadata)
&& Objects.equals(locality, node.locality)
&& Objects.equals(listeningAddresses, node.listeningAddresses)
&& Objects.equals(buildVersion, node.buildVersion)
&& Objects.equals(userAgentName, node.userAgentName)
&& Objects.equals(userAgentVersion, node.userAgentVersion)
&& Objects.equals(clientFeatures, node.clientFeatures);
}
@Override
public int hashCode() {
return Objects
.hash(id, cluster, metadata, locality, listeningAddresses, buildVersion, userAgentName,
userAgentVersion, clientFeatures);
}
public static final class Builder {
private String id = "";
private String cluster = "";
@Nullable
private Map metadata;
@Nullable
private Locality locality;
// TODO(sanjaypujare): eliminate usage of listening_addresses field.
private final List listeningAddresses = new ArrayList<>();
private String buildVersion = "";
private String userAgentName = "";
@Nullable
private String userAgentVersion;
private final List clientFeatures = new ArrayList<>();
private Builder() {
}
@VisibleForTesting
public Builder setId(String id) {
this.id = checkNotNull(id, "id");
return this;
}
@CanIgnoreReturnValue
public Builder setCluster(String cluster) {
this.cluster = checkNotNull(cluster, "cluster");
return this;
}
@CanIgnoreReturnValue
public Builder setMetadata(Map metadata) {
this.metadata = checkNotNull(metadata, "metadata");
return this;
}
@CanIgnoreReturnValue
public Builder setLocality(Locality locality) {
this.locality = checkNotNull(locality, "locality");
return this;
}
@CanIgnoreReturnValue
Builder addListeningAddresses(Address address) {
listeningAddresses.add(checkNotNull(address, "address"));
return this;
}
@CanIgnoreReturnValue
public Builder setBuildVersion(String buildVersion) {
this.buildVersion = checkNotNull(buildVersion, "buildVersion");
return this;
}
@CanIgnoreReturnValue
public Builder setUserAgentName(String userAgentName) {
this.userAgentName = checkNotNull(userAgentName, "userAgentName");
return this;
}
@CanIgnoreReturnValue
public Builder setUserAgentVersion(String userAgentVersion) {
this.userAgentVersion = checkNotNull(userAgentVersion, "userAgentVersion");
return this;
}
@CanIgnoreReturnValue
public Builder addClientFeatures(String clientFeature) {
this.clientFeatures.add(checkNotNull(clientFeature, "clientFeature"));
return this;
}
public Node build() {
return new Node(
id, cluster, metadata, locality, listeningAddresses, buildVersion, userAgentName,
userAgentVersion, clientFeatures);
}
}
public static Builder newBuilder() {
return new Builder();
}
public Builder toBuilder() {
Builder builder = new Builder();
builder.id = id;
builder.cluster = cluster;
builder.metadata = metadata;
builder.locality = locality;
builder.buildVersion = buildVersion;
builder.listeningAddresses.addAll(listeningAddresses);
builder.userAgentName = userAgentName;
builder.userAgentVersion = userAgentVersion;
builder.clientFeatures.addAll(clientFeatures);
return builder;
}
public String getId() {
return id;
}
String getCluster() {
return cluster;
}
@Nullable
Map getMetadata() {
return metadata;
}
@Nullable
Locality getLocality() {
return locality;
}
List getListeningAddresses() {
return listeningAddresses;
}
@SuppressWarnings("deprecation")
@VisibleForTesting
public io.envoyproxy.envoy.config.core.v3.Node toEnvoyProtoNode() {
io.envoyproxy.envoy.config.core.v3.Node.Builder builder =
io.envoyproxy.envoy.config.core.v3.Node.newBuilder();
builder.setId(id);
builder.setCluster(cluster);
if (metadata != null) {
Struct.Builder structBuilder = Struct.newBuilder();
for (Map.Entry entry : metadata.entrySet()) {
structBuilder.putFields(entry.getKey(), convertToValue(entry.getValue()));
}
builder.setMetadata(structBuilder);
}
if (locality != null) {
builder.setLocality(
io.envoyproxy.envoy.config.core.v3.Locality.newBuilder()
.setRegion(locality.region())
.setZone(locality.zone())
.setSubZone(locality.subZone()));
}
for (Address address : listeningAddresses) {
builder.addListeningAddresses(address.toEnvoyProtoAddress());
}
builder.setUserAgentName(userAgentName);
if (userAgentVersion != null) {
builder.setUserAgentVersion(userAgentVersion);
}
builder.addAllClientFeatures(clientFeatures);
return builder.build();
}
}
/**
* Converts Java representation of the given JSON value to protobuf's {@link
* com.google.protobuf.Value} representation.
*
* The given {@code rawObject} must be a valid JSON value in Java representation, which is
* either a {@code Map}, {@code List>}, {@code String}, {@code Double}, {@code
* Boolean}, or {@code null}.
*/
private static Value convertToValue(Object rawObject) {
Value.Builder valueBuilder = Value.newBuilder();
if (rawObject == null) {
valueBuilder.setNullValue(NullValue.NULL_VALUE);
} else if (rawObject instanceof Double) {
valueBuilder.setNumberValue((Double) rawObject);
} else if (rawObject instanceof String) {
valueBuilder.setStringValue((String) rawObject);
} else if (rawObject instanceof Boolean) {
valueBuilder.setBoolValue((Boolean) rawObject);
} else if (rawObject instanceof Map) {
Struct.Builder structBuilder = Struct.newBuilder();
@SuppressWarnings("unchecked")
Map map = (Map) rawObject;
for (Map.Entry entry : map.entrySet()) {
structBuilder.putFields(entry.getKey(), convertToValue(entry.getValue()));
}
valueBuilder.setStructValue(structBuilder);
} else if (rawObject instanceof List) {
ListValue.Builder listBuilder = ListValue.newBuilder();
List> list = (List>) rawObject;
for (Object obj : list) {
listBuilder.addValues(convertToValue(obj));
}
valueBuilder.setListValue(listBuilder);
}
return valueBuilder.build();
}
/**
* See corresponding Envoy proto message {@link io.envoyproxy.envoy.config.core.v3.Address}.
*/
static final class Address {
private final String address;
private final int port;
Address(String address, int port) {
this.address = checkNotNull(address, "address");
this.port = port;
}
io.envoyproxy.envoy.config.core.v3.Address toEnvoyProtoAddress() {
return
io.envoyproxy.envoy.config.core.v3.Address.newBuilder().setSocketAddress(
io.envoyproxy.envoy.config.core.v3.SocketAddress.newBuilder().setAddress(address)
.setPortValue(port)).build();
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("address", address)
.add("port", port)
.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Address address1 = (Address) o;
return port == address1.port && Objects.equals(address, address1.address);
}
@Override
public int hashCode() {
return Objects.hash(address, port);
}
}
}