From aa2176562842aba0966524fbc6c2b444e69c285d Mon Sep 17 00:00:00 2001 From: Jared Tan Date: Fri, 6 Mar 2020 21:07:39 +0800 Subject: [PATCH] support http api for upstream trace. (#4399) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * support http api. * e2e Co-authored-by: 吴晟 Wu Sheng Co-authored-by: kezhenxu94 --- .github/workflows/e2e.yaml | 2 + .gitignore | 1 + docs/en/protocols/HTTP-API-Protocol.md | 200 +++++++++ docs/en/protocols/Trace-Data-Protocol-v2.md | 3 + oap-server/pom.xml | 8 + .../server/jetty/JettyJsonHandler.java | 19 +- .../server-library/library-util/pom.xml | 12 + .../library/util/ProtoBufJsonUtils.java | 46 +++ .../provider/RegisterModuleProvider.java | 11 + .../ServiceInstancePingServletHandler.java | 111 +++++ ...ServiceInstanceRegisterServletHandler.java | 152 +++++++ .../rest/ServiceRegisterServletHandler.java | 92 +++++ .../trace/provider/TraceModuleProvider.java | 25 +- .../TraceSegmentCollectServletHandler.java | 76 ++++ .../v6/rest/reader/SegmentJsonReader.java | 33 ++ .../v6/rest/reader/StreamJsonReader.java | 25 ++ .../reader/UpstreamSegmentJsonReader.java | 41 ++ .../segment/ProtoBufJsonUtilsTest.java | 139 +++++++ test/e2e/e2e-http-api-with-nginx-lua/pom.xml | 195 +++++++++ .../src/docker/rc.d/rc0-prepare.sh | 95 +++++ .../src/docker/rc.d/rc1-startup.sh | 48 +++ .../client/SampleClientApplication.java | 29 ++ .../e2e/sample/client/TestController.java | 32 ++ .../src/main/resources/application.yml | 22 + .../e2e/SampleVerificationITCase.java | 386 ++++++++++++++++++ ...e2e.SampleVerificationITCase.endpoints.yml | 28 ++ ...e2e.SampleVerificationITCase.instances.yml | 34 ++ ...VerificationITCase.serviceInstanceTopo.yml | 41 ++ ....e2e.SampleVerificationITCase.services.yml | 27 ++ ...king.e2e.SampleVerificationITCase.topo.yml | 47 +++ ...ng.e2e.SampleVerificationITCase.traces.yml | 40 ++ test/e2e/pom.xml | 1 + .../known-oap-backend-dependencies-es7.txt | 3 +- .../known-oap-backend-dependencies.txt | 3 +- 34 files changed, 2014 insertions(+), 13 deletions(-) create mode 100644 docs/en/protocols/HTTP-API-Protocol.md create mode 100644 oap-server/server-library/library-util/src/main/java/org/apache/skywalking/oap/server/library/util/ProtoBufJsonUtils.java create mode 100644 oap-server/server-receiver-plugin/skywalking-register-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/register/provider/handler/v6/rest/ServiceInstancePingServletHandler.java create mode 100644 oap-server/server-receiver-plugin/skywalking-register-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/register/provider/handler/v6/rest/ServiceInstanceRegisterServletHandler.java create mode 100644 oap-server/server-receiver-plugin/skywalking-register-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/register/provider/handler/v6/rest/ServiceRegisterServletHandler.java create mode 100644 oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/handler/v6/rest/TraceSegmentCollectServletHandler.java create mode 100644 oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/handler/v6/rest/reader/SegmentJsonReader.java create mode 100644 oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/handler/v6/rest/reader/StreamJsonReader.java create mode 100644 oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/handler/v6/rest/reader/UpstreamSegmentJsonReader.java create mode 100644 oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/test/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/listener/segment/ProtoBufJsonUtilsTest.java create mode 100755 test/e2e/e2e-http-api-with-nginx-lua/pom.xml create mode 100755 test/e2e/e2e-http-api-with-nginx-lua/src/docker/rc.d/rc0-prepare.sh create mode 100755 test/e2e/e2e-http-api-with-nginx-lua/src/docker/rc.d/rc1-startup.sh create mode 100644 test/e2e/e2e-http-api-with-nginx-lua/src/main/java/org/apache/skywalking/e2e/sample/client/SampleClientApplication.java create mode 100644 test/e2e/e2e-http-api-with-nginx-lua/src/main/java/org/apache/skywalking/e2e/sample/client/TestController.java create mode 100644 test/e2e/e2e-http-api-with-nginx-lua/src/main/resources/application.yml create mode 100644 test/e2e/e2e-http-api-with-nginx-lua/src/test/java/org/apache/skywalking/e2e/SampleVerificationITCase.java create mode 100644 test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.endpoints.yml create mode 100644 test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.instances.yml create mode 100644 test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.serviceInstanceTopo.yml create mode 100644 test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.services.yml create mode 100644 test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.topo.yml create mode 100644 test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.traces.yml diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index e19c1de3a7..513f9e8df5 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -116,6 +116,8 @@ jobs: ./mvnw --batch-mode -f test/e2e/pom.xml -pl e2e-base clean install - name: 6.x Agents & 7.x Backend run: export E2E_VERSION=jdk8-1.5 && bash -x test/e2e/run.sh e2e-6.x-agent-7.x-oap-compatibility + - name: Http API with Nginx Lua Tests In Single Node Mode(JDK8) + run: export E2E_VERSION=jdk8-1.5 && bash -x test/e2e/run.sh e2e-http-api-with-nginx-lua Profile: runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index c8c84886d0..a5bd42a680 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ OALLexer.tokens .externalToolBuilders /test/plugin/dist /test/plugin/workspace +e2e-* diff --git a/docs/en/protocols/HTTP-API-Protocol.md b/docs/en/protocols/HTTP-API-Protocol.md new file mode 100644 index 0000000000..3b42a4223b --- /dev/null +++ b/docs/en/protocols/HTTP-API-Protocol.md @@ -0,0 +1,200 @@ +# HTTP API Protocol + +HTTP API Protocol defines the API data format, including api request and response data format. + +### Do register + +Detail information about data format can be found in [Register service](https://github.com/apache/skywalking-data-collect-protocol/tree/master/register/Register.proto). +And register steps followings [SkyWalking Trace Data Protocol v2](Trace-Data-Protocol-v2.md). + +- Service Register + +> POST http://localhost:12800/v2/service/register + +Input: + +```json +{ + "services": [ + { + "type": "normal", + "serviceName": "Service Name" + } + ] +} +``` + +Output JSON Array: + +```json +[ + { + "key": "Service Name", + "value": 2 + } +] +``` + +- Service instance Register + +> POST http://localhost:12800/v2/instance/register + +Input: + +```json +{ + "instances": [ + { + "time": 1582428603392, + "instanceUUID": "NAME:Service Instance Name", + "properties": [ + { + "key": "language", + "value": "Lua" + } + ], + "serviceId": 2 + } + ] +} +``` + +OutPut: + +```json +[ + { + "key": "NAME:Service Instance Name", + "value": 0 + } +] +``` + +- Service instance heartbeat + +> POST http://localhost:12800/v2/instance/heartbeat + +Input: + +```json +{ + "serviceInstanceId":20, + "time": 1582428603392, + "serviceInstanceUUID":"NAME:Service Instance Name" +} +``` + +OutPut: + +```json +{} +``` +If your instance does not exist, you need to clean your local service instance metadata in your application and re-do register: + +```json +{ + "commands": [ + { + "command": "ServiceMetadataReset", + "args": [ + { + "key": "SerialNumber", + "value": "44bd2664-03c7-46bc-8652-52fcde0e7699" + } + ] + } + ] +} +``` + +## Trace Report + +### POST http://localhost:12800/v2/segments + +Input: + +```json +{ + "spans": [ + { + "operationName": "/tier2/lb", + "startTime": 1582461179910, + "tags": [], + "endTime": 1582461179922, + "spanType": "Exit", + "logs":[], + "spanId": 1, + "isError": false, + "parentSpanId": 0, + "componentId": 6000, + "peer": "User Service Name-nginx:upstream_ip:port", + "spanLayer": "HTTP" + }, + { + "operationName": "/tier2/lb", + "startTime": 1582461179910, + "tags": [ + { + "key": "http.method", + "value": "GET" + }, + { + "key": "http.params", + "value": "http://127.0.0.1/tier2/lb" + } + ], + "endTime": 1582461179922, + "spanType": "Entry", + "logs": [], + "spanId": 0, + "isError": false, + "parentSpanId": -1, + "componentId": 6000, + "refs": [ + { + "parentTraceSegmentId": { + "idParts": [ + 1582461179038, + 794206293, + 69887 + ] + }, + "parentEndpointId": 0, + "entryEndpointId": 0, + "parentServiceInstanceId": 1, + "parentEndpoint": "/ingress", + "networkAddress": "#User Service Name-nginx:upstream_ip:port", + "parentSpanId": 1, + "entryServiceInstanceId": 1, + "networkAddressId": 0, + "entryEndpoint": "/ingress" + } + ], + "spanLayer": "HTTP" + } + ], + "serviceInstanceId": 1, + "serviceId": 1, + "traceSegmentId": { + "idParts": [ + 1582461179044, + 794206293, + 69887 + ] + }, + "globalTraceIds": [ + { + "idParts": [ + 1582461179038, + 794206293, + 69887 + ] + } + ] +} +``` + OutPut: + + ```json + +``` \ No newline at end of file diff --git a/docs/en/protocols/Trace-Data-Protocol-v2.md b/docs/en/protocols/Trace-Data-Protocol-v2.md index a80a8404c2..26079bc4d7 100644 --- a/docs/en/protocols/Trace-Data-Protocol-v2.md +++ b/docs/en/protocols/Trace-Data-Protocol-v2.md @@ -7,6 +7,9 @@ Trace data protocol is defined and provided in [gRPC format](https://github.com/ For each agent/SDK, it needs to register service id and service instance id before reporting any kind of trace or metrics data. +Since SkyWalking v7.x, SkyWalking provided register and uplink trace data through HTTP API way. +[HTTP API Protocol](HTTP-API-Protocol.md) defined the API data format. + ### Step 1. Do register [Register service](https://github.com/apache/skywalking-data-collect-protocol/tree/master/register/Register.proto) takes charge of all register methods. At step 1, we need `doServiceRegister`, then `doServiceInstanceRegister`. diff --git a/oap-server/pom.xml b/oap-server/pom.xml index a90031f8e7..88562cb4db 100755 --- a/oap-server/pom.xml +++ b/oap-server/pom.xml @@ -90,6 +90,7 @@ 3.25.0-GA 3.5 + 3.11.4 @@ -263,6 +264,13 @@ grpc-protobuf ${grpc.version} + + + com.google.protobuf + protobuf-java-util + ${protobuf-java-util.version} + + io.grpc grpc-stub diff --git a/oap-server/server-library/library-server/src/main/java/org/apache/skywalking/oap/server/library/server/jetty/JettyJsonHandler.java b/oap-server/server-library/library-server/src/main/java/org/apache/skywalking/oap/server/library/server/jetty/JettyJsonHandler.java index f2bbdedcc8..b48a3248e6 100644 --- a/oap-server/server-library/library-server/src/main/java/org/apache/skywalking/oap/server/library/server/jetty/JettyJsonHandler.java +++ b/oap-server/server-library/library-server/src/main/java/org/apache/skywalking/oap/server/library/server/jetty/JettyJsonHandler.java @@ -19,6 +19,7 @@ package org.apache.skywalking.oap.server.library.server.jetty; import com.google.gson.JsonElement; +import java.io.BufferedReader; import java.io.IOException; import java.io.PrintWriter; import java.util.Enumeration; @@ -84,25 +85,25 @@ public abstract class JettyJsonHandler extends JettyHandler { @Override protected final void doDelete(HttpServletRequest req, - HttpServletResponse resp) throws ServletException, IOException { + HttpServletResponse resp) throws ServletException, IOException { super.doDelete(req, resp); } @Override protected final void doOptions(HttpServletRequest req, - HttpServletResponse resp) throws ServletException, IOException { + HttpServletResponse resp) throws ServletException, IOException { super.doOptions(req, resp); } @Override protected final void doTrace(HttpServletRequest req, - HttpServletResponse resp) throws ServletException, IOException { + HttpServletResponse resp) throws ServletException, IOException { super.doTrace(req, resp); } @Override protected final void service(HttpServletRequest req, - HttpServletResponse resp) throws ServletException, IOException { + HttpServletResponse resp) throws ServletException, IOException { super.service(req, resp); } @@ -189,4 +190,14 @@ public abstract class JettyJsonHandler extends JettyHandler { out.flush(); out.close(); } + + public String getJsonBody(HttpServletRequest req) throws IOException { + StringBuffer stringBuffer = new StringBuffer(); + String line = null; + BufferedReader reader = req.getReader(); + while ((line = reader.readLine()) != null) { + stringBuffer.append(line); + } + return stringBuffer.toString(); + } } diff --git a/oap-server/server-library/library-util/pom.xml b/oap-server/server-library/library-util/pom.xml index 93b2fa78e8..6f5b29a4b1 100644 --- a/oap-server/server-library/library-util/pom.xml +++ b/oap-server/server-library/library-util/pom.xml @@ -31,6 +31,7 @@ 1.18.0 + 3.11.4 @@ -53,5 +54,16 @@ ${ststem-rules.version} test + + + com.google.protobuf + protobuf-java + ${protobuf-java.version} + + + + com.google.protobuf + protobuf-java-util + diff --git a/oap-server/server-library/library-util/src/main/java/org/apache/skywalking/oap/server/library/util/ProtoBufJsonUtils.java b/oap-server/server-library/library-util/src/main/java/org/apache/skywalking/oap/server/library/util/ProtoBufJsonUtils.java new file mode 100644 index 0000000000..09aba12a0e --- /dev/null +++ b/oap-server/server-library/library-util/src/main/java/org/apache/skywalking/oap/server/library/util/ProtoBufJsonUtils.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 + * + * http://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 org.apache.skywalking.oap.server.library.util; + +import com.google.protobuf.Message; +import com.google.protobuf.util.JsonFormat; +import java.io.IOException; + +public class ProtoBufJsonUtils { + + public static String toJSON(Message sourceMessage) throws IOException { + return JsonFormat.printer().print(sourceMessage); + } + + /** + * Extract data from a JSON String and use them to construct a Protocol Buffers Message. + * + * @param json A JSON data string to parse + * @param targetBuilder A Message builder to use to construct the resulting Message + * @throws com.google.protobuf.InvalidProtocolBufferException Thrown in case of invalid Message data + */ + public static void fromJSON(String json, Message.Builder targetBuilder) throws IOException { + JsonFormat.parser() + .usingTypeRegistry(JsonFormat.TypeRegistry.newBuilder() + .add(targetBuilder.getDescriptorForType()) + .build()) + .ignoringUnknownFields() + .merge(json, targetBuilder); + } +} diff --git a/oap-server/server-receiver-plugin/skywalking-register-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/register/provider/RegisterModuleProvider.java b/oap-server/server-receiver-plugin/skywalking-register-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/register/provider/RegisterModuleProvider.java index 798dea3d77..f7b3dc0a81 100644 --- a/oap-server/server-receiver-plugin/skywalking-register-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/register/provider/RegisterModuleProvider.java +++ b/oap-server/server-receiver-plugin/skywalking-register-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/register/provider/RegisterModuleProvider.java @@ -20,12 +20,16 @@ package org.apache.skywalking.oap.server.receiver.register.provider; import org.apache.skywalking.oap.server.core.CoreModule; import org.apache.skywalking.oap.server.core.server.GRPCHandlerRegister; +import org.apache.skywalking.oap.server.core.server.JettyHandlerRegister; import org.apache.skywalking.oap.server.library.module.ModuleConfig; import org.apache.skywalking.oap.server.library.module.ModuleDefine; import org.apache.skywalking.oap.server.library.module.ModuleProvider; import org.apache.skywalking.oap.server.receiver.register.module.RegisterModule; import org.apache.skywalking.oap.server.receiver.register.provider.handler.v6.grpc.RegisterServiceHandler; import org.apache.skywalking.oap.server.receiver.register.provider.handler.v6.grpc.ServiceInstancePingServiceHandler; +import org.apache.skywalking.oap.server.receiver.register.provider.handler.v6.rest.ServiceInstancePingServletHandler; +import org.apache.skywalking.oap.server.receiver.register.provider.handler.v6.rest.ServiceInstanceRegisterServletHandler; +import org.apache.skywalking.oap.server.receiver.register.provider.handler.v6.rest.ServiceRegisterServletHandler; import org.apache.skywalking.oap.server.receiver.sharing.server.SharingServerModule; public class RegisterModuleProvider extends ModuleProvider { @@ -56,6 +60,13 @@ public class RegisterModuleProvider extends ModuleProvider { .getService(GRPCHandlerRegister.class); grpcHandlerRegister.addHandler(new RegisterServiceHandler(getManager())); grpcHandlerRegister.addHandler(new ServiceInstancePingServiceHandler(getManager())); + + JettyHandlerRegister jettyHandlerRegister = getManager().find(SharingServerModule.NAME) + .provider() + .getService(JettyHandlerRegister.class); + jettyHandlerRegister.addHandler(new ServiceRegisterServletHandler(getManager())); + jettyHandlerRegister.addHandler(new ServiceInstanceRegisterServletHandler(getManager())); + jettyHandlerRegister.addHandler(new ServiceInstancePingServletHandler(getManager())); } @Override diff --git a/oap-server/server-receiver-plugin/skywalking-register-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/register/provider/handler/v6/rest/ServiceInstancePingServletHandler.java b/oap-server/server-receiver-plugin/skywalking-register-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/register/provider/handler/v6/rest/ServiceInstancePingServletHandler.java new file mode 100644 index 0000000000..738f6611c8 --- /dev/null +++ b/oap-server/server-receiver-plugin/skywalking-register-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/register/provider/handler/v6/rest/ServiceInstancePingServletHandler.java @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 + * + * http://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 org.apache.skywalking.oap.server.receiver.register.provider.handler.v6.rest; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import java.io.IOException; +import java.util.Objects; +import javax.servlet.http.HttpServletRequest; +import org.apache.skywalking.apm.network.common.Command; +import org.apache.skywalking.apm.network.common.Commands; +import org.apache.skywalking.apm.network.register.v2.ServiceInstancePingPkg; +import org.apache.skywalking.apm.network.trace.component.command.ServiceResetCommand; +import org.apache.skywalking.oap.server.core.CoreModule; +import org.apache.skywalking.oap.server.core.cache.ServiceInstanceInventoryCache; +import org.apache.skywalking.oap.server.core.command.CommandService; +import org.apache.skywalking.oap.server.core.register.ServiceInstanceInventory; +import org.apache.skywalking.oap.server.core.register.service.IServiceInstanceInventoryRegister; +import org.apache.skywalking.oap.server.core.register.service.IServiceInventoryRegister; +import org.apache.skywalking.oap.server.library.module.ModuleManager; +import org.apache.skywalking.oap.server.library.server.jetty.ArgumentsParseException; +import org.apache.skywalking.oap.server.library.server.jetty.JettyJsonHandler; +import org.apache.skywalking.oap.server.library.util.ProtoBufJsonUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ServiceInstancePingServletHandler extends JettyJsonHandler { + + private static final Logger logger = LoggerFactory.getLogger(ServiceInstancePingServletHandler.class); + + private final IServiceInstanceInventoryRegister serviceInstanceInventoryRegister; + private final ServiceInstanceInventoryCache serviceInstanceInventoryCache; + private final IServiceInventoryRegister serviceInventoryRegister; + private final CommandService commandService; + private final Gson gson = new Gson(); + + public ServiceInstancePingServletHandler(ModuleManager moduleManager) { + this.serviceInstanceInventoryRegister = moduleManager.find(CoreModule.NAME).provider().getService( + IServiceInstanceInventoryRegister.class); + this.serviceInstanceInventoryCache = moduleManager.find(CoreModule.NAME).provider().getService( + ServiceInstanceInventoryCache.class); + this.serviceInventoryRegister = moduleManager.find(CoreModule.NAME).provider().getService( + IServiceInventoryRegister.class); + this.commandService = moduleManager.find(CoreModule.NAME).provider().getService(CommandService.class); + } + + @Override + public String pathSpec() { + return "/v2/instance/heartbeat"; + } + + @Override + protected JsonElement doGet(HttpServletRequest req) throws ArgumentsParseException { + throw new UnsupportedOperationException(); + } + + @Override + protected JsonElement doPost(HttpServletRequest req) throws ArgumentsParseException, IOException { + JsonObject responseJson = new JsonObject(); + + try { + ServiceInstancePingPkg.Builder builder = ServiceInstancePingPkg.newBuilder(); + ProtoBufJsonUtils.fromJSON(getJsonBody(req), builder); + ServiceInstancePingPkg instancePingPkg = builder.build(); + + int serviceInstanceId = instancePingPkg.getServiceInstanceId(); + long heartBeatTime = instancePingPkg.getTime(); + String serviceInstanceUUID = instancePingPkg.getServiceInstanceUUID(); + serviceInstanceInventoryRegister.heartbeat(serviceInstanceId, heartBeatTime); + + ServiceInstanceInventory serviceInstanceInventory = serviceInstanceInventoryCache.get(serviceInstanceId); + if (Objects.nonNull(serviceInstanceInventory)) { + serviceInventoryRegister.heartbeat(serviceInstanceInventory.getServiceId(), heartBeatTime); + } else { + logger.warn( + "Can't found service by service instance id from cache, service instance id is: {}", + serviceInstanceId + ); + + final ServiceResetCommand resetCommand = commandService.newResetCommand( + serviceInstanceId, heartBeatTime, serviceInstanceUUID); + final Command command = resetCommand.serialize().build(); + final Commands nextCommands = Commands.newBuilder().addCommands(command).build(); + return gson.fromJson(ProtoBufJsonUtils.toJSON(nextCommands), JsonElement.class); + } + + } catch (IOException e) { + responseJson.addProperty("error", e.getMessage()); + logger.error(e.getMessage(), e); + } + + return responseJson; + } +} diff --git a/oap-server/server-receiver-plugin/skywalking-register-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/register/provider/handler/v6/rest/ServiceInstanceRegisterServletHandler.java b/oap-server/server-receiver-plugin/skywalking-register-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/register/provider/handler/v6/rest/ServiceInstanceRegisterServletHandler.java new file mode 100644 index 0000000000..f930fc37e4 --- /dev/null +++ b/oap-server/server-receiver-plugin/skywalking-register-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/register/provider/handler/v6/rest/ServiceInstanceRegisterServletHandler.java @@ -0,0 +1,152 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 + * + * http://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 org.apache.skywalking.oap.server.receiver.register.provider.handler.v6.rest; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import javax.servlet.http.HttpServletRequest; +import org.apache.skywalking.apm.network.common.KeyStringValuePair; +import org.apache.skywalking.apm.network.register.v2.ServiceInstance; +import org.apache.skywalking.apm.network.register.v2.ServiceInstances; +import org.apache.skywalking.oap.server.core.CoreModule; +import org.apache.skywalking.oap.server.core.cache.ServiceInventoryCache; +import org.apache.skywalking.oap.server.core.register.ServiceInstanceInventory; +import org.apache.skywalking.oap.server.core.register.ServiceInventory; +import org.apache.skywalking.oap.server.core.register.service.IServiceInstanceInventoryRegister; +import org.apache.skywalking.oap.server.library.module.ModuleManager; +import org.apache.skywalking.oap.server.library.server.jetty.ArgumentsParseException; +import org.apache.skywalking.oap.server.library.server.jetty.JettyJsonHandler; +import org.apache.skywalking.oap.server.library.util.ProtoBufJsonUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.apache.skywalking.oap.server.core.register.ServiceInstanceInventory.PropertyUtil.HOST_NAME; +import static org.apache.skywalking.oap.server.core.register.ServiceInstanceInventory.PropertyUtil.IPV4S; +import static org.apache.skywalking.oap.server.core.register.ServiceInstanceInventory.PropertyUtil.LANGUAGE; +import static org.apache.skywalking.oap.server.core.register.ServiceInstanceInventory.PropertyUtil.OS_NAME; +import static org.apache.skywalking.oap.server.core.register.ServiceInstanceInventory.PropertyUtil.PROCESS_NO; + +public class ServiceInstanceRegisterServletHandler extends JettyJsonHandler { + + private static final Logger logger = LoggerFactory.getLogger(ServiceInstanceRegisterServletHandler.class); + private static final String INSTANCE_CUSTOMIZED_NAME_PREFIX = "NAME:"; + + private final IServiceInstanceInventoryRegister serviceInstanceInventoryRegister; + private final ServiceInventoryCache serviceInventoryCache; + + private static final String KEY = "key"; + private static final String VALUE = "value"; + + public ServiceInstanceRegisterServletHandler(ModuleManager moduleManager) { + this.serviceInventoryCache = moduleManager.find(CoreModule.NAME) + .provider() + .getService(ServiceInventoryCache.class); + this.serviceInstanceInventoryRegister = moduleManager.find(CoreModule.NAME).provider().getService( + IServiceInstanceInventoryRegister.class); + } + + @Override + public String pathSpec() { + return "/v2/instance/register"; + } + + @Override + protected JsonElement doGet(HttpServletRequest req) throws ArgumentsParseException { + throw new UnsupportedOperationException(); + } + + @Override + protected JsonElement doPost(HttpServletRequest req) throws ArgumentsParseException { + + JsonObject responseJson = new JsonObject(); + JsonArray jsonArray = new JsonArray(); + + try { + ServiceInstances.Builder builder = ServiceInstances.newBuilder(); + ProtoBufJsonUtils.fromJSON(getJsonBody(req), builder); + List serviceInstances = builder.build().getInstancesList(); + + serviceInstances.forEach(instance -> { + long time = instance.getTime(); + int serviceId = instance.getServiceId(); + String instanceUUID = instance.getInstanceUUID(); + + JsonObject instanceProperties = new JsonObject(); + List ipv4s = new ArrayList<>(); + + for (KeyStringValuePair property : instance.getPropertiesList()) { + String key = property.getKey(); + switch (key) { + case HOST_NAME: + instanceProperties.addProperty(HOST_NAME, property.getValue()); + break; + case OS_NAME: + instanceProperties.addProperty(OS_NAME, property.getValue()); + break; + case LANGUAGE: + instanceProperties.addProperty(LANGUAGE, property.getValue()); + break; + case "ipv4": + ipv4s.add(property.getValue()); + break; + case PROCESS_NO: + instanceProperties.addProperty(PROCESS_NO, property.getValue()); + break; + default: + instanceProperties.addProperty(key, property.getValue()); + } + } + instanceProperties.addProperty(IPV4S, ServiceInstanceInventory.PropertyUtil.ipv4sSerialize(ipv4s)); + + String instanceName = null; + if (instanceUUID.startsWith(INSTANCE_CUSTOMIZED_NAME_PREFIX)) { + instanceName = instanceUUID.substring(INSTANCE_CUSTOMIZED_NAME_PREFIX.length()); + } + + ServiceInventory serviceInventory = serviceInventoryCache.get(serviceId); + + if (instanceName == null) { + instanceName = serviceInventory.getName(); + if (instanceProperties.has(PROCESS_NO)) { + instanceName += "-pid:" + instanceProperties.get(PROCESS_NO).getAsString(); + } + if (instanceProperties.has(HOST_NAME)) { + instanceName += "@" + instanceProperties.get(HOST_NAME).getAsString(); + } + } + int instanceId = serviceInstanceInventoryRegister.getOrCreate( + serviceId, instanceName, instanceUUID, time, instanceProperties); + + responseJson.addProperty(KEY, instanceUUID); + responseJson.addProperty(VALUE, instanceId); + jsonArray.add(responseJson); + }); + + return jsonArray; + } catch (IOException e) { + responseJson.addProperty("error", e.getMessage()); + logger.error(e.getMessage(), e); + } + return responseJson; + } +} diff --git a/oap-server/server-receiver-plugin/skywalking-register-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/register/provider/handler/v6/rest/ServiceRegisterServletHandler.java b/oap-server/server-receiver-plugin/skywalking-register-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/register/provider/handler/v6/rest/ServiceRegisterServletHandler.java new file mode 100644 index 0000000000..10242bc0d3 --- /dev/null +++ b/oap-server/server-receiver-plugin/skywalking-register-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/register/provider/handler/v6/rest/ServiceRegisterServletHandler.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 + * + * http://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 org.apache.skywalking.oap.server.receiver.register.provider.handler.v6.rest; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import java.io.IOException; +import java.util.List; +import javax.servlet.http.HttpServletRequest; +import org.apache.skywalking.apm.network.register.v2.Service; +import org.apache.skywalking.apm.network.register.v2.Services; +import org.apache.skywalking.oap.server.core.CoreModule; +import org.apache.skywalking.oap.server.core.register.NodeType; +import org.apache.skywalking.oap.server.core.register.service.IServiceInventoryRegister; +import org.apache.skywalking.oap.server.library.module.ModuleManager; +import org.apache.skywalking.oap.server.library.server.jetty.ArgumentsParseException; +import org.apache.skywalking.oap.server.library.server.jetty.JettyJsonHandler; +import org.apache.skywalking.oap.server.library.util.ProtoBufJsonUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ServiceRegisterServletHandler extends JettyJsonHandler { + + private static final Logger logger = LoggerFactory.getLogger(ServiceRegisterServletHandler.class); + + private final IServiceInventoryRegister serviceInventoryRegister; + + private static final String KEY = "key"; + private static final String VALUE = "value"; + + public ServiceRegisterServletHandler(ModuleManager moduleManager) { + serviceInventoryRegister = moduleManager.find(CoreModule.NAME) + .provider() + .getService(IServiceInventoryRegister.class); + } + + @Override + public String pathSpec() { + return "/v2/service/register"; + } + + @Override + protected JsonElement doGet(HttpServletRequest req) throws ArgumentsParseException { + throw new UnsupportedOperationException(); + } + + @Override + protected JsonElement doPost(HttpServletRequest req) throws ArgumentsParseException { + JsonArray responseArray = new JsonArray(); + + try { + Services.Builder builder = Services.newBuilder(); + ProtoBufJsonUtils.fromJSON(getJsonBody(req), builder); + List serviceList = builder.build().getServicesList(); + + serviceList.forEach(service -> { + int serviceId = serviceInventoryRegister.getOrCreate(service.getServiceName(), + NodeType.fromRegisterServiceType( + service.getType()), null + ); + + JsonObject mapping = new JsonObject(); + mapping.addProperty(KEY, service.getServiceName()); + mapping.addProperty(VALUE, serviceId); + responseArray.add(mapping); + }); + } catch (IOException e) { + JsonObject mapping = new JsonObject(); + mapping.addProperty("error", e.getMessage()); + responseArray.add(mapping); + logger.error(e.getMessage(), e); + } + return responseArray; + } +} diff --git a/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/TraceModuleProvider.java b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/TraceModuleProvider.java index 3eb3d5f1ff..b087241564 100755 --- a/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/TraceModuleProvider.java +++ b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/TraceModuleProvider.java @@ -18,10 +18,12 @@ package org.apache.skywalking.oap.server.receiver.trace.provider; +import java.io.IOException; import org.apache.skywalking.oap.server.configuration.api.ConfigurationModule; import org.apache.skywalking.oap.server.configuration.api.DynamicConfigurationService; import org.apache.skywalking.oap.server.core.CoreModule; import org.apache.skywalking.oap.server.core.server.GRPCHandlerRegister; +import org.apache.skywalking.oap.server.core.server.JettyHandlerRegister; import org.apache.skywalking.oap.server.library.module.ModuleConfig; import org.apache.skywalking.oap.server.library.module.ModuleDefine; import org.apache.skywalking.oap.server.library.module.ModuleProvider; @@ -30,6 +32,7 @@ import org.apache.skywalking.oap.server.library.module.ServiceNotProvidedExcepti import org.apache.skywalking.oap.server.receiver.sharing.server.SharingServerModule; import org.apache.skywalking.oap.server.receiver.trace.module.TraceModule; import org.apache.skywalking.oap.server.receiver.trace.provider.handler.v6.grpc.TraceSegmentReportServiceHandler; +import org.apache.skywalking.oap.server.receiver.trace.provider.handler.v6.rest.TraceSegmentCollectServletHandler; import org.apache.skywalking.oap.server.receiver.trace.provider.parser.ISegmentParserService; import org.apache.skywalking.oap.server.receiver.trace.provider.parser.SegmentParseV2; import org.apache.skywalking.oap.server.receiver.trace.provider.parser.SegmentParserListenerManager; @@ -41,8 +44,6 @@ import org.apache.skywalking.oap.server.receiver.trace.provider.parser.listener. import org.apache.skywalking.oap.server.receiver.trace.provider.parser.standardization.SegmentStandardizationWorker; import org.apache.skywalking.oap.server.telemetry.TelemetryModule; -import java.io.IOException; - public class TraceModuleProvider extends ModuleProvider { private final TraceServiceModuleConfig moduleConfig; @@ -80,7 +81,8 @@ public class TraceModuleProvider extends ModuleProvider { segmentProducerV2 = new SegmentParseV2.Producer(getManager(), listenerManager(), moduleConfig); - this.registerServiceImplementation(ISegmentParserService.class, new SegmentParserServiceImpl(segmentProducerV2)); + this.registerServiceImplementation( + ISegmentParserService.class, new SegmentParserServiceImpl(segmentProducerV2)); } public SegmentParserListenerManager listenerManager() { @@ -99,19 +101,28 @@ public class TraceModuleProvider extends ModuleProvider { public void start() throws ModuleStartException { DynamicConfigurationService dynamicConfigurationService = getManager().find(ConfigurationModule.NAME) .provider() - .getService(DynamicConfigurationService.class); + .getService( + DynamicConfigurationService.class); GRPCHandlerRegister grpcHandlerRegister = getManager().find(SharingServerModule.NAME) .provider() .getService(GRPCHandlerRegister.class); + JettyHandlerRegister jettyHandlerRegister = getManager().find(SharingServerModule.NAME) + .provider() + .getService(JettyHandlerRegister.class); try { dynamicConfigurationService.registerConfigChangeWatcher(thresholds); dynamicConfigurationService.registerConfigChangeWatcher(uninstrumentedGatewaysConfig); grpcHandlerRegister.addHandler(new TraceSegmentReportServiceHandler(segmentProducerV2, getManager())); - SegmentStandardizationWorker standardizationWorkerV2 = new SegmentStandardizationWorker(getManager(), segmentProducerV2, moduleConfig - .getBufferPath(), moduleConfig.getBufferOffsetMaxFileSize(), moduleConfig.getBufferDataMaxFileSize(), moduleConfig - .isBufferFileCleanWhenRestart()); + jettyHandlerRegister.addHandler(new TraceSegmentCollectServletHandler(segmentProducerV2)); + + SegmentStandardizationWorker standardizationWorkerV2 = new SegmentStandardizationWorker( + getManager(), segmentProducerV2, moduleConfig + .getBufferPath(), moduleConfig.getBufferOffsetMaxFileSize(), moduleConfig.getBufferDataMaxFileSize(), + moduleConfig + .isBufferFileCleanWhenRestart() + ); segmentProducerV2.setStandardizationWorker(standardizationWorkerV2); } catch (IOException e) { throw new ModuleStartException(e.getMessage(), e); diff --git a/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/handler/v6/rest/TraceSegmentCollectServletHandler.java b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/handler/v6/rest/TraceSegmentCollectServletHandler.java new file mode 100644 index 0000000000..44bb24320c --- /dev/null +++ b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/handler/v6/rest/TraceSegmentCollectServletHandler.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 + * + * http://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 org.apache.skywalking.oap.server.receiver.trace.provider.handler.v6.rest; + +import com.google.gson.JsonElement; +import java.io.BufferedReader; +import javax.servlet.http.HttpServletRequest; +import org.apache.skywalking.apm.network.language.agent.UpstreamSegment; +import org.apache.skywalking.oap.server.library.server.jetty.JettyJsonHandler; +import org.apache.skywalking.oap.server.receiver.trace.provider.handler.v6.rest.reader.UpstreamSegmentJsonReader; +import org.apache.skywalking.oap.server.receiver.trace.provider.parser.SegmentParseV2; +import org.apache.skywalking.oap.server.receiver.trace.provider.parser.SegmentSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TraceSegmentCollectServletHandler extends JettyJsonHandler { + + private static final Logger logger = LoggerFactory.getLogger(TraceSegmentCollectServletHandler.class); + + private final SegmentParseV2.Producer segmentProducer; + + private UpstreamSegmentJsonReader upstreamSegmentJsonReader = new UpstreamSegmentJsonReader(); + + public TraceSegmentCollectServletHandler(SegmentParseV2.Producer segmentProducer) { + this.segmentProducer = segmentProducer; + } + + @Override + public String pathSpec() { + return "/v2/segments"; + } + + @Override + protected JsonElement doGet(HttpServletRequest req) { + throw new UnsupportedOperationException(); + } + + @Override + protected JsonElement doPost(HttpServletRequest req) { + if (logger.isDebugEnabled()) { + logger.debug("receive stream segment"); + } + + StringBuilder stringBuilder = new StringBuilder(); + String line = null; + try { + BufferedReader reader = req.getReader(); + while ((line = reader.readLine()) != null) { + stringBuilder.append(line); + } + UpstreamSegment upstreamSegment = upstreamSegmentJsonReader.read(stringBuilder.toString()).build(); + + segmentProducer.send(upstreamSegment, SegmentSource.Agent); + } catch (Exception e) { + logger.error(e.getMessage(), e); + } + + return null; + } +} diff --git a/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/handler/v6/rest/reader/SegmentJsonReader.java b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/handler/v6/rest/reader/SegmentJsonReader.java new file mode 100644 index 0000000000..2e78dc1cf4 --- /dev/null +++ b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/handler/v6/rest/reader/SegmentJsonReader.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 + * + * http://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 org.apache.skywalking.oap.server.receiver.trace.provider.handler.v6.rest.reader; + +import java.io.IOException; +import org.apache.skywalking.apm.network.language.agent.v2.SegmentObject; +import org.apache.skywalking.oap.server.library.util.ProtoBufJsonUtils; + +public class SegmentJsonReader implements StreamJsonReader { + + @Override + public SegmentObject.Builder read(String json) throws IOException { + SegmentObject.Builder builder = SegmentObject.newBuilder(); + ProtoBufJsonUtils.fromJSON(json, builder); + return builder; + } +} diff --git a/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/handler/v6/rest/reader/StreamJsonReader.java b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/handler/v6/rest/reader/StreamJsonReader.java new file mode 100644 index 0000000000..13b7028c90 --- /dev/null +++ b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/handler/v6/rest/reader/StreamJsonReader.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 + * + * http://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 org.apache.skywalking.oap.server.receiver.trace.provider.handler.v6.rest.reader; + +import java.io.IOException; + +public interface StreamJsonReader { + T read(String json) throws IOException; +} diff --git a/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/handler/v6/rest/reader/UpstreamSegmentJsonReader.java b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/handler/v6/rest/reader/UpstreamSegmentJsonReader.java new file mode 100644 index 0000000000..3344952b53 --- /dev/null +++ b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/handler/v6/rest/reader/UpstreamSegmentJsonReader.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 + * + * http://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 org.apache.skywalking.oap.server.receiver.trace.provider.handler.v6.rest.reader; + +import java.io.IOException; +import org.apache.skywalking.apm.network.language.agent.UpstreamSegment; +import org.apache.skywalking.apm.network.language.agent.v2.SegmentObject; +import org.apache.skywalking.oap.server.library.util.ProtoBufJsonUtils; + +public class UpstreamSegmentJsonReader implements StreamJsonReader { + + private final SegmentJsonReader segmentJsonReader = new SegmentJsonReader(); + + @Override + public UpstreamSegment.Builder read(String json) throws IOException { + UpstreamSegment.Builder upstreamSegmentBuilder = UpstreamSegment.newBuilder(); + ProtoBufJsonUtils.fromJSON(json, upstreamSegmentBuilder); + + SegmentObject.Builder segmentBuilder = segmentJsonReader.read(json); + + upstreamSegmentBuilder.setSegment(segmentBuilder.build().toByteString()); + + return upstreamSegmentBuilder; + } +} diff --git a/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/test/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/listener/segment/ProtoBufJsonUtilsTest.java b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/test/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/listener/segment/ProtoBufJsonUtilsTest.java new file mode 100644 index 0000000000..559a90876e --- /dev/null +++ b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/test/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/listener/segment/ProtoBufJsonUtilsTest.java @@ -0,0 +1,139 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 + * + * http://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 org.apache.skywalking.oap.server.receiver.trace.provider.parser.listener.segment; + +import java.io.IOException; +import org.apache.skywalking.apm.network.common.Command; +import org.apache.skywalking.apm.network.common.Commands; +import org.apache.skywalking.apm.network.language.agent.UpstreamSegment; +import org.apache.skywalking.apm.network.language.agent.v2.SegmentObject; +import org.apache.skywalking.oap.server.library.util.ProtoBufJsonUtils; +import org.junit.Assert; +import org.junit.Test; + +public class ProtoBufJsonUtilsTest { + @Test + public void testProtoBuf() { + String json = "{\n" + + " \"spans\": [\n" + + " {\n" + + " \"operationName\": \"/tier2/lb\",\n" + + " \"startTime\": 1582526028207,\n" + + " \"endTime\": 1582526028221,\n" + + " \"spanType\": \"Exit\",\n" + + " \"spanId\": 1,\n" + + " \"isError\": false,\n" + + " \"parentSpanId\": 0,\n" + + " \"componentId\": 6000,\n" + + " \"peer\": \"User Service Name-nginx:upstream_ip:port\",\n" + + " \"spanLayer\": \"HTTP\"\n" + + " },\n" + + " {\n" + + " \"operationName\": \"/tier2/lb\",\n" + + " \"startTime\": 1582526028207,\n" + + " \"tags\": [\n" + + " {\n" + + " \"key\": \"http.method\",\n" + + " \"value\": \"GET\"\n" + + " },\n" + + " {\n" + + " \"key\": \"http.params\",\n" + + " \"value\": \"http://127.0.0.1/tier2/lb\"\n" + + " }\n" + + " ],\n" + + " \"endTime\": 1582526028221,\n" + + " \"spanType\": \"Entry\",\n" + + " \"spanId\": 0,\n" + + " \"isError\": false,\n" + + " \"parentSpanId\": -1,\n" + + " \"componentId\": 6000,\n" + + " \"refs\": [\n" + + " {\n" + + " \"parentTraceSegmentId\": {\n" + + " \"idParts\": [\n" + + " 1582526028032,\n" + + " 794206293,\n" + + " 69887\n" + + " ]\n" + + " },\n" + + " \"parentEndpointId\": 0,\n" + + " \"entryEndpointId\": 0,\n" + + " \"parentServiceInstanceId\": 1,\n" + + " \"parentEndpoint\": \"/ingress\",\n" + + " \"networkAddress\": \"#User Service Name-nginx:upstream_ip:port\",\n" + + " \"parentSpanId\": 1,\n" + + " \"entryServiceInstanceId\": 1,\n" + + " \"networkAddressId\": 0,\n" + + " \"entryEndpoint\": \"/ingress\"\n" + + " }\n" + + " ],\n" + + " \"spanLayer\": \"HTTP\"\n" + + " }\n" + + " ],\n" + + " \"serviceInstanceId\": 1,\n" + + " \"serviceId\": 1,\n" + + " \"traceSegmentId\": {\n" + + " \"idParts\": [\n" + + " 1582526028040,\n" + + " 794206293,\n" + + " 69887\n" + + " ]\n" + + " },\n" + + " \"globalTraceIds\": [\n" + + " {\n" + + " \"idParts\": [\n" + + " 1582526028032,\n" + + " 794206293,\n" + + " 69887\n" + + " ]\n" + + " }\n" + + " ]\n" + + "}"; + + UpstreamSegment.Builder builder = UpstreamSegment.newBuilder(); + try { + ProtoBufJsonUtils.fromJSON(json, builder); + UpstreamSegment upstreamSegment = builder.build(); + Assert.assertEquals(1582526028032L, upstreamSegment.getGlobalTraceIds(0).getIdParts(0)); + + SegmentObject.Builder segBuilder = SegmentObject.newBuilder(); + ProtoBufJsonUtils.fromJSON(json, segBuilder); + SegmentObject segmentObject = segBuilder.build(); + Assert.assertEquals(2, segmentObject.getSpansCount()); + } catch (IOException e) { + e.printStackTrace(); + } + + } + + @Test + public void testToJson() { + String json = "{\n" + + " \"commands\": [{\n" + + " }]\n" + + "}"; + try { + Command command = Command.newBuilder().build(); + final Commands nextCommands = Commands.newBuilder().addCommands(command).build(); + Assert.assertEquals(json, ProtoBufJsonUtils.toJSON(nextCommands)); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/test/e2e/e2e-http-api-with-nginx-lua/pom.xml b/test/e2e/e2e-http-api-with-nginx-lua/pom.xml new file mode 100755 index 0000000000..012723a7fe --- /dev/null +++ b/test/e2e/e2e-http-api-with-nginx-lua/pom.xml @@ -0,0 +1,195 @@ + + + + + + apache-skywalking-e2e + org.apache.skywalking + 1.0.0 + + 4.0.0 + + e2e-http-api-with-nginx-lua + + + 1.1 + skywalking-e2e-container-${build.id}-http-api-with-nginx-lua + + + + + + org.apache.skywalking + e2e-base + ${project.version} + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + true + true + true + + + + + repackage + + + + + + + io.fabric8 + docker-maven-plugin + + %a-%t-%i + + + skyapm/e2e-container:${e2e.container.version} + e2e-container-app-for-nginx-lua + + + ${project.build.finalName}.jar + -Dskywalking.agent.cool_down_threshold=1 + + + + +webapp.host:webapp.port:8080 + +client.host:client.port:9090 + + + + ${sw.home}:/sw + ${project.build.directory}:/home + + ${project.basedir}/src/docker/rc.d/rc1-startup.sh:/rc.d/rc1-startup.sh:ro + + + + + + + http://${docker.host.address}:${client.port}/e2e/info + + GET + 200 + + + + + + + + openresty/openresty + ${e2e.container.name.prefix}-openresty + + + + c6aca516200573a64db7060cefb5d3608c571c3a + + + + e2e-container-app-for-nginx-lua:upstream + + + +nginx.host:nginx.port:8080 + + + + ${project.basedir}/src/docker/rc.d:/rc.d:ro + + + + + + http://${docker.host.address}:${nginx.port} + + + + + /rc.d/rc0-prepare.sh + + + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + ${webapp.host} + + + ${webapp.port} + + + ${client.host} + + + ${client.port} + + + ${nginx.port} + + + + + + + verify + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${maven-failsafe-plugin.version} + + + + http://${nginx.host}:${nginx.port}/ingress + + + + + + + verify + + + + + + + diff --git a/test/e2e/e2e-http-api-with-nginx-lua/src/docker/rc.d/rc0-prepare.sh b/test/e2e/e2e-http-api-with-nginx-lua/src/docker/rc.d/rc0-prepare.sh new file mode 100755 index 0000000000..39b7ed5e2f --- /dev/null +++ b/test/e2e/e2e-http-api-with-nginx-lua/src/docker/rc.d/rc0-prepare.sh @@ -0,0 +1,95 @@ +#!/usr/bin/env bash +# Licensed to the SkyAPM under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF 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 +# +# http://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. + +#!/bin/bash + +set -e + +var_application_file="/var/nginx/conf.d/nginx.conf" + +var_upstream_ip=$(cat /etc/hosts|grep upstream|awk '{print $1'}) + +apt-get update && apt-get -y install git + +echo 'git clone skyalking-nginx-lua lib from https://github.com/apache/skywalking-nginx-lua.git' + +git clone https://github.com/apache/skywalking-nginx-lua.git /usr/share/skywalking-nginx-lua \ + && cd /usr/share/skywalking-nginx-lua \ + && git checkout ${SKYWALKING_NINGX_LUA_GIT_COMMIT_ID} \ + && ls ./ \ + && mkdir -p /var/nginx/conf.d + + +generateNginxConf() { + cat <> ${var_application_file} +worker_processes 1; +daemon off; +error_log /dev/stdout error; + +events { + worker_connections 1024; +} +http { + lua_package_path "/usr/share/skywalking-nginx-lua/lib/skywalking/?.lua;;"; + # Buffer represents the register inform and the queue of the finished segment + lua_shared_dict tracing_buffer 100m; + + # Init is the timer setter and keeper + # Setup an infinite loop timer to do register and trace report. + init_worker_by_lua_block { + local metadata_buffer = ngx.shared.tracing_buffer + + metadata_buffer:set('serviceName', 'User_Service_Name') + -- Instance means the number of Nginx deloyment, does not mean the worker instances + metadata_buffer:set('serviceInstanceName', 'User_Service_Instance_Name') + + require("client"):startBackendTimer("http://${var_upstream_ip}:12800") + } + log_format sw_trace escape=json "$uri $request_body"; + + server { + listen 8080; + + location /nginx/e2e/info { + + rewrite_by_lua_block { + require("tracer"):start("User_Service_Name") + } + + proxy_pass http://upstream:9090/e2e/info; + + body_filter_by_lua_block { + require("tracer"):finish() + } + + log_by_lua_block { + require("tracer"):prepareForReport() + } + } + } +} +EOT +} + +generateNginxConf; + +echo 'generated nginx.conf:' + +cat ${var_application_file} + +/usr/bin/openresty -c ${var_application_file} +sync diff --git a/test/e2e/e2e-http-api-with-nginx-lua/src/docker/rc.d/rc1-startup.sh b/test/e2e/e2e-http-api-with-nginx-lua/src/docker/rc.d/rc1-startup.sh new file mode 100755 index 0000000000..9328f7c6e7 --- /dev/null +++ b/test/e2e/e2e-http-api-with-nginx-lua/src/docker/rc.d/rc1-startup.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# Licensed to the SkyAPM under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF 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 +# +# http://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. + +echo 'starting OAP server...' \ + && SW_STORAGE_ES_BULK_ACTIONS=1 \ + && SW_STORAGE_ES_FLUSH_INTERVAL=1 \ + && SW_RECEIVER_BUFFER_PATH=/tmp/oap/trace_buffer1 \ + && SW_SERVICE_MESH_BUFFER_PATH=/tmp/oap/mesh_buffer1 \ + && start_oap 'init' + +echo 'starting Web app...' \ + && start_webapp '0.0.0.0' 8080 + +echo 'starting instrumented services...' \ + && start_instrumented_services + +check_tcp 127.0.0.1 \ + 9090 \ + 60 \ + 10 \ + "waiting for the instrumented service to be ready" + +if [[ $? -ne 0 ]]; then + echo "instrumented service failed to start in 30 * 10 seconds: " + cat ${SERVICE_LOG}/* + exit 1 +fi + +echo "SkyWalking e2e container is ready for tests" + +tail -f ${OAP_LOG_DIR}/* \ + ${WEBAPP_LOG_DIR}/* \ + ${SERVICE_LOG}/* \ + ${ES_HOME}/logs/stdout.log diff --git a/test/e2e/e2e-http-api-with-nginx-lua/src/main/java/org/apache/skywalking/e2e/sample/client/SampleClientApplication.java b/test/e2e/e2e-http-api-with-nginx-lua/src/main/java/org/apache/skywalking/e2e/sample/client/SampleClientApplication.java new file mode 100644 index 0000000000..c661c173e2 --- /dev/null +++ b/test/e2e/e2e-http-api-with-nginx-lua/src/main/java/org/apache/skywalking/e2e/sample/client/SampleClientApplication.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 + * + * http://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 org.apache.skywalking.e2e.sample.client; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SampleClientApplication { + public static void main(String[] args) { + SpringApplication.run(SampleClientApplication.class, args); + } +} diff --git a/test/e2e/e2e-http-api-with-nginx-lua/src/main/java/org/apache/skywalking/e2e/sample/client/TestController.java b/test/e2e/e2e-http-api-with-nginx-lua/src/main/java/org/apache/skywalking/e2e/sample/client/TestController.java new file mode 100644 index 0000000000..03319c73b0 --- /dev/null +++ b/test/e2e/e2e-http-api-with-nginx-lua/src/main/java/org/apache/skywalking/e2e/sample/client/TestController.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 + * + * http://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 org.apache.skywalking.e2e.sample.client; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/e2e") +public class TestController { + @GetMapping("/info") + public String hello() { + return "e2e-container-info"; + } +} diff --git a/test/e2e/e2e-http-api-with-nginx-lua/src/main/resources/application.yml b/test/e2e/e2e-http-api-with-nginx-lua/src/main/resources/application.yml new file mode 100644 index 0000000000..83d1e3df9a --- /dev/null +++ b/test/e2e/e2e-http-api-with-nginx-lua/src/main/resources/application.yml @@ -0,0 +1,22 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF 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 +# +# http://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. + +server: + port: 9090 + +spring: + main: + banner-mode: 'off' diff --git a/test/e2e/e2e-http-api-with-nginx-lua/src/test/java/org/apache/skywalking/e2e/SampleVerificationITCase.java b/test/e2e/e2e-http-api-with-nginx-lua/src/test/java/org/apache/skywalking/e2e/SampleVerificationITCase.java new file mode 100644 index 0000000000..33eafb4887 --- /dev/null +++ b/test/e2e/e2e-http-api-with-nginx-lua/src/test/java/org/apache/skywalking/e2e/SampleVerificationITCase.java @@ -0,0 +1,386 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 + * + * http://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 org.apache.skywalking.e2e; + +import java.io.InputStream; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.skywalking.e2e.metrics.AtLeastOneOfMetricsMatcher; +import org.apache.skywalking.e2e.metrics.Metrics; +import org.apache.skywalking.e2e.metrics.MetricsQuery; +import org.apache.skywalking.e2e.metrics.MetricsValueMatcher; +import org.apache.skywalking.e2e.service.Service; +import org.apache.skywalking.e2e.service.ServicesMatcher; +import org.apache.skywalking.e2e.service.ServicesQuery; +import org.apache.skywalking.e2e.service.endpoint.Endpoint; +import org.apache.skywalking.e2e.service.endpoint.EndpointQuery; +import org.apache.skywalking.e2e.service.endpoint.Endpoints; +import org.apache.skywalking.e2e.service.endpoint.EndpointsMatcher; +import org.apache.skywalking.e2e.service.instance.Instance; +import org.apache.skywalking.e2e.service.instance.Instances; +import org.apache.skywalking.e2e.service.instance.InstancesMatcher; +import org.apache.skywalking.e2e.service.instance.InstancesQuery; +import org.apache.skywalking.e2e.topo.Call; +import org.apache.skywalking.e2e.topo.ServiceInstanceTopoData; +import org.apache.skywalking.e2e.topo.ServiceInstanceTopoMatcher; +import org.apache.skywalking.e2e.topo.ServiceInstanceTopoQuery; +import org.apache.skywalking.e2e.topo.TopoData; +import org.apache.skywalking.e2e.topo.TopoMatcher; +import org.apache.skywalking.e2e.topo.TopoQuery; +import org.apache.skywalking.e2e.trace.Trace; +import org.apache.skywalking.e2e.trace.TracesMatcher; +import org.apache.skywalking.e2e.trace.TracesQuery; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.ClassPathResource; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.web.client.RestTemplate; +import org.yaml.snakeyaml.Yaml; + +import static org.apache.skywalking.e2e.metrics.MetricsMatcher.verifyMetrics; +import static org.apache.skywalking.e2e.metrics.MetricsQuery.ALL_ENDPOINT_METRICS; +import static org.apache.skywalking.e2e.metrics.MetricsQuery.ALL_INSTANCE_METRICS; +import static org.apache.skywalking.e2e.metrics.MetricsQuery.ALL_SERVICE_INSTANCE_RELATION_CLIENT_METRICS; +import static org.apache.skywalking.e2e.metrics.MetricsQuery.ALL_SERVICE_INSTANCE_RELATION_SERVER_METRICS; +import static org.apache.skywalking.e2e.metrics.MetricsQuery.ALL_SERVICE_METRICS; +import static org.apache.skywalking.e2e.metrics.MetricsQuery.ALL_SERVICE_RELATION_CLIENT_METRICS; +import static org.apache.skywalking.e2e.metrics.MetricsQuery.ALL_SERVICE_RELATION_SERVER_METRICS; +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(SpringJUnit4ClassRunner.class) +public class SampleVerificationITCase { + private static final Logger LOGGER = LoggerFactory.getLogger(SampleVerificationITCase.class); + + private final RestTemplate restTemplate = new RestTemplate(); + private final int retryInterval = 30; + + private SimpleQueryClient queryClient; + private String nginxServiceUrl; + + @Before + public void setUp() { + final String swWebappHost = System.getProperty("sw.webapp.host", "127.0.0.1"); + final String swWebappPort = System.getProperty("sw.webapp.port", "32783"); + final String nginxHost = System.getProperty("nginx.host", "127.0.0.1"); + final String nginxPort = System.getProperty("nginx.port", "32782"); + queryClient = new SimpleQueryClient(swWebappHost, swWebappPort); + nginxServiceUrl = "http://" + nginxHost + ":" + nginxPort; + } + + @Test(timeout = 1200000) + @DirtiesContext + public void verify() throws Exception { + final LocalDateTime minutesAgo = LocalDateTime.now(ZoneOffset.UTC); + + while (true) { + final List services = queryClient.services( + new ServicesQuery().start(minutesAgo).end(LocalDateTime.now())); + + if (!services.isEmpty() && services.size() >= 2) { + break; + } + Thread.sleep(10000L); + } + + while (true) { + try { + final Map user = new HashMap<>(); + user.put("name", "SkyWalking"); + final ResponseEntity responseEntity = restTemplate.getForEntity( + nginxServiceUrl + "/nginx/e2e/info", String.class); + LOGGER.info("responseEntity: {}", responseEntity); + assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(responseEntity.getBody()).isEqualTo("e2e-container-info"); + final List traces = queryClient.traces(new TracesQuery().start(minutesAgo) + .end(LocalDateTime.now()) + .orderByDuration()); + if (!traces.isEmpty() && traces.size() >= 2) { + break; + } + Thread.sleep(10000L); + } catch (Exception ignored) { + } + } + + doRetryableVerification(() -> { + try { + verifyTraces(minutesAgo); + } catch (Exception e) { + LOGGER.warn(e.getMessage(), e); + } + }); + + doRetryableVerification(() -> { + try { + verifyServices(minutesAgo); + } catch (Exception e) { + LOGGER.warn(e.getMessage(), e); + } + }); + + doRetryableVerification(() -> { + try { + verifyTopo(minutesAgo); + } catch (Exception e) { + LOGGER.warn(e.getMessage(), e); + } + }); + + doRetryableVerification(() -> { + try { + verifyServiceInstanceTopo(minutesAgo); + } catch (Exception e) { + LOGGER.warn(e.getMessage(), e); + } + }); + } + + private void verifyTopo(LocalDateTime minutesAgo) throws Exception { + final LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC); + + final TopoData topoData = queryClient.topo(new TopoQuery().stepByMinute() + .start(minutesAgo.minusDays(1)) + .end(now)); + LOGGER.info("topoData: {}", topoData); + + InputStream expectedInputStream = new ClassPathResource( + "expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.topo.yml") + .getInputStream(); + + final TopoMatcher topoMatcher = new Yaml().loadAs(expectedInputStream, TopoMatcher.class); + topoMatcher.verify(topoData); + verifyServiceRelationMetrics(topoData.getCalls(), minutesAgo); + } + + private void verifyServiceInstanceTopo(LocalDateTime minutesAgo) throws Exception { + final LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC); + + final ServiceInstanceTopoData topoData = queryClient.serviceInstanceTopo( + new ServiceInstanceTopoQuery().stepByMinute() + .start(minutesAgo + .minusDays(1)) + .end(now) + .clientServiceId("1") + .serverServiceId("3")); + LOGGER.info("instanceTopoData: {}", topoData); + + InputStream expectedInputStream = new ClassPathResource( + "expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.serviceInstanceTopo.yml") + .getInputStream(); + + final ServiceInstanceTopoMatcher topoMatcher = new Yaml().loadAs( + expectedInputStream, ServiceInstanceTopoMatcher.class); + topoMatcher.verify(topoData); + verifyServiceInstanceRelationMetrics(topoData.getCalls(), minutesAgo); + } + + private void verifyServices(LocalDateTime minutesAgo) throws Exception { + final LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC); + + final List services = queryClient.services(new ServicesQuery().start(minutesAgo).end(now)); + LOGGER.info("services: {}", services); + + InputStream expectedInputStream = new ClassPathResource( + "expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.services.yml") + .getInputStream(); + + final ServicesMatcher servicesMatcher = new Yaml().loadAs(expectedInputStream, ServicesMatcher.class); + servicesMatcher.verify(services); + + for (Service service : services) { + LOGGER.info("verifying service instances: {}", service); + + verifyServiceMetrics(service); + + Instances instances = verifyServiceInstances(minutesAgo, now, service); + + verifyInstancesMetrics(instances); + + Endpoints endpoints = verifyServiceEndpoints(minutesAgo, now, service); + + verifyEndpointsMetrics(endpoints); + } + } + + private Instances verifyServiceInstances(LocalDateTime minutesAgo, LocalDateTime now, + Service service) throws Exception { + Instances instances = queryClient.instances(new InstancesQuery().serviceId(service.getKey()) + .start(minutesAgo) + .end(now)); + LOGGER.info("instances: {}", instances); + InputStream expectedInputStream; + if ("User_Service_Name".equals(service.getLabel())) { + expectedInputStream = new ClassPathResource( + "expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.nginxInstances.yml") + .getInputStream(); + } else { + expectedInputStream = new ClassPathResource( + "expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.instances.yml") + .getInputStream(); + } + final InstancesMatcher instancesMatcher = new Yaml().loadAs(expectedInputStream, InstancesMatcher.class); + instancesMatcher.verify(instances); + return instances; + } + + private Endpoints verifyServiceEndpoints(LocalDateTime minutesAgo, LocalDateTime now, + Service service) throws Exception { + Endpoints endpoints = queryClient.endpoints(new EndpointQuery().serviceId(service.getKey())); + LOGGER.info("instances: {}", endpoints); + InputStream expectedInputStream; + + if ("/e2e/health-check".equals(endpoints.getEndpoints().get(0).getLabel())) { + expectedInputStream = new ClassPathResource( + "org.apache.skywalking.e2e.SampleVerificationITCase.nginxEndpoints.yml") + .getInputStream(); + } else { + expectedInputStream = new ClassPathResource( + "expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.endpoints.yml") + .getInputStream(); + } + final EndpointsMatcher endpointsMatcher = new Yaml().loadAs(expectedInputStream, EndpointsMatcher.class); + endpointsMatcher.verify(endpoints); + return endpoints; + } + + private void verifyInstancesMetrics(Instances instances) throws Exception { + for (Instance instance : instances.getInstances()) { + for (String metricsName : ALL_INSTANCE_METRICS) { + LOGGER.info("verifying service instance response time: {}", instance); + final Metrics instanceMetrics = queryClient.metrics(new MetricsQuery().stepByMinute() + .metricsName(metricsName) + .id(instance.getKey())); + LOGGER.info("instanceMetrics: {}", instanceMetrics); + AtLeastOneOfMetricsMatcher instanceRespTimeMatcher = new AtLeastOneOfMetricsMatcher(); + MetricsValueMatcher greaterThanZero = new MetricsValueMatcher(); + greaterThanZero.setValue("ge 0"); + instanceRespTimeMatcher.setValue(greaterThanZero); + instanceRespTimeMatcher.verify(instanceMetrics); + LOGGER.info("{}: {}", metricsName, instanceMetrics); + } + } + } + + private void verifyEndpointsMetrics(Endpoints endpoints) throws Exception { + for (Endpoint endpoint : endpoints.getEndpoints()) { + if (!endpoint.getLabel().equals("/e2e/info") || !endpoint.getLabel() + .equals("/nginx/e2e/info")) { + continue; + } + for (String metricName : ALL_ENDPOINT_METRICS) { + LOGGER.info("verifying endpoint {}, metrics: {}", endpoint, metricName); + final Metrics metrics = queryClient.metrics(new MetricsQuery().stepByMinute() + .metricsName(metricName) + .id(endpoint.getKey())); + LOGGER.info("metrics: {}", metrics); + AtLeastOneOfMetricsMatcher instanceRespTimeMatcher = new AtLeastOneOfMetricsMatcher(); + MetricsValueMatcher greaterThanZero = new MetricsValueMatcher(); + greaterThanZero.setValue("ge 0"); + instanceRespTimeMatcher.setValue(greaterThanZero); + instanceRespTimeMatcher.verify(metrics); + LOGGER.info("{}: {}", metricName, metrics); + } + } + } + + private void verifyServiceMetrics(Service service) throws Exception { + for (String metricName : ALL_SERVICE_METRICS) { + LOGGER.info("verifying service {}, metrics: {}", service, metricName); + final Metrics serviceMetrics = queryClient.metrics(new MetricsQuery().stepByMinute() + .metricsName(metricName) + .id(service.getKey())); + LOGGER.info("serviceMetrics: {}", serviceMetrics); + AtLeastOneOfMetricsMatcher instanceRespTimeMatcher = new AtLeastOneOfMetricsMatcher(); + MetricsValueMatcher greaterThanZero = new MetricsValueMatcher(); + greaterThanZero.setValue("ge 0"); + instanceRespTimeMatcher.setValue(greaterThanZero); + instanceRespTimeMatcher.verify(serviceMetrics); + LOGGER.info("{}: {}", metricName, serviceMetrics); + } + } + + private void verifyTraces(LocalDateTime minutesAgo) throws Exception { + final LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC); + + final List traces = queryClient.traces(new TracesQuery().start(minutesAgo).end(now).orderByDuration()); + LOGGER.info("traces: {}", traces); + + InputStream expectedInputStream = new ClassPathResource( + "expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.traces.yml") + .getInputStream(); + + final TracesMatcher tracesMatcher = new Yaml().loadAs(expectedInputStream, TracesMatcher.class); + tracesMatcher.verifyLoosely(traces); + } + + private void verifyServiceInstanceRelationMetrics(List calls, + final LocalDateTime minutesAgo) throws Exception { + verifyRelationMetrics( + calls, minutesAgo, ALL_SERVICE_INSTANCE_RELATION_CLIENT_METRICS, + ALL_SERVICE_INSTANCE_RELATION_SERVER_METRICS + ); + } + + private void verifyServiceRelationMetrics(List calls, final LocalDateTime minutesAgo) throws Exception { + verifyRelationMetrics( + calls, minutesAgo, ALL_SERVICE_RELATION_CLIENT_METRICS, ALL_SERVICE_RELATION_SERVER_METRICS); + } + + private void verifyRelationMetrics(List calls, final LocalDateTime minutesAgo, String[] relationClientMetrics, + String[] relationServerMetrics) throws Exception { + for (Call call : calls) { + for (String detectPoint : call.getDetectPoints()) { + switch (detectPoint) { + case "CLIENT": { + for (String metricName : relationClientMetrics) { + verifyMetrics(queryClient, metricName, call.getId(), minutesAgo); + } + break; + } + case "SERVER": { + for (String metricName : relationServerMetrics) { + verifyMetrics(queryClient, metricName, call.getId(), minutesAgo); + } + break; + } + } + } + } + } + + private void doRetryableVerification(Runnable runnable) throws InterruptedException { + while (true) { + try { + runnable.run(); + break; + } catch (Throwable ignored) { + Thread.sleep(retryInterval); + } + } + } +} diff --git a/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.endpoints.yml b/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.endpoints.yml new file mode 100644 index 0000000000..ed06316a40 --- /dev/null +++ b/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.endpoints.yml @@ -0,0 +1,28 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF 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 +# +# http://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. + +# 1 health-check by docker-maven-plugin +# 1 drop table if exists, because we have `ddl-auto: create-drop` +# 1 drop sequence +# 1 create sequence +# 1 create table statement + +endpoints: + - key: not null + label: /e2e/info + + + diff --git a/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.instances.yml b/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.instances.yml new file mode 100644 index 0000000000..26bb314d2b --- /dev/null +++ b/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.instances.yml @@ -0,0 +1,34 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF 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 +# +# http://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. + +# 1 health-check by docker-maven-plugin +# 1 drop table if exists, because we have `ddl-auto: create-drop` +# 1 drop sequence +# 1 create sequence +# 1 create table statement + +instances: + - key: 2 + label: not null + attributes: + - name: os_name + value: not null + - name: host_name + value: not null + - name: process_no + value: gt 0 + - name: ipv4s + value: not null diff --git a/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.serviceInstanceTopo.yml b/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.serviceInstanceTopo.yml new file mode 100644 index 0000000000..6661a99b46 --- /dev/null +++ b/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.serviceInstanceTopo.yml @@ -0,0 +1,41 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF 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 +# +# http://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. + +# 1 health-check by docker-maven-plugin +# 1 drop table if exists, because we have `ddl-auto: create-drop` +# 1 drop sequence +# 1 create sequence +# 1 create table statement + +nodes: + - id: 1 + name: User + type: USER + serviceId: 1 + serviceName: User + isReal: false + - id: 3 + name: not null + serviceId: 3 + serviceName: User_Service_Name + type: not null + isReal: true +calls: + - id: 1_3 + source: 1 + detectPoints: + - SERVER + target: 3 diff --git a/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.services.yml b/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.services.yml new file mode 100644 index 0000000000..ef0df12df8 --- /dev/null +++ b/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.services.yml @@ -0,0 +1,27 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF 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 +# +# http://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. + +# 1 health-check by docker-maven-plugin +# 1 drop table if exists, because we have `ddl-auto: create-drop` +# 1 drop sequence +# 1 create sequence +# 1 create table statement + +services: + - key: 2 + label: "Your_ApplicationName" + - key: 3 + label: "User_Service_Name" \ No newline at end of file diff --git a/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.topo.yml b/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.topo.yml new file mode 100644 index 0000000000..2ca7456216 --- /dev/null +++ b/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.topo.yml @@ -0,0 +1,47 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF 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 +# +# http://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. + +# 1 health-check by docker-maven-plugin +# 1 drop table if exists, because we have `ddl-auto: create-drop` +# 1 drop sequence +# 1 create sequence +# 1 create table statement + +nodes: + - id: 1 + name: User + type: USER + isReal: false + - id: 2 + name: Your_ApplicationName + type: Tomcat + isReal: true + - id: 3 + name: User_Service_Name + type: Nginx + isReal: true +calls: + - id: 1_3 + source: 1 + detectPoints: + - SERVER + target: 3 + - id: 3_2 + source: 3 + detectPoints: + - CLIENT + - SERVER + target: 2 \ No newline at end of file diff --git a/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.traces.yml b/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.traces.yml new file mode 100644 index 0000000000..0552a1ce44 --- /dev/null +++ b/test/e2e/e2e-http-api-with-nginx-lua/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.traces.yml @@ -0,0 +1,40 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF 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 +# +# http://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. + +# 1 health-check by docker-maven-plugin +# 1 drop table if exists, because we have `ddl-auto: create-drop` +# 1 drop sequence +# 1 create sequence +# 1 create table statement + +traces: + - key: not null + endpointNames: + - /e2e/info + duration: ge 0 + start: gt 0 + isError: false + traceIds: + - not null + + - key: not null + endpointNames: + - /nginx/e2e/info + duration: ge 0 + start: gt 0 + isError: false + traceIds: + - not null \ No newline at end of file diff --git a/test/e2e/pom.xml b/test/e2e/pom.xml index 0a6c8b520c..31790ae6f0 100644 --- a/test/e2e/pom.xml +++ b/test/e2e/pom.xml @@ -44,6 +44,7 @@ e2e-6.x-agent-7.x-oap-compatibility e2e-profile e2e-protocol + e2e-http-api-with-nginx-lua diff --git a/tools/dependencies/known-oap-backend-dependencies-es7.txt b/tools/dependencies/known-oap-backend-dependencies-es7.txt index 59c3f8df44..2f20ac243b 100755 --- a/tools/dependencies/known-oap-backend-dependencies-es7.txt +++ b/tools/dependencies/known-oap-backend-dependencies-es7.txt @@ -142,7 +142,8 @@ opencensus-contrib-grpc-metrics-0.24.0.jar parent-join-client-7.0.0.jar perfmark-api-0.19.0.jar proto-google-common-protos-1.12.0.jar -protobuf-java-3.4.0.jar +protobuf-java-3.11.4.jar +protobuf-java-util-3.11.4.jar rank-eval-client-7.0.0.jar reactive-streams-1.0.2.jar reflectasm-1.11.7.jar diff --git a/tools/dependencies/known-oap-backend-dependencies.txt b/tools/dependencies/known-oap-backend-dependencies.txt index 836cced4b0..8b9e85415f 100755 --- a/tools/dependencies/known-oap-backend-dependencies.txt +++ b/tools/dependencies/known-oap-backend-dependencies.txt @@ -140,7 +140,8 @@ opencensus-contrib-grpc-metrics-0.24.0.jar parent-join-client-6.3.2.jar perfmark-api-0.19.0.jar proto-google-common-protos-1.12.0.jar -protobuf-java-3.4.0.jar +protobuf-java-3.11.4.jar +protobuf-java-util-3.11.4.jar rank-eval-client-6.3.2.jar reactive-streams-1.0.2.jar reflectasm-1.11.7.jar -- GitLab