Skip to content

Commit

Permalink
LDS Config Dump built from metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
sergiitk committed Mar 22, 2021
1 parent 86393b5 commit 478dfbd
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 74 deletions.
7 changes: 2 additions & 5 deletions xds/src/main/java/io/grpc/xds/AbstractXdsClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -318,11 +318,8 @@ private void startRpcStream() {
stopwatch.reset().start();
}

/**
* Returns the latest accepted version of the given resource type.
*/
// Must be synchronized.
protected String getCurrentVersion(ResourceType type) {
@Override
String getCurrentVersion(ResourceType type) {
String version;
switch (type) {
case LDS:
Expand Down
81 changes: 37 additions & 44 deletions xds/src/main/java/io/grpc/xds/ClientXdsClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
import com.google.protobuf.util.Durations;
import com.google.re2j.Pattern;
import com.google.re2j.PatternSyntaxException;
import io.envoyproxy.envoy.admin.v3.ListenersConfigDump;
import io.envoyproxy.envoy.config.cluster.v3.CircuitBreakers.Thresholds;
import io.envoyproxy.envoy.config.cluster.v3.Cluster;
import io.envoyproxy.envoy.config.cluster.v3.Cluster.CustomClusterType;
Expand Down Expand Up @@ -375,14 +374,6 @@ private static StructOrError<FilterConfig> parseRawFilterConfig(
}
}

@Override
ListenersConfigDump dumpLdsConfig() {
ListenersConfigDump.Builder ldsConfig = ListenersConfigDump.newBuilder()
.setVersionInfo(getCurrentVersion(ResourceType.LDS));

return ldsConfig.build();
}

private static StructOrError<VirtualHost> parseVirtualHost(
io.envoyproxy.envoy.config.route.v3.VirtualHost proto, boolean parseFilter) {
String name = proto.getName();
Expand Down Expand Up @@ -1181,6 +1172,7 @@ Collection<String> getSubscribedResources(ResourceType type) {
return resources.isEmpty() ? null : resources.keySet();
}

@Override
Map<String, ResourceMetadata> getSubscribedResourcesMetadata(ResourceType type) {
Map<String, ResourceMetadata> metadataMap = new HashMap<>();
for (Map.Entry<String, ResourceSubscriber> entry : getSubscribedResourcesMap(type).entrySet()) {
Expand All @@ -1190,8 +1182,7 @@ Map<String, ResourceMetadata> getSubscribedResourcesMetadata(ResourceType type)
}

@Nullable
ResourceMetadata getSubscribedResourceMetadata(
ResourceType type, String resourceName) {
ResourceMetadata getSubscribedResourceMetadata(ResourceType type, String resourceName) {
Map<String, ResourceSubscriber> resources = getSubscribedResourcesMap(type);
if (resources.isEmpty() || !resources.containsKey(resourceName)) {
return null;
Expand Down Expand Up @@ -1429,16 +1420,16 @@ private static String combineErrors(List<String> errors) {
}

/**
* Captures ResourceSubscriber metadata, used by the xDS config dump.
* Captures resource metadata, used by the xDS config dump.
*/
static final class ResourceMetadata {
static final class ClientResourceMetadata implements ResourceMetadata {
private final String version;
private final ResourceMetadataStatus status;
private final long updateTime;
@Nullable private final Any rawResource;
@Nullable private final UpdateFailureState errorState;

private ResourceMetadata(
private ClientResourceMetadata(
ResourceMetadataStatus status, String version, long updateTime, @Nullable Any rawResource,
@Nullable UpdateFailureState errorState) {
this.status = checkNotNull(status, "status");
Expand All @@ -1448,83 +1439,84 @@ private ResourceMetadata(
this.errorState = errorState;
}

static ResourceMetadata newResourceMetadataUnknown() {
return new ResourceMetadata(ResourceMetadataStatus.UNKNOWN, "", 0, null, null);
static ClientResourceMetadata newResourceMetadataUnknown() {
return new ClientResourceMetadata(ResourceMetadataStatus.UNKNOWN, "", 0, null, null);
}

static ResourceMetadata newResourceMetadataRequested() {
return new ResourceMetadata(ResourceMetadataStatus.REQUESTED, "", 0, null, null);
static ClientResourceMetadata newResourceMetadataRequested() {
return new ClientResourceMetadata(ResourceMetadataStatus.REQUESTED, "", 0, null, null);
}

static ResourceMetadata newResourceMetadataDoesNotExist() {
return new ResourceMetadata(ResourceMetadataStatus.DOES_NOT_EXIST, "", 0, null, null);
static ClientResourceMetadata newResourceMetadataDoesNotExist() {
return new ClientResourceMetadata(ResourceMetadataStatus.DOES_NOT_EXIST, "", 0, null, null);
}

static ResourceMetadata newResourceMetadataAcked(
static ClientResourceMetadata newResourceMetadataAcked(
Any resource, String version, long updateTime) {
checkNotNull(resource, "resource");
return new ResourceMetadata(
return new ClientResourceMetadata(
ResourceMetadataStatus.ACKED, version, updateTime, resource, null);
}

static ResourceMetadata newResourceMetadataNacked(
static ClientResourceMetadata newResourceMetadataNacked(
ResourceMetadata metadata, String failedVersion, long failedUpdateTime,
String failedDetails) {
checkNotNull(metadata, "metadata");
return new ResourceMetadata(ResourceMetadataStatus.NACKED,
return new ClientResourceMetadata(ResourceMetadataStatus.NACKED,
metadata.getVersion(), metadata.getUpdateTime(), metadata.getRawResource(),
new UpdateFailureState(failedVersion, failedUpdateTime, failedDetails));
new ClientUpdateFailureState(failedVersion, failedUpdateTime, failedDetails));
}

String getVersion() {
@Override
public String getVersion() {
return version;
}

ResourceMetadataStatus getStatus() {
@Override
public ResourceMetadataStatus getStatus() {
return status;
}

long getUpdateTime() {
@Override
public long getUpdateTime() {
return updateTime;
}

@Override
@Nullable
Any getRawResource() {
public Any getRawResource() {
return rawResource;
}

@Override
@Nullable
UpdateFailureState getErrorState() {
public UpdateFailureState getErrorState() {
return errorState;
}

enum ResourceMetadataStatus {
UNKNOWN, REQUESTED, DOES_NOT_EXIST, ACKED, NACKED
}

static final class UpdateFailureState {
static final class ClientUpdateFailureState implements UpdateFailureState {
private final String failedVersion;
private final long failedUpdateTime;
private final String failedDetails;

private UpdateFailureState(
private ClientUpdateFailureState(
String failedVersion, long failedUpdateTime, String failedDetails) {
this.failedVersion = checkNotNull(failedVersion, "failedVersion");
this.failedUpdateTime = failedUpdateTime;
this.failedDetails = checkNotNull(failedDetails, "failedDetails");
}

/** The rejected version string of the last failed update attempt. */
@Override
public String getFailedVersion() {
return failedVersion;
}

/** Details about the last failed update attempt. */
@Override
public long getFailedUpdateTime() {
return failedUpdateTime;
}

/** Timestamp of the last failed update attempt. */
@Override
public String getFailedDetails() {
return failedDetails;
}
Expand Down Expand Up @@ -1566,7 +1558,7 @@ private final class ResourceSubscriber {
this.resource = resource;
// Initialize metadata in UNKNOWN state to cover the case when resource subscriber,
// is created but not yet requested because the client is in backoff.
this.metadata = ResourceMetadata.newResourceMetadataUnknown();
this.metadata = ClientResourceMetadata.newResourceMetadataUnknown();
if (isInBackoff()) {
return;
}
Expand Down Expand Up @@ -1608,7 +1600,7 @@ public String toString() {
}

// Initial fetch scheduled or rescheduled, transition metadata state to REQUESTED.
metadata = ResourceMetadata.newResourceMetadataRequested();
metadata = ClientResourceMetadata.newResourceMetadataRequested();
respTimer = getSyncContext().schedule(
new ResourceNotFound(), INITIAL_RESOURCE_FETCH_TIMEOUT_SEC, TimeUnit.SECONDS,
getTimeService());
Expand All @@ -1631,7 +1623,8 @@ void onData(ResourceUpdate data, Any resource, String version, long updateTime)
respTimer.cancel();
respTimer = null;
}
this.metadata = ResourceMetadata.newResourceMetadataAcked(resource, version, updateTime);
this.metadata =
ClientResourceMetadata.newResourceMetadataAcked(resource, version, updateTime);
ResourceUpdate oldData = this.data;
this.data = data;
absent = false;
Expand All @@ -1655,7 +1648,7 @@ void onAbsent() {
if (!absent) {
data = null;
absent = true;
metadata = ResourceMetadata.newResourceMetadataDoesNotExist();
metadata = ClientResourceMetadata.newResourceMetadataDoesNotExist();
for (ResourceWatcher watcher : watchers) {
watcher.onResourceDoesNotExist(resource);
}
Expand All @@ -1673,7 +1666,7 @@ void onError(Status error) {
}

void onRejected(String rejectedVersion, long rejectedTime, String rejectedDetails) {
metadata = ResourceMetadata
metadata = ClientResourceMetadata
.newResourceMetadataNacked(metadata, rejectedVersion, rejectedTime, rejectedDetails);
}

Expand Down
66 changes: 65 additions & 1 deletion xds/src/main/java/io/grpc/xds/CsdsService.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.annotations.VisibleForTesting;
import com.google.protobuf.Timestamp;
import io.envoyproxy.envoy.admin.v3.ClientResourceStatus;
import io.envoyproxy.envoy.admin.v3.ListenersConfigDump;
import io.envoyproxy.envoy.admin.v3.ListenersConfigDump.DynamicListener;
import io.envoyproxy.envoy.admin.v3.ListenersConfigDump.DynamicListenerState;
import io.envoyproxy.envoy.service.status.v3.ClientConfig;
import io.envoyproxy.envoy.service.status.v3.ClientStatusDiscoveryServiceGrpc;
import io.envoyproxy.envoy.service.status.v3.ClientStatusRequest;
Expand All @@ -29,6 +34,11 @@
import io.grpc.StatusException;
import io.grpc.internal.ObjectPool;
import io.grpc.stub.StreamObserver;
import io.grpc.xds.AbstractXdsClient.ResourceType;
import io.grpc.xds.XdsClient.ResourceMetadata;
import io.grpc.xds.XdsClient.ResourceMetadata.ResourceMetadataStatus;
import java.util.Map;
import java.util.concurrent.TimeUnit;

// TODO(sergiitk): finish description, update since.
/**
Expand Down Expand Up @@ -118,9 +128,63 @@ private ClientStatusResponse getConfigDumpForRequest(ClientStatusRequest request
}

private ClientConfig getClientConfigForXdsClient(XdsClient xdsClient) {
ListenersConfigDump ldsConfig = dumpLdsConfig(xdsClient.getCurrentVersion(ResourceType.LDS),
xdsClient.getSubscribedResourcesMetadata(ResourceType.LDS));

return ClientConfig.newBuilder()
.setNode(xdsClient.getNode().toEnvoyProtoNode())
.addXdsConfig(PerXdsConfig.newBuilder().setListenerConfig(xdsClient.dumpLdsConfig()))
.addXdsConfig(PerXdsConfig.newBuilder().setListenerConfig(ldsConfig))
.build();
}

private static ListenersConfigDump dumpLdsConfig(
String version, Map<String, ResourceMetadata> resourcesMetadata) {
ListenersConfigDump.Builder ldsConfig = ListenersConfigDump.newBuilder();
for (Map.Entry<String, ResourceMetadata> entry : resourcesMetadata.entrySet()) {
ldsConfig.addDynamicListeners(buildDynamicListener(entry.getKey(), entry.getValue()));
}
return ldsConfig.setVersionInfo(version).build();
}

private static DynamicListener buildDynamicListener(String name, ResourceMetadata metadata) {
DynamicListenerState.Builder listenerState = DynamicListenerState.newBuilder()
.setVersionInfo(metadata.getVersion())
.setLastUpdated(nanosToTimestamp(metadata.getUpdateTime()));
if (metadata.getRawResource() != null) {
listenerState.setListener(metadata.getRawResource());
}
// TODO(sergiitk): Add error state if present.
return DynamicListener.newBuilder()
.setName(name)
.setClientStatus(metadataStatusToClientStatus(metadata.getStatus()))
.setActiveState(listenerState)
.build();
}

private static Timestamp nanosToTimestamp(long updateTimeNanos) {
// TODO(sergiitk): build directly from nanos.
long millis = TimeUnit.NANOSECONDS.toMillis(updateTimeNanos);
// See https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#google.protobuf.Timestamp
return Timestamp.newBuilder()
.setSeconds(millis / 1000)
.setNanos((int) ((millis % 1000) * 1000000))
.build();
}

private static ClientResourceStatus metadataStatusToClientStatus(ResourceMetadataStatus status) {
switch (status) {
case UNKNOWN:
return ClientResourceStatus.UNKNOWN;
case DOES_NOT_EXIST:
return ClientResourceStatus.DOES_NOT_EXIST;
case REQUESTED:
return ClientResourceStatus.REQUESTED;
case ACKED:
return ClientResourceStatus.ACKED;
case NACKED:
return ClientResourceStatus.NACKED;
default:
throw new AssertionError("Unexpected ResourceMetadataStatus: " + status);
}
}
}
47 changes: 41 additions & 6 deletions xds/src/main/java/io/grpc/xds/XdsClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@
import com.google.common.base.MoreObjects;
import com.google.common.base.MoreObjects.ToStringHelper;
import com.google.common.collect.ImmutableList;
import io.envoyproxy.envoy.admin.v3.ListenersConfigDump;
import com.google.protobuf.Any;
import io.grpc.Status;
import io.grpc.xds.AbstractXdsClient.ResourceType;
import io.grpc.xds.Endpoints.DropOverload;
import io.grpc.xds.Endpoints.LocalityLbEndpoints;
import io.grpc.xds.EnvoyProtoData.Node;
Expand Down Expand Up @@ -356,6 +357,33 @@ public String toString() {
interface ResourceUpdate {
}

interface ResourceMetadata {
String getVersion();

ResourceMetadataStatus getStatus();

long getUpdateTime();

@Nullable Any getRawResource();

@Nullable UpdateFailureState getErrorState();

enum ResourceMetadataStatus {
UNKNOWN, REQUESTED, DOES_NOT_EXIST, ACKED, NACKED
}

interface UpdateFailureState {
/** The rejected version string of the last failed update attempt. */
String getFailedVersion();

/** Details about the last failed update attempt. */
long getFailedUpdateTime();

/** Timestamp of the last failed update attempt. */
String getFailedDetails();
}
}

/**
* Watcher interface for a single requested xDS resource.
*/
Expand Down Expand Up @@ -411,6 +439,18 @@ Node getNode() {
throw new UnsupportedOperationException();
}

/**
* Returns the latest accepted version of the given resource type.
*/
// Must be synchronized.
String getCurrentVersion(ResourceType type) {
throw new UnsupportedOperationException();
}

Map<String, ResourceMetadata> getSubscribedResourcesMetadata(ResourceType type) {
throw new UnsupportedOperationException();
}

/**
* Registers a data watcher for the given LDS resource.
*/
Expand All @@ -425,11 +465,6 @@ void cancelLdsResourceWatch(String resourceName, LdsResourceWatcher watcher) {
throw new UnsupportedOperationException();
}

ListenersConfigDump dumpLdsConfig() {
// TODO(sergiitk): docs.
throw new UnsupportedOperationException();
}

/**
* Registers a data watcher for the given RDS resource.
*/
Expand Down
Loading

0 comments on commit 478dfbd

Please sign in to comment.