Skip to content

Commit

Permalink
introduce virtual host snapshot
Browse files Browse the repository at this point in the history
  • Loading branch information
jrhee17 committed Feb 18, 2025
1 parent 533f798 commit 0f968cc
Show file tree
Hide file tree
Showing 28 changed files with 638 additions and 345 deletions.
27 changes: 11 additions & 16 deletions xds/src/main/java/com/linecorp/armeria/xds/ClusterResourceNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

import static com.google.common.base.Strings.isNullOrEmpty;
import static com.linecorp.armeria.xds.XdsType.CLUSTER;
import static java.util.Objects.requireNonNull;

import java.util.Objects;

Expand All @@ -28,16 +27,10 @@
import io.envoyproxy.envoy.config.cluster.v3.Cluster.EdsClusterConfig;
import io.envoyproxy.envoy.config.core.v3.ConfigSource;
import io.envoyproxy.envoy.config.endpoint.v3.ClusterLoadAssignment;
import io.envoyproxy.envoy.config.route.v3.Route;
import io.envoyproxy.envoy.config.route.v3.VirtualHost;
import io.grpc.Status;

final class ClusterResourceNode extends AbstractResourceNodeWithPrimer<ClusterXdsResource> {

@Nullable
private final VirtualHost virtualHost;
@Nullable
private final Route route;
private final int index;
private final EndpointSnapshotWatcher snapshotWatcher = new EndpointSnapshotWatcher();
private final SnapshotWatcher<ClusterSnapshot> parentWatcher;
Expand All @@ -49,19 +42,15 @@ final class ClusterResourceNode extends AbstractResourceNodeWithPrimer<ClusterXd
ResourceNodeType resourceNodeType) {
super(xdsBootstrap, configSource, CLUSTER, resourceName, primer, parentWatcher, resourceNodeType);
this.parentWatcher = parentWatcher;
virtualHost = null;
route = null;
index = -1;
}

ClusterResourceNode(@Nullable ConfigSource configSource,
String resourceName, XdsBootstrapImpl xdsBootstrap,
@Nullable RouteXdsResource primer, SnapshotWatcher<ClusterSnapshot> parentWatcher,
VirtualHost virtualHost, Route route, int index, ResourceNodeType resourceNodeType) {
@Nullable VirtualHostXdsResource primer, SnapshotWatcher<ClusterSnapshot> parentWatcher,
int index, ResourceNodeType resourceNodeType) {
super(xdsBootstrap, configSource, CLUSTER, resourceName, primer, parentWatcher, resourceNodeType);
this.parentWatcher = parentWatcher;
this.virtualHost = requireNonNull(virtualHost, "virtualHost");
this.route = requireNonNull(route, "route");
this.index = index;
}

Expand All @@ -86,10 +75,16 @@ public void doOnChanged(ClusterXdsResource resource) {
children().add(node);
xdsBootstrap().subscribe(node);
} else {
parentWatcher.snapshotUpdated(new ClusterSnapshot(resource));
final ClusterSnapshot clusterSnapshot = new ClusterSnapshot(resource);
parentWatcher.snapshotUpdated(clusterSnapshot);
}
}

@Override
public void close() {
super.close();
}

