Skip to content

Commit 6ece50c

Browse files
authored
test: Initial ADS integration test (#42)
Signed-off-by: Joey Bratton <[email protected]>
1 parent b412429 commit 6ece50c

File tree

14 files changed

+453
-7
lines changed

14 files changed

+453
-7
lines changed

.circleci/config.yml

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
version: 2
22
jobs:
33
build:
4-
docker:
5-
- image: circleci/openjdk:8-jdk
4+
machine: true
65

76
working_directory: ~/repo
87

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,6 @@ dependency-reduced-pom.xml
2222
buildNumber.properties
2323
.mvn/timing.properties
2424
.testcontainers*
25+
26+
# Mac
27+
.DS_Store

buildscripts/checkstyle.xml

+1
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@
184184
<module name="AbbreviationAsWordInName">
185185
<property name="ignoreFinal" value="false"/>
186186
<property name="allowedAbbreviationLength" value="1"/>
187+
<property name="allowedAbbreviations" value="IT"/>
187188
</module>
188189
<module name="OverloadMethodsDeclarationOrder"/>
189190
<module name="VariableDeclarationUsageDistance"/>

cache/src/main/java/io/envoyproxy/controlplane/cache/TestResources.java

+24-3
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,11 @@
3838
@VisibleForTesting
3939
public class TestResources {
4040

41-
private static final String LOCALHOST = "127.0.0.1";
41+
private static final String ANY_ADDRESS = "0.0.0.0";
42+
private static final String LOCALHOST = "127.0.0.1";
4243

4344
/**
44-
* Returns a new test cluster.
45+
* Returns a new test cluster using EDS.
4546
*
4647
* @param clusterName name of the new cluster
4748
*/
@@ -60,6 +61,26 @@ public static Cluster createCluster(String clusterName) {
6061
.build();
6162
}
6263

64+
/**
65+
* Returns a new test cluster not using EDS.
66+
*
67+
* @param clusterName name of the new cluster
68+
* @param address address to use for the cluster endpoint
69+
* @param port port to use for the cluster endpoint
70+
*/
71+
public static Cluster createCluster(String clusterName, String address, int port) {
72+
return Cluster.newBuilder()
73+
.setName(clusterName)
74+
.setConnectTimeout(Durations.fromSeconds(5))
75+
.setType(DiscoveryType.STRICT_DNS)
76+
.addHosts(Address.newBuilder()
77+
.setSocketAddress(SocketAddress.newBuilder()
78+
.setAddress(address)
79+
.setPortValue(port)
80+
.setProtocolValue(Protocol.TCP_VALUE)))
81+
.build();
82+
}
83+
6384
/**
6485
* Returns a new test endpoint for the given cluster.
6586
*
@@ -106,7 +127,7 @@ public static Listener createListener(String listenerName, int port, String rout
106127
.setName(listenerName)
107128
.setAddress(Address.newBuilder()
108129
.setSocketAddress(SocketAddress.newBuilder()
109-
.setAddress(LOCALHOST)
130+
.setAddress(ANY_ADDRESS)
110131
.setPortValue(port)
111132
.setProtocol(Protocol.TCP)))
112133
.addFilterChains(FilterChain.newBuilder()

pom.xml

+26-2
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,28 @@
2323
<!-- Dependency Versions -->
2424
<assertj.version>3.9.0</assertj.version>
2525
<auto-value.version>1.5.3</auto-value.version>
26+
<awaitility.version>3.1.0</awaitility.version>
2627
<checkstyle.version>8.8</checkstyle.version>
2728
<grpc.version>1.10.0</grpc.version>
2829
<guava.version>24.1-jre</guava.version>
2930
<junit.version>4.12</junit.version>
3031
<reactor.version>3.1.3.RELEASE</reactor.version>
32+
<rest-assured.version>3.0.7</rest-assured.version>
3133
<slf4j.version>1.7.25</slf4j.version>
34+
<testcontainers.version>1.7.0</testcontainers.version>
3235

3336
<!-- Maven Plugin Versions -->
3437
<jacoco-maven-plugin.version>0.8.0</jacoco-maven-plugin.version>
3538
<maven-checkstyle-plugin.version>3.0.0</maven-checkstyle-plugin.version>
39+
<maven-failsafe-plugin.version>2.21.0</maven-failsafe-plugin.version>
3640
<maven-gpg-plugin.version>1.6</maven-gpg-plugin.version>
3741
<maven-javadoc-plugin.version>3.0.0</maven-javadoc-plugin.version>
3842
<maven-release-plugin.version>2.5.3</maven-release-plugin.version>
3943
<maven-source-plugin.version>3.0.1</maven-source-plugin.version>
4044
<nexus-staging-maven-plugin.version>1.6.8</nexus-staging-maven-plugin.version>
45+
46+
<jacoco.report-dir>${project.build.directory}/coverage-reports</jacoco.report-dir>
47+
<jacoco.report-dir.data-file>${jacoco.output}/jacoco-merged.exec</jacoco.report-dir.data-file>
4148
</properties>
4249

4350
<name>java-control-plane</name>
@@ -175,16 +182,33 @@
175182
<version>${jacoco-maven-plugin.version}</version>
176183
<executions>
177184
<execution>
185+
<id>jacoco-prepare-ut-agent</id>
178186
<goals>
179187
<goal>prepare-agent</goal>
180188
</goals>
189+
<configuration>
190+
<destFile>${jacoco.report-dir.data-file}</destFile>
191+
</configuration>
181192
</execution>
182193
<execution>
183-
<id>report</id>
184-
<phase>prepare-package</phase>
194+
<id>jacoco-prepare-it-agent</id>
195+
<goals>
196+
<goal>prepare-agent-integration</goal>
197+
</goals>
198+
<configuration>
199+
<destFile>${jacoco.report-dir.data-file}</destFile>
200+
<append>true</append>
201+
</configuration>
202+
</execution>
203+
<execution>
204+
<id>jacoco-report</id>
185205
<goals>
186206
<goal>report</goal>
187207
</goals>
208+
<configuration>
209+
<dataFile>${jacoco.report-dir.data-file}</dataFile>
210+
<outputDirectory>${jacoco.report-dir}</outputDirectory>
211+
</configuration>
188212
</execution>
189213
</executions>
190214
</plugin>

server/pom.xml

+42
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,54 @@
3131
<scope>test</scope>
3232
</dependency>
3333

34+
<dependency>
35+
<groupId>io.rest-assured</groupId>
36+
<artifactId>rest-assured</artifactId>
37+
<version>${rest-assured.version}</version>
38+
<scope>test</scope>
39+
</dependency>
40+
41+
<dependency>
42+
<groupId>org.awaitility</groupId>
43+
<artifactId>awaitility</artifactId>
44+
<version>${awaitility.version}</version>
45+
<scope>test</scope>
46+
</dependency>
47+
3448
<dependency>
3549
<groupId>org.slf4j</groupId>
3650
<artifactId>slf4j-simple</artifactId>
3751
<version>${slf4j.version}</version>
3852
<scope>test</scope>
3953
</dependency>
54+
55+
<dependency>
56+
<groupId>org.testcontainers</groupId>
57+
<artifactId>testcontainers</artifactId>
58+
<version>${testcontainers.version}</version>
59+
<scope>test</scope>
60+
</dependency>
4061
</dependencies>
4162

63+
<build>
64+
<plugins>
65+
<plugin>
66+
<groupId>org.apache.maven.plugins</groupId>
67+
<artifactId>maven-failsafe-plugin</artifactId>
68+
<version>${maven-failsafe-plugin.version}</version>
69+
<configuration>
70+
<parallel>methods</parallel>
71+
<threadCount>10</threadCount>
72+
</configuration>
73+
<executions>
74+
<execution>
75+
<goals>
76+
<goal>integration-test</goal>
77+
<goal>verify</goal>
78+
</goals>
79+
</execution>
80+
</executions>
81+
</plugin>
82+
</plugins>
83+
</build>
4284
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package io.envoyproxy.controlplane.server;
2+
3+
import static io.envoyproxy.controlplane.server.TestSnapshots.createSnapshot;
4+
import static io.restassured.RestAssured.given;
5+
import static org.assertj.core.api.Assertions.assertThat;
6+
import static org.awaitility.Awaitility.await;
7+
import static org.hamcrest.Matchers.containsString;
8+
9+
import envoy.api.v2.Discovery.DiscoveryRequest;
10+
import envoy.api.v2.Discovery.DiscoveryResponse;
11+
import io.envoyproxy.controlplane.cache.SimpleCache;
12+
import io.grpc.netty.NettyServerBuilder;
13+
import io.restassured.http.ContentType;
14+
import java.util.concurrent.CountDownLatch;
15+
import java.util.concurrent.TimeUnit;
16+
import org.junit.ClassRule;
17+
import org.junit.Test;
18+
import org.junit.rules.RuleChain;
19+
import org.testcontainers.containers.Network;
20+
21+
public class DiscoveryServerAdsIT {
22+
23+
private static final String CONFIG = "envoy/ads.config.yaml";
24+
private static final String GROUP = "key";
25+
private static final Integer LISTENER_PORT = 10000;
26+
27+
private static final CountDownLatch onStreamOpenLatch = new CountDownLatch(1);
28+
private static final CountDownLatch onStreamRequestLatch = new CountDownLatch(1);
29+
private static final CountDownLatch onStreamResponseLatch = new CountDownLatch(1);
30+
31+
private static final NettyGrpcServerRule ADS = new NettyGrpcServerRule() {
32+
@Override
33+
protected void configureServerBuilder(NettyServerBuilder builder) {
34+
final SimpleCache<String> cache = new SimpleCache<>(true, node -> GROUP);
35+
36+
final DiscoveryServerCallbacks callbacks = new DiscoveryServerCallbacks() {
37+
@Override
38+
public void onStreamOpen(long streamId, String typeUrl) {
39+
onStreamOpenLatch.countDown();
40+
}
41+
42+
@Override
43+
public void onStreamRequest(long streamId, DiscoveryRequest request) {
44+
onStreamRequestLatch.countDown();
45+
}
46+
47+
@Override
48+
public void onStreamResponse(long streamId, DiscoveryRequest request, DiscoveryResponse response) {
49+
onStreamResponseLatch.countDown();
50+
}
51+
};
52+
53+
cache.setSnapshot(
54+
GROUP,
55+
createSnapshot("upstream", "upstream", EchoContainer.PORT, "listener0", LISTENER_PORT, "route0", "1"));
56+
57+
DiscoveryServer server = new DiscoveryServer(callbacks, cache);
58+
59+
builder.addService(server.getAggregatedDiscoveryServiceImpl());
60+
}
61+
};
62+
63+
private static final Network NETWORK = Network.newNetwork();
64+
65+
private static final EnvoyContainer ENVOY = new EnvoyContainer(CONFIG, () -> ADS.getServer().getPort())
66+
.withExposedPorts(LISTENER_PORT)
67+
.withNetwork(NETWORK);
68+
69+
private static final EchoContainer UPSTREAM = new EchoContainer()
70+
.withNetwork(NETWORK)
71+
.withNetworkAliases("upstream");
72+
73+
@ClassRule
74+
public static final RuleChain RULES = RuleChain.outerRule(UPSTREAM)
75+
.around(ADS)
76+
.around(ENVOY);
77+
78+
@Test
79+
public void validateTestRequestToEchoServerViaEnvoy() throws InterruptedException {
80+
assertThat(onStreamOpenLatch.await(15, TimeUnit.SECONDS)).isTrue()
81+
.overridingErrorMessage("failed to open ADS stream");
82+
83+
assertThat(onStreamRequestLatch.await(15, TimeUnit.SECONDS)).isTrue()
84+
.overridingErrorMessage("failed to receive ADS request");
85+
86+
assertThat(onStreamResponseLatch.await(15, TimeUnit.SECONDS)).isTrue()
87+
.overridingErrorMessage("failed to send ADS response");
88+
89+
String baseUri = String.format("http://%s:%d", ENVOY.getContainerIpAddress(), ENVOY.getMappedPort(LISTENER_PORT));
90+
91+
await().atMost(5, TimeUnit.SECONDS).ignoreExceptions().untilAsserted(
92+
() -> given().baseUri(baseUri).contentType(ContentType.TEXT)
93+
.when().get("/")
94+
.then().statusCode(200)
95+
.and().body(containsString(UPSTREAM.response)));
96+
}
97+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package io.envoyproxy.controlplane.server;
2+
3+
import java.util.UUID;
4+
import org.testcontainers.containers.GenericContainer;
5+
import org.testcontainers.containers.wait.strategy.Wait;
6+
7+
class EchoContainer extends GenericContainer<EchoContainer> {
8+
9+
public static final Integer PORT = 5678;
10+
11+
public final String response = UUID.randomUUID().toString();
12+
13+
EchoContainer() {
14+
super("hashicorp/http-echo:latest");
15+
}
16+
17+
@Override
18+
protected void configure() {
19+
super.configure();
20+
21+
getExposedPorts().add(0, PORT);
22+
23+
withCommand(String.format("-text=%s", response));
24+
25+
waitingFor(Wait.forHttp("/").forStatusCode(200));
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package io.envoyproxy.controlplane.server;
2+
3+
import com.github.dockerjava.api.command.InspectContainerResponse;
4+
import java.util.function.Supplier;
5+
import org.slf4j.Logger;
6+
import org.slf4j.LoggerFactory;
7+
import org.testcontainers.containers.BindMode;
8+
import org.testcontainers.containers.GenericContainer;
9+
import org.testcontainers.containers.output.Slf4jLogConsumer;
10+
11+
class EnvoyContainer extends GenericContainer<EnvoyContainer> {
12+
13+
private static final Logger LOGGER = LoggerFactory.getLogger(EnvoyContainer.class);
14+
15+
private static final String CONFIG_DEST = "/etc/envoy/envoy.yaml";
16+
private static final String HOST_IP_SCRIPT = "docker/host_ip.sh";
17+
private static final String HOST_IP_SCRIPT_DEST = "/usr/local/bin/host_ip.sh";
18+
private static final String LAUNCH_ENVOY_SCRIPT = "envoy/launch_envoy.sh";
19+
private static final String LAUNCH_ENVOY_SCRIPT_DEST = "/usr/local/bin/launch_envoy.sh";
20+
21+
static final int ADMIN_PORT = 9901;
22+
23+
private final String config;
24+
private final Supplier<Integer> controlPlanePortSupplier;
25+
26+
EnvoyContainer(String config, Supplier<Integer> controlPlanePortSupplier) {
27+
super("envoyproxy/envoy-alpine:latest");
28+
29+
this.config = config;
30+
this.controlPlanePortSupplier = controlPlanePortSupplier;
31+
}
32+
33+
@Override
34+
protected void configure() {
35+
super.configure();
36+
37+
withClasspathResourceMapping(HOST_IP_SCRIPT, HOST_IP_SCRIPT_DEST, BindMode.READ_ONLY);
38+
withClasspathResourceMapping(LAUNCH_ENVOY_SCRIPT, LAUNCH_ENVOY_SCRIPT_DEST, BindMode.READ_ONLY);
39+
withClasspathResourceMapping(config, CONFIG_DEST, BindMode.READ_ONLY);
40+
41+
withCommand(
42+
"/bin/sh", "/usr/local/bin/launch_envoy.sh",
43+
Integer.toString(controlPlanePortSupplier.get()),
44+
CONFIG_DEST,
45+
"-l", "debug");
46+
47+
getExposedPorts().add(0, ADMIN_PORT);
48+
}
49+
50+
@Override
51+
protected void containerIsStarting(InspectContainerResponse containerInfo) {
52+
followOutput(new Slf4jLogConsumer(LOGGER).withPrefix("ENVOY"));
53+
54+
super.containerIsStarting(containerInfo);
55+
}
56+
}

0 commit comments

Comments
 (0)