Skip to content

Commit 0ecca0f

Browse files
jsuerethaabmass
andauthoredMay 25, 2021
Initial implementation of e2e tests (GoogleCloudPlatform#109)
* First stopping point. Just some scaffolding/basic compilation. * Ran code formatter. * First cut at one scenario handled. * Iniital scenario and uber-jar build. * Add dockerfile for end-to-end tests. * Add readme for e2e test. * Add cloud build configurations. * Send integer status code instead of string. * Implement basicTrace integration test. * Fix register strings. * Cleanups for review. * Update e2e-test-server/src/main/java/com/google/cloud/opentelemetry/endtoend/Server.java Co-authored-by: Aaron Abbott <aaronabbott@google.com> * Update e2e-test-server/src/main/java/com/google/cloud/opentelemetry/endtoend/Server.java Co-authored-by: Aaron Abbott <aaronabbott@google.com> * Update e2e-test-server/src/main/java/com/google/cloud/opentelemetry/endtoend/ScenarioHandlerManager.java Co-authored-by: Aaron Abbott <aaronabbott@google.com> * Update e2e-test-server/src/main/java/com/google/cloud/opentelemetry/endtoend/ScenarioHandlerManager.java Co-authored-by: Aaron Abbott <aaronabbott@google.com> * Fixes from review. * Fixes from review. * Fixes from review. Co-authored-by: Aaron Abbott <aaronabbott@google.com>
1 parent 998c9da commit 0ecca0f

17 files changed

+715
-1
lines changed
 

‎build.gradle

+2
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ subprojects {
111111
openTelemetryInstrumentationVersion = '1.0.1'
112112
junitVersion = '4.13'
113113
mockitoVersion = '3.5.10'
114+
pubSubVersion = '1.111.2'
114115
testContainersVersion = '1.15.1'
115116
wiremockVersion = '2.27.2'
116117

@@ -124,6 +125,7 @@ subprojects {
124125
google_cloud_trace_grpc : "com.google.api.grpc:grpc-google-cloud-trace-v2:${googleCloudVersion}",
125126
google_cloud_monitoring : "com.google.cloud:google-cloud-monitoring:${cloudMonitoringVersion}",
126127
google_cloud_monitoring_grpc : "com.google.cloud:grpc-google-cloud-monitoring-v3:${cloudMonitoringVersion}",
128+
google_cloud_pubsub : "com.google.cloud:google-cloud-pubsub:${pubSubVersion}",
127129
slf4j : "org.slf4j:slf4j-api:${slf4jVersion}",
128130
opentelemetry_api : "io.opentelemetry:opentelemetry-api:${openTelemetryVersion}",
129131
opentelemetry_sdk : "io.opentelemetry:opentelemetry-sdk:${openTelemetryVersion}",

‎cloudbuild-e2e-gce.yaml

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Copyright 2021 Google
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
steps:
16+
# Wait for the image to exist
17+
- name: "docker"
18+
id: wait-for-image
19+
entrypoint: "sh"
20+
timeout: 3m
21+
env: ["_TEST_SERVER_IMAGE=${_TEST_SERVER_IMAGE}"]
22+
args:
23+
- e2e-test-server/wait-for-image.sh
24+
25+
# Run the test
26+
- name: $_TEST_RUNNER_IMAGE
27+
id: run-tests-gce
28+
dir: /
29+
env: ["PROJECT_ID=$PROJECT_ID"]
30+
args:
31+
- gce
32+
- --image=$_TEST_SERVER_IMAGE
33+
34+
logsBucket: gs://opentelemetry-ops-e2e-cloud-build-logs
35+
substitutions:
36+
_TEST_RUNNER_IMAGE: gcr.io/opentelemetry-ops-e2e/opentelemetry-operations-e2e-testing:0.5.23
37+
_TEST_SERVER_IMAGE: gcr.io/${PROJECT_ID}/opentelemetry-operations-java-e2e-test-server:${SHORT_SHA}

‎cloudbuild-e2e-gke.yaml

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Copyright 2021 Google
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0 #
8+
# Unless required by applicable law or agreed to in writing, software
9+
# distributed under the License is distributed on an "AS IS" BASIS,
10+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
# See the License for the specific language governing permissions and
12+
# limitations under the License.
13+
14+
steps:
15+
# Wait for the image to exist
16+
- name: "docker"
17+
id: wait-for-image
18+
entrypoint: "sh"
19+
timeout: 3m
20+
env: ["_TEST_SERVER_IMAGE=${_TEST_SERVER_IMAGE}"]
21+
args:
22+
- e2e-test-server/wait-for-image.sh
23+
24+
# Run the test
25+
- name: $_TEST_RUNNER_IMAGE
26+
id: run-tests-gke
27+
dir: /
28+
env: ["PROJECT_ID=$PROJECT_ID"]
29+
args:
30+
- gke
31+
- --image=$_TEST_SERVER_IMAGE
32+
33+
logsBucket: gs://opentelemetry-ops-e2e-cloud-build-logs
34+
substitutions:
35+
_TEST_RUNNER_IMAGE: gcr.io/opentelemetry-ops-e2e/opentelemetry-operations-e2e-testing:0.5.23
36+
_TEST_SERVER_IMAGE: gcr.io/${PROJECT_ID}/opentelemetry-operations-java-e2e-test-server:${SHORT_SHA}

‎cloudbuild-e2e-image.yaml

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Copyright 2021 Google
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
steps:
16+
# If the image doesn't exist, create a skip file for the next step to know
17+
- name: "gcr.io/cloud-builders/gcloud"
18+
entrypoint: "bash"
19+
args:
20+
- -c
21+
- |
22+
existing_tags=$(
23+
gcloud container images list-tags \
24+
--filter="tags:${SHORT_SHA}" --format=json \
25+
${_TEST_SERVER_IMAGE_NAME}
26+
)
27+
28+
if [ "$existing_tags" == "[]" ]; then
29+
echo "Image doesn't exist, will build it"
30+
else
31+
echo "Image already exists, will skip building"
32+
touch skip
33+
fi
34+
35+
# If skip doesn't exist, build and push
36+
- name: docker
37+
id: build-test-server
38+
entrypoint: "sh"
39+
args:
40+
- -c
41+
- |
42+
if [ -e "skip" ]; then
43+
return
44+
else
45+
docker build --tag=${_TEST_SERVER_IMAGE} --file=e2e.Dockerfile .
46+
docker push ${_TEST_SERVER_IMAGE}
47+
fi
48+
49+
logsBucket: gs://opentelemetry-ops-e2e-cloud-build-logs
50+
substitutions:
51+
_TEST_SERVER_IMAGE: ${_TEST_SERVER_IMAGE_NAME}:${SHORT_SHA}
52+
_TEST_SERVER_IMAGE_NAME: gcr.io/${PROJECT_ID}/opentelemetry-operations-java-e2e-test-server

‎cloudbuild-e2e-local.yaml

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Copyright 2021 Google
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
steps:
16+
# Wait for the image to exist
17+
- name: "docker"
18+
id: wait-for-image
19+
entrypoint: "sh"
20+
timeout: 3m
21+
env: ["_TEST_SERVER_IMAGE=${_TEST_SERVER_IMAGE}"]
22+
args:
23+
- e2e-test-server/wait-for-image.sh
24+
25+
# Run the test
26+
- name: $_TEST_RUNNER_IMAGE
27+
id: run-tests-local
28+
dir: /
29+
env: ["PROJECT_ID=$PROJECT_ID"]
30+
args:
31+
- local
32+
- --image=$_TEST_SERVER_IMAGE
33+
- --network=cloudbuild
34+
35+
logsBucket: gs://opentelemetry-ops-e2e-cloud-build-logs
36+
substitutions:
37+
_TEST_RUNNER_IMAGE: gcr.io/opentelemetry-ops-e2e/opentelemetry-operations-e2e-testing:0.5.23
38+
_TEST_SERVER_IMAGE: gcr.io/${PROJECT_ID}/opentelemetry-operations-java-e2e-test-server:${SHORT_SHA}

‎e2e-test-server/README.md

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# End to End tests
2+
3+
A set of end to end integration tests. These are run inside various environments to ensure
4+
GCP automagiks work and data flows into google cloud.
5+
6+
7+
## Building a Docker image.
8+
9+
From the *root* directory of the projet, run:
10+
11+
```
12+
docker build . --file=e2e.Dockerfile
13+
```

‎e2e-test-server/build.gradle

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright 2021 Google
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
plugins {
17+
id 'java'
18+
id 'application'
19+
id 'com.github.johnrengelman.shadow'
20+
}
21+
22+
application {
23+
mainClassName = 'com.google.cloud.opentelemetry.endtoend.Server'
24+
}
25+
26+
description = 'End-To-End integration testing server'
27+
28+
dependencies {
29+
compile(libraries.opentelemetry_api)
30+
compile(libraries.opentelemetry_sdk)
31+
compile(libraries.google_cloud_trace)
32+
compile(libraries.google_cloud_pubsub)
33+
compile project(':exporter-trace')
34+
runtimeOnly project(':detector-resources')
35+
}
36+
37+
tasks.build.dependsOn tasks.shadowJar

‎e2e-test-server/gradle.properties

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
release.enabled=false
2+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright 2021 Google
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.cloud.opentelemetry.endtoend;
17+
18+
import com.google.pubsub.v1.ProjectSubscriptionName;
19+
import com.google.pubsub.v1.ProjectTopicName;
20+
21+
/** Constants we use in this test. Note: Some are pulled from the env. */
22+
public class Constants {
23+
24+
public static String INSTRUMENTING_MODULE_NAME = "opentelemetry-ops-e2e-test-server";
25+
public static String SCENARIO = "scenario";
26+
public static String STATUS_CODE = "status_code";
27+
public static String TEST_ID = "test_id";
28+
29+
// TODO: Add good error messages below.
30+
public static String SUBCRIPTION_MODE = System.getenv().getOrDefault("SUBSCRIPTION_MODE", "");
31+
public static String PROJECT_ID = System.getenv().getOrDefault("PROJECT_ID", "");
32+
public static String REQUEST_SUBSCRIPTION_NAME =
33+
System.getenv().getOrDefault("REQUEST_SUBSCRIPTION_NAME", "");
34+
public static String RESPONSE_TOPIC_NAME =
35+
System.getenv().getOrDefault("RESPONSE_TOPIC_NAME", "");
36+
37+
public static ProjectSubscriptionName getRequestSubscription() {
38+
return ProjectSubscriptionName.of(PROJECT_ID, REQUEST_SUBSCRIPTION_NAME);
39+
}
40+
41+
/** @return The pubsub channel we get test requests from. */
42+
public static ProjectTopicName getResponseTopic() {
43+
return ProjectTopicName.of(PROJECT_ID, RESPONSE_TOPIC_NAME);
44+
}
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright 2021 Google
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.cloud.opentelemetry.endtoend;
17+
18+
import com.google.protobuf.ByteString;
19+
import java.util.Map;
20+
21+
/** Abstraction of an "RPC Request" in our e2e integration test. */
22+
public interface Request {
23+
/** The test scenario requested. */
24+
String testId();
25+
26+
/** Text map headers (for eventual trace propagation). */
27+
Map<String, String> headers();
28+
29+
/** Incoming message data. */
30+
ByteString data();
31+
32+
static Request make(
33+
final String testId, final Map<String, String> headers, final ByteString data) {
34+
return new Request() {
35+
public String testId() {
36+
return testId;
37+
}
38+
39+
public Map<String, String> headers() {
40+
return headers;
41+
}
42+
43+
public ByteString data() {
44+
return data;
45+
}
46+
};
47+
}
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright 2021 Google
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.cloud.opentelemetry.endtoend;
17+
18+
import com.google.api.gax.rpc.StatusCode.Code;
19+
import com.google.protobuf.ByteString;
20+
21+
/**
22+
* An "RPC Response", generified.
23+
*
24+
* <p>This class exposes the minimum API to write rpc-like integration tests.
25+
*/
26+
public interface Response {
27+
/**
28+
* Status code associate with response.
29+
*
30+
* <p>If this is `OK`, then data will be empty.
31+
*/
32+
Code statusCode();
33+
/** string explanation of error codes. */
34+
ByteString data();
35+
36+
static Response make(final Code code, final ByteString data) {
37+
return new Response() {
38+
public Code statusCode() {
39+
return code;
40+
}
41+
42+
public ByteString data() {
43+
return data;
44+
}
45+
};
46+
}
47+
48+
public static Response internalError(Throwable t) {
49+
return make(Code.INTERNAL, ByteString.copyFromUtf8(t.toString()));
50+
}
51+
52+
public static Response invalidArgument(String message) {
53+
return make(Code.INVALID_ARGUMENT, ByteString.copyFromUtf8(message));
54+
}
55+
56+
public static Response unimplemented(String message) {
57+
return make(Code.UNIMPLEMENTED, ByteString.copyFromUtf8(message));
58+
}
59+
60+
public static Response ok() {
61+
return make(Code.OK, ByteString.EMPTY);
62+
}
63+
64+
public static Response EMPTY = make(Code.UNKNOWN, ByteString.EMPTY);
65+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright 2021 Google
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.cloud.opentelemetry.endtoend;
17+
18+
/** A handler for testing scenarios. */
19+
public interface ScenarioHandler {
20+
/** Handles a given tracing scenario request. Failures should be thrown. */
21+
public Response handle(Request request);
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* Copyright 2021 Google
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.cloud.opentelemetry.endtoend;
17+
18+
import com.google.cloud.opentelemetry.trace.TraceConfiguration;
19+
import com.google.cloud.opentelemetry.trace.TraceExporter;
20+
import io.opentelemetry.api.trace.Span;
21+
import io.opentelemetry.api.trace.Tracer;
22+
import io.opentelemetry.sdk.OpenTelemetrySdk;
23+
import io.opentelemetry.sdk.trace.SdkTracerProvider;
24+
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
25+
import java.io.IOException;
26+
import java.time.Duration;
27+
import java.util.HashMap;
28+
import java.util.Map;
29+
import java.util.function.Function;
30+
31+
/**
32+
* A container for all scenarios we handle.
33+
*
34+
* <p>We "dependency inject" scenarios into the constructor.
35+
*/
36+
public class ScenarioHandlerManager {
37+
private Map<String, ScenarioHandler> scenarios = new HashMap<>();
38+
39+
public ScenarioHandlerManager() {
40+
register("/health", this::health);
41+
register("/basicTrace", this::basicTrace);
42+
}
43+
44+
/** Health check test. */
45+
private Response health(Request request) {
46+
return Response.ok();
47+
}
48+
49+
/** Basic trace test. */
50+
private Response basicTrace(Request request) {
51+
return withTemporaryTracer(
52+
(tracer) -> {
53+
Span span =
54+
tracer
55+
.spanBuilder("basicTrace")
56+
.setAttribute(Constants.TEST_ID, request.testId())
57+
.startSpan();
58+
try {
59+
return Response.ok();
60+
} finally {
61+
span.end();
62+
}
63+
});
64+
}
65+
66+
/** Default test scenario runner for unknown test cases. */
67+
private Response unimplemented(Request request) {
68+
return Response.unimplemented("UNhandled request: " + request.testId());
69+
}
70+
71+
/** Registers test scenario "urls" with handlers for the requests. */
72+
private void register(String scenario, ScenarioHandler handler) {
73+
scenarios.putIfAbsent(scenario, handler);
74+
}
75+
76+
/** Handles a test scenario, or use `unimplemented`. */
77+
public Response handleScenario(String scenario, Request request) {
78+
ScenarioHandler handler = scenarios.getOrDefault(scenario, this::unimplemented);
79+
return handler.handle(request);
80+
}
81+
82+
/**
83+
* Helper to configure an OTel SDK exporting to cloud trace within the context of a function call.
84+
*/
85+
private static <R> R withTemporaryTracer(Function<Tracer, R> handler) {
86+
try {
87+
OpenTelemetrySdk sdk = setupTraceExporter();
88+
try {
89+
Tracer tracer = sdk.getTracer(Constants.INSTRUMENTING_MODULE_NAME);
90+
return handler.apply(tracer);
91+
} finally {
92+
sdk.getSdkTracerProvider().shutdown();
93+
}
94+
} catch (IOException e) {
95+
// Lift checked exception into runtime to feed through lambda impls and make it out to
96+
// test status.
97+
throw new RuntimeException(e);
98+
}
99+
}
100+
101+
/** Set up an OpenTelemetrySDK w/ export to google cloud. */
102+
private static OpenTelemetrySdk setupTraceExporter() throws IOException {
103+
// Using default project ID and Credentials
104+
TraceConfiguration configuration =
105+
TraceConfiguration.builder().setDeadline(Duration.ofMillis(30000)).build();
106+
107+
TraceExporter traceExporter = TraceExporter.createWithConfiguration(configuration);
108+
// Register the TraceExporter with OpenTelemetry
109+
return OpenTelemetrySdk.builder()
110+
.setTracerProvider(
111+
SdkTracerProvider.builder()
112+
.addSpanProcessor(BatchSpanProcessor.builder(traceExporter).build())
113+
.build())
114+
.build();
115+
}
116+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/*
2+
* Copyright 2021 Google
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.cloud.opentelemetry.endtoend;
17+
18+
import com.google.api.core.ApiFuture;
19+
import com.google.api.core.ApiFutureCallback;
20+
import com.google.api.core.ApiFutures;
21+
import com.google.cloud.pubsub.v1.AckReplyConsumer;
22+
import com.google.cloud.pubsub.v1.Publisher;
23+
import com.google.cloud.pubsub.v1.Subscriber;
24+
import com.google.common.util.concurrent.MoreExecutors;
25+
import com.google.pubsub.v1.PubsubMessage;
26+
27+
/**
28+
* Server implements the "integration test driver" for this language.
29+
*
30+
* <p>It is responsible for the following:
31+
*
32+
* <ul>
33+
* <li>Setting up a subscriber queue for inbound "RPC Request" messages
34+
* <li>Converting incoming pub sub messages to {@link Request}
35+
* <li>Setting up a publisher queue for outbound "RPC Response" messages
36+
* <li>Converting from outbound {@link Response} to pubsub messages.
37+
* <li>Handling any/all failures escaping the test scenario.
38+
* </ul>
39+
*
40+
* <p>This class includes a main method which runs the integration test driver using locally
41+
* available credentials to acccess pubsub channels.
42+
*/
43+
public class Server implements AutoCloseable {
44+
private final ScenarioHandlerManager scenarioHandlers = new ScenarioHandlerManager();
45+
private final Publisher publisher;
46+
private final Subscriber subscriber;
47+
48+
public Server() throws Exception {
49+
this.publisher = Publisher.newBuilder(Constants.getResponseTopic()).build();
50+
this.subscriber =
51+
Subscriber.newBuilder(Constants.getRequestSubscription(), this::handleMessage).build();
52+
subscriber.addListener(
53+
new Subscriber.Listener() {
54+
@Override
55+
public void failed(Subscriber.State from, Throwable failure) {
56+
// Handle failure. This is called when the Subscriber encountered a fatal error and is
57+
// shutting down.
58+
System.err.println(failure);
59+
}
60+
},
61+
MoreExecutors.directExecutor());
62+
}
63+
64+
/** Starts the subcriber pulling requests. */
65+
public void start() {
66+
subscriber.startAsync().awaitRunning();
67+
}
68+
69+
/** Closes our subscriptions. */
70+
public void close() {
71+
if (subscriber != null) {
72+
subscriber.stopAsync();
73+
subscriber.awaitTerminated();
74+
}
75+
if (publisher != null) {
76+
publisher.shutdown();
77+
}
78+
}
79+
80+
/** This method converts from {@link Response} to pubsub and sends out the publisher channel. */
81+
public void respond(final String testId, final Response response) {
82+
final PubsubMessage message =
83+
PubsubMessage.newBuilder()
84+
.putAttributes(Constants.TEST_ID, testId)
85+
.putAttributes(Constants.STATUS_CODE, Integer.toString(response.statusCode().ordinal()))
86+
.setData(response.data())
87+
.build();
88+
ApiFuture<String> messageIdFuture = publisher.publish(message);
89+
ApiFutures.addCallback(
90+
messageIdFuture,
91+
new ApiFutureCallback<String>() {
92+
public void onSuccess(String messageId) {}
93+
94+
public void onFailure(Throwable t) {
95+
System.out.println("failed to publish response to test: " + testId);
96+
t.printStackTrace();
97+
}
98+
},
99+
MoreExecutors.directExecutor());
100+
}
101+
102+
/** Execute a scenario based on the incoming message from the test runner. */
103+
public void handleMessage(PubsubMessage message, AckReplyConsumer consumer) {
104+
if (!message.containsAttributes(Constants.TEST_ID)) {
105+
consumer.nack();
106+
return;
107+
}
108+
String testId = message.getAttributesOrDefault(Constants.TEST_ID, "");
109+
if (!message.containsAttributes(Constants.SCENARIO)) {
110+
respond(
111+
testId,
112+
Response.invalidArgument(
113+
String.format("Expected attribute \"%s\" is missing", Constants.SCENARIO)));
114+
consumer.ack();
115+
return;
116+
}
117+
String scenario = message.getAttributesOrDefault(Constants.SCENARIO, "");
118+
Request request = Request.make(testId, message.getAttributesMap(), message.getData());
119+
120+
// Run the given request/response cycle through a handler and respond with results.
121+
Response response = Response.EMPTY;
122+
try {
123+
response = scenarioHandlers.handleScenario(scenario, request);
124+
} catch (Throwable e) {
125+
response = Response.internalError(e);
126+
} finally {
127+
respond(testId, response);
128+
consumer.ack();
129+
}
130+
}
131+
132+
/** Runs our server. */
133+
public static void main(String[] args) throws Exception {
134+
try (Server server = new Server()) {
135+
server.start();
136+
// Docs for Subscriber recommend doing this to block main thread while daemon thread consumes
137+
// stuff.
138+
for (; ; ) {
139+
Thread.sleep(Long.MAX_VALUE);
140+
}
141+
}
142+
}
143+
}

‎e2e-test-server/wait-for-image.sh

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/bin/sh
2+
#
3+
# Copyright 2021 Google
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# https://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
while true; do
18+
docker pull $_TEST_SERVER_IMAGE
19+
pull_success=$?
20+
21+
if [ $pull_success -ne 0 ]; then
22+
echo "Image couldn't be pulled yet, will continue to retry"
23+
else
24+
echo "Image pulled successfully, continuing onto test"
25+
break
26+
fi
27+
sleep 5
28+
done

‎e2e.Dockerfile

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
2+
# Copyright 2021 Google
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# https://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
# Build relative to root of repository i.e. `docker build --file e2e.Dockerfile --tag=$tag ..`
17+
FROM gradle:6.9-jdk11-hotspot as builder
18+
19+
COPY --chown=gradle:gradle . /app/src
20+
WORKDIR /app/src
21+
RUN gradle :e2e-test-server:build
22+
23+
FROM openjdk:11-jre-slim
24+
COPY --from=builder /app/src/e2e-test-server/build/libs/*-all.jar /app/app.jar
25+
WORKDIR /app
26+
CMD java -jar app.jar

‎settings.gradle

+5-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ include ":exporter-metrics"
2929
include ":examples-metrics"
3030
include ":exporter-auto"
3131
include ":detector-resources"
32+
include ":e2e-test-server"
3233

3334
project(':exporter-trace').projectDir =
3435
"$rootDir/exporters/trace" as File
@@ -46,4 +47,7 @@ project(':exporter-auto').projectDir =
4647
"$rootDir/exporters/auto" as File
4748

4849
project(':detector-resources').projectDir =
49-
"$rootDir/detectors/resources" as File
50+
"$rootDir/detectors/resources" as File
51+
52+
project(':e2e-test-server').projectDir =
53+
"$rootDir/e2e-test-server" as File

0 commit comments

Comments
 (0)
Please sign in to comment.