private class EndpointSnapshotWatcher implements SnapshotWatcher<EndpointSnapshot> {
@Override
public void snapshotUpdated(EndpointSnapshot newSnapshot) {
Expand All @@ -100,8 +95,8 @@ public void snapshotUpdated(EndpointSnapshot newSnapshot) {
if (!Objects.equals(newSnapshot.xdsResource().primer(), current)) {
return;
}
parentWatcher.snapshotUpdated(
new ClusterSnapshot(current, newSnapshot, virtualHost, route, index));
final ClusterSnapshot clusterSnapshot = new ClusterSnapshot(current, newSnapshot, index);
parentWatcher.snapshotUpdated(clusterSnapshot);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public final class ClusterRoot extends AbstractRoot<ClusterSnapshot> {
if (cluster != null) {
node = staticCluster(xdsBootstrap, resourceName, this, cluster);
} else {
final ConfigSource configSource = configSourceMapper.cdsConfigSource(null, resourceName);
final ConfigSource configSource = configSourceMapper.cdsConfigSource(resourceName);
node = new ClusterResourceNode(configSource, resourceName, xdsBootstrap,
null, this, ResourceNodeType.DYNAMIC);
xdsBootstrap.subscribe(node);
Expand Down
45 changes: 6 additions & 39 deletions xds/src/main/java/com/linecorp/armeria/xds/ClusterSnapshot.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@
import com.linecorp.armeria.common.annotation.UnstableApi;

import io.envoyproxy.envoy.config.cluster.v3.Cluster;
import io.envoyproxy.envoy.config.route.v3.Route;
import io.envoyproxy.envoy.config.route.v3.VirtualHost;

/**
* A snapshot of a {@link Cluster} resource.
Expand All @@ -34,27 +32,17 @@ public final class ClusterSnapshot implements Snapshot<ClusterXdsResource> {
private final ClusterXdsResource clusterXdsResource;
@Nullable
private final EndpointSnapshot endpointSnapshot;
@Nullable
private final VirtualHost virtualHost;
@Nullable
private final Route route;
private final int index;

ClusterSnapshot(ClusterXdsResource clusterXdsResource, EndpointSnapshot endpointSnapshot,
@Nullable VirtualHost virtualHost, @Nullable Route route, int index) {
ClusterSnapshot(ClusterXdsResource clusterXdsResource,
@Nullable EndpointSnapshot endpointSnapshot, int index) {
this.clusterXdsResource = clusterXdsResource;
this.endpointSnapshot = endpointSnapshot;
this.virtualHost = virtualHost;
this.route = route;
this.index = index;
}

ClusterSnapshot(ClusterXdsResource clusterXdsResource) {
this.clusterXdsResource = clusterXdsResource;
endpointSnapshot = null;
virtualHost = null;
route = null;
index = -1;
this(clusterXdsResource, null, -1);
}

@Override
Expand All @@ -70,22 +58,6 @@ public EndpointSnapshot endpointSnapshot() {
return endpointSnapshot;
}

/**
* The {@link VirtualHost} this {@link Cluster} belongs to.
*/
@Nullable
public VirtualHost virtualHost() {
return virtualHost;
}

/**
* The {@link Route} this {@link Cluster} belongs to.
*/
@Nullable
public Route route() {
return route;
}

int index() {
return index;
}
Expand All @@ -99,15 +71,13 @@ public boolean equals(Object object) {
return false;
}
final ClusterSnapshot that = (ClusterSnapshot) object;
return index == that.index && Objects.equal(clusterXdsResource, that.clusterXdsResource) &&
Objects.equal(endpointSnapshot, that.endpointSnapshot) &&
Objects.equal(virtualHost, that.virtualHost) &&
Objects.equal(route, that.route);
return Objects.equal(clusterXdsResource, that.clusterXdsResource) &&
Objects.equal(endpointSnapshot, that.endpointSnapshot);
}

@Override
public int hashCode() {
return Objects.hashCode(clusterXdsResource, endpointSnapshot, virtualHost, route, index);
return Objects.hashCode(clusterXdsResource, endpointSnapshot);
}

@Override
Expand All @@ -116,9 +86,6 @@ public String toString() {
.omitNullValues()
.add("clusterXdsResource", clusterXdsResource)
.add("endpointSnapshot", endpointSnapshot)
.add("virtualHost", virtualHost)
.add("route", route)
.add("index", index)
.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ final class CompositeXdsStream implements XdsStream {
CompositeXdsStream(GrpcClientBuilder clientBuilder, Node node, Backoff backoff,
EventExecutor eventLoop, XdsResponseHandler handler,
SubscriberStorage subscriberStorage) {
for (XdsType type: XdsType.values()) {
for (XdsType type: XdsType.discoverableTypes()) {
final SotwXdsStream stream = new SotwXdsStream(
SotwDiscoveryStub.basic(type, clientBuilder), node, backoff, eventLoop,
handler, subscriberStorage, EnumSet.of(type));
Expand Down
28 changes: 3 additions & 25 deletions xds/src/main/java/com/linecorp/armeria/xds/ConfigSourceMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ final class ConfigSourceMapper {
@Nullable
private final ConfigSource bootstrapAdsConfig;
@Nullable
private ConfigSource parentConfigSource;
private final ConfigSource parentConfigSource;

ConfigSourceMapper(Bootstrap bootstrap) {
this(bootstrap, null);
Expand Down Expand Up @@ -78,18 +78,7 @@ ConfigSource edsConfigSource(@Nullable ConfigSource configSource, String resourc
throw new IllegalArgumentException("Cannot find an EDS config source for " + resourceName);
}

ConfigSource cdsConfigSource(@Nullable ConfigSource configSource, String resourceName) {
if (configSource != null) {
if (configSource.hasApiConfigSource()) {
return configSource;
}
if (configSource.hasSelf() && parentConfigSource != null) {
return parentConfigSource;
}
if (configSource.hasAds() && bootstrapAdsConfig != null) {
return bootstrapAdsConfig;
}
}
ConfigSource cdsConfigSource(String resourceName) {
if (bootstrapCdsConfig != null && bootstrapCdsConfig.hasApiConfigSource()) {
return bootstrapCdsConfig;
}
Expand All @@ -114,18 +103,7 @@ ConfigSource rdsConfigSource(@Nullable ConfigSource configSource, String resourc
throw new IllegalArgumentException("Cannot find an RDS config source for route: " + resourceName);
}

ConfigSource ldsConfigSource(@Nullable ConfigSource configSource, String resourceName) {
if (configSource != null) {
if (configSource.hasApiConfigSource()) {
return configSource;
}
if (configSource.hasSelf() && parentConfigSource != null) {
return parentConfigSource;
}
if (configSource.hasAds() && bootstrapAdsConfig != null) {
return bootstrapAdsConfig;
}
}
ConfigSource ldsConfigSource(String resourceName) {
if (bootstrapLdsConfig != null && bootstrapLdsConfig.hasApiConfigSource()) {
return bootstrapLdsConfig;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public final class ListenerRoot extends AbstractRoot<ListenerSnapshot> {
node = new ListenerResourceNode(null, resourceName, xdsBootstrap, this, ResourceNodeType.STATIC);
node.onChanged(listenerXdsResource);
} else {
final ConfigSource configSource = configSourceMapper.ldsConfigSource(null, resourceName);
final ConfigSource configSource = configSourceMapper.ldsConfigSource(resourceName);
node = new ListenerResourceNode(configSource, resourceName, xdsBootstrap,
this, ResourceNodeType.DYNAMIC);
xdsBootstrap.subscribe(node);
Expand Down
83 changes: 83 additions & 0 deletions xds/src/main/java/com/linecorp/armeria/xds/RouteEntry.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright 2025 LINE Corporation
*
* LINE Corporation licenses this file to you 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:
*
* https://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 com.linecorp.armeria.xds;

import java.util.Objects;

import com.google.common.base.MoreObjects;

import com.linecorp.armeria.common.annotation.Nullable;

import io.envoyproxy.envoy.config.route.v3.Route;
import io.envoyproxy.envoy.config.route.v3.RouteAction;

/**
* Represents a {@link Route}.
*/
public final class RouteEntry {

private final Route route;
@Nullable
private final ClusterSnapshot clusterSnapshot;

RouteEntry(Route route, @Nullable ClusterSnapshot clusterSnapshot) {
this.route = route;
this.clusterSnapshot = clusterSnapshot;
}

/**
* The {@link Route}.
*/
public Route route() {
return route;
}

/**
* The {@link ClusterSnapshot} that is represented by {@link RouteAction#getCluster()}.
* If the {@link RouteAction} does not reference a cluster, the returned value may be {@code null}.
*/
@Nullable
public ClusterSnapshot clusterSnapshot() {
return clusterSnapshot;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final RouteEntry that = (RouteEntry) o;
return Objects.equals(route, that.route) &&
Objects.equals(clusterSnapshot, that.clusterSnapshot);
}

@Override
public int hashCode() {
return Objects.hash(route, clusterSnapshot);
}

@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("route", route)
.add("clusterSnapshot", clusterSnapshot)
.toString();
}
}
Loading

0 comments on commit 0f968cc

Please sign in to comment.