
io.envoyproxy.controlplane.cache.Snapshot Maven / Gradle / Ivy
package io.envoyproxy.controlplane.cache;
import static io.envoyproxy.controlplane.cache.Resources.CLUSTER_TYPE_URL;
import static io.envoyproxy.controlplane.cache.Resources.ENDPOINT_TYPE_URL;
import static io.envoyproxy.controlplane.cache.Resources.LISTENER_TYPE_URL;
import static io.envoyproxy.controlplane.cache.Resources.ROUTE_TYPE_URL;
import static io.envoyproxy.controlplane.cache.Resources.SECRET_TYPE_URL;
import com.google.auto.value.AutoValue;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.protobuf.Message;
import io.envoyproxy.envoy.api.v2.Cluster;
import io.envoyproxy.envoy.api.v2.ClusterLoadAssignment;
import io.envoyproxy.envoy.api.v2.Listener;
import io.envoyproxy.envoy.api.v2.RouteConfiguration;
import io.envoyproxy.envoy.api.v2.auth.Secret;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* {@code Snapshot} is a data class that contains an internally consistent snapshot of xDS resources. Snapshots should
* have distinct versions per node group.
*/
@AutoValue
public abstract class Snapshot {
/**
* Returns a new {@link Snapshot} instance that is versioned uniformly across all resources.
*
* @param clusters the cluster resources in this snapshot
* @param endpoints the endpoint resources in this snapshot
* @param listeners the listener resources in this snapshot
* @param routes the route resources in this snapshot
* @param version the version associated with all resources in this snapshot
*/
public static Snapshot create(
Iterable clusters,
Iterable endpoints,
Iterable listeners,
Iterable routes,
Iterable secrets,
String version) {
return new AutoValue_Snapshot(
SnapshotResources.create(clusters, version),
SnapshotResources.create(endpoints, version),
SnapshotResources.create(listeners, version),
SnapshotResources.create(routes, version),
SnapshotResources.create(secrets, version));
}
/**
* Returns a new {@link Snapshot} instance that has separate versions for each resource type.
*
* @param clusters the cluster resources in this snapshot
* @param clustersVersion the version of the cluster resources
* @param endpoints the endpoint resources in this snapshot
* @param endpointsVersion the version of the endpoint resources
* @param listeners the listener resources in this snapshot
* @param listenersVersion the version of the listener resources
* @param routes the route resources in this snapshot
* @param routesVersion the version of the route resources
*/
public static Snapshot create(
Iterable clusters,
String clustersVersion,
Iterable endpoints,
String endpointsVersion,
Iterable listeners,
String listenersVersion,
Iterable routes,
String routesVersion,
Iterable secrets,
String secretsVersion) {
// TODO(snowp): add a builder alternative
return new AutoValue_Snapshot(
SnapshotResources.create(clusters, clustersVersion),
SnapshotResources.create(endpoints, endpointsVersion),
SnapshotResources.create(listeners, listenersVersion),
SnapshotResources.create(routes, routesVersion),
SnapshotResources.create(secrets, secretsVersion));
}
/**
* Returns a new {@link Snapshot} instance that has separate versions for each resource type.
*
* @param clusters the cluster resources in this snapshot
* @param clusterVersionResolver version resolver of the clusters in this snapshot
* @param endpoints the endpoint resources in this snapshot
* @param endpointVersionResolver version resolver of the endpoints in this snapshot
* @param listeners the listener resources in this snapshot
* @param listenerVersionResolver version resolver of listeners in this snapshot
* @param routes the route resources in this snapshot
* @param routeVersionResolver version resolver of the routes in this snapshot
* @param secrets the secret resources in this snapshot
* @param secretVersionResolver version resolver of the secrets in this snapshot
*/
public static Snapshot create(
Iterable clusters,
ResourceVersionResolver clusterVersionResolver,
Iterable endpoints,
ResourceVersionResolver endpointVersionResolver,
Iterable listeners,
ResourceVersionResolver listenerVersionResolver,
Iterable routes,
ResourceVersionResolver routeVersionResolver,
Iterable secrets,
ResourceVersionResolver secretVersionResolver) {
return new AutoValue_Snapshot(
SnapshotResources.create(clusters, clusterVersionResolver),
SnapshotResources.create(endpoints, endpointVersionResolver),
SnapshotResources.create(listeners, listenerVersionResolver),
SnapshotResources.create(routes, routeVersionResolver),
SnapshotResources.create(secrets, secretVersionResolver));
}
/**
* Creates an empty snapshot with the given version.
*
* @param version the version of the snapshot resources
*/
public static Snapshot createEmpty(String version) {
return create(Collections.emptySet(), Collections.emptySet(),
Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), version);
}
/**
* Returns all cluster items in the CDS payload.
*/
public abstract SnapshotResources clusters();
/**
* Returns all endpoint items in the EDS payload.
*/
public abstract SnapshotResources endpoints();
/**
* Returns all listener items in the LDS payload.
*/
public abstract SnapshotResources listeners();
/**
* Returns all route items in the RDS payload.
*/
public abstract SnapshotResources routes();
/**
* Returns all secret items in the SDS payload.
*/
public abstract SnapshotResources secrets();
/**
* Asserts that all dependent resources are included in the snapshot. All EDS resources are listed by name in CDS
* resources, and all RDS resources are listed by name in LDS resources.
*
* Note that clusters and listeners are requested without name references, so Envoy will accept the snapshot list
* of clusters as-is, even if it does not match all references found in xDS.
*
* @throws SnapshotConsistencyException if the snapshot is not consistent
*/
public void ensureConsistent() throws SnapshotConsistencyException {
Set clusterEndpointRefs = Resources.getResourceReferences(clusters().resources().values());
ensureAllResourceNamesExist(CLUSTER_TYPE_URL, ENDPOINT_TYPE_URL, clusterEndpointRefs, endpoints().resources());
Set listenerRouteRefs = Resources.getResourceReferences(listeners().resources().values());
ensureAllResourceNamesExist(LISTENER_TYPE_URL, ROUTE_TYPE_URL, listenerRouteRefs, routes().resources());
}
/**
* Returns the resources with the given type.
*
* @param typeUrl the URL for the requested resource type
*/
public Map resources(String typeUrl) {
if (Strings.isNullOrEmpty(typeUrl)) {
return ImmutableMap.of();
}
switch (typeUrl) {
case CLUSTER_TYPE_URL:
return clusters().resources();
case ENDPOINT_TYPE_URL:
return endpoints().resources();
case LISTENER_TYPE_URL:
return listeners().resources();
case ROUTE_TYPE_URL:
return routes().resources();
case SECRET_TYPE_URL:
return secrets().resources();
default:
return ImmutableMap.of();
}
}
/**
* Returns the version in this snapshot for the given resource type.
*
* @param typeUrl the URL for the requested resource type
*/
public String version(String typeUrl) {
return version(typeUrl, Collections.emptyList());
}
/**
* Returns the version in this snapshot for the given resource type.
*
* @param typeUrl the URL for the requested resource type
* @param resourceNames list of requested resource names,
* used to calculate a version for the given resources
*/
public String version(String typeUrl, List resourceNames) {
if (Strings.isNullOrEmpty(typeUrl)) {
return "";
}
switch (typeUrl) {
case CLUSTER_TYPE_URL:
return clusters().version(resourceNames);
case ENDPOINT_TYPE_URL:
return endpoints().version(resourceNames);
case LISTENER_TYPE_URL:
return listeners().version(resourceNames);
case ROUTE_TYPE_URL:
return routes().version(resourceNames);
case SECRET_TYPE_URL:
return secrets().version(resourceNames);
default:
return "";
}
}
/**
* Asserts that all of the given resource names have corresponding values in the given resources collection.
*
* @param parentTypeUrl the type of the parent resources (source of the resource name refs)
* @param dependencyTypeUrl the type of the given dependent resources
* @param resourceNames the set of dependent resource names that must exist
* @param resources the collection of resources whose names are being checked
* @throws SnapshotConsistencyException if a name is given that does not exist in the resources collection
*/
private static void ensureAllResourceNamesExist(
String parentTypeUrl,
String dependencyTypeUrl,
Set resourceNames,
Map resources) throws SnapshotConsistencyException {
if (resourceNames.size() != resources.size()) {
throw new SnapshotConsistencyException(
String.format(
"Mismatched %s -> %s reference and resource lengths, [%s] != %d",
parentTypeUrl,
dependencyTypeUrl,
String.join(", ", resourceNames),
resources.size()));
}
for (String name : resourceNames) {
if (!resources.containsKey(name)) {
throw new SnapshotConsistencyException(
String.format(
"%s named '%s', referenced by a %s, not listed in [%s]",
dependencyTypeUrl,
name,
parentTypeUrl,
String.join(", ", resources.keySet())));
}
}
}
}