Skip to content

Commit ade2dd2

Browse files
authored
xds: Change XdsClusterConfig to have children field instead of endpoint (grpc#11888)
* Change XdsConfig to match spec with a `children` object holding either `a list of leaf cluster names` or `an EdsUpdate`. Removed intermediate aggregate nodes from `XdsConfig.clusters`.
1 parent fc8571a commit ade2dd2

File tree

4 files changed

+209
-47
lines changed

4 files changed

+209
-47
lines changed

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

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import io.grpc.xds.XdsRouteConfigureResource.RdsUpdate;
2727
import java.io.Closeable;
2828
import java.util.HashMap;
29+
import java.util.List;
2930
import java.util.Map;
3031
import java.util.Objects;
3132

@@ -103,19 +104,17 @@ public ImmutableMap<String, StatusOr<XdsClusterConfig>> getClusters() {
103104
static final class XdsClusterConfig {
104105
private final String clusterName;
105106
private final CdsUpdate clusterResource;
106-
private final StatusOr<EdsUpdate> endpoint; //Will be null for non-EDS clusters
107+
private final ClusterChild children; // holds details
107108

108-
XdsClusterConfig(String clusterName, CdsUpdate clusterResource,
109-
StatusOr<EdsUpdate> endpoint) {
109+
XdsClusterConfig(String clusterName, CdsUpdate clusterResource, ClusterChild details) {
110110
this.clusterName = checkNotNull(clusterName, "clusterName");
111111
this.clusterResource = checkNotNull(clusterResource, "clusterResource");
112-
this.endpoint = endpoint;
112+
this.children = checkNotNull(details, "details");
113113
}
114114

115115
@Override
116116
public int hashCode() {
117-
int endpointHash = (endpoint != null) ? endpoint.hashCode() : 0;
118-
return clusterName.hashCode() + clusterResource.hashCode() + endpointHash;
117+
return clusterName.hashCode() + clusterResource.hashCode() + children.hashCode();
119118
}
120119

121120
@Override
@@ -126,15 +125,16 @@ public boolean equals(Object obj) {
126125
XdsClusterConfig o = (XdsClusterConfig) obj;
127126
return Objects.equals(clusterName, o.clusterName)
128127
&& Objects.equals(clusterResource, o.clusterResource)
129-
&& Objects.equals(endpoint, o.endpoint);
128+
&& Objects.equals(children, o.children);
130129
}
131130

132131
@Override
133132
public String toString() {
134133
StringBuilder builder = new StringBuilder();
135134
builder.append("XdsClusterConfig{clusterName=").append(clusterName)
136135
.append(", clusterResource=").append(clusterResource)
137-
.append(", endpoint=").append(endpoint).append("}");
136+
.append(", children={").append(children)
137+
.append("}");
138138
return builder.toString();
139139
}
140140

@@ -146,8 +146,60 @@ public CdsUpdate getClusterResource() {
146146
return clusterResource;
147147
}
148148

149-
public StatusOr<EdsUpdate> getEndpoint() {
150-
return endpoint;
149+
public ClusterChild getChildren() {
150+
return children;
151+
}
152+
153+
interface ClusterChild {}
154+
155+
/** Endpoint info for EDS and LOGICAL_DNS clusters. If there was an
156+
* error, endpoints will be null and resolution_note will be set.
157+
*/
158+
static final class EndpointConfig implements ClusterChild {
159+
private final StatusOr<EdsUpdate> endpoint;
160+
161+
public EndpointConfig(StatusOr<EdsUpdate> endpoint) {
162+
this.endpoint = checkNotNull(endpoint, "endpoint");
163+
}
164+
165+
@Override
166+
public int hashCode() {
167+
return endpoint.hashCode();
168+
}
169+
170+
@Override
171+
public boolean equals(Object obj) {
172+
if (!(obj instanceof EndpointConfig)) {
173+
return false;
174+
}
175+
return Objects.equals(endpoint, ((EndpointConfig)obj).endpoint);
176+
}
177+
178+
public StatusOr<EdsUpdate> getEndpoint() {
179+
return endpoint;
180+
}
181+
}
182+
183+
// The list of leaf clusters for an aggregate cluster.
184+
static final class AggregateConfig implements ClusterChild {
185+
private final List<String> leafNames;
186+
187+
public AggregateConfig(List<String> leafNames) {
188+
this.leafNames = checkNotNull(leafNames, "leafNames");
189+
}
190+
191+
@Override
192+
public int hashCode() {
193+
return leafNames.hashCode();
194+
}
195+
196+
@Override
197+
public boolean equals(Object obj) {
198+
if (!(obj instanceof AggregateConfig)) {
199+
return false;
200+
}
201+
return Objects.equals(leafNames, ((AggregateConfig) obj).leafNames);
202+
}
151203
}
152204
}
153205

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

Lines changed: 118 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,25 @@
2929
import io.grpc.StatusOr;
3030
import io.grpc.SynchronizationContext;
3131
import io.grpc.xds.VirtualHost.Route.RouteAction.ClusterWeight;
32+
import io.grpc.xds.XdsClusterResource.CdsUpdate.ClusterType;
33+
import io.grpc.xds.XdsConfig.XdsClusterConfig.AggregateConfig;
34+
import io.grpc.xds.XdsConfig.XdsClusterConfig.EndpointConfig;
3235
import io.grpc.xds.XdsRouteConfigureResource.RdsUpdate;
3336
import io.grpc.xds.client.XdsClient;
3437
import io.grpc.xds.client.XdsClient.ResourceWatcher;
3538
import io.grpc.xds.client.XdsLogger;
3639
import io.grpc.xds.client.XdsResourceType;
3740
import java.io.Closeable;
3841
import java.io.IOException;
42+
import java.util.ArrayList;
3943
import java.util.Collections;
4044
import java.util.HashMap;
4145
import java.util.HashSet;
4246
import java.util.List;
4347
import java.util.Map;
4448
import java.util.Objects;
4549
import java.util.Set;
50+
import java.util.stream.Collectors;
4651
import javax.annotation.Nullable;
4752

4853
/**
@@ -299,27 +304,123 @@ XdsConfig buildConfig() {
299304
Map<String, ? extends XdsWatcherBase<?>> cdsWatchers =
300305
resourceWatchers.get(CLUSTER_RESOURCE).watchers;
301306

302-
// Iterate CDS watchers
303-
for (XdsWatcherBase<?> watcher : cdsWatchers.values()) {
304-
CdsWatcher cdsWatcher = (CdsWatcher) watcher;
305-
String clusterName = cdsWatcher.resourceName();
306-
StatusOr<XdsClusterResource.CdsUpdate> cdsUpdate = cdsWatcher.getData();
307-
if (cdsUpdate.hasValue()) {
308-
XdsConfig.XdsClusterConfig clusterConfig;
309-
String edsName = cdsUpdate.getValue().edsServiceName();
310-
EdsWatcher edsWatcher = (EdsWatcher) edsWatchers.get(edsName);
311-
312-
// Only EDS type clusters have endpoint data
313-
StatusOr<XdsEndpointResource.EdsUpdate> data =
314-
edsWatcher != null ? edsWatcher.getData() : null;
315-
clusterConfig = new XdsConfig.XdsClusterConfig(clusterName, cdsUpdate.getValue(), data);
316-
builder.addCluster(clusterName, StatusOr.fromValue(clusterConfig));
307+
// Only care about aggregates from LDS/RDS or subscriptions and the leaf clusters
308+
List<String> topLevelClusters =
309+
cdsWatchers.values().stream()
310+
.filter(XdsDependencyManager::isTopLevelCluster)
311+
.map(w -> w.resourceName())
312+
.collect(Collectors.toList());
313+
314+
// Flatten multi-level aggregates into lists of leaf clusters
315+
Set<String> leafNames =
316+
addTopLevelClustersToBuilder(builder, edsWatchers, cdsWatchers, topLevelClusters);
317+
318+
addLeavesToBuilder(builder, edsWatchers, leafNames);
319+
320+
return builder.build();
321+
}
322+
323+
private void addLeavesToBuilder(XdsConfig.XdsConfigBuilder builder,
324+
Map<String, ? extends XdsWatcherBase<?>> edsWatchers,
325+
Set<String> leafNames) {
326+
for (String clusterName : leafNames) {
327+
CdsWatcher cdsWatcher = getCluster(clusterName);
328+
StatusOr<XdsClusterResource.CdsUpdate> cdsUpdateOr = cdsWatcher.getData();
329+
330+
if (cdsUpdateOr.hasValue()) {
331+
XdsClusterResource.CdsUpdate cdsUpdate = cdsUpdateOr.getValue();
332+
if (cdsUpdate.clusterType() == ClusterType.EDS) {
333+
EdsWatcher edsWatcher = (EdsWatcher) edsWatchers.get(cdsUpdate.edsServiceName());
334+
if (edsWatcher != null) {
335+
EndpointConfig child = new EndpointConfig(edsWatcher.getData());
336+
builder.addCluster(clusterName, StatusOr.fromValue(
337+
new XdsConfig.XdsClusterConfig(clusterName, cdsUpdate, child)));
338+
} else {
339+
builder.addCluster(clusterName, StatusOr.fromStatus(Status.UNAVAILABLE.withDescription(
340+
"EDS resource not found for cluster " + clusterName)));
341+
}
342+
} else if (cdsUpdate.clusterType() == ClusterType.LOGICAL_DNS) {
343+
// TODO get the resolved endpoint configuration
344+
builder.addCluster(clusterName, StatusOr.fromValue(
345+
new XdsConfig.XdsClusterConfig(clusterName, cdsUpdate, new EndpointConfig(null))));
346+
}
317347
} else {
318-
builder.addCluster(clusterName, StatusOr.fromStatus(cdsUpdate.getStatus()));
348+
builder.addCluster(clusterName, StatusOr.fromStatus(cdsUpdateOr.getStatus()));
319349
}
320350
}
351+
}
321352

322-
return builder.build();
353+
// Adds the top-level clusters to the builder and returns the leaf cluster names
354+
private Set<String> addTopLevelClustersToBuilder(
355+
XdsConfig.XdsConfigBuilder builder, Map<String, ? extends XdsWatcherBase<?>> edsWatchers,
356+
Map<String, ? extends XdsWatcherBase<?>> cdsWatchers, List<String> topLevelClusters) {
357+
358+
Set<String> leafClusterNames = new HashSet<>();
359+
for (String clusterName : topLevelClusters) {
360+
CdsWatcher cdsWatcher = (CdsWatcher) cdsWatchers.get(clusterName);
361+
StatusOr<XdsClusterResource.CdsUpdate> cdsWatcherDataOr = cdsWatcher.getData();
362+
if (!cdsWatcher.hasDataValue()) {
363+
builder.addCluster(clusterName, StatusOr.fromStatus(cdsWatcherDataOr.getStatus()));
364+
continue;
365+
}
366+
367+
XdsClusterResource.CdsUpdate cdsUpdate = cdsWatcherDataOr.getValue();
368+
XdsConfig.XdsClusterConfig.ClusterChild child;
369+
switch (cdsUpdate.clusterType()) {
370+
case AGGREGATE:
371+
List<String> leafNames = getLeafNames(cdsUpdate);
372+
child = new AggregateConfig(leafNames);
373+
leafClusterNames.addAll(leafNames);
374+
break;
375+
case EDS:
376+
EdsWatcher edsWatcher = (EdsWatcher) edsWatchers.get(cdsUpdate.edsServiceName());
377+
if (edsWatcher != null) {
378+
child = new EndpointConfig(edsWatcher.getData());
379+
} else {
380+
builder.addCluster(clusterName, StatusOr.fromStatus(Status.UNAVAILABLE.withDescription(
381+
"EDS resource not found for cluster " + clusterName)));
382+
continue;
383+
}
384+
break;
385+
case LOGICAL_DNS:
386+
// TODO get the resolved endpoint configuration
387+
child = new EndpointConfig(null);
388+
break;
389+
default:
390+
throw new IllegalStateException("Unexpected value: " + cdsUpdate.clusterType());
391+
}
392+
builder.addCluster(clusterName, StatusOr.fromValue(
393+
new XdsConfig.XdsClusterConfig(clusterName, cdsUpdate, child)));
394+
}
395+
396+
return leafClusterNames;
397+
}
398+
399+
private List<String> getLeafNames(XdsClusterResource.CdsUpdate cdsUpdate) {
400+
List<String> childNames = new ArrayList<>();
401+
402+
for (String cluster : cdsUpdate.prioritizedClusterNames()) {
403+
StatusOr<XdsClusterResource.CdsUpdate> data = getCluster(cluster).getData();
404+
if (data == null || !data.hasValue() || data.getValue() == null) {
405+
childNames.add(cluster);
406+
continue;
407+
}
408+
if (data.getValue().clusterType() == ClusterType.AGGREGATE) {
409+
childNames.addAll(getLeafNames(data.getValue()));
410+
} else {
411+
childNames.add(cluster);
412+
}
413+
}
414+
415+
return childNames;
416+
}
417+
418+
private static boolean isTopLevelCluster(XdsWatcherBase<?> cdsWatcher) {
419+
if (! (cdsWatcher instanceof CdsWatcher)) {
420+
return false;
421+
}
422+
return ((CdsWatcher)cdsWatcher).parentContexts.values().stream()
423+
.anyMatch(depth -> depth == 1);
323424
}
324425

325426
@Override

0 commit comments

Comments
 (0)