Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add HTTP response span for grid status. Add tracing for GraphqlHandler. #8908

Merged
merged 2 commits into from
Nov 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion java/server/src/org/openqa/selenium/grid/commands/Hub.java
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ protected Handlers createHandlers(Config config) {
handler.addHandler(distributor);

Router router = new Router(tracer, clientFactory, sessions, queuer, distributor);
GraphqlHandler graphqlHandler = new GraphqlHandler(distributor, serverOptions.getExternalUri());
GraphqlHandler graphqlHandler = new GraphqlHandler(tracer, distributor, serverOptions.getExternalUri());
HttpHandler readinessCheck = req -> {
boolean ready = router.isReady() && bus.isReady();
return new HttpResponse()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ protected Handlers createHandlers(Config config) {
.setContent(Contents.utf8String("Standalone is " + ready));
};

GraphqlHandler graphqlHandler = new GraphqlHandler(distributor, serverOptions.getExternalUri());
GraphqlHandler graphqlHandler = new GraphqlHandler(tracer, distributor, serverOptions.getExternalUri());

Routable ui;
URL uiRoot = getClass().getResource("/javascript/grid-ui/build");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ java_library(
"//java/client/src/org/openqa/selenium:core",
"//java/client/src/org/openqa/selenium/json",
"//java/client/src/org/openqa/selenium/remote/http",
"//java/client/src/org/openqa/selenium/remote",
"//java/server/src/org/openqa/selenium/grid/data",
"//java/server/src/org/openqa/selenium/grid/distributor",
artifact("com.google.guava:guava"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,18 @@
import graphql.schema.idl.SchemaParser;
import graphql.schema.idl.TypeDefinitionRegistry;
import org.openqa.selenium.grid.distributor.Distributor;
import org.openqa.selenium.internal.Require;
import org.openqa.selenium.json.Json;
import org.openqa.selenium.remote.http.Contents;
import org.openqa.selenium.remote.http.HttpHandler;
import org.openqa.selenium.remote.http.HttpMethod;
import org.openqa.selenium.remote.http.HttpRequest;
import org.openqa.selenium.remote.http.HttpResponse;
import org.openqa.selenium.remote.tracing.AttributeKey;
import org.openqa.selenium.remote.tracing.EventAttribute;
import org.openqa.selenium.remote.tracing.EventAttributeValue;
import org.openqa.selenium.remote.tracing.Span;
import org.openqa.selenium.remote.tracing.Tracer;

import java.io.IOException;
import java.io.InputStream;
Expand All @@ -50,18 +56,26 @@
import static org.openqa.selenium.json.Json.JSON_UTF_8;
import static org.openqa.selenium.json.Json.MAP_TYPE;
import static org.openqa.selenium.remote.http.Contents.utf8String;
import static org.openqa.selenium.remote.tracing.HttpTracing.newSpanAsChildOf;
import static org.openqa.selenium.remote.tracing.Tags.HTTP_RESPONSE;
import static org.openqa.selenium.remote.tracing.Tags.HTTP_RESPONSE_EVENT;
import static org.openqa.selenium.remote.tracing.Tags.HTTP_REQUEST;
import static org.openqa.selenium.remote.tracing.Tags.HTTP_REQUEST_EVENT;

public class GraphqlHandler implements HttpHandler {

public static final String GRID_SCHEMA = "/org/openqa/selenium/grid/graphql/selenium-grid-schema.graphqls";
public static final Json JSON = new Json();
private final Tracer tracer;
private final Distributor distributor;
private final URI publicUri;
private final GraphQL graphQl;

public GraphqlHandler(Distributor distributor, URI publicUri) {
this.distributor = Objects.requireNonNull(distributor);
this.publicUri = Objects.requireNonNull(publicUri);

public GraphqlHandler(Tracer tracer, Distributor distributor, URI publicUri) {
this.distributor = Require.nonNull("Distributor", distributor);
this.publicUri = Require.nonNull("Uri", publicUri);
this.tracer = Require.nonNull("Tracer", tracer);

GraphQLSchema schema = new SchemaGenerator()
.makeExecutableSchema(buildTypeDefinitionRegistry(), buildRuntimeWiring());
Expand All @@ -88,34 +102,66 @@ public GraphqlHandler(Distributor distributor, URI publicUri) {

@Override
public HttpResponse execute(HttpRequest req) throws UncheckedIOException {
Map<String, Object> inputs = JSON.toType(Contents.string(req), MAP_TYPE);
try (Span span = newSpanAsChildOf(tracer, req, "grid.status")) {
HttpResponse response;
Map<String, Object> inputs = JSON.toType(Contents.string(req), MAP_TYPE);

if (!(inputs.get("query") instanceof String)) {
return new HttpResponse()
.setStatus(HTTP_INTERNAL_ERROR)
.setContent(Contents.utf8String("Unable to find query"));
}
Map<String, EventAttributeValue> attributeMap = new HashMap<>();
attributeMap.put(AttributeKey.LOGGER_CLASS.getKey(),
EventAttribute.setValue(getClass().getName()));

String query = (String) inputs.get("query");
@SuppressWarnings("unchecked") Map<String, Object> variables = inputs.get("variables") instanceof Map ?
(Map<String, Object>) inputs.get("variables") :
new HashMap<>();
HTTP_REQUEST.accept(span, req);
HTTP_REQUEST_EVENT.accept(attributeMap, req);

ExecutionInput executionInput = ExecutionInput.newExecutionInput(query)
.variables(variables)
.build();
if (!(inputs.get("query") instanceof String)) {
response = new HttpResponse()
.setStatus(HTTP_INTERNAL_ERROR)
.setContent(Contents.utf8String("Unable to find query"));

ExecutionResult result = graphQl.execute(executionInput);
HTTP_RESPONSE.accept(span, response);
HTTP_RESPONSE_EVENT.accept(attributeMap, response);

if (result.isDataPresent()) {
return new HttpResponse()
.addHeader("Content-Type", JSON_UTF_8)
.setContent(utf8String(JSON.toJson(result.toSpecification())));
}
attributeMap.put(AttributeKey.EXCEPTION_MESSAGE.getKey(),
EventAttribute.setValue("Unable to find query"));
span.addEvent(AttributeKey.EXCEPTION_EVENT.getKey(), attributeMap);
return response;
}

String query = (String) inputs.get("query");
@SuppressWarnings("unchecked") Map<String, Object> variables = inputs.get("variables") instanceof Map ?
(Map<String, Object>) inputs.get("variables") :
new HashMap<>();

ExecutionInput executionInput = ExecutionInput.newExecutionInput(query)
.variables(variables)
.build();

ExecutionResult result = graphQl.execute(executionInput);

return new HttpResponse()
.setStatus(HTTP_INTERNAL_ERROR)
.setContent(utf8String(JSON.toJson(result.getErrors())));
if (result.isDataPresent()) {
response = new HttpResponse()
.addHeader("Content-Type", JSON_UTF_8)
.setContent(utf8String(JSON.toJson(result.toSpecification())));

HTTP_RESPONSE.accept(span, response);
HTTP_RESPONSE_EVENT.accept(attributeMap, response);
span.addEvent("Graphql query executed", attributeMap);

return response;
}

response = new HttpResponse()
.setStatus(HTTP_INTERNAL_ERROR)
.setContent(utf8String(JSON.toJson(result.getErrors())));
HTTP_RESPONSE.accept(span, response);
HTTP_RESPONSE_EVENT.accept(attributeMap, response);

attributeMap.put(AttributeKey.EXCEPTION_MESSAGE.getKey(),
EventAttribute.setValue("Error while executing the query"));
span.addEvent(AttributeKey.EXCEPTION_EVENT.getKey(), attributeMap);

return response;
}
}

private RuntimeWiring buildRuntimeWiring() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
import static org.openqa.selenium.remote.tracing.Tags.EXCEPTION;
import static org.openqa.selenium.remote.tracing.Tags.HTTP_RESPONSE;
import static org.openqa.selenium.remote.tracing.Tags.HTTP_RESPONSE_EVENT;
import static org.openqa.selenium.remote.tracing.Tags.HTTP_REQUEST;
import static org.openqa.selenium.remote.tracing.Tags.HTTP_REQUEST_EVENT;

class GridStatusHandler implements HttpHandler {

Expand Down Expand Up @@ -95,10 +97,14 @@ class GridStatusHandler implements HttpHandler {
public HttpResponse execute(HttpRequest req) {
long start = System.currentTimeMillis();

try (Span span = newSpanAsChildOf(tracer, req, "router.status")) {
try (Span span = newSpanAsChildOf(tracer, req, "grid.status")) {
Map<String, EventAttributeValue> attributeMap = new HashMap<>();
attributeMap.put(AttributeKey.LOGGER_CLASS.getKey(),
EventAttribute.setValue(getClass().getName()));

HTTP_REQUEST.accept(span, req);
HTTP_REQUEST_EVENT.accept(attributeMap, req);

DistributorStatus status;
try {
status = EXECUTOR_SERVICE.submit(span.wrap(distributor::getStatus)).get(2, SECONDS);
Expand All @@ -108,24 +114,34 @@ public HttpResponse execute(HttpRequest req) {
EXCEPTION.accept(attributeMap, e);
attributeMap.put(AttributeKey.EXCEPTION_MESSAGE.getKey(),
EventAttribute.setValue("Unable to get distributor status due to execution error or timeout: " + e.getMessage()));
span.addEvent(AttributeKey.EXCEPTION_EVENT.getKey(), attributeMap);

return new HttpResponse().setContent(asJson(
HttpResponse response = new HttpResponse().setContent(asJson(
ImmutableMap.of("value", ImmutableMap.of(
"ready", false,
"message", "Unable to read distributor status."))));

HTTP_RESPONSE.accept(span, response);
HTTP_RESPONSE_EVENT.accept(attributeMap, response);
span.addEvent(AttributeKey.EXCEPTION_EVENT.getKey(), attributeMap);

return response;
} catch (InterruptedException e) {
span.setAttribute("error", true);
span.setStatus(Status.ABORTED);
EXCEPTION.accept(attributeMap, e);
attributeMap.put(AttributeKey.EXCEPTION_MESSAGE.getKey(),
EventAttribute.setValue("Interruption while getting distributor status: " + e.getMessage()));

Thread.currentThread().interrupt();
return new HttpResponse().setContent(asJson(
HttpResponse response = new HttpResponse().setContent(asJson(
ImmutableMap.of("value", ImmutableMap.of(
"ready", false,
"message", "Reading distributor status was interrupted."))));

HTTP_RESPONSE.accept(span, response);
HTTP_RESPONSE_EVENT.accept(attributeMap, response);
span.addEvent(AttributeKey.EXCEPTION_EVENT.getKey(), attributeMap);

Thread.currentThread().interrupt();
return response;
}

boolean ready = status.hasCapacity();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ protected Handlers createHandlers(Config config) {
distributorUrl,
secretOptions.getRegistrationSecret());

GraphqlHandler graphqlHandler = new GraphqlHandler(distributor, serverOptions.getExternalUri());
GraphqlHandler graphqlHandler = new GraphqlHandler(tracer, distributor, serverOptions.getExternalUri());

Route handler = Route.combine(
new Router(tracer, clientFactory, sessions, queuer, distributor).with(networkOptions.getSpecComplianceChecks()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ public void setupGrid() {

@Test
public void shouldBeAbleToGetGridUri() {
GraphqlHandler handler = new GraphqlHandler(distributor, publicUri);
GraphqlHandler handler = new GraphqlHandler(tracer, distributor, publicUri);

Map<String, Object> topLevel = executeQuery(handler, "{ grid { uri } }");

Expand All @@ -132,7 +132,7 @@ public void shouldBeAbleToGetGridUri() {

@Test
public void shouldReturnAnEmptyListForNodesIfNoneAreRegistered() {
GraphqlHandler handler = new GraphqlHandler(distributor, publicUri);
GraphqlHandler handler = new GraphqlHandler(tracer, distributor, publicUri);

Map<String, Object> topLevel = executeQuery(handler, "{ grid { nodes { uri } } }");

Expand Down Expand Up @@ -163,7 +163,7 @@ public boolean test(Capabilities capabilities) {
distributor.add(node);
wait.until(obj -> distributor.getStatus().hasCapacity());

GraphqlHandler handler = new GraphqlHandler(distributor, publicUri);
GraphqlHandler handler = new GraphqlHandler(tracer, distributor, publicUri);
Map<String, Object> topLevel = executeQuery(handler, "{ grid { nodes { uri } } }");

assertThat(topLevel).describedAs(topLevel.toString()).isEqualTo(
Expand Down Expand Up @@ -209,7 +209,7 @@ public void shouldBeAbleToGetSessionInfo() throws URISyntaxException {
String query = String.format(
"{ session (id: \"%s\") { id, capabilities, startTime, uri } }", sessionId);

GraphqlHandler handler = new GraphqlHandler(distributor, publicUri);
GraphqlHandler handler = new GraphqlHandler(tracer, distributor, publicUri);
Map<String, Object> result = executeQuery(handler, query);

assertThat(result).describedAs(result.toString()).isEqualTo(
Expand Down Expand Up @@ -257,7 +257,7 @@ public void shouldBeAbleToGetNodeInfoForSession() throws URISyntaxException {
slot);
String query = String.format("{ session (id: \"%s\") { nodeId, nodeUri } }", sessionId);

GraphqlHandler handler = new GraphqlHandler(distributor, publicUri);
GraphqlHandler handler = new GraphqlHandler(tracer, distributor, publicUri);
Map<String, Object> result = executeQuery(handler, query);

assertThat(result).describedAs(result.toString()).isEqualTo(
Expand Down Expand Up @@ -307,7 +307,7 @@ public void shouldBeAbleToGetSlotInfoForSession() throws URISyntaxException {
String query = String.format(
"{ session (id: \"%s\") { slot { id, stereotype, lastStarted } } }", sessionId);

GraphqlHandler handler = new GraphqlHandler(distributor, publicUri);
GraphqlHandler handler = new GraphqlHandler(tracer, distributor, publicUri);
Map<String, Object> result = executeQuery(handler, query);

assertThat(result).describedAs(result.toString()).isEqualTo(
Expand Down Expand Up @@ -342,7 +342,7 @@ public void shouldBeAbleToGetSessionDuration() throws URISyntaxException {

String query = String.format("{ session (id: \"%s\") { sessionDurationMillis } }", sessionId);

GraphqlHandler handler = new GraphqlHandler(distributor, publicUri);
GraphqlHandler handler = new GraphqlHandler(tracer, distributor, publicUri);
Map<String, Object> result = executeQuery(handler, query);

assertThat(result)
Expand Down Expand Up @@ -370,7 +370,7 @@ public void shouldThrowExceptionWhenSessionNotFound() throws URISyntaxException
String randomSessionId = UUID.randomUUID().toString();
String query = "{ session (id: \"" + randomSessionId + "\") { sessionDurationMillis } }";

GraphqlHandler handler = new GraphqlHandler(distributor, publicUri);
GraphqlHandler handler = new GraphqlHandler(tracer, distributor, publicUri);
Map<String, Object> result = executeQuery(handler, query);
assertThat(result)
.containsEntry("data", null)
Expand Down Expand Up @@ -399,7 +399,7 @@ public void shouldThrowExceptionWhenSessionIsEmpty() throws URISyntaxException {

String query = "{ session (id: \"\") { sessionDurationMillis } }";

GraphqlHandler handler = new GraphqlHandler(distributor, publicUri);
GraphqlHandler handler = new GraphqlHandler(tracer, distributor, publicUri);
Map<String, Object> result = executeQuery(handler, query);
assertThat(result)
.containsEntry("data", null)
Expand Down