diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index 70daca447..88c101982 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -71,10 +71,12 @@ jobs: ./mvnw -Dcheckstyle.skip -Drat.skip -T2 -Dmaven.compile.fork -Dmaven.compiler.maxmem=3072 -DskipTests clean install ./mvnw -f test/e2e/pom.xml -pl e2e-base clean install - name: Cluster Tests (ES6/ZK/JDK8) - run: export E2E_VERSION=jdk8-1.3 && bash -x test/e2e/run.sh e2e-cluster/test-runner + run: export E2E_VERSION=jdk8-1.3 && bash -x test/e2e/run.sh e2e-cluster/e2e-cluster-test-runner + - name: Cluster With Gateway Tests (ES6/ZK/JDK8) + run: export E2E_VERSION=jdk8-1.3 && bash -x test/e2e/run.sh e2e-cluster-with-gateway/e2e-cluster-with-gateway-test-runner + - name: Cluster Tests (ES7/ZK/JDK8) + run: export E2E_VERSION=jdk8-1.3 DIST_PACKAGE=apache-skywalking-apm-bin-es7.tar.gz ES_VERSION=7.4.2 && bash -x test/e2e/run.sh e2e-cluster/e2e-cluster-test-runner - name: TTL ES Tests(JDK8) run: export E2E_VERSION=jdk8-1.3 && bash -x test/e2e/run.sh e2e-ttl/e2e-ttl-es - name: TTL ES7 Tests(JDK8) - run: export E2E_VERSION=jdk8-1.3 DIST_PACKAGE=apache-skywalking-apm-bin-es7.tar.gz ES_VERSION=7.0.0 && bash -x test/e2e/run.sh e2e-ttl/e2e-ttl-es - - name: Cluster Tests (ES7/ZK/JDK8) - run: export E2E_VERSION=jdk8-1.3 DIST_PACKAGE=apache-skywalking-apm-bin-es7.tar.gz ES_VERSION=7.0.0 && bash -x test/e2e/run.sh e2e-cluster/test-runner + run: export E2E_VERSION=jdk8-1.3 DIST_PACKAGE=apache-skywalking-apm-bin-es7.tar.gz ES_VERSION=7.4.2 && bash -x test/e2e/run.sh e2e-ttl/e2e-ttl-es diff --git a/docs/en/guides/storage-extention.md b/docs/en/guides/storage-extention.md index 2c95fa507..5436332f1 100644 --- a/docs/en/guides/storage-extention.md +++ b/docs/en/guides/storage-extention.md @@ -28,6 +28,11 @@ Here is the list of all DAO interfaces in storage 1. IAggregationQueryDAO 1. IAlarmQueryDAO 1. IHistoryDeleteDAO +1. IMetricsDAO +1. IRecordDAO +1. IRegisterDAO +1. ILogQueryDAO +1. ITopNRecordsQueryDAO ## Register all service implementations In `public void prepare()`, use `this#registerServiceImplementation` method to do register binding your implementation with the above interfaces. diff --git a/oap-server/server-bootstrap/src/main/resources/official_analysis.oal b/oap-server/server-bootstrap/src/main/resources/official_analysis.oal index f6263d8dd..da9b92ca2 100755 --- a/oap-server/server-bootstrap/src/main/resources/official_analysis.oal +++ b/oap-server/server-bootstrap/src/main/resources/official_analysis.oal @@ -53,6 +53,24 @@ service_relation_server_p75 = from(ServiceRelation.latency).filter(detectPoint = service_relation_client_p50 = from(ServiceRelation.latency).filter(detectPoint == DetectPoint.CLIENT).p50(10); service_relation_server_p50 = from(ServiceRelation.latency).filter(detectPoint == DetectPoint.SERVER).p50(10); +// Service Instance relation scope metrics for topology +service_instance_relation_client_cpm = from(ServiceInstanceRelation.*).filter(detectPoint == DetectPoint.CLIENT).cpm(); +service_instance_relation_server_cpm = from(ServiceInstanceRelation.*).filter(detectPoint == DetectPoint.SERVER).cpm(); +service_instance_relation_client_call_sla = from(ServiceInstanceRelation.*).filter(detectPoint == DetectPoint.CLIENT).percent(status == true); +service_instance_relation_server_call_sla = from(ServiceInstanceRelation.*).filter(detectPoint == DetectPoint.SERVER).percent(status == true); +service_instance_relation_client_resp_time = from(ServiceInstanceRelation.latency).filter(detectPoint == DetectPoint.CLIENT).longAvg(); +service_instance_relation_server_resp_time = from(ServiceInstanceRelation.latency).filter(detectPoint == DetectPoint.SERVER).longAvg(); +service_instance_relation_client_p99 = from(ServiceInstanceRelation.latency).filter(detectPoint == DetectPoint.CLIENT).p99(10); +service_instance_relation_server_p99 = from(ServiceInstanceRelation.latency).filter(detectPoint == DetectPoint.SERVER).p99(10); +service_instance_relation_client_p95 = from(ServiceInstanceRelation.latency).filter(detectPoint == DetectPoint.CLIENT).p95(10); +service_instance_relation_server_p95 = from(ServiceInstanceRelation.latency).filter(detectPoint == DetectPoint.SERVER).p95(10); +service_instance_relation_client_p90 = from(ServiceInstanceRelation.latency).filter(detectPoint == DetectPoint.CLIENT).p90(10); +service_instance_relation_server_p90 = from(ServiceInstanceRelation.latency).filter(detectPoint == DetectPoint.SERVER).p90(10); +service_instance_relation_client_p75 = from(ServiceInstanceRelation.latency).filter(detectPoint == DetectPoint.CLIENT).p75(10); +service_instance_relation_server_p75 = from(ServiceInstanceRelation.latency).filter(detectPoint == DetectPoint.SERVER).p75(10); +service_instance_relation_client_p50 = from(ServiceInstanceRelation.latency).filter(detectPoint == DetectPoint.CLIENT).p50(10); +service_instance_relation_server_p50 = from(ServiceInstanceRelation.latency).filter(detectPoint == DetectPoint.SERVER).p50(10); + // Service Instance Scope metrics service_instance_sla = from(ServiceInstance.*).percent(status == true); service_instance_resp_time= from(ServiceInstance.latency).longAvg(); diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/manual/relation/instance/ServiceInstanceCallRelationDispatcher.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/manual/relation/instance/ServiceInstanceCallRelationDispatcher.java new file mode 100644 index 000000000..894625dd4 --- /dev/null +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/manual/relation/instance/ServiceInstanceCallRelationDispatcher.java @@ -0,0 +1,65 @@ +/* + * 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.core.analysis.manual.relation.instance; + +import org.apache.skywalking.oap.server.core.analysis.SourceDispatcher; +import org.apache.skywalking.oap.server.core.analysis.worker.MetricsStreamProcessor; +import org.apache.skywalking.oap.server.core.source.ServiceInstanceRelation; + +/** + * @author zhangwei + */ +public class ServiceInstanceCallRelationDispatcher implements SourceDispatcher { + + @Override + public void dispatch(ServiceInstanceRelation source) { + switch (source.getDetectPoint()) { + case SERVER: + serverSide(source); + break; + case CLIENT: + clientSide(source); + break; + } + } + + private void serverSide(ServiceInstanceRelation source) { + ServiceInstanceRelationServerSideMetrics metrics = new ServiceInstanceRelationServerSideMetrics(); + metrics.setTimeBucket(source.getTimeBucket()); + metrics.setSourceServiceId(source.getSourceServiceId()); + metrics.setSourceServiceInstanceId(source.getSourceServiceInstanceId()); + metrics.setDestServiceId(source.getDestServiceId()); + metrics.setDestServiceInstanceId(source.getDestServiceInstanceId()); + metrics.setComponentId(source.getComponentId()); + metrics.buildEntityId(); + MetricsStreamProcessor.getInstance().in(metrics); + } + + private void clientSide(ServiceInstanceRelation source) { + ServiceInstanceRelationClientSideMetrics metrics = new ServiceInstanceRelationClientSideMetrics(); + metrics.setTimeBucket(source.getTimeBucket()); + metrics.setSourceServiceId(source.getSourceServiceId()); + metrics.setSourceServiceInstanceId(source.getSourceServiceInstanceId()); + metrics.setDestServiceId(source.getDestServiceId()); + metrics.setDestServiceInstanceId(source.getDestServiceInstanceId()); + metrics.setComponentId(source.getComponentId()); + metrics.buildEntityId(); + MetricsStreamProcessor.getInstance().in(metrics); + } +} diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/manual/relation/instance/ServiceInstanceRelationClientSideMetrics.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/manual/relation/instance/ServiceInstanceRelationClientSideMetrics.java new file mode 100644 index 000000000..2cac11d26 --- /dev/null +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/manual/relation/instance/ServiceInstanceRelationClientSideMetrics.java @@ -0,0 +1,209 @@ +/* + * 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.core.analysis.manual.relation.instance; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; +import org.apache.skywalking.oap.server.core.Const; +import org.apache.skywalking.oap.server.core.analysis.Stream; +import org.apache.skywalking.oap.server.core.analysis.manual.RelationDefineUtil; +import org.apache.skywalking.oap.server.core.analysis.metrics.Metrics; +import org.apache.skywalking.oap.server.core.analysis.worker.MetricsStreamProcessor; +import org.apache.skywalking.oap.server.core.remote.grpc.proto.RemoteData; +import org.apache.skywalking.oap.server.core.source.DefaultScopeDefine; +import org.apache.skywalking.oap.server.core.storage.StorageBuilder; +import org.apache.skywalking.oap.server.core.storage.annotation.Column; +import org.apache.skywalking.oap.server.core.storage.annotation.IDColumn; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author zhangwei + */ +@Stream(name = ServiceInstanceRelationClientSideMetrics.INDEX_NAME, scopeId = DefaultScopeDefine.SERVICE_INSTANCE_RELATION, builder = ServiceInstanceRelationClientSideMetrics.Builder.class, processor = MetricsStreamProcessor.class) +public class ServiceInstanceRelationClientSideMetrics extends Metrics { + + public static final String INDEX_NAME = "service_instance_relation_client_side"; + public static final String SOURCE_SERVICE_ID = "source_service_id"; + public static final String SOURCE_SERVICE_INSTANCE_ID = "source_service_instance_id"; + public static final String DEST_SERVICE_ID = "dest_service_id"; + public static final String DEST_SERVICE_INSTANCE_ID = "dest_service_instance_id"; + public static final String COMPONENT_ID = "component_id"; + + @Setter @Getter @Column(columnName = SOURCE_SERVICE_ID) private int sourceServiceId; + @Setter @Getter @Column(columnName = SOURCE_SERVICE_INSTANCE_ID) @IDColumn private int sourceServiceInstanceId; + @Setter @Getter @Column(columnName = DEST_SERVICE_ID) private int destServiceId; + @Setter @Getter @Column(columnName = DEST_SERVICE_INSTANCE_ID) @IDColumn private int destServiceInstanceId; + @Setter @Getter @Column(columnName = COMPONENT_ID) @IDColumn private int componentId; + @Setter(AccessLevel.PRIVATE) @Getter @Column(columnName = ENTITY_ID) @IDColumn private String entityId; + + @Override + public String id() { + String splitJointId = String.valueOf(getTimeBucket()); + splitJointId += Const.ID_SPLIT + RelationDefineUtil.buildEntityId( + new RelationDefineUtil.RelationDefine(sourceServiceInstanceId, destServiceInstanceId, componentId) + ); + return splitJointId; + } + + public void buildEntityId() { + String splitJointId = String.valueOf(sourceServiceInstanceId); + splitJointId += Const.ID_SPLIT + destServiceInstanceId; + splitJointId += Const.ID_SPLIT + componentId; + entityId = splitJointId; + } + + @Override + public void combine(Metrics metrics) { + + } + + @Override + public void calculate() { + + } + + @Override + public Metrics toHour() { + ServiceInstanceRelationClientSideMetrics metrics = new ServiceInstanceRelationClientSideMetrics(); + metrics.setTimeBucket(toTimeBucketInHour()); + metrics.setSourceServiceId(getSourceServiceId()); + metrics.setSourceServiceInstanceId(getSourceServiceInstanceId()); + metrics.setDestServiceId(getDestServiceId()); + metrics.setDestServiceInstanceId(getDestServiceInstanceId()); + metrics.setComponentId(getComponentId()); + metrics.setEntityId(getEntityId()); + return metrics; + } + + @Override + public Metrics toDay() { + ServiceInstanceRelationClientSideMetrics metrics = new ServiceInstanceRelationClientSideMetrics(); + metrics.setTimeBucket(toTimeBucketInHour()); + metrics.setSourceServiceId(getSourceServiceId()); + metrics.setSourceServiceInstanceId(getSourceServiceInstanceId()); + metrics.setDestServiceId(getDestServiceId()); + metrics.setDestServiceInstanceId(getDestServiceInstanceId()); + metrics.setComponentId(getComponentId()); + metrics.setEntityId(getEntityId()); + return metrics; + } + + @Override + public Metrics toMonth() { + ServiceInstanceRelationClientSideMetrics metrics = new ServiceInstanceRelationClientSideMetrics(); + metrics.setTimeBucket(toTimeBucketInHour()); + metrics.setSourceServiceId(getSourceServiceId()); + metrics.setSourceServiceInstanceId(getSourceServiceInstanceId()); + metrics.setDestServiceId(getDestServiceId()); + metrics.setDestServiceInstanceId(getDestServiceInstanceId()); + metrics.setComponentId(getComponentId()); + metrics.setEntityId(getEntityId()); + return metrics; + } + + @Override + public int remoteHashCode() { + int result = sourceServiceInstanceId; + result = 31 * result + destServiceInstanceId; + result = 31 * result + componentId; + return result; + } + + @Override + public void deserialize(RemoteData remoteData) { + setEntityId(remoteData.getDataStrings(0)); + + setSourceServiceId(remoteData.getDataIntegers(0)); + setSourceServiceInstanceId(remoteData.getDataIntegers(1)); + setDestServiceId(remoteData.getDataIntegers(2)); + setDestServiceInstanceId(remoteData.getDataIntegers(3)); + setComponentId(remoteData.getDataIntegers(4)); + + setTimeBucket(remoteData.getDataLongs(0)); + } + + @Override + public RemoteData.Builder serialize() { + RemoteData.Builder remoteBuilder = RemoteData.newBuilder(); + + remoteBuilder.addDataIntegers(getSourceServiceId()); + remoteBuilder.addDataIntegers(getSourceServiceInstanceId()); + remoteBuilder.addDataIntegers(getDestServiceId()); + remoteBuilder.addDataIntegers(getDestServiceInstanceId()); + remoteBuilder.addDataIntegers(getComponentId()); + + remoteBuilder.addDataStrings(getEntityId()); + + remoteBuilder.addDataLongs(getTimeBucket()); + return remoteBuilder; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ServiceInstanceRelationClientSideMetrics that = (ServiceInstanceRelationClientSideMetrics) o; + + if (sourceServiceInstanceId != that.sourceServiceInstanceId) return false; + if (destServiceInstanceId != that.destServiceInstanceId) return false; + return componentId == that.componentId; + } + + @Override + public int hashCode() { + int result = sourceServiceInstanceId; + result = 31 * result + destServiceInstanceId; + result = 31 * result + componentId; + return result; + } + + public static class Builder implements StorageBuilder { + + + @Override + public ServiceInstanceRelationClientSideMetrics map2Data(Map dbMap) { + ServiceInstanceRelationClientSideMetrics metrics = new ServiceInstanceRelationClientSideMetrics(); + metrics.setEntityId((String) dbMap.get(ENTITY_ID)); + metrics.setSourceServiceId(((Number)dbMap.get(SOURCE_SERVICE_ID)).intValue()); + metrics.setSourceServiceInstanceId(((Number) dbMap.get(SOURCE_SERVICE_INSTANCE_ID)).intValue()); + metrics.setDestServiceId(((Number)dbMap.get(DEST_SERVICE_ID)).intValue()); + metrics.setDestServiceInstanceId(((Number) dbMap.get(DEST_SERVICE_INSTANCE_ID)).intValue()); + metrics.setComponentId(((Number) dbMap.get(COMPONENT_ID)).intValue()); + metrics.setTimeBucket(((Number) dbMap.get(TIME_BUCKET)).longValue()); + return metrics; + } + + @Override + public Map data2Map(ServiceInstanceRelationClientSideMetrics storageData) { + Map map = new HashMap<>(); + map.put(ENTITY_ID, storageData.getEntityId()); + map.put(SOURCE_SERVICE_ID, storageData.getSourceServiceId()); + map.put(SOURCE_SERVICE_INSTANCE_ID, storageData.getSourceServiceInstanceId()); + map.put(DEST_SERVICE_ID, storageData.getDestServiceId()); + map.put(DEST_SERVICE_INSTANCE_ID, storageData.getDestServiceInstanceId()); + map.put(COMPONENT_ID, storageData.getComponentId()); + map.put(TIME_BUCKET, storageData.getTimeBucket()); + return map; + } + } +} diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/manual/relation/instance/ServiceInstanceRelationServerSideMetrics.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/manual/relation/instance/ServiceInstanceRelationServerSideMetrics.java new file mode 100644 index 000000000..decf0947e --- /dev/null +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/manual/relation/instance/ServiceInstanceRelationServerSideMetrics.java @@ -0,0 +1,208 @@ +/* + * 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.core.analysis.manual.relation.instance; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; +import org.apache.skywalking.oap.server.core.Const; +import org.apache.skywalking.oap.server.core.analysis.Stream; +import org.apache.skywalking.oap.server.core.analysis.manual.RelationDefineUtil; +import org.apache.skywalking.oap.server.core.analysis.metrics.Metrics; +import org.apache.skywalking.oap.server.core.analysis.worker.MetricsStreamProcessor; +import org.apache.skywalking.oap.server.core.remote.grpc.proto.RemoteData; +import org.apache.skywalking.oap.server.core.source.DefaultScopeDefine; +import org.apache.skywalking.oap.server.core.storage.StorageBuilder; +import org.apache.skywalking.oap.server.core.storage.annotation.Column; +import org.apache.skywalking.oap.server.core.storage.annotation.IDColumn; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author zhangwei + */ +@Stream(name = ServiceInstanceRelationServerSideMetrics.INDEX_NAME, scopeId = DefaultScopeDefine.SERVICE_INSTANCE_RELATION, builder = ServiceInstanceRelationServerSideMetrics.Builder.class, processor = MetricsStreamProcessor.class) +public class ServiceInstanceRelationServerSideMetrics extends Metrics { + + public static final String INDEX_NAME = "service_instance_relation_server_side"; + public static final String SOURCE_SERVICE_ID = "source_service_id"; + public static final String SOURCE_SERVICE_INSTANCE_ID = "source_service_instance_id"; + public static final String DEST_SERVICE_ID = "dest_service_id"; + public static final String DEST_SERVICE_INSTANCE_ID = "dest_service_instance_id"; + public static final String COMPONENT_ID = "component_id"; + + @Setter @Getter @Column(columnName = SOURCE_SERVICE_ID) private int sourceServiceId; + @Setter @Getter @Column(columnName = SOURCE_SERVICE_INSTANCE_ID) @IDColumn private int sourceServiceInstanceId; + @Setter @Getter @Column(columnName = DEST_SERVICE_ID) private int destServiceId; + @Setter @Getter @Column(columnName = DEST_SERVICE_INSTANCE_ID) @IDColumn private int destServiceInstanceId; + @Setter @Getter @Column(columnName = COMPONENT_ID) @IDColumn private int componentId; + @Setter(AccessLevel.PRIVATE) @Getter @Column(columnName = ENTITY_ID) @IDColumn private String entityId; + + @Override + public String id() { + String splitJointId = String.valueOf(getTimeBucket()); + splitJointId += Const.ID_SPLIT + RelationDefineUtil.buildEntityId( + new RelationDefineUtil.RelationDefine(sourceServiceInstanceId, destServiceInstanceId, componentId) + ); + return splitJointId; + } + + public void buildEntityId() { + String splitJointId = String.valueOf(sourceServiceInstanceId); + splitJointId += Const.ID_SPLIT + destServiceInstanceId; + splitJointId += Const.ID_SPLIT + componentId; + entityId = splitJointId; + } + + @Override + public void combine(Metrics metrics) { + + } + + @Override + public void calculate() { + + } + + @Override + public Metrics toHour() { + ServiceInstanceRelationServerSideMetrics metrics = new ServiceInstanceRelationServerSideMetrics(); + metrics.setTimeBucket(toTimeBucketInHour()); + metrics.setSourceServiceId(getSourceServiceId()); + metrics.setSourceServiceInstanceId(getSourceServiceInstanceId()); + metrics.setDestServiceId(getDestServiceId()); + metrics.setDestServiceInstanceId(getDestServiceInstanceId()); + metrics.setComponentId(getComponentId()); + metrics.setEntityId(getEntityId()); + return metrics; + } + + @Override + public Metrics toDay() { + ServiceInstanceRelationServerSideMetrics metrics = new ServiceInstanceRelationServerSideMetrics(); + metrics.setTimeBucket(toTimeBucketInDay()); + metrics.setSourceServiceId(getSourceServiceId()); + metrics.setSourceServiceInstanceId(getSourceServiceInstanceId()); + metrics.setDestServiceId(getDestServiceId()); + metrics.setDestServiceInstanceId(getDestServiceInstanceId()); + metrics.setComponentId(getComponentId()); + metrics.setEntityId(getEntityId()); + return metrics; + } + + @Override + public Metrics toMonth() { + ServiceInstanceRelationServerSideMetrics metrics = new ServiceInstanceRelationServerSideMetrics(); + metrics.setTimeBucket(toTimeBucketInMonth()); + metrics.setSourceServiceId(getSourceServiceId()); + metrics.setSourceServiceInstanceId(getSourceServiceInstanceId()); + metrics.setDestServiceId(getDestServiceId()); + metrics.setDestServiceInstanceId(getDestServiceInstanceId()); + metrics.setComponentId(getComponentId()); + metrics.setEntityId(getEntityId()); + return metrics; + } + + @Override + public int remoteHashCode() { + int result = sourceServiceInstanceId; + result = 31 * result + destServiceInstanceId; + result = 31 * result + componentId; + return result; + } + + @Override + public void deserialize(RemoteData remoteData) { + setEntityId(remoteData.getDataStrings(0)); + + setSourceServiceId(remoteData.getDataIntegers(0)); + setSourceServiceInstanceId(remoteData.getDataIntegers(1)); + setDestServiceId(remoteData.getDataIntegers(2)); + setDestServiceInstanceId(remoteData.getDataIntegers(3)); + setComponentId(remoteData.getDataIntegers(4)); + + setTimeBucket(remoteData.getDataLongs(0)); + } + + @Override + public RemoteData.Builder serialize() { + RemoteData.Builder remoteBuilder = RemoteData.newBuilder(); + + remoteBuilder.addDataIntegers(getSourceServiceId()); + remoteBuilder.addDataIntegers(getSourceServiceInstanceId()); + remoteBuilder.addDataIntegers(getDestServiceId()); + remoteBuilder.addDataIntegers(getDestServiceInstanceId()); + remoteBuilder.addDataIntegers(getComponentId()); + + remoteBuilder.addDataStrings(getEntityId()); + + remoteBuilder.addDataLongs(getTimeBucket()); + return remoteBuilder; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ServiceInstanceRelationServerSideMetrics that = (ServiceInstanceRelationServerSideMetrics) o; + + if (sourceServiceInstanceId != that.sourceServiceInstanceId) return false; + if (destServiceInstanceId != that.destServiceInstanceId) return false; + return componentId == that.componentId; + } + + @Override + public int hashCode() { + int result = sourceServiceInstanceId; + result = 31 * result + destServiceInstanceId; + result = 31 * result + componentId; + return result; + } + + public static class Builder implements StorageBuilder { + + @Override + public ServiceInstanceRelationServerSideMetrics map2Data(Map dbMap) { + ServiceInstanceRelationServerSideMetrics metrics = new ServiceInstanceRelationServerSideMetrics(); + metrics.setEntityId((String) dbMap.get(ENTITY_ID)); + metrics.setSourceServiceId(((Number)dbMap.get(SOURCE_SERVICE_ID)).intValue()); + metrics.setSourceServiceInstanceId(((Number) dbMap.get(SOURCE_SERVICE_INSTANCE_ID)).intValue()); + metrics.setDestServiceId(((Number)dbMap.get(DEST_SERVICE_ID)).intValue()); + metrics.setDestServiceInstanceId(((Number) dbMap.get(DEST_SERVICE_INSTANCE_ID)).intValue()); + metrics.setComponentId(((Number) dbMap.get(COMPONENT_ID)).intValue()); + metrics.setTimeBucket(((Number) dbMap.get(TIME_BUCKET)).longValue()); + return metrics; + } + + @Override + public Map data2Map(ServiceInstanceRelationServerSideMetrics storageData) { + Map map = new HashMap<>(); + map.put(ENTITY_ID, storageData.getEntityId()); + map.put(SOURCE_SERVICE_ID, storageData.getSourceServiceId()); + map.put(SOURCE_SERVICE_INSTANCE_ID, storageData.getSourceServiceInstanceId()); + map.put(DEST_SERVICE_ID, storageData.getDestServiceId()); + map.put(DEST_SERVICE_INSTANCE_ID, storageData.getDestServiceInstanceId()); + map.put(COMPONENT_ID, storageData.getComponentId()); + map.put(TIME_BUCKET, storageData.getTimeBucket()); + return map; + } + } +} diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/manual/servicerelation/ServiceCallRelationDispatcher.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/manual/relation/service/ServiceCallRelationDispatcher.java similarity index 96% rename from oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/manual/servicerelation/ServiceCallRelationDispatcher.java rename to oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/manual/relation/service/ServiceCallRelationDispatcher.java index b9468fbfa..f2ab87780 100644 --- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/manual/servicerelation/ServiceCallRelationDispatcher.java +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/manual/relation/service/ServiceCallRelationDispatcher.java @@ -16,7 +16,7 @@ * */ -package org.apache.skywalking.oap.server.core.analysis.manual.servicerelation; +package org.apache.skywalking.oap.server.core.analysis.manual.relation.service; import org.apache.skywalking.oap.server.core.analysis.SourceDispatcher; import org.apache.skywalking.oap.server.core.analysis.worker.MetricsStreamProcessor; diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/manual/servicerelation/ServiceRelationClientSideMetrics.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/manual/relation/service/ServiceRelationClientSideMetrics.java similarity index 98% rename from oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/manual/servicerelation/ServiceRelationClientSideMetrics.java rename to oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/manual/relation/service/ServiceRelationClientSideMetrics.java index 6f34e42fc..4481baad2 100644 --- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/manual/servicerelation/ServiceRelationClientSideMetrics.java +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/manual/relation/service/ServiceRelationClientSideMetrics.java @@ -16,7 +16,7 @@ * */ -package org.apache.skywalking.oap.server.core.analysis.manual.servicerelation; +package org.apache.skywalking.oap.server.core.analysis.manual.relation.service; import java.util.*; import lombok.*; diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/manual/servicerelation/ServiceRelationServerSideMetrics.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/manual/relation/service/ServiceRelationServerSideMetrics.java similarity index 98% rename from oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/manual/servicerelation/ServiceRelationServerSideMetrics.java rename to oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/manual/relation/service/ServiceRelationServerSideMetrics.java index 12de7873e..658a248d8 100644 --- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/manual/servicerelation/ServiceRelationServerSideMetrics.java +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/manual/relation/service/ServiceRelationServerSideMetrics.java @@ -16,7 +16,7 @@ * */ -package org.apache.skywalking.oap.server.core.analysis.manual.servicerelation; +package org.apache.skywalking.oap.server.core.analysis.manual.relation.service; import java.util.*; import lombok.*; diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/cache/CacheUpdateTimer.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/cache/CacheUpdateTimer.java index edd4ccf19..f209b0a90 100644 --- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/cache/CacheUpdateTimer.java +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/cache/CacheUpdateTimer.java @@ -54,6 +54,7 @@ public enum CacheUpdateTimer { private void update(ModuleDefineHolder moduleDefineHolder) { updateServiceInventory(moduleDefineHolder); + updateServiceInstanceInventory(moduleDefineHolder); updateNetAddressInventory(moduleDefineHolder); } @@ -77,6 +78,26 @@ public enum CacheUpdateTimer { }); } + private void updateServiceInstanceInventory(ModuleDefineHolder moduleDefineHolder) { + IServiceInstanceInventoryCacheDAO instanceInventoryCacheDAO = moduleDefineHolder.find(StorageModule.NAME).provider().getService(IServiceInstanceInventoryCacheDAO.class); + ServiceInstanceInventoryCache instanceInventoryCache = moduleDefineHolder.find(CoreModule.NAME).provider().getService(ServiceInstanceInventoryCache.class); + List instanceInventories = instanceInventoryCacheDAO.loadLastUpdate(System.currentTimeMillis() - 60000); + + instanceInventories.forEach(instanceInventory -> { + ServiceInstanceInventory cache = instanceInventoryCache.get(instanceInventory.getSequence()); + if (Objects.nonNull(cache)) { + if (cache.getMappingServiceInstanceId() != instanceInventory.getMappingServiceInstanceId()) { + cache.setMappingServiceInstanceId(instanceInventory.getMappingServiceInstanceId()); + cache.setServiceInstanceNodeType(instanceInventory.getServiceInstanceNodeType()); + cache.setProperties(instanceInventory.getProperties()); + logger.info("Update the cache of service instance inventory, instance id: {}", instanceInventory.getSequence()); + } + } else { + logger.warn("Unable to found the id of {} in service instance inventory cache.", instanceInventory.getSequence()); + } + }); + } + private void updateNetAddressInventory(ModuleDefineHolder moduleDefineHolder) { INetworkAddressInventoryCacheDAO addressInventoryCacheDAO = moduleDefineHolder.find(StorageModule.NAME).provider().getService(INetworkAddressInventoryCacheDAO.class); NetworkAddressInventoryCache addressInventoryCache = moduleDefineHolder.find(CoreModule.NAME).provider().getService(NetworkAddressInventoryCache.class); diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/ServiceInstanceTopologyBuilder.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/ServiceInstanceTopologyBuilder.java new file mode 100644 index 000000000..9f507a141 --- /dev/null +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/ServiceInstanceTopologyBuilder.java @@ -0,0 +1,206 @@ +/* + * 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.core.query; + +import groovy.util.logging.Slf4j; +import org.apache.skywalking.oap.server.core.Const; +import org.apache.skywalking.oap.server.core.CoreModule; +import org.apache.skywalking.oap.server.core.cache.ServiceInstanceInventoryCache; +import org.apache.skywalking.oap.server.core.cache.ServiceInventoryCache; +import org.apache.skywalking.oap.server.core.config.IComponentLibraryCatalogService; +import org.apache.skywalking.oap.server.core.query.entity.*; +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.source.DetectPoint; +import org.apache.skywalking.oap.server.library.module.ModuleManager; +import org.apache.skywalking.oap.server.library.util.BooleanUtils; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import static java.util.Objects.isNull; + +/** + * @author zhangwei + */ +@Slf4j +public class ServiceInstanceTopologyBuilder { + + private final ServiceInventoryCache serviceInventoryCache; + private final ServiceInstanceInventoryCache serviceInstanceInventoryCache; + private final IComponentLibraryCatalogService componentLibraryCatalogService; + + public ServiceInstanceTopologyBuilder(ModuleManager moduleManager) { + this.serviceInventoryCache = moduleManager.find(CoreModule.NAME).provider().getService(ServiceInventoryCache.class); + this.serviceInstanceInventoryCache = moduleManager.find(CoreModule.NAME).provider().getService(ServiceInstanceInventoryCache.class); + this.componentLibraryCatalogService = moduleManager.find(CoreModule.NAME).provider().getService(IComponentLibraryCatalogService.class); + } + + ServiceInstanceTopology build(List serviceInstanceRelationClientCalls, List serviceInstanceRelationServerCalls) { + filterZeroSourceOrTargetReference(serviceInstanceRelationClientCalls); + filterZeroSourceOrTargetReference(serviceInstanceRelationServerCalls); + + Map nodes = new HashMap<>(); + List calls = new LinkedList<>(); + HashMap callMap = new HashMap<>(); + + for (Call.CallDetail clientCall : serviceInstanceRelationClientCalls) { + ServiceInstanceInventory sourceInstance = serviceInstanceInventoryCache.get(clientCall.getSource()); + ServiceInstanceInventory targetInstance = serviceInstanceInventoryCache.get(clientCall.getTarget()); + + if (isNull(sourceInstance) || isNull(targetInstance)) { + continue; + } + + if (targetInstance.getMappingServiceInstanceId() != Const.NONE) { + continue; + } + + if (!nodes.containsKey(sourceInstance.getSequence())) { + ServiceInventory sourceService = serviceInventoryCache.get(sourceInstance.getServiceId()); + nodes.put(sourceInstance.getSequence(), buildNode(sourceService, sourceInstance)); + } + + if (!nodes.containsKey(targetInstance.getSequence())) { + ServiceInventory targetService = serviceInventoryCache.get(targetInstance.getServiceId()); + nodes.put(targetInstance.getSequence(), buildNode(targetService, targetInstance)); + if (BooleanUtils.valueToBoolean(targetInstance.getIsAddress())) { + nodes.get(targetInstance.getSequence()).setType(componentLibraryCatalogService.getServerNameBasedOnComponent(clientCall.getComponentId())); + } + } + + String callId = sourceInstance.getSequence() + Const.ID_SPLIT + targetInstance.getSequence(); + if (!callMap.containsKey(callId)) { + Call call = new Call(); + + callMap.put(callId, call); + + call.setSource(clientCall.getSource()); + call.setTarget(clientCall.getTarget()); + call.setId(clientCall.getId()); + call.addDetectPoint(DetectPoint.CLIENT); + call.addSourceComponent(componentLibraryCatalogService.getComponentName(clientCall.getComponentId())); + calls.add(call); + } else { + Call call = callMap.get(callId); + call.addDetectPoint(DetectPoint.CLIENT); + call.addSourceComponent(componentLibraryCatalogService.getComponentName(clientCall.getComponentId())); + } + } + + for (Call.CallDetail serverCall : serviceInstanceRelationServerCalls) { + ServiceInstanceInventory sourceInstance = serviceInstanceInventoryCache.get(serverCall.getSource()); + ServiceInstanceInventory targetInstance = serviceInstanceInventoryCache.get(serverCall.getTarget()); + + if (isNull(sourceInstance) || isNull(targetInstance)) { + continue; + } + + if (sourceInstance.getSequence() == Const.USER_INSTANCE_ID) { + if (!nodes.containsKey(sourceInstance.getSequence())) { + ServiceInstanceNode visualUserNode = new ServiceInstanceNode(); + visualUserNode.setId(sourceInstance.getSequence()); + visualUserNode.setName(Const.USER_CODE); + visualUserNode.setServiceId(Const.USER_SERVICE_ID); + visualUserNode.setServiceName(Const.USER_CODE); + visualUserNode.setType(Const.USER_CODE.toUpperCase()); + visualUserNode.setReal(false); + nodes.put(sourceInstance.getSequence(), visualUserNode); + } + } + + if (BooleanUtils.valueToBoolean(sourceInstance.getIsAddress())) { + if (!nodes.containsKey(sourceInstance.getSequence())) { + ServiceInventory sourceService = serviceInventoryCache.get(sourceInstance.getServiceId()); + ServiceInstanceNode conjecturalNode = new ServiceInstanceNode(); + conjecturalNode.setId(sourceInstance.getSequence()); + conjecturalNode.setName(sourceInstance.getName()); + conjecturalNode.setServiceId(sourceService.getSequence()); + conjecturalNode.setServiceName(sourceService.getName()); + conjecturalNode.setType(componentLibraryCatalogService.getServerNameBasedOnComponent(serverCall.getComponentId())); + conjecturalNode.setReal(true); + nodes.put(sourceInstance.getSequence(), conjecturalNode); + } + } + + String callId = sourceInstance.getSequence() + Const.ID_SPLIT + targetInstance.getSequence(); + if (!callMap.containsKey(callId)) { + Call call = new Call(); + callMap.put(callId, call); + + call.setSource(serverCall.getSource()); + call.setTarget(serverCall.getTarget()); + call.setId(callId); + call.addDetectPoint(DetectPoint.SERVER); + call.addTargetComponent(componentLibraryCatalogService.getComponentName(serverCall.getComponentId())); + + calls.add(call); + } else { + Call call = callMap.get(callId); + + call.addDetectPoint(DetectPoint.SERVER); + call.addTargetComponent(componentLibraryCatalogService.getComponentName(serverCall.getComponentId())); + } + + if (!nodes.containsKey(sourceInstance.getSequence())) { + ServiceInventory sourceService = serviceInventoryCache.get(sourceInstance.getServiceId()); + nodes.put(sourceInstance.getSequence(), buildNode(sourceService, sourceInstance)); + } + + if (!nodes.containsKey(targetInstance.getSequence())) { + ServiceInventory targetService = serviceInventoryCache.get(targetInstance.getServiceId()); + nodes.put(targetInstance.getSequence(), buildNode(targetService, targetInstance)); + } + + if (nodes.containsKey(targetInstance.getSequence())) { + nodes.get(targetInstance.getSequence()).setType(componentLibraryCatalogService.getComponentName(serverCall.getComponentId())); + } + } + + ServiceInstanceTopology topology = new ServiceInstanceTopology(); + topology.getCalls().addAll(calls); + topology.getNodes().addAll(nodes.values()); + return topology; + } + + private ServiceInstanceNode buildNode(ServiceInventory serviceInventory, ServiceInstanceInventory instanceInventory) { + ServiceInstanceNode instanceNode = new ServiceInstanceNode(); + instanceNode.setId(instanceInventory.getSequence()); + instanceNode.setName(instanceInventory.getName()); + instanceNode.setServiceId(serviceInventory.getSequence()); + instanceNode.setServiceName(serviceInventory.getName()); + if (BooleanUtils.valueToBoolean(instanceInventory.getIsAddress())) { + instanceNode.setReal(false); + } else { + instanceNode.setReal(true); + } + return instanceNode; + } + + private void filterZeroSourceOrTargetReference(List serviceRelationClientCalls) { + for (int i = serviceRelationClientCalls.size() - 1; i >= 0; i--) { + Call.CallDetail call = serviceRelationClientCalls.get(i); + if (call.getSource() == 0 || call.getTarget() == 0) { + serviceRelationClientCalls.remove(i); + } + } + } +} diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/TopologyQueryService.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/TopologyQueryService.java index c06cd1015..5b52cfd0d 100644 --- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/TopologyQueryService.java +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/TopologyQueryService.java @@ -19,11 +19,6 @@ package org.apache.skywalking.oap.server.core.query; import com.google.common.base.Strings; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; import org.apache.skywalking.oap.server.core.Const; import org.apache.skywalking.oap.server.core.CoreModule; import org.apache.skywalking.oap.server.core.analysis.Downsampling; @@ -31,6 +26,7 @@ import org.apache.skywalking.oap.server.core.cache.EndpointInventoryCache; import org.apache.skywalking.oap.server.core.config.IComponentLibraryCatalogService; import org.apache.skywalking.oap.server.core.query.entity.Call; import org.apache.skywalking.oap.server.core.query.entity.Node; +import org.apache.skywalking.oap.server.core.query.entity.ServiceInstanceTopology; import org.apache.skywalking.oap.server.core.query.entity.Topology; import org.apache.skywalking.oap.server.core.source.DetectPoint; import org.apache.skywalking.oap.server.core.storage.StorageModule; @@ -42,6 +38,12 @@ import org.apache.skywalking.oap.server.library.util.CollectionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + /** * @author peng-yongsheng */ @@ -93,9 +95,7 @@ public class TopologyQueryService implements Service { List serviceRelationClientCalls = getTopologyQueryDAO().loadClientSideServiceRelations(downsampling, startTB, endTB); TopologyBuilder builder = new TopologyBuilder(moduleManager); - Topology topology = builder.build(serviceRelationClientCalls, serviceRelationServerCalls); - - return topology; + return builder.build(serviceRelationClientCalls, serviceRelationServerCalls); } public Topology getServiceTopology(final Downsampling downsampling, final long startTB, final long endTB, final int serviceId) throws IOException { @@ -127,6 +127,16 @@ public class TopologyQueryService implements Service { return topology; } + public ServiceInstanceTopology getServiceInstanceTopology(final int clientServiceId, final int serverServiceId, final Downsampling downsampling, final long startTB, final long endTB) throws IOException { + logger.debug("ClientServiceId: {}, ServerServiceId: {}, Downsampling: {}, startTimeBucket: {}, endTimeBucket: {}", clientServiceId, serverServiceId, downsampling, startTB, endTB); + + List serviceInstanceRelationClientCalls = getTopologyQueryDAO().loadClientSideServiceInstanceRelations(clientServiceId, serverServiceId, downsampling, startTB, endTB); + List serviceInstanceRelationServerCalls = getTopologyQueryDAO().loadServerSideServiceInstanceRelations(clientServiceId, serverServiceId, downsampling, startTB, endTB); + + ServiceInstanceTopologyBuilder builder = new ServiceInstanceTopologyBuilder(moduleManager); + return builder.build(serviceInstanceRelationClientCalls, serviceInstanceRelationServerCalls); + } + public Topology getEndpointTopology(final Downsampling downsampling, final long startTB, final long endTB, final int endpointId) throws IOException { List serverSideCalls = getTopologyQueryDAO().loadSpecifiedDestOfServerSideEndpointRelations(downsampling, startTB, endTB, endpointId); diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/entity/ServiceInstanceNode.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/entity/ServiceInstanceNode.java new file mode 100644 index 000000000..23df1ad19 --- /dev/null +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/entity/ServiceInstanceNode.java @@ -0,0 +1,37 @@ +/* + * 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.core.query.entity; + +import lombok.Getter; +import lombok.Setter; + +/** + * @author zhangwei + */ +@Setter +@Getter +public class ServiceInstanceNode { + + private int id; + private String name; + private int serviceId; + private String serviceName; + private String type; + private boolean isReal; +} diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/entity/ServiceInstanceTopology.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/entity/ServiceInstanceTopology.java new file mode 100644 index 000000000..8c2f34200 --- /dev/null +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/entity/ServiceInstanceTopology.java @@ -0,0 +1,39 @@ +/* + * 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.core.query.entity; + +import lombok.Getter; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author zhangwei + */ +@Getter +public class ServiceInstanceTopology { + + private final List nodes; + private final List calls; + + public ServiceInstanceTopology() { + this.nodes = new ArrayList<>(); + this.calls = new ArrayList<>(); + } +} diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/register/ServiceInstanceInventory.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/register/ServiceInstanceInventory.java index 34f53a5b7..0f9ffa1bd 100644 --- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/register/ServiceInstanceInventory.java +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/register/ServiceInstanceInventory.java @@ -52,8 +52,10 @@ public class ServiceInstanceInventory extends RegisterSource { public static final String NAME = "name"; public static final String INSTANCE_UUID = "instance_uuid"; public static final String SERVICE_ID = "service_id"; - private static final String IS_ADDRESS = "is_address"; + public static final String IS_ADDRESS = "is_address"; private static final String ADDRESS_ID = "address_id"; + public static final String NODE_TYPE = "node_type"; + public static final String MAPPING_SERVICE_ID = "mapping_service_instance_id"; public static final String PROPERTIES = "properties"; private static final Gson GSON = new Gson(); @@ -62,9 +64,13 @@ public class ServiceInstanceInventory extends RegisterSource { @Setter @Getter @Column(columnName = SERVICE_ID) private int serviceId; @Setter @Getter @Column(columnName = IS_ADDRESS) private int isAddress; @Setter @Getter @Column(columnName = ADDRESS_ID) private int addressId; + @Setter(AccessLevel.PRIVATE) @Getter(AccessLevel.PACKAGE) @Column(columnName = NODE_TYPE) private int nodeType; + @Setter @Getter @Column(columnName = MAPPING_SERVICE_ID) private int mappingServiceInstanceId; @Getter(AccessLevel.PRIVATE) @Column(columnName = PROPERTIES) private String prop; @Getter private JsonObject properties; + @Setter @Getter private boolean resetServiceInstanceMapping = false; + public static String buildId(int serviceId, String uuid) { return serviceId + Const.ID_SPLIT + uuid + Const.ID_SPLIT + BooleanUtils.FALSE + Const.ID_SPLIT + Const.NONE; } @@ -73,6 +79,14 @@ public class ServiceInstanceInventory extends RegisterSource { return serviceId + Const.ID_SPLIT + BooleanUtils.TRUE + Const.ID_SPLIT + addressId; } + public NodeType getServiceInstanceNodeType() { + return NodeType.get(nodeType); + } + + public void setServiceInstanceNodeType(NodeType nodeType) { + this.nodeType = nodeType.value(); + } + @Override public String id() { if (BooleanUtils.TRUE == isAddress) { return buildId(serviceId, addressId); @@ -129,12 +143,35 @@ public class ServiceInstanceInventory extends RegisterSource { return true; } + public ServiceInstanceInventory getClone() { + ServiceInstanceInventory inventory = new ServiceInstanceInventory(); + inventory.setInstanceUUID(instanceUUID); + inventory.setName(name); + inventory.setServiceId(serviceId); + inventory.setIsAddress(isAddress); + inventory.setAddressId(addressId); + inventory.setNodeType(nodeType); + inventory.setMappingServiceInstanceId(mappingServiceInstanceId); + inventory.setProp(prop); + inventory.setResetServiceInstanceMapping(resetServiceInstanceMapping); + + inventory.setSequence(getSequence()); + inventory.setRegisterTime(getRegisterTime()); + inventory.setHeartbeatTime(getHeartbeatTime()); + inventory.setLastUpdateTime(getLastUpdateTime()); + + return inventory; + } + @Override public RemoteData.Builder serialize() { RemoteData.Builder remoteBuilder = RemoteData.newBuilder(); remoteBuilder.addDataIntegers(getSequence()); remoteBuilder.addDataIntegers(serviceId); remoteBuilder.addDataIntegers(isAddress); remoteBuilder.addDataIntegers(addressId); + remoteBuilder.addDataIntegers(nodeType); + remoteBuilder.addDataIntegers(mappingServiceInstanceId); + remoteBuilder.addDataIntegers(resetServiceInstanceMapping ? 1 : 0); remoteBuilder.addDataLongs(getRegisterTime()); remoteBuilder.addDataLongs(getHeartbeatTime()); @@ -143,6 +180,7 @@ public class ServiceInstanceInventory extends RegisterSource { remoteBuilder.addDataStrings(Strings.isNullOrEmpty(name) ? Const.EMPTY_STRING : name); remoteBuilder.addDataStrings(Strings.isNullOrEmpty(instanceUUID) ? Const.EMPTY_STRING : instanceUUID); remoteBuilder.addDataStrings(Strings.isNullOrEmpty(prop) ? Const.EMPTY_STRING : prop); + return remoteBuilder; } @@ -151,6 +189,9 @@ public class ServiceInstanceInventory extends RegisterSource { setServiceId(remoteData.getDataIntegers(1)); setIsAddress(remoteData.getDataIntegers(2)); setAddressId(remoteData.getDataIntegers(3)); + setNodeType(remoteData.getDataIntegers(4)); + setMappingServiceInstanceId(remoteData.getDataIntegers(5)); + setResetServiceInstanceMapping(remoteData.getDataIntegers(6) == 1); setRegisterTime(remoteData.getDataLongs(0)); setHeartbeatTime(remoteData.getDataLongs(1)); @@ -165,6 +206,25 @@ public class ServiceInstanceInventory extends RegisterSource { return 0; } + @Override public boolean combine(RegisterSource registerSource) { + boolean isChanged = super.combine(registerSource); + ServiceInstanceInventory instanceInventory = (ServiceInstanceInventory) registerSource; + + if (instanceInventory.getLastUpdateTime() >= this.getLastUpdateTime()) { + this.nodeType = instanceInventory.getNodeType(); + this.resetServiceInstanceMapping = instanceInventory.isResetServiceInstanceMapping(); + setProp(instanceInventory.getProp()); + if (instanceInventory.isResetServiceInstanceMapping()) { + this.mappingServiceInstanceId = Const.NONE; + } else if (Const.NONE != instanceInventory.getMappingServiceInstanceId()) { + this.mappingServiceInstanceId = instanceInventory.mappingServiceInstanceId; + } + isChanged = true; + } + + return isChanged; + } + public static class Builder implements StorageBuilder { @Override public ServiceInstanceInventory map2Data(Map dbMap) { @@ -178,6 +238,9 @@ public class ServiceInstanceInventory extends RegisterSource { inventory.setHeartbeatTime(((Number)dbMap.get(HEARTBEAT_TIME)).longValue()); inventory.setLastUpdateTime(((Number)dbMap.get(LAST_UPDATE_TIME)).longValue()); + inventory.setNodeType(((Number)dbMap.get(NODE_TYPE)).intValue()); + inventory.setMappingServiceInstanceId(((Number)dbMap.get(MAPPING_SERVICE_ID)).intValue()); + inventory.setName((String)dbMap.get(NAME)); inventory.setInstanceUUID((String)dbMap.get(INSTANCE_UUID)); inventory.setProp((String)dbMap.get(PROPERTIES)); @@ -195,6 +258,9 @@ public class ServiceInstanceInventory extends RegisterSource { map.put(HEARTBEAT_TIME, storageData.getHeartbeatTime()); map.put(LAST_UPDATE_TIME, storageData.getLastUpdateTime()); + map.put(NODE_TYPE, storageData.getNodeType()); + map.put(MAPPING_SERVICE_ID, storageData.getMappingServiceInstanceId()); + map.put(NAME, storageData.getName()); map.put(INSTANCE_UUID, storageData.getInstanceUUID()); map.put(PROPERTIES, storageData.getProp()); diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/register/service/IServiceInstanceInventoryRegister.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/register/service/IServiceInstanceInventoryRegister.java index e4eda92ba..5dbb3e5a3 100644 --- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/register/service/IServiceInstanceInventoryRegister.java +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/register/service/IServiceInstanceInventoryRegister.java @@ -19,6 +19,7 @@ package org.apache.skywalking.oap.server.core.register.service; import com.google.gson.JsonObject; +import org.apache.skywalking.oap.server.core.register.NodeType; import org.apache.skywalking.oap.server.library.module.Service; /** @@ -29,7 +30,23 @@ public interface IServiceInstanceInventoryRegister extends Service { int getOrCreate(int serviceId, String serviceInstanceName, String uuid, long registerTime, JsonObject properties); - int getOrCreate(int serviceId, int addressId, long registerTime); + int getOrCreate(int serviceId, String serviceInstanceName, int addressId, long registerTime); + + void update(int serviceInstanceId, NodeType nodeType, JsonObject properties); void heartbeat(int serviceInstanceId, long heartBeatTime); + + void updateMapping(int serviceInstanceId, int mappingServiceInstanceId); + + /** + * Reset the {@link org.apache.skywalking.oap.server.core.register.ServiceInstanceInventory#mappingServiceInstanceId} + * of a given service id. + * + * There are cases when the mapping service id needs to be reset to {@code 0}, for example, when an + * uninstrumented gateway joins, the mapping service id of the services that are delegated by this gateway + * should be reset to {@code 0}, allowing the gateway to appear in the topology, see #3308 for more detail. + * + * @param serviceInstanceId id of the service whose mapping service id is to be reset + */ + void resetMapping(int serviceInstanceId); } diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/register/service/NetworkAddressInventoryRegister.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/register/service/NetworkAddressInventoryRegister.java index 2f3e4e623..d3123ea32 100644 --- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/register/service/NetworkAddressInventoryRegister.java +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/register/service/NetworkAddressInventoryRegister.java @@ -73,7 +73,7 @@ public class NetworkAddressInventoryRegister implements INetworkAddressInventory int serviceId = getServiceInventoryRegister().getOrCreate(addressId, networkAddress, properties); if (serviceId != Const.NONE) { - int serviceInstanceId = getServiceInstanceInventoryRegister().getOrCreate(serviceId, addressId, System.currentTimeMillis()); + int serviceInstanceId = getServiceInstanceInventoryRegister().getOrCreate(serviceId, networkAddress, addressId, System.currentTimeMillis()); if (serviceInstanceId != Const.NONE) { return addressId; diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/register/service/ServiceInstanceInventoryRegister.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/register/service/ServiceInstanceInventoryRegister.java index 7a45aa6f4..a5813055b 100644 --- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/register/service/ServiceInstanceInventoryRegister.java +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/register/service/ServiceInstanceInventoryRegister.java @@ -19,9 +19,9 @@ package org.apache.skywalking.oap.server.core.register.service; import com.google.gson.JsonObject; -import java.util.Objects; import org.apache.skywalking.oap.server.core.*; import org.apache.skywalking.oap.server.core.cache.ServiceInstanceInventoryCache; +import org.apache.skywalking.oap.server.core.register.NodeType; import org.apache.skywalking.oap.server.core.register.ServiceInstanceInventory; import org.apache.skywalking.oap.server.core.register.worker.InventoryStreamProcessor; import org.apache.skywalking.oap.server.library.module.ModuleDefineHolder; @@ -29,6 +29,7 @@ import org.apache.skywalking.oap.server.library.util.BooleanUtils; import org.slf4j.*; import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; /** * @author peng-yongsheng @@ -66,6 +67,7 @@ public class ServiceInstanceInventoryRegister implements IServiceInstanceInvento serviceInstanceInventory.setInstanceUUID(uuid); serviceInstanceInventory.setIsAddress(BooleanUtils.FALSE); serviceInstanceInventory.setAddressId(Const.NONE); + serviceInstanceInventory.setMappingServiceInstanceId(Const.NONE); serviceInstanceInventory.setRegisterTime(registerTime); serviceInstanceInventory.setHeartbeatTime(registerTime); @@ -77,7 +79,7 @@ public class ServiceInstanceInventoryRegister implements IServiceInstanceInvento return serviceInstanceId; } - @Override public int getOrCreate(int serviceId, int addressId, long registerTime) { + @Override public int getOrCreate(int serviceId, String serviceInstanceName, int addressId, long registerTime) { if (logger.isDebugEnabled()) { logger.debug("get or create service instance by getAddress id, service id: {}, getAddress id: {}, registerTime: {}", serviceId, addressId, registerTime); } @@ -87,9 +89,10 @@ public class ServiceInstanceInventoryRegister implements IServiceInstanceInvento if (serviceInstanceId == Const.NONE) { ServiceInstanceInventory serviceInstanceInventory = new ServiceInstanceInventory(); serviceInstanceInventory.setServiceId(serviceId); - serviceInstanceInventory.setName(Const.EMPTY_STRING); + serviceInstanceInventory.setName(serviceInstanceName); serviceInstanceInventory.setIsAddress(BooleanUtils.TRUE); serviceInstanceInventory.setAddressId(addressId); + serviceInstanceInventory.setMappingServiceInstanceId(Const.NONE); serviceInstanceInventory.setRegisterTime(registerTime); serviceInstanceInventory.setHeartbeatTime(registerTime); @@ -99,13 +102,63 @@ public class ServiceInstanceInventoryRegister implements IServiceInstanceInvento return serviceInstanceId; } + @Override + public void update(int serviceInstanceId, NodeType nodeType, JsonObject properties) { + ServiceInstanceInventory instanceInventory = getServiceInstanceInventoryCache().get(serviceInstanceId); + if (nonNull(instanceInventory)) { + if (properties != null || !compare(instanceInventory, nodeType)) { + instanceInventory = instanceInventory.getClone(); + instanceInventory.setServiceInstanceNodeType(nodeType); + instanceInventory.setProperties(properties); + instanceInventory.setLastUpdateTime(System.currentTimeMillis()); + + InventoryStreamProcessor.getInstance().in(instanceInventory); + } + } else { + logger.warn("ServiceInstance {} nodeType/properties update, but not found in storage.", serviceInstanceId); + } + } + @Override public void heartbeat(int serviceInstanceId, long heartBeatTime) { ServiceInstanceInventory serviceInstanceInventory = getServiceInstanceInventoryCache().get(serviceInstanceId); - if (Objects.nonNull(serviceInstanceInventory)) { + if (nonNull(serviceInstanceInventory)) { serviceInstanceInventory.setHeartbeatTime(heartBeatTime); InventoryStreamProcessor.getInstance().in(serviceInstanceInventory); } else { logger.warn("Service instance {} heartbeat, but not found in storage.", serviceInstanceId); } } + + @Override public void updateMapping(int serviceInstanceId, int mappingServiceInstanceId) { + ServiceInstanceInventory instanceInventory = getServiceInstanceInventoryCache().get(serviceInstanceId); + if (nonNull(instanceInventory)) { + instanceInventory = instanceInventory.getClone(); + instanceInventory.setMappingServiceInstanceId(mappingServiceInstanceId); + instanceInventory.setLastUpdateTime(System.currentTimeMillis()); + + InventoryStreamProcessor.getInstance().in(instanceInventory); + } else { + logger.warn("ServiceInstance {} mapping update, but not found in storage.", serviceInstanceId); + } + } + + @Override public void resetMapping(int serviceInstanceId) { + ServiceInstanceInventory instanceInventory = getServiceInstanceInventoryCache().get(serviceInstanceId); + if (nonNull(instanceInventory) && instanceInventory.getMappingServiceInstanceId() != Const.NONE) { + instanceInventory = instanceInventory.getClone(); + instanceInventory.setLastUpdateTime(System.currentTimeMillis()); + instanceInventory.setResetServiceInstanceMapping(true); + + InventoryStreamProcessor.getInstance().in(instanceInventory); + } else { + logger.warn("ServiceInstance {} mapping update, but not found in storage.", serviceInstanceId); + } + } + + private boolean compare(ServiceInstanceInventory newServiceInstanceInventory, NodeType nodeType) { + if (nonNull(newServiceInstanceInventory)) { + return nodeType.equals(newServiceInstanceInventory.getServiceInstanceNodeType()); + } + return true; + } } diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/storage/cache/IServiceInstanceInventoryCacheDAO.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/storage/cache/IServiceInstanceInventoryCacheDAO.java index 11e3ee128..ad129025b 100644 --- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/storage/cache/IServiceInstanceInventoryCacheDAO.java +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/storage/cache/IServiceInstanceInventoryCacheDAO.java @@ -21,6 +21,8 @@ package org.apache.skywalking.oap.server.core.storage.cache; import org.apache.skywalking.oap.server.core.register.ServiceInstanceInventory; import org.apache.skywalking.oap.server.core.storage.DAO; +import java.util.List; + /** * @author peng-yongsheng */ @@ -31,4 +33,6 @@ public interface IServiceInstanceInventoryCacheDAO extends DAO { int getServiceInstanceId(int serviceId, String uuid); int getServiceInstanceId(int serviceId, int addressId); + + List loadLastUpdate(long lastUpdateTime); } diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/storage/query/ITopologyQueryDAO.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/storage/query/ITopologyQueryDAO.java index cafd8baa9..6fe777faf 100644 --- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/storage/query/ITopologyQueryDAO.java +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/storage/query/ITopologyQueryDAO.java @@ -37,5 +37,9 @@ public interface ITopologyQueryDAO extends Service { List loadClientSideServiceRelations(Downsampling downsampling, long startTB, long endTB) throws IOException; + List loadServerSideServiceInstanceRelations(int clientServiceId, int serverServiceId, Downsampling downsampling, long startTB, long endTB) throws IOException; + + List loadClientSideServiceInstanceRelations(int clientServiceId, int serverServiceId, Downsampling downsampling, long startTB, long endTB) throws IOException; + List loadSpecifiedDestOfServerSideEndpointRelations(Downsampling downsampling, long startTB, long endTB, int destEndpointId) throws IOException; } diff --git a/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/resolver/TopologyQuery.java b/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/resolver/TopologyQuery.java index c3903400e..1f0f77c69 100644 --- a/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/resolver/TopologyQuery.java +++ b/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/resolver/TopologyQuery.java @@ -19,13 +19,17 @@ package org.apache.skywalking.oap.query.graphql.resolver; import com.coxautodev.graphql.tools.GraphQLQueryResolver; -import java.io.IOException; import org.apache.skywalking.oap.query.graphql.type.Duration; import org.apache.skywalking.oap.server.core.CoreModule; -import org.apache.skywalking.oap.server.core.query.*; +import org.apache.skywalking.oap.server.core.query.DurationUtils; +import org.apache.skywalking.oap.server.core.query.StepToDownsampling; +import org.apache.skywalking.oap.server.core.query.TopologyQueryService; +import org.apache.skywalking.oap.server.core.query.entity.ServiceInstanceTopology; import org.apache.skywalking.oap.server.core.query.entity.Topology; import org.apache.skywalking.oap.server.library.module.ModuleManager; +import java.io.IOException; + /** * @author peng-yongsheng */ @@ -59,6 +63,13 @@ public class TopologyQuery implements GraphQLQueryResolver { return getQueryService().getServiceTopology(StepToDownsampling.transform(duration.getStep()), startTimeBucket, endTimeBucket, serviceId); } + public ServiceInstanceTopology getServiceInstanceTopology(final int clientServiceId, final int serverServiceId, final Duration duration) throws IOException { + long startTimeBucket = DurationUtils.INSTANCE.exchangeToTimeBucket(duration.getStart()); + long endTimeBucket = DurationUtils.INSTANCE.exchangeToTimeBucket(duration.getEnd()); + + return getQueryService().getServiceInstanceTopology(clientServiceId, serverServiceId, StepToDownsampling.transform(duration.getStep()), startTimeBucket, endTimeBucket); + } + public Topology getEndpointTopology(final int endpointId, final Duration duration) throws IOException { long startTimeBucket = DurationUtils.INSTANCE.exchangeToTimeBucket(duration.getStart()); long endTimeBucket = DurationUtils.INSTANCE.exchangeToTimeBucket(duration.getEnd()); diff --git a/oap-server/server-query-plugin/query-graphql-plugin/src/main/resources/query-protocol b/oap-server/server-query-plugin/query-graphql-plugin/src/main/resources/query-protocol index c970b1972..e47462fd6 160000 --- a/oap-server/server-query-plugin/query-graphql-plugin/src/main/resources/query-protocol +++ b/oap-server/server-query-plugin/query-graphql-plugin/src/main/resources/query-protocol @@ -1 +1 @@ -Subproject commit c970b1972ad69d2622eb0b41a067c230d46b3da8 +Subproject commit e47462fd6af92d42d1c161cf1cec975661148ab0 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 3890f9e00..5df7956be 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 @@ -40,6 +40,7 @@ import org.apache.skywalking.oap.server.receiver.trace.provider.parser.SegmentPa import org.apache.skywalking.oap.server.receiver.trace.provider.parser.SegmentParserServiceImpl; import org.apache.skywalking.oap.server.receiver.trace.provider.parser.listener.endpoint.MultiScopesSpanListener; import org.apache.skywalking.oap.server.receiver.trace.provider.parser.listener.segment.SegmentSpanListener; +import org.apache.skywalking.oap.server.receiver.trace.provider.parser.listener.service.ServiceInstanceMappingSpanListener; import org.apache.skywalking.oap.server.receiver.trace.provider.parser.listener.service.ServiceMappingSpanListener; import org.apache.skywalking.oap.server.receiver.trace.provider.parser.standardization.SegmentStandardizationWorker; import org.apache.skywalking.oap.server.telemetry.TelemetryModule; @@ -92,6 +93,7 @@ public class TraceModuleProvider extends ModuleProvider { if (moduleConfig.isTraceAnalysis()) { listenerManager.add(new MultiScopesSpanListener.Factory()); listenerManager.add(new ServiceMappingSpanListener.Factory()); + listenerManager.add(new ServiceInstanceMappingSpanListener.Factory()); } listenerManager.add(new SegmentSpanListener.Factory(moduleConfig.getSampleRate())); diff --git a/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/listener/endpoint/MultiScopesSpanListener.java b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/listener/endpoint/MultiScopesSpanListener.java index 0c925f546..7cb31c28f 100755 --- a/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/listener/endpoint/MultiScopesSpanListener.java +++ b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/listener/endpoint/MultiScopesSpanListener.java @@ -159,17 +159,22 @@ public class MultiScopesSpanListener implements EntrySpanListener, ExitSpanListe int destServiceId = serviceInventoryCache.getServiceId(peerId); int mappingServiceId = serviceInventoryCache.get(destServiceId).getMappingServiceId(); int destInstanceId = instanceInventoryCache.getServiceInstanceId(destServiceId, peerId); + int mappingServiceInstanceId = instanceInventoryCache.get(destInstanceId).getMappingServiceInstanceId(); sourceBuilder.setSourceEndpointId(Const.USER_ENDPOINT_ID); sourceBuilder.setSourceServiceInstanceId(segmentCoreInfo.getServiceInstanceId()); sourceBuilder.setSourceServiceId(segmentCoreInfo.getServiceId()); sourceBuilder.setDestEndpointId(spanDecorator.getOperationNameId()); - sourceBuilder.setDestServiceInstanceId(destInstanceId); if (Const.NONE == mappingServiceId) { sourceBuilder.setDestServiceId(destServiceId); } else { sourceBuilder.setDestServiceId(mappingServiceId); } + if (Const.NONE == mappingServiceInstanceId) { + sourceBuilder.setDestServiceInstanceId(destInstanceId); + } else { + sourceBuilder.setDestServiceInstanceId(mappingServiceInstanceId); + } sourceBuilder.setDetectPoint(DetectPoint.CLIENT); sourceBuilder.setComponentId(spanDecorator.getComponentId()); setPublicAttrs(sourceBuilder, spanDecorator); diff --git a/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/listener/service/ServiceInstanceMappingSpanListener.java b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/listener/service/ServiceInstanceMappingSpanListener.java new file mode 100644 index 000000000..c7bef92e4 --- /dev/null +++ b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/listener/service/ServiceInstanceMappingSpanListener.java @@ -0,0 +1,133 @@ +/* + * 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.service; + +import lombok.Getter; +import lombok.Setter; +import org.apache.skywalking.apm.network.language.agent.SpanLayer; +import org.apache.skywalking.oap.server.core.Const; +import org.apache.skywalking.oap.server.core.CoreModule; +import org.apache.skywalking.oap.server.core.cache.NetworkAddressInventoryCache; +import org.apache.skywalking.oap.server.core.cache.ServiceInstanceInventoryCache; +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.service.IServiceInstanceInventoryRegister; +import org.apache.skywalking.oap.server.library.module.ModuleManager; +import org.apache.skywalking.oap.server.receiver.trace.provider.TraceServiceModuleConfig; +import org.apache.skywalking.oap.server.receiver.trace.provider.parser.decorator.SegmentCoreInfo; +import org.apache.skywalking.oap.server.receiver.trace.provider.parser.decorator.SpanDecorator; +import org.apache.skywalking.oap.server.receiver.trace.provider.parser.listener.EntrySpanListener; +import org.apache.skywalking.oap.server.receiver.trace.provider.parser.listener.SpanListener; +import org.apache.skywalking.oap.server.receiver.trace.provider.parser.listener.SpanListenerFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author zhangwei + */ +public class ServiceInstanceMappingSpanListener implements EntrySpanListener { + + private static final Logger logger = LoggerFactory.getLogger(ServiceInstanceMappingSpanListener.class); + + private final IServiceInstanceInventoryRegister serviceInstanceInventoryRegister; + private final TraceServiceModuleConfig config; + private final ServiceInventoryCache serviceInventoryCache; + private final ServiceInstanceInventoryCache serviceInstanceInventoryCache; + private final NetworkAddressInventoryCache networkAddressInventoryCache; + private final List serviceInstanceMappings = new ArrayList<>(); + private final List serviceInstancesToResetMapping = new ArrayList<>(); + + public ServiceInstanceMappingSpanListener(ModuleManager moduleManager, TraceServiceModuleConfig config) { + this.serviceInstanceInventoryCache = moduleManager.find(CoreModule.NAME).provider().getService(ServiceInstanceInventoryCache.class); + this.serviceInventoryCache = moduleManager.find(CoreModule.NAME).provider().getService(ServiceInventoryCache.class); + this.serviceInstanceInventoryRegister = moduleManager.find(CoreModule.NAME).provider().getService(IServiceInstanceInventoryRegister.class); + this.networkAddressInventoryCache = moduleManager.find(CoreModule.NAME).provider().getService(NetworkAddressInventoryCache.class); + this.config = config; + } + + @Override + public void parseEntry(SpanDecorator spanDecorator, SegmentCoreInfo segmentCoreInfo) { + if (logger.isDebugEnabled()) { + logger.debug("service instance mapping listener parse reference"); + } + if (!spanDecorator.getSpanLayer().equals(SpanLayer.MQ)) { + if (spanDecorator.getRefsCount() > 0) { + for (int i = 0; i < spanDecorator.getRefsCount(); i++) { + int networkAddressId = spanDecorator.getRefs(i).getNetworkAddressId(); + String address = networkAddressInventoryCache.get(networkAddressId).getName(); + int serviceInstanceId = serviceInstanceInventoryCache.getServiceInstanceId(serviceInventoryCache.getServiceId(networkAddressId), networkAddressId); + + if (config.getUninstrumentedGatewaysConfig().isAddressConfiguredAsGateway(address)) { + if (logger.isDebugEnabled()) { + logger.debug("{} is configured as gateway, will reset its mapping service instance id", serviceInstanceId); + } + ServiceInstanceInventory instanceInventory = serviceInstanceInventoryCache.get(serviceInstanceId); + if (instanceInventory.getMappingServiceInstanceId() != Const.NONE && !serviceInstancesToResetMapping.contains(serviceInstanceId)) { + serviceInstancesToResetMapping.add(serviceInstanceId); + } + } else { + ServiceInstanceMapping serviceMapping = new ServiceInstanceMapping(); + serviceMapping.setServiceInstanceId(serviceInstanceId); + serviceMapping.setMappingServiceInstanceId(segmentCoreInfo.getServiceInstanceId()); + serviceInstanceMappings.add(serviceMapping); + } + } + } + } + } + + @Override + public void build() { + serviceInstanceMappings.forEach(instanceMapping -> { + if (logger.isDebugEnabled()) { + logger.debug("service instance mapping listener build, service id: {}, mapping service id: {}", instanceMapping.getServiceInstanceId(), instanceMapping.getMappingServiceInstanceId()); + } + serviceInstanceInventoryRegister.updateMapping(instanceMapping.getServiceInstanceId(), instanceMapping.getMappingServiceInstanceId()); + }); + serviceInstancesToResetMapping.forEach(instanceId -> { + if (logger.isDebugEnabled()) { + logger.debug("service instance mapping listener build, reset mapping of service id: {}", instanceId); + } + serviceInstanceInventoryRegister.resetMapping(instanceId); + }); + } + + @Override + public boolean containsPoint(Point point) { + return Point.Entry.equals(point); + } + + public static class Factory implements SpanListenerFactory { + + @Override + public SpanListener create(ModuleManager moduleManager, TraceServiceModuleConfig config) { + return new ServiceInstanceMappingSpanListener(moduleManager, config); + } + } + + @Getter + @Setter + private static class ServiceInstanceMapping { + private int serviceInstanceId; + private int mappingServiceInstanceId; + } +} diff --git a/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/standardization/SpanIdExchanger.java b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/standardization/SpanIdExchanger.java index f61850bfc..7fcbb9efa 100644 --- a/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/standardization/SpanIdExchanger.java +++ b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/standardization/SpanIdExchanger.java @@ -20,18 +20,27 @@ package org.apache.skywalking.oap.server.receiver.trace.provider.parser.standard import com.google.common.base.Strings; import com.google.gson.JsonObject; -import java.util.List; import org.apache.skywalking.apm.network.common.KeyStringValuePair; import org.apache.skywalking.apm.network.language.agent.SpanLayer; -import org.apache.skywalking.oap.server.core.*; +import org.apache.skywalking.oap.server.core.Const; +import org.apache.skywalking.oap.server.core.CoreModule; +import org.apache.skywalking.oap.server.core.cache.ServiceInstanceInventoryCache; import org.apache.skywalking.oap.server.core.cache.ServiceInventoryCache; import org.apache.skywalking.oap.server.core.config.IComponentLibraryCatalogService; -import org.apache.skywalking.oap.server.core.register.*; -import org.apache.skywalking.oap.server.core.register.service.*; +import org.apache.skywalking.oap.server.core.register.NodeType; +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.IEndpointInventoryRegister; +import org.apache.skywalking.oap.server.core.register.service.INetworkAddressInventoryRegister; +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.core.source.DetectPoint; import org.apache.skywalking.oap.server.library.module.ModuleManager; import org.apache.skywalking.oap.server.receiver.trace.provider.parser.decorator.SpanDecorator; -import org.slf4j.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; /** * @author peng-yongsheng @@ -43,6 +52,8 @@ public class SpanIdExchanger implements IdExchanger { private static SpanIdExchanger EXCHANGER; private final ServiceInventoryCache serviceInventoryCacheDAO; private final IServiceInventoryRegister serviceInventoryRegister; + private final ServiceInstanceInventoryCache serviceInstanceInventoryCacheDAO; + private final IServiceInstanceInventoryRegister serviceInstanceInventoryRegister; private final IEndpointInventoryRegister endpointInventoryRegister; private final INetworkAddressInventoryRegister networkAddressInventoryRegister; private final IComponentLibraryCatalogService componentLibraryCatalogService; @@ -57,6 +68,8 @@ public class SpanIdExchanger implements IdExchanger { private SpanIdExchanger(ModuleManager moduleManager) { this.serviceInventoryCacheDAO = moduleManager.find(CoreModule.NAME).provider().getService(ServiceInventoryCache.class); this.serviceInventoryRegister = moduleManager.find(CoreModule.NAME).provider().getService(IServiceInventoryRegister.class); + this.serviceInstanceInventoryCacheDAO = moduleManager.find(CoreModule.NAME).provider().getService(ServiceInstanceInventoryCache.class); + this.serviceInstanceInventoryRegister = moduleManager.find(CoreModule.NAME).provider().getService(IServiceInstanceInventoryRegister.class); this.endpointInventoryRegister = moduleManager.find(CoreModule.NAME).provider().getService(IEndpointInventoryRegister.class); this.networkAddressInventoryRegister = moduleManager.find(CoreModule.NAME).provider().getService(INetworkAddressInventoryRegister.class); this.componentLibraryCatalogService = moduleManager.find(CoreModule.NAME).provider().getService(IComponentLibraryCatalogService.class); @@ -116,6 +129,9 @@ public class SpanIdExchanger implements IdExchanger { } } serviceInventoryRegister.update(newServiceInventory.getSequence(), nodeType, properties); + + ServiceInstanceInventory newServiceInstanceInventory = serviceInstanceInventoryCacheDAO.get(serviceInstanceInventoryCacheDAO.getServiceInstanceId(newServiceInventory.getSequence(), peerId)); + serviceInstanceInventoryRegister.update(newServiceInstanceInventory.getSequence(), nodeType, properties); } if (standardBuilder.getOperationNameId() == Const.NONE) { diff --git a/oap-server/server-receiver-plugin/zipkin-receiver-plugin/src/test/java/org/apache/skywalking/oap/server/receiver/zipkin/analysis/transform/SpringSleuthSegmentBuilderTest.java b/oap-server/server-receiver-plugin/zipkin-receiver-plugin/src/test/java/org/apache/skywalking/oap/server/receiver/zipkin/analysis/transform/SpringSleuthSegmentBuilderTest.java index b4ecf2ee0..c6ba1cce6 100644 --- a/oap-server/server-receiver-plugin/zipkin-receiver-plugin/src/test/java/org/apache/skywalking/oap/server/receiver/zipkin/analysis/transform/SpringSleuthSegmentBuilderTest.java +++ b/oap-server/server-receiver-plugin/zipkin-receiver-plugin/src/test/java/org/apache/skywalking/oap/server/receiver/zipkin/analysis/transform/SpringSleuthSegmentBuilderTest.java @@ -96,7 +96,7 @@ public class SpringSleuthSegmentBuilderTest implements SegmentListener { } } - @Override public int getOrCreate(int serviceId, int addressId, long registerTime) { + @Override public int getOrCreate(int serviceId, String serviceInstanceName, int addressId, long registerTime) { String key = "VitualAppCode:" + serviceId + ",getAddress:" + addressId; if (applicationInstRegister.containsKey(key)) { return applicationInstRegister.get(key); @@ -107,9 +107,24 @@ public class SpringSleuthSegmentBuilderTest implements SegmentListener { } } + @Override + public void update(int serviceInstanceId, NodeType nodeType, JsonObject properties) { + + } + @Override public void heartbeat(int serviceInstanceId, long heartBeatTime) { } + + @Override + public void updateMapping(int serviceInstanceId, int mappingServiceInstanceId) { + + } + + @Override + public void resetMapping(int serviceInstanceId) { + + } }; Whitebox.setInternalState(CoreRegisterLinker.class, "SERVICE_INVENTORY_REGISTER", applicationIDService); diff --git a/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/StorageModuleElasticsearchProvider.java b/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/StorageModuleElasticsearchProvider.java index 008ef8639..821cd838f 100644 --- a/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/StorageModuleElasticsearchProvider.java +++ b/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/StorageModuleElasticsearchProvider.java @@ -111,7 +111,7 @@ public class StorageModuleElasticsearchProvider extends ModuleProvider { this.registerServiceImplementation(IHistoryDeleteDAO.class, new HistoryDeleteEsDAO(getManager(), elasticSearchClient, new ElasticsearchStorageTTL())); this.registerServiceImplementation(IServiceInventoryCacheDAO.class, new ServiceInventoryCacheEsDAO(elasticSearchClient, config.getResultWindowMaxSize())); - this.registerServiceImplementation(IServiceInstanceInventoryCacheDAO.class, new ServiceInstanceInventoryCacheDAO(elasticSearchClient)); + this.registerServiceImplementation(IServiceInstanceInventoryCacheDAO.class, new ServiceInstanceInventoryCacheDAO(elasticSearchClient, config.getResultWindowMaxSize())); this.registerServiceImplementation(IEndpointInventoryCacheDAO.class, new EndpointInventoryCacheEsDAO(elasticSearchClient)); this.registerServiceImplementation(INetworkAddressInventoryCacheDAO.class, new NetworkAddressInventoryCacheEsDAO(elasticSearchClient, config.getResultWindowMaxSize())); diff --git a/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/cache/ServiceInstanceInventoryCacheDAO.java b/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/cache/ServiceInstanceInventoryCacheDAO.java index 730e9e986..80d5ce22b 100644 --- a/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/cache/ServiceInstanceInventoryCacheDAO.java +++ b/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/cache/ServiceInstanceInventoryCacheDAO.java @@ -19,16 +19,23 @@ package org.apache.skywalking.oap.server.storage.plugin.elasticsearch.cache; import org.apache.skywalking.oap.server.core.Const; -import org.apache.skywalking.oap.server.core.register.*; +import org.apache.skywalking.oap.server.core.register.RegisterSource; +import org.apache.skywalking.oap.server.core.register.ServiceInstanceInventory; import org.apache.skywalking.oap.server.core.storage.cache.IServiceInstanceInventoryCacheDAO; import org.apache.skywalking.oap.server.library.client.elasticsearch.ElasticSearchClient; +import org.apache.skywalking.oap.server.library.util.BooleanUtils; import org.apache.skywalking.oap.server.storage.plugin.elasticsearch.base.EsDAO; import org.elasticsearch.action.get.GetResponse; import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.builder.SearchSourceBuilder; -import org.slf4j.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; /** * @author peng-yongsheng @@ -38,9 +45,11 @@ public class ServiceInstanceInventoryCacheDAO extends EsDAO implements IServiceI private static final Logger logger = LoggerFactory.getLogger(ServiceInstanceInventoryCacheDAO.class); protected final ServiceInstanceInventory.Builder builder = new ServiceInstanceInventory.Builder(); + protected final int resultWindowMaxSize; - public ServiceInstanceInventoryCacheDAO(ElasticSearchClient client) { + public ServiceInstanceInventoryCacheDAO(ElasticSearchClient client, int resultWindowMaxSize) { super(client); + this.resultWindowMaxSize = resultWindowMaxSize; } @Override public ServiceInstanceInventory get(int serviceInstanceId) { @@ -72,6 +81,30 @@ public class ServiceInstanceInventoryCacheDAO extends EsDAO implements IServiceI return get(id); } + @Override public List loadLastUpdate(long lastUpdateTime) { + List instanceInventories = new ArrayList<>(); + + try { + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); + + BoolQueryBuilder boolQuery = QueryBuilders.boolQuery(); + boolQuery.must().add(QueryBuilders.termQuery(ServiceInstanceInventory.IS_ADDRESS, BooleanUtils.TRUE)); + boolQuery.must().add(QueryBuilders.rangeQuery(ServiceInstanceInventory.LAST_UPDATE_TIME).gte(lastUpdateTime)); + + searchSourceBuilder.query(boolQuery); + searchSourceBuilder.size(resultWindowMaxSize); + + SearchResponse response = getClient().search(ServiceInstanceInventory.INDEX_NAME, searchSourceBuilder); + + for (SearchHit searchHit : response.getHits().getHits()) { + instanceInventories.add(this.builder.map2Data(searchHit.getSourceAsMap())); + } + } catch (Throwable t) { + logger.error(t.getMessage(), t); + } + return instanceInventories; + } + private int get(String id) { try { GetResponse response = getClient().get(ServiceInstanceInventory.INDEX_NAME, id); diff --git a/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/query/TopologyQueryEsDAO.java b/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/query/TopologyQueryEsDAO.java index 68e215234..5f31c32f0 100644 --- a/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/query/TopologyQueryEsDAO.java +++ b/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/query/TopologyQueryEsDAO.java @@ -18,13 +18,14 @@ package org.apache.skywalking.oap.server.storage.plugin.elasticsearch.query; -import java.io.IOException; -import java.util.*; import org.apache.skywalking.oap.server.core.UnexpectedException; import org.apache.skywalking.oap.server.core.analysis.Downsampling; import org.apache.skywalking.oap.server.core.analysis.manual.RelationDefineUtil; import org.apache.skywalking.oap.server.core.analysis.manual.endpointrelation.EndpointRelationServerSideMetrics; -import org.apache.skywalking.oap.server.core.analysis.manual.servicerelation.*; +import org.apache.skywalking.oap.server.core.analysis.manual.relation.instance.ServiceInstanceRelationClientSideMetrics; +import org.apache.skywalking.oap.server.core.analysis.manual.relation.instance.ServiceInstanceRelationServerSideMetrics; +import org.apache.skywalking.oap.server.core.analysis.manual.relation.service.ServiceRelationClientSideMetrics; +import org.apache.skywalking.oap.server.core.analysis.manual.relation.service.ServiceRelationServerSideMetrics; import org.apache.skywalking.oap.server.core.analysis.metrics.Metrics; import org.apache.skywalking.oap.server.core.query.entity.Call; import org.apache.skywalking.oap.server.core.source.DetectPoint; @@ -34,11 +35,16 @@ import org.apache.skywalking.oap.server.library.client.elasticsearch.ElasticSear import org.apache.skywalking.oap.server.library.util.CollectionUtils; import org.apache.skywalking.oap.server.storage.plugin.elasticsearch.base.EsDAO; import org.elasticsearch.action.search.SearchResponse; -import org.elasticsearch.index.query.*; +import org.elasticsearch.index.query.BoolQueryBuilder; +import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.aggregations.AggregationBuilders; import org.elasticsearch.search.aggregations.bucket.terms.Terms; import org.elasticsearch.search.builder.SearchSourceBuilder; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + /** * @author peng-yongsheng */ @@ -111,6 +117,48 @@ public class TopologyQueryEsDAO extends EsDAO implements ITopologyQueryDAO { return load(sourceBuilder, indexName, DetectPoint.CLIENT); } + @Override + public List loadServerSideServiceInstanceRelations(int clientServiceId, int serverServiceId, Downsampling downsampling, long startTB, long endTB) throws IOException { + String indexName = ModelName.build(downsampling, ServiceInstanceRelationServerSideMetrics.INDEX_NAME); + SearchSourceBuilder sourceBuilder = SearchSourceBuilder.searchSource(); + sourceBuilder.size(0); + setInstanceQueryCondition(sourceBuilder, startTB, endTB, clientServiceId, serverServiceId); + + return load(sourceBuilder, indexName, DetectPoint.SERVER); + } + + @Override + public List loadClientSideServiceInstanceRelations(int clientServiceId, int serverServiceId, Downsampling downsampling, long startTB, long endTB) throws IOException { + String indexName = ModelName.build(downsampling, ServiceInstanceRelationClientSideMetrics.INDEX_NAME); + SearchSourceBuilder sourceBuilder = SearchSourceBuilder.searchSource(); + sourceBuilder.size(0); + setInstanceQueryCondition(sourceBuilder, startTB, endTB, clientServiceId, serverServiceId); + + return load(sourceBuilder, indexName, DetectPoint.CLIENT); + } + + private void setInstanceQueryCondition(SearchSourceBuilder sourceBuilder, long startTB, long endTB, int clientServiceId, int serverServiceId) { + BoolQueryBuilder boolQuery = QueryBuilders.boolQuery(); + boolQuery.must().add(QueryBuilders.rangeQuery(EndpointRelationServerSideMetrics.TIME_BUCKET).gte(startTB).lte(endTB)); + + BoolQueryBuilder serviceIdBoolQuery = new BoolQueryBuilder(); + boolQuery.must(serviceIdBoolQuery); + + BoolQueryBuilder serverRelationBoolQuery = new BoolQueryBuilder(); + serviceIdBoolQuery.should(serverRelationBoolQuery); + + serverRelationBoolQuery.must(QueryBuilders.termQuery(ServiceInstanceRelationServerSideMetrics.SOURCE_SERVICE_ID, clientServiceId)); + serverRelationBoolQuery.must(QueryBuilders.termQuery(ServiceInstanceRelationServerSideMetrics.DEST_SERVICE_ID, serverServiceId)); + + BoolQueryBuilder clientRelationBoolQuery = new BoolQueryBuilder(); + serviceIdBoolQuery.should(clientRelationBoolQuery); + + clientRelationBoolQuery.must(QueryBuilders.termQuery(ServiceInstanceRelationServerSideMetrics.DEST_SERVICE_ID, clientServiceId)); + clientRelationBoolQuery.must(QueryBuilders.termQuery(ServiceInstanceRelationServerSideMetrics.SOURCE_SERVICE_ID, serverServiceId)); + + sourceBuilder.query(boolQuery); + } + @Override public List loadSpecifiedDestOfServerSideEndpointRelations(Downsampling downsampling, long startTB, long endTB, int destEndpointId) throws IOException { String indexName = ModelName.build(downsampling, EndpointRelationServerSideMetrics.INDEX_NAME); diff --git a/oap-server/server-storage-plugin/storage-elasticsearch7-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch7/StorageModuleElasticsearch7Provider.java b/oap-server/server-storage-plugin/storage-elasticsearch7-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch7/StorageModuleElasticsearch7Provider.java index e3cffac9d..7a22bae87 100644 --- a/oap-server/server-storage-plugin/storage-elasticsearch7-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch7/StorageModuleElasticsearch7Provider.java +++ b/oap-server/server-storage-plugin/storage-elasticsearch7-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch7/StorageModuleElasticsearch7Provider.java @@ -113,7 +113,7 @@ public class StorageModuleElasticsearch7Provider extends ModuleProvider { this.registerServiceImplementation(IHistoryDeleteDAO.class, new HistoryDeleteEsDAO(getManager(), elasticSearch7Client, new ElasticsearchStorageTTL())); this.registerServiceImplementation(IServiceInventoryCacheDAO.class, new ServiceInventoryCacheEs7DAO(elasticSearch7Client, config.getResultWindowMaxSize())); - this.registerServiceImplementation(IServiceInstanceInventoryCacheDAO.class, new ServiceInstanceInventoryCacheEs7DAO(elasticSearch7Client)); + this.registerServiceImplementation(IServiceInstanceInventoryCacheDAO.class, new ServiceInstanceInventoryCacheEs7DAO(elasticSearch7Client, config.getResultWindowMaxSize())); this.registerServiceImplementation(IEndpointInventoryCacheDAO.class, new EndpointInventoryCacheEs7DAO(elasticSearch7Client)); this.registerServiceImplementation(INetworkAddressInventoryCacheDAO.class, new NetworkAddressInventoryCacheEs7DAO(elasticSearch7Client, config.getResultWindowMaxSize())); diff --git a/oap-server/server-storage-plugin/storage-elasticsearch7-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch7/cache/ServiceInstanceInventoryCacheEs7DAO.java b/oap-server/server-storage-plugin/storage-elasticsearch7-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch7/cache/ServiceInstanceInventoryCacheEs7DAO.java index 1d6c6df3e..9559e6398 100644 --- a/oap-server/server-storage-plugin/storage-elasticsearch7-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch7/cache/ServiceInstanceInventoryCacheEs7DAO.java +++ b/oap-server/server-storage-plugin/storage-elasticsearch7-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch7/cache/ServiceInstanceInventoryCacheEs7DAO.java @@ -36,8 +36,8 @@ public class ServiceInstanceInventoryCacheEs7DAO extends ServiceInstanceInventor private static final Logger logger = LoggerFactory.getLogger(ServiceInstanceInventoryCacheEs7DAO.class); - public ServiceInstanceInventoryCacheEs7DAO(ElasticSearchClient client) { - super(client); + public ServiceInstanceInventoryCacheEs7DAO(ElasticSearchClient client, int resultWindowMaxSize) { + super(client, resultWindowMaxSize); } @Override diff --git a/oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/h2/dao/H2ServiceInstanceInventoryCacheDAO.java b/oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/h2/dao/H2ServiceInstanceInventoryCacheDAO.java index a4d13abde..8eeb234b5 100644 --- a/oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/h2/dao/H2ServiceInstanceInventoryCacheDAO.java +++ b/oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/h2/dao/H2ServiceInstanceInventoryCacheDAO.java @@ -19,9 +19,16 @@ package org.apache.skywalking.oap.server.storage.plugin.jdbc.h2.dao; import java.io.IOException; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + import org.apache.skywalking.oap.server.core.register.ServiceInstanceInventory; import org.apache.skywalking.oap.server.core.storage.cache.IServiceInstanceInventoryCacheDAO; import org.apache.skywalking.oap.server.library.client.jdbc.hikaricp.JDBCHikariCPClient; +import org.apache.skywalking.oap.server.library.util.BooleanUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -55,6 +62,36 @@ public class H2ServiceInstanceInventoryCacheDAO extends H2SQLExecutor implements return getByID(id); } + @Override + public List loadLastUpdate(long lastUpdateTime) { + List instanceInventories = new ArrayList<>(); + + try { + StringBuilder sql = new StringBuilder("select * from "); + sql.append(ServiceInstanceInventory.INDEX_NAME); + sql.append(" where ").append(ServiceInstanceInventory.IS_ADDRESS).append("=? "); + sql.append(" and ").append(ServiceInstanceInventory.LAST_UPDATE_TIME).append(">?"); + + try (Connection connection = h2Client.getConnection()) { + try (ResultSet resultSet = h2Client.executeQuery(connection, sql.toString(), BooleanUtils.TRUE, lastUpdateTime)) { + ServiceInstanceInventory serviceInstanceInventory; + do { + serviceInstanceInventory = (ServiceInstanceInventory) toStorageData(resultSet, ServiceInstanceInventory.INDEX_NAME, new ServiceInstanceInventory.Builder()); + if (serviceInstanceInventory != null) { + instanceInventories.add(serviceInstanceInventory); + } + } + while (serviceInstanceInventory != null); + } + } catch (SQLException e) { + throw new IOException(e); + } + } catch (Throwable e) { + logger.error(e.getMessage(), e); + } + return instanceInventories; + } + private int getByID(String id) { return getEntityIDByID(h2Client, ServiceInstanceInventory.SEQUENCE, ServiceInstanceInventory.INDEX_NAME, id); } diff --git a/oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/h2/dao/H2TopologyQueryDAO.java b/oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/h2/dao/H2TopologyQueryDAO.java index 835486535..b2cf68c57 100644 --- a/oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/h2/dao/H2TopologyQueryDAO.java +++ b/oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/h2/dao/H2TopologyQueryDAO.java @@ -24,7 +24,10 @@ import java.util.*; import org.apache.skywalking.oap.server.core.analysis.Downsampling; import org.apache.skywalking.oap.server.core.analysis.manual.RelationDefineUtil; import org.apache.skywalking.oap.server.core.analysis.manual.endpointrelation.EndpointRelationServerSideMetrics; -import org.apache.skywalking.oap.server.core.analysis.manual.servicerelation.*; +import org.apache.skywalking.oap.server.core.analysis.manual.relation.instance.ServiceInstanceRelationClientSideMetrics; +import org.apache.skywalking.oap.server.core.analysis.manual.relation.instance.ServiceInstanceRelationServerSideMetrics; +import org.apache.skywalking.oap.server.core.analysis.manual.relation.service.ServiceRelationClientSideMetrics; +import org.apache.skywalking.oap.server.core.analysis.manual.relation.service.ServiceRelationServerSideMetrics; import org.apache.skywalking.oap.server.core.analysis.metrics.Metrics; import org.apache.skywalking.oap.server.core.query.entity.*; import org.apache.skywalking.oap.server.core.source.DetectPoint; @@ -64,6 +67,24 @@ public class H2TopologyQueryDAO implements ITopologyQueryDAO { return loadServiceCalls(tableName, startTB, endTB, ServiceRelationServerSideMetrics.SOURCE_SERVICE_ID, ServiceRelationServerSideMetrics.DEST_SERVICE_ID, new ArrayList<>(0), true); } + @Override + public List loadServerSideServiceInstanceRelations(int clientServiceId, int serverServiceId, Downsampling downsampling, long startTB, long endTB) throws IOException { + String tableName = ModelName.build(downsampling, ServiceInstanceRelationServerSideMetrics.INDEX_NAME); + return loadServiceInstanceCalls(tableName, startTB, endTB, + ServiceInstanceRelationServerSideMetrics.SOURCE_SERVICE_ID, + ServiceInstanceRelationServerSideMetrics.DEST_SERVICE_ID, + clientServiceId, serverServiceId, false); + } + + @Override + public List loadClientSideServiceInstanceRelations(int clientServiceId, int serverServiceId, Downsampling downsampling, long startTB, long endTB) throws IOException { + String tableName = ModelName.build(downsampling, ServiceInstanceRelationClientSideMetrics.INDEX_NAME); + return loadServiceInstanceCalls(tableName, startTB, endTB, + ServiceInstanceRelationClientSideMetrics.SOURCE_SERVICE_ID, + ServiceInstanceRelationClientSideMetrics.DEST_SERVICE_ID, + clientServiceId, serverServiceId, true); + } + @Override public List loadSpecifiedDestOfServerSideEndpointRelations(Downsampling downsampling, long startTB, long endTB, int destEndpointId) throws IOException { String tableName = ModelName.build(downsampling, EndpointRelationServerSideMetrics.INDEX_NAME); @@ -108,6 +129,29 @@ public class H2TopologyQueryDAO implements ITopologyQueryDAO { return calls; } + private List loadServiceInstanceCalls(String tableName, long startTB, long endTB, String sourceCName, + String descCName, int sourceServiceId, int destServiceId, boolean isClientSide) throws IOException { + Object[] conditions = new Object[]{startTB, endTB, sourceServiceId, destServiceId, destServiceId, sourceServiceId}; + StringBuilder serviceIdMatchSql = new StringBuilder("and ((") + .append(sourceCName).append("=? and ").append(descCName).append("=?").append(") or (") + .append(sourceCName).append("=? and ").append(descCName).append("=?").append("))"); + List calls = new ArrayList<>(); + try (Connection connection = h2Client.getConnection()) { + try (ResultSet resultSet = h2Client.executeQuery(connection, "select " + + Metrics.ENTITY_ID + + " from " + tableName + " where " + + Metrics.TIME_BUCKET + ">= ? and " + Metrics.TIME_BUCKET + "<=? " + + serviceIdMatchSql.toString() + + " group by " + Metrics.ENTITY_ID, + conditions)) { + buildCalls(resultSet, calls, isClientSide); + } + } catch (SQLException e) { + throw new IOException(e); + } + return calls; + } + private List loadEndpointFromSide(String tableName, long startTB, long endTB, String sourceCName, String destCName, int id, boolean isSourceId) throws IOException { Object[] conditions = new Object[3]; diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/SimpleQueryClient.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/SimpleQueryClient.java index 1cff8984e..10c7c7666 100644 --- a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/SimpleQueryClient.java +++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/SimpleQueryClient.java @@ -29,9 +29,7 @@ import org.apache.skywalking.e2e.service.endpoint.EndpointQuery; import org.apache.skywalking.e2e.service.endpoint.Endpoints; import org.apache.skywalking.e2e.service.instance.Instances; import org.apache.skywalking.e2e.service.instance.InstancesQuery; -import org.apache.skywalking.e2e.topo.TopoData; -import org.apache.skywalking.e2e.topo.TopoQuery; -import org.apache.skywalking.e2e.topo.TopoResponse; +import org.apache.skywalking.e2e.topo.*; import org.apache.skywalking.e2e.trace.Trace; import org.apache.skywalking.e2e.trace.TracesData; import org.apache.skywalking.e2e.trace.TracesQuery; @@ -179,6 +177,30 @@ public class SimpleQueryClient { return Objects.requireNonNull(responseEntity.getBody()).getData().getTopo(); } + public ServiceInstanceTopoData serviceInstanceTopo(final ServiceInstanceTopoQuery query) throws Exception { + final URL queryFileUrl = Resources.getResource("instanceTopo.gql"); + final String queryString = Resources.readLines(queryFileUrl, Charset.forName("UTF8")) + .stream() + .filter(it -> !it.startsWith("#")) + .collect(Collectors.joining()) + .replace("{step}", query.step()) + .replace("{start}", query.start()) + .replace("{end}", query.end()) + .replace("{clientServiceId}", query.clientServiceId()) + .replace("{serverServiceId}", query.serverServiceId()); + final ResponseEntity> responseEntity = restTemplate.exchange( + new RequestEntity<>(queryString, HttpMethod.POST, URI.create(endpointUrl)), + new ParameterizedTypeReference>() { + } + ); + + if (responseEntity.getStatusCode() != HttpStatus.OK) { + throw new RuntimeException("Response status != 200, actual: " + responseEntity.getStatusCode()); + } + + return Objects.requireNonNull(responseEntity.getBody()).getData().getTopo(); + } + public Metrics metrics(final MetricsQuery query) throws Exception { final URL queryFileUrl = Resources.getResource("metrics.gql"); final String queryString = Resources.readLines(queryFileUrl, Charset.forName("UTF8")) diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/assertor/VariableExpressParser.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/assertor/VariableExpressParser.java index e509a7bfc..73daaa2ef 100644 --- a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/assertor/VariableExpressParser.java +++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/assertor/VariableExpressParser.java @@ -31,18 +31,18 @@ import static java.util.Objects.isNull; public class VariableExpressParser { - public static T parse(String express, List actual, Function getFiled) { - express = express.trim(); - if (!(express.startsWith("${") && express.endsWith("}"))) { + public static T parse(final String express, List actual, Function getFiled) { + String variable = express.trim(); + if (!(variable.startsWith("${") && variable.endsWith("}"))) { return null; } - express = express.substring(2, express.length() - 1); + variable = variable.substring(2, variable.length() - 1); - int startIndexOfIndex = express.lastIndexOf("["); - String regex = express.substring(0, startIndexOfIndex); - int endIndexOfIndex = express.indexOf("]", startIndexOfIndex); - int expectedIndex = Integer.parseInt(express.substring(startIndexOfIndex + 1, endIndexOfIndex)); + int startIndexOfIndex = variable.lastIndexOf("["); + String regex = variable.substring(0, startIndexOfIndex); + int endIndexOfIndex = variable.indexOf("]", startIndexOfIndex); + int expectedIndex = Integer.parseInt(variable.substring(startIndexOfIndex + 1, endIndexOfIndex)); int mappingIndex = 0; T mapping = null; diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/ServiceInstanceNode.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/ServiceInstanceNode.java new file mode 100644 index 000000000..58a6ed804 --- /dev/null +++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/ServiceInstanceNode.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.e2e.topo; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.Accessors; + +/** + * @author zhangwei + */ +@Setter +@Getter +@Accessors(chain = true) +@ToString +public class ServiceInstanceNode { + + private String id; + private String name; + private String serviceId; + private String serviceName; + private String type; + private String isReal; +} diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/ServiceInstanceNodeMatcher.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/ServiceInstanceNodeMatcher.java new file mode 100644 index 000000000..13393ecf1 --- /dev/null +++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/ServiceInstanceNodeMatcher.java @@ -0,0 +1,87 @@ +/* + * 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.topo; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.apache.skywalking.e2e.verification.AbstractMatcher; + +import static java.util.Objects.nonNull; + +/** + * @author zhangwei + */ +@Setter +@Getter +@ToString +public class ServiceInstanceNodeMatcher extends AbstractMatcher { + + private String id; + private String name; + private String serviceId; + private String serviceName; + private String type; + private String isReal; + + @Override + public void verify(ServiceInstanceNode node) { + if (nonNull(getId())) { + final String expected = this.getId(); + final String actual = node.getId(); + + doVerify(expected, actual); + } + + if (nonNull(getName())) { + final String expected = this.getName(); + final String actual = node.getName(); + + doVerify(expected, actual); + } + + if (nonNull(getServiceId())) { + final String expected = this.getServiceId(); + final String actual = node.getServiceId(); + + doVerify(expected, actual); + } + + if (nonNull(getServiceName())) { + final String expected = this.getServiceName(); + final String actual = node.getServiceName(); + + doVerify(expected, actual); + } + + if (nonNull(getType())) { + final String expected = this.getType(); + final String actual = node.getType(); + + doVerify(expected, actual); + } + + if (nonNull(getIsReal())) { + final String expected = this.getIsReal(); + final String actual = String.valueOf(node.getIsReal()); + + doVerify(expected, actual); + } + } +} diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/ServiceInstanceTopoData.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/ServiceInstanceTopoData.java new file mode 100644 index 000000000..650841068 --- /dev/null +++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/ServiceInstanceTopoData.java @@ -0,0 +1,45 @@ +/* + * 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.topo; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.Accessors; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author zhangwei + */ +@Setter +@Getter +@Accessors(chain = true) +@ToString +public class ServiceInstanceTopoData { + + private List nodes; + private List calls; + + public ServiceInstanceTopoData() { + this.nodes = new ArrayList<>(); + this.calls = new ArrayList<>(); + } +} diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/ServiceInstanceTopoMatcher.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/ServiceInstanceTopoMatcher.java new file mode 100644 index 000000000..94f7445c6 --- /dev/null +++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/ServiceInstanceTopoMatcher.java @@ -0,0 +1,107 @@ +/* + * 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.topo; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.apache.skywalking.e2e.assertor.VariableExpressParser; +import org.apache.skywalking.e2e.verification.AbstractMatcher; + +import java.util.List; + +import static java.util.Objects.nonNull; +import static org.assertj.core.api.Assertions.fail; + +/** + * @author zhangwei + */ +@Setter +@Getter +@ToString +public class ServiceInstanceTopoMatcher extends AbstractMatcher { + + private List nodes; + private List calls; + + @Override + public void verify(ServiceInstanceTopoData topoData) { + if (nonNull(getNodes())) { + verifyNodes(topoData); + } + + if (nonNull(getCalls())) { + convertNodeId(getCalls(), topoData.getNodes()); + verifyCalls(topoData); + } + } + + private void verifyNodes(ServiceInstanceTopoData topoData) { + for (int i = 0; i < getNodes().size(); i++) { + boolean matched = false; + for (int j = 0; j < topoData.getNodes().size(); j++) { + try { + getNodes().get(i).verify(topoData.getNodes().get(j)); + matched = true; + } catch (Throwable ignored) { + } + } + if (!matched) { + fail("Expected: %s\nActual: %s", getNodes(), topoData.getNodes()); + } + } + } + + private void verifyCalls(ServiceInstanceTopoData topoData) { + for (int i = 0; i < getCalls().size(); i++) { + boolean matched = false; + for (int j = 0; j < topoData.getCalls().size(); j++) { + try { + getCalls().get(i).verify(topoData.getCalls().get(j)); + matched = true; + } catch (Throwable ignored) { + } + } + if (!matched) { + fail("Expected: %s\nActual: %s", getCalls(), topoData.getCalls()); + } + } + } + + private static void convertNodeId(List callMatchers, List nodes) { + for (CallMatcher callMatcher : callMatchers) { + ServiceInstanceNode sourceNode = VariableExpressParser.parse(callMatcher.getSource(), nodes, ServiceInstanceNode::getName); + ServiceInstanceNode targetNode = VariableExpressParser.parse(callMatcher.getTarget(), nodes, ServiceInstanceNode::getName); + + boolean convert = false; + if (nonNull(sourceNode)) { + callMatcher.setSource(sourceNode.getId()); + convert = true; + } + if (nonNull(targetNode)) { + callMatcher.setTarget(targetNode.getId()); + convert = true; + } + + if (convert) { + callMatcher.setId(String.join("_", callMatcher.getSource(), callMatcher.getTarget())); + } + } + } +} diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/ServiceInstanceTopoQuery.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/ServiceInstanceTopoQuery.java new file mode 100644 index 000000000..d0e69f4b7 --- /dev/null +++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/ServiceInstanceTopoQuery.java @@ -0,0 +1,48 @@ +/* + * 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.topo; + +import org.apache.skywalking.e2e.AbstractQuery; + +/** + * @author zhangwei + */ +public class ServiceInstanceTopoQuery extends AbstractQuery { + + private String clientServiceId; + private String serverServiceId; + + public ServiceInstanceTopoQuery clientServiceId(String clientServiceId) { + this.clientServiceId = clientServiceId; + return this; + } + + public ServiceInstanceTopoQuery serverServiceId(String serverServiceId) { + this.serverServiceId = serverServiceId; + return this; + } + + public String clientServiceId() { + return clientServiceId; + } + + public String serverServiceId() { + return serverServiceId; + } +} diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/ServiceInstanceTopoResponse.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/ServiceInstanceTopoResponse.java new file mode 100644 index 000000000..425de378d --- /dev/null +++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/ServiceInstanceTopoResponse.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.topo; + +import lombok.Getter; +import lombok.Setter; + +/** + * @author zhangwei + */ +@Setter +@Getter +public class ServiceInstanceTopoResponse { + + private ServiceInstanceTopoData topo; +} diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/TopoData.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/TopoData.java index bb6c8870a..3a361273d 100644 --- a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/TopoData.java +++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/TopoData.java @@ -50,4 +50,12 @@ public class TopoData { this.calls = calls; return this; } + + @Override + public String toString() { + return "TopoData{" + + "nodes=" + nodes + + ", calls=" + calls + + '}'; + } } diff --git a/test/e2e/e2e-base/src/main/resources/instanceTopo.gql b/test/e2e/e2e-base/src/main/resources/instanceTopo.gql new file mode 100644 index 000000000..08c02f1e7 --- /dev/null +++ b/test/e2e/e2e-base/src/main/resources/instanceTopo.gql @@ -0,0 +1,45 @@ +# 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. + +{ + "query": "query queryInstanceTopo($clientServiceId: ID!, $serverServiceId: ID!, $duration: Duration!) { + topo: getServiceInstanceTopology(clientServiceId: $clientServiceId, serverServiceId: $serverServiceId, duration: $duration) { + nodes { + id + name + serviceId + serviceName + type + isReal + } + calls { + id + source + detectPoints + target + } + } + }", + "variables": { + "duration": { + "start": "{start}", + "end": "{end}", + "step": "{step}" + }, + "clientServiceId": "{clientServiceId}", + "serverServiceId": "{serverServiceId}" + } +} diff --git a/test/e2e/e2e-base/src/test/java/org/apache/skywalking/e2e/TestServiceInstanceTopoMatcher.java b/test/e2e/e2e-base/src/test/java/org/apache/skywalking/e2e/TestServiceInstanceTopoMatcher.java new file mode 100644 index 000000000..816d5d103 --- /dev/null +++ b/test/e2e/e2e-base/src/test/java/org/apache/skywalking/e2e/TestServiceInstanceTopoMatcher.java @@ -0,0 +1,79 @@ +/* + * 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 org.apache.skywalking.e2e.assertor.exception.VariableNotFoundException; +import org.apache.skywalking.e2e.topo.*; +import org.junit.Before; +import org.junit.Test; +import org.springframework.core.io.ClassPathResource; +import org.yaml.snakeyaml.Yaml; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +/** + * @author zhangwei + */ +public class TestServiceInstanceTopoMatcher { + + private ServiceInstanceTopoMatcher topoMatcher; + + @Before + public void setUp() throws IOException { + try (InputStream expectedInputStream = new ClassPathResource("serviceInstanceTopo.yml").getInputStream()) { + topoMatcher = new Yaml().loadAs(expectedInputStream, ServiceInstanceTopoMatcher.class); + } + } + + @Test + public void shouldSuccess() { + final List nodes = new ArrayList<>(); + nodes.add(new ServiceInstanceNode().setId("2").setName("e2e-cluster-provider-pid:1582@2ffd0ee4eeb1").setServiceId("2").setServiceName("e2e-cluster-provider").setType("Tomcat").setIsReal("true")); + nodes.add(new ServiceInstanceNode().setId("3").setName("e2e-cluster-provider-pid:1583@2ffd0ee4eeb1").setServiceId("2").setServiceName("e2e-cluster-provider").setType("Tomcat").setIsReal("true")); + nodes.add(new ServiceInstanceNode().setId("4").setName("e2e-cluster-consumer-pid:1591@2ffd0ee4eeb1").setServiceId("3").setServiceName("e2e-cluster-consumer").setIsReal("true")); + + + final List calls = new ArrayList<>(); + calls.add(new Call().setId("4_3").setSource("4").setTarget("3")); + calls.add(new Call().setId("4_2").setSource("4").setTarget("2")); + + final ServiceInstanceTopoData topoData = new ServiceInstanceTopoData().setNodes(nodes).setCalls(calls); + + topoMatcher.verify(topoData); + } + + @Test(expected = VariableNotFoundException.class) + public void shouldVariableNotFound() { + final List nodes = new ArrayList<>(); + nodes.add(new ServiceInstanceNode().setId("2").setName("e2e-cluster-provider-pid:1582@2ffd0ee4eeb1").setServiceId("2").setServiceName("e2e-cluster-provider").setType("Tomcat").setIsReal("true")); + nodes.add(new ServiceInstanceNode().setId("3").setName("e2e-cluster-Aprovider-pid:1583@2ffd0ee4eeb1").setServiceId("2").setServiceName("e2e-cluster-provider").setType("Tomcat").setIsReal("true")); + nodes.add(new ServiceInstanceNode().setId("4").setName("e2e-cluster-consumer-pid:1591@2ffd0ee4eeb1").setServiceId("3").setServiceName("e2e-cluster-consumer").setIsReal("true")); + + + final List calls = new ArrayList<>(); + calls.add(new Call().setId("4_3").setSource("4").setTarget("3")); + calls.add(new Call().setId("4_2").setSource("4").setTarget("2")); + + final ServiceInstanceTopoData topoData = new ServiceInstanceTopoData().setNodes(nodes).setCalls(calls); + + topoMatcher.verify(topoData); + } +} diff --git a/test/e2e/e2e-base/src/test/resources/serviceInstanceTopo.yml b/test/e2e/e2e-base/src/test/resources/serviceInstanceTopo.yml new file mode 100644 index 000000000..f700d61ea --- /dev/null +++ b/test/e2e/e2e-base/src/test/resources/serviceInstanceTopo.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: not null + name: not null + serviceId: not null + serviceName: e2e-cluster-consumer + isReal: true + - id: not null + name: not null + serviceId: not null + serviceName: e2e-cluster-provider + type: Tomcat + isReal: true + - id: not null + name: not null + serviceId: not null + serviceName: e2e-cluster-provider + type: Tomcat + isReal: true +calls: + - id: not null + source: ${e2e-cluster-consumer(.*)[0]} + target: ${e2e-cluster-provider(.*)[0]} + - id: not null + source: ${e2e-cluster-consumer(.*)[0]} + target: ${e2e-cluster-provider(.*)[1]} diff --git a/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-consumer/pom.xml b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-consumer/pom.xml new file mode 100644 index 000000000..dd6e9583e --- /dev/null +++ b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-consumer/pom.xml @@ -0,0 +1,56 @@ + + + + + + + e2e-cluster-with-gateway + org.apache.skywalking + 1.0.0 + + 4.0.0 + jar + + e2e-cluster-with-gateway-consumer + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + true + true + true + + + + + repackage + + + + + + + + diff --git a/test/e2e/e2e-cluster/consumer/src/main/java/org/apache/skywalking/e2e/cluster/Service1Application.java b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-consumer/src/main/java/org/apache/skywalking/e2e/cluster/Service1Application.java similarity index 100% rename from test/e2e/e2e-cluster/consumer/src/main/java/org/apache/skywalking/e2e/cluster/Service1Application.java rename to test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-consumer/src/main/java/org/apache/skywalking/e2e/cluster/Service1Application.java diff --git a/test/e2e/e2e-cluster/consumer/src/main/java/org/apache/skywalking/e2e/cluster/TestController.java b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-consumer/src/main/java/org/apache/skywalking/e2e/cluster/TestController.java similarity index 88% rename from test/e2e/e2e-cluster/consumer/src/main/java/org/apache/skywalking/e2e/cluster/TestController.java rename to test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-consumer/src/main/java/org/apache/skywalking/e2e/cluster/TestController.java index a50088b41..c10c1a436 100644 --- a/test/e2e/e2e-cluster/consumer/src/main/java/org/apache/skywalking/e2e/cluster/TestController.java +++ b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-consumer/src/main/java/org/apache/skywalking/e2e/cluster/TestController.java @@ -42,9 +42,12 @@ public class TestController { @PostMapping("/users") public User createAuthor(@RequestBody final User user) throws InterruptedException { Thread.sleep(1000L); - final ResponseEntity response = restTemplate.postForEntity( - "http://127.0.0.1:9099/e2e/users", user, User.class - ); + ResponseEntity response = null; + for (int i = 0; i < 2; i++) { + response = restTemplate.postForEntity( + "http://127.0.0.1:9099/e2e/users", user, User.class + ); + } return response.getBody(); } } diff --git a/test/e2e/e2e-cluster/consumer/src/main/java/org/apache/skywalking/e2e/cluster/User.java b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-consumer/src/main/java/org/apache/skywalking/e2e/cluster/User.java similarity index 100% rename from test/e2e/e2e-cluster/consumer/src/main/java/org/apache/skywalking/e2e/cluster/User.java rename to test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-consumer/src/main/java/org/apache/skywalking/e2e/cluster/User.java diff --git a/test/e2e/e2e-cluster/consumer/src/main/resources/application.yml b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-consumer/src/main/resources/application.yml similarity index 98% rename from test/e2e/e2e-cluster/consumer/src/main/resources/application.yml rename to test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-consumer/src/main/resources/application.yml index 757e7cbe0..ff8f0bf04 100644 --- a/test/e2e/e2e-cluster/consumer/src/main/resources/application.yml +++ b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-consumer/src/main/resources/application.yml @@ -15,7 +15,7 @@ # limitations under the License. server: - port: 9091 + port: 9092 spring: main: diff --git a/test/e2e/e2e-cluster/gateway/pom.xml b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-gateway/pom.xml similarity index 95% rename from test/e2e/e2e-cluster/gateway/pom.xml rename to test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-gateway/pom.xml index afc8ff353..307f81aec 100644 --- a/test/e2e/e2e-cluster/gateway/pom.xml +++ b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-gateway/pom.xml @@ -20,13 +20,13 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - e2e-cluster + e2e-cluster-with-gateway org.apache.skywalking 1.0.0 4.0.0 - gateway + e2e-cluster-with-gateway-gateway diff --git a/test/e2e/e2e-cluster/gateway/src/main/java/org/apache/skywalking/e2e/GatewayApplication.java b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-gateway/src/main/java/org/apache/skywalking/e2e/GatewayApplication.java similarity index 100% rename from test/e2e/e2e-cluster/gateway/src/main/java/org/apache/skywalking/e2e/GatewayApplication.java rename to test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-gateway/src/main/java/org/apache/skywalking/e2e/GatewayApplication.java diff --git a/test/e2e/e2e-cluster/gateway/src/main/resources/application.yml b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-gateway/src/main/resources/application.yml similarity index 87% rename from test/e2e/e2e-cluster/gateway/src/main/resources/application.yml rename to test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-gateway/src/main/resources/application.yml index 32131fc57..cac4773ca 100644 --- a/test/e2e/e2e-cluster/gateway/src/main/resources/application.yml +++ b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-gateway/src/main/resources/application.yml @@ -24,5 +24,13 @@ zuul: ignoredServices: '*' routes: api: + serviceId: api path: /e2e/users - url: http://127.0.0.1:9090 + +ribbon: + eureka: + enabled: false + +api: + ribbon: + listOfServers: http://127.0.0.1:9090,http://127.0.0.1:9091 \ No newline at end of file diff --git a/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-provider/pom.xml b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-provider/pom.xml new file mode 100644 index 000000000..cf472bfe8 --- /dev/null +++ b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-provider/pom.xml @@ -0,0 +1,69 @@ + + + + + + + e2e-cluster-with-gateway + org.apache.skywalking + 1.0.0 + + 4.0.0 + jar + + e2e-cluster-with-gateway-provider + + + + org.springframework.boot + spring-boot-starter-data-jpa + ${spring.boot.version} + + + com.h2database + h2 + ${h2.version} + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + true + true + true + + + + + repackage + + + + + + + + diff --git a/test/e2e/e2e-cluster/provider/src/main/java/org/apache/skywalking/e2e/cluster/Service0Application.java b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-provider/src/main/java/org/apache/skywalking/e2e/cluster/Service0Application.java similarity index 100% rename from test/e2e/e2e-cluster/provider/src/main/java/org/apache/skywalking/e2e/cluster/Service0Application.java rename to test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-provider/src/main/java/org/apache/skywalking/e2e/cluster/Service0Application.java diff --git a/test/e2e/e2e-cluster/provider/src/main/java/org/apache/skywalking/e2e/cluster/TestController.java b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-provider/src/main/java/org/apache/skywalking/e2e/cluster/TestController.java similarity index 100% rename from test/e2e/e2e-cluster/provider/src/main/java/org/apache/skywalking/e2e/cluster/TestController.java rename to test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-provider/src/main/java/org/apache/skywalking/e2e/cluster/TestController.java diff --git a/test/e2e/e2e-cluster/provider/src/main/java/org/apache/skywalking/e2e/cluster/User.java b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-provider/src/main/java/org/apache/skywalking/e2e/cluster/User.java similarity index 100% rename from test/e2e/e2e-cluster/provider/src/main/java/org/apache/skywalking/e2e/cluster/User.java rename to test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-provider/src/main/java/org/apache/skywalking/e2e/cluster/User.java diff --git a/test/e2e/e2e-cluster/provider/src/main/java/org/apache/skywalking/e2e/cluster/UserRepo.java b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-provider/src/main/java/org/apache/skywalking/e2e/cluster/UserRepo.java similarity index 100% rename from test/e2e/e2e-cluster/provider/src/main/java/org/apache/skywalking/e2e/cluster/UserRepo.java rename to test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-provider/src/main/java/org/apache/skywalking/e2e/cluster/UserRepo.java diff --git a/test/e2e/e2e-cluster/provider/src/main/resources/application.yml b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-provider/src/main/resources/application.yml similarity index 100% rename from test/e2e/e2e-cluster/provider/src/main/resources/application.yml rename to test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-provider/src/main/resources/application.yml diff --git a/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-test-runner/pom.xml b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-test-runner/pom.xml new file mode 100755 index 000000000..2dbfab7e5 --- /dev/null +++ b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-test-runner/pom.xml @@ -0,0 +1,220 @@ + + + + + + + e2e-cluster-with-gateway + org.apache.skywalking + 1.0.0 + + 4.0.0 + + e2e-cluster-with-gateway-test-runner + + + + + org.apache.skywalking + e2e-base + ${project.version} + + + + org.apache.skywalking + e2e-cluster-with-gateway-gateway + ${project.version} + + + + org.apache.skywalking + e2e-cluster-with-gateway-provider + ${project.version} + + + + org.apache.skywalking + e2e-cluster-with-gateway-consumer + ${project.version} + + + + + + e2e-cluster-with-gateway-provider + e2e-cluster-with-gateway-consumer + e2e-cluster-with-gateway-gateway + 1.1 + skywalking-e2e-container-${build.id}-cluster-with-gateway + + + + + + io.fabric8 + docker-maven-plugin + + %a-%t-%i + Always + + + elastic/elasticsearch:${elasticsearch.version} + ${e2e.container.name.prefix}-elasticsearch + + + es.port:9200 + + + + http://localhost:${es.port} + GET + 200 + + + + + single-node + + + + + zookeeper:${zookeeper.image.version} + ${e2e.container.name.prefix}-zookeeper + + + zk.port:2181 + + + binding to port + + + + + + skyapm/e2e-container:${e2e.container.version} + ${e2e.container.name.prefix} + + + cluster + ${elasticsearch.version} + + ${e2e.container.name.prefix}-elasticsearch:9200 + + + ${e2e.container.name.prefix}-zookeeper:2181 + + + + ${provider.name}-${project.version}.jar + + + -DSW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800 + -DSW_AGENT_NAME=${provider.name} + -Dserver.port=9090 + + + + ${provider.name}-${project.version}.jar + + + -DSW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11801 + -DSW_AGENT_NAME=${provider.name} + -Dserver.port=9091 + + + + ${consumer.name}-${project.version}.jar + + + -DSW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11801 + -DSW_AGENT_NAME=${consumer.name} + -Dserver.port=9092 + + + + ${e2e.container.name.prefix}-elasticsearch + ${e2e.container.name.prefix}-zookeeper + + + +webapp.host:webapp.port:8081 + +service.host:service.port:9092 + + + ${e2e.container.name.prefix}-elasticsearch + ${e2e.container.name.prefix}-zookeeper + + + + ${sw.home}:/sw + + ../${gateway.name}/target/${gateway.name}-${project.version}.jar:/home/${gateway.name}-${project.version}.jar + + + ../${provider.name}/target/${provider.name}-${project.version}.jar:/home/${provider.name}-${project.version}.jar + + + ../${consumer.name}/target/${consumer.name}-${project.version}.jar:/home/${consumer.name}-${project.version}.jar + + + ${project.basedir}/src/docker/rc.d:/rc.d:ro + + + ${project.basedir}/src/docker/clusterize.awk:/clusterize.awk + + + + + SkyWalking e2e container is ready for tests + + + + + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + ${webapp.host} + ${webapp.port} + ${service.host} + ${service.port} + ${provider.name} + ${consumer.name} + 127.0.0.1:9099 + + + + + + verify + + + + + + + + diff --git a/test/e2e/e2e-cluster/test-runner/src/docker/clusterize.awk b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-test-runner/src/docker/clusterize.awk similarity index 100% rename from test/e2e/e2e-cluster/test-runner/src/docker/clusterize.awk rename to test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-test-runner/src/docker/clusterize.awk diff --git a/test/e2e/e2e-cluster/test-runner/src/docker/rc.d/rc0-prepare.sh b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-test-runner/src/docker/rc.d/rc0-prepare.sh similarity index 100% rename from test/e2e/e2e-cluster/test-runner/src/docker/rc.d/rc0-prepare.sh rename to test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-test-runner/src/docker/rc.d/rc0-prepare.sh diff --git a/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-test-runner/src/docker/rc.d/rc1-startup.sh b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-test-runner/src/docker/rc.d/rc1-startup.sh new file mode 100755 index 000000000..977d278cc --- /dev/null +++ b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-test-runner/src/docker/rc.d/rc1-startup.sh @@ -0,0 +1,99 @@ +#!/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 gateway service...' \ + && java -jar /home/e2e-cluster-with-gateway-gateway-1.0.0.jar 2>&1 > /tmp/gateway.log & + +check_tcp 127.0.0.1 \ + 9099 \ + 60 \ + 10 \ + 'waiting for the gateway service to be ready' + +if [[ $? -ne 0 ]]; then + echo "gateway service failed to start in 60 * 10 seconds: " + cat /tmp/gateway.log + exit 1 +fi + +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' 8081 + +if test "${MODE}" = "cluster"; then + # start another OAP server in a different port + echo 'starting OAP server...' \ + && SW_CORE_GRPC_PORT=11801 \ + SW_CORE_REST_PORT=12801 \ + SW_STORAGE_ES_BULK_ACTIONS=1 \ + SW_STORAGE_ES_FLUSH_INTERVAL=1 \ + SW_RECEIVER_BUFFER_PATH=/tmp/oap/trace_buffer2 \ + SW_SERVICE_MESH_BUFFER_PATH=/tmp/oap/mesh_buffer2 \ + start_oap 'no-init' +fi + +echo 'starting instrumented services...' && start_instrumented_services + +check_tcp 127.0.0.1 \ + 9090 \ + 60 \ + 10 \ + "waiting for the instrumented service 0 to be ready" + +if [[ $? -ne 0 ]]; then + echo "instrumented service 0 failed to start in 30 * 10 seconds: " + cat ${SERVICE_LOG}/* + exit 1 +fi + +check_tcp 127.0.0.1 \ + 9091 \ + 60 \ + 10 \ + "waiting for the instrumented service 1 to be ready" + +if [[ $? -ne 0 ]]; then + echo "instrumented service 1 failed to start in 24 * 10 seconds: " + cat ${SERVICE_LOG}/* + exit 1 +fi + +check_tcp 127.0.0.1 \ + 9092 \ + 60 \ + 10 \ + "waiting for the instrumented service 2 to be ready" + +if [[ $? -ne 0 ]]; then + echo "instrumented service 2 failed to start in 24 * 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/elasticsearch.log \ + ${ES_HOME}/logs/stdout.log \ No newline at end of file diff --git a/test/e2e/e2e-cluster/test-runner/src/test/java/org/apache/skywalking/e2e/ClusterVerificationITCase.java b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-test-runner/src/test/java/org/apache/skywalking/e2e/ClusterVerificationITCase.java similarity index 84% rename from test/e2e/e2e-cluster/test-runner/src/test/java/org/apache/skywalking/e2e/ClusterVerificationITCase.java rename to test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-test-runner/src/test/java/org/apache/skywalking/e2e/ClusterVerificationITCase.java index 513486d1b..cbe582715 100755 --- a/test/e2e/e2e-cluster/test-runner/src/test/java/org/apache/skywalking/e2e/ClusterVerificationITCase.java +++ b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-test-runner/src/test/java/org/apache/skywalking/e2e/ClusterVerificationITCase.java @@ -33,9 +33,7 @@ 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.TopoData; -import org.apache.skywalking.e2e.topo.TopoMatcher; -import org.apache.skywalking.e2e.topo.TopoQuery; +import org.apache.skywalking.e2e.topo.*; import org.apache.skywalking.e2e.trace.Trace; import org.apache.skywalking.e2e.trace.TracesMatcher; import org.apache.skywalking.e2e.trace.TracesQuery; @@ -61,6 +59,7 @@ import java.util.Map; import java.util.concurrent.TimeUnit; import static org.apache.skywalking.e2e.metrics.MetricsQuery.*; +import static org.assertj.core.api.Assertions.fail; /** * @author kezhenxu94 @@ -76,8 +75,15 @@ public class ClusterVerificationITCase { private String instrumentedServiceUrl; private long retryInterval = TimeUnit.SECONDS.toMillis(30); + private String providerName; + private String gateWayName; + + @Before public void setUp() { + providerName = System.getProperty("provider.name", "e2e-cluster-provider"); + gateWayName = System.getProperty("gateway.name", "127.0.0.1:9099"); + final String swWebappHost = System.getProperty("sw.webapp.host", "127.0.0.1"); final String swWebappPort = System.getProperty("sw.webapp.port", "32791"); final String instrumentedServiceHost = System.getProperty("service.host", "127.0.0.1"); @@ -114,6 +120,7 @@ public class ClusterVerificationITCase { } private void verifyTopo(LocalDateTime minutesAgo) throws Exception { + String clientServiceId = null, serverServiceId = null; boolean valid = false; while (!valid) { try { @@ -131,12 +138,51 @@ public class ClusterVerificationITCase { final TopoMatcher topoMatcher = yaml.loadAs(expectedInputStream, TopoMatcher.class); topoMatcher.verify(topoData); valid = true; + for (Node node : topoData.getNodes()) { + if (gateWayName.equals(node.getName())) { + clientServiceId = node.getId(); + } else if (providerName.equals(node.getName())) { + serverServiceId = node.getId(); + } + } } catch (Throwable t) { LOGGER.warn(t.getMessage(), t); generateTraffic(); Thread.sleep(retryInterval); } } + verifyServiceInstanceTopo(minutesAgo, clientServiceId, serverServiceId); + } + + private void verifyServiceInstanceTopo(LocalDateTime minutesAgo, String clientServiceId, String serverServiceId) throws Exception { + if (clientServiceId == null || serverServiceId == null) { + fail("clientService or serverService not found"); + } + boolean valid = false; + while (!valid) { + try { + final ServiceInstanceTopoData topoData = queryClient.serviceInstanceTopo( + new ServiceInstanceTopoQuery() + .stepByMinute() + .start(minutesAgo.minusDays(1)) + .end(LocalDateTime.now(ZoneOffset.UTC).plusMinutes(1)) + .clientServiceId(clientServiceId) + .serverServiceId(serverServiceId) + ); + + LOGGER.info("Actual service instance topology: {}", topoData); + + InputStream expectedInputStream = + new ClassPathResource("expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.serviceInstanceTopo.yml").getInputStream(); + final ServiceInstanceTopoMatcher topoMatcher = yaml.loadAs(expectedInputStream, ServiceInstanceTopoMatcher.class); + topoMatcher.verify(topoData); + valid = true; + }catch (Throwable t){ + LOGGER.warn(t.getMessage(), t); + generateTraffic(); + Thread.sleep(retryInterval); + } + } } private void verifyServices(LocalDateTime minutesAgo) throws Exception { @@ -194,8 +240,16 @@ public class ClusterVerificationITCase { .end(LocalDateTime.now(ZoneOffset.UTC).plusMinutes(1)) ); } - InputStream expectedInputStream = - new ClassPathResource("expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.instances.yml").getInputStream(); + + InputStream expectedInputStream; + if (providerName.equals(service.getLabel())) { + expectedInputStream = + new ClassPathResource("expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.providerInstances.yml").getInputStream(); + } else { + expectedInputStream = + new ClassPathResource("expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.instances.yml").getInputStream(); + } + final InstancesMatcher instancesMatcher = yaml.loadAs(expectedInputStream, InstancesMatcher.class); instancesMatcher.verify(instances); return instances; diff --git a/test/e2e/e2e-cluster/test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.endpoints.yml b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.endpoints.yml similarity index 100% rename from test/e2e/e2e-cluster/test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.endpoints.yml rename to test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.endpoints.yml diff --git a/test/e2e/e2e-cluster/test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.instances.yml b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.instances.yml similarity index 100% rename from test/e2e/e2e-cluster/test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.instances.yml rename to test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.instances.yml diff --git a/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.providerInstances.yml b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.providerInstances.yml new file mode 100644 index 000000000..a9e60c2a1 --- /dev/null +++ b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.providerInstances.yml @@ -0,0 +1,45 @@ +# 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: gt 0 + 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 + - key: gt 0 + 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-cluster-with-gateway/e2e-cluster-with-gateway-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.serviceInstanceTopo.yml b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.serviceInstanceTopo.yml new file mode 100644 index 000000000..c79dc0ed3 --- /dev/null +++ b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.serviceInstanceTopo.yml @@ -0,0 +1,48 @@ +# 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: not null + name: 127.0.0.1:9099 + type: Unknown + serviceId: not null + serviceName: 127.0.0.1:9099 + isReal: true + - id: not null + name: not null + serviceId: not null + serviceName: e2e-cluster-with-gateway-provider + type: Tomcat + isReal: true + - id: not null + name: not null + serviceId: not null + serviceName: e2e-cluster-with-gateway-provider + type: Tomcat + isReal: true +calls: + - id: not null + source: ${127.0.0.1:9099[0]} + target: ${e2e-cluster-with-gateway-provider(.*)[0]} + - id: not null + source: ${127.0.0.1:9099[0]} + target: ${e2e-cluster-with-gateway-provider(.*)[1]} diff --git a/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.services.yml b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.services.yml new file mode 100644 index 000000000..c0da43088 --- /dev/null +++ b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.services.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 + +services: + - key: not null + label: e2e-cluster-with-gateway-provider + + - key: not null + label: e2e-cluster-with-gateway-consumer diff --git a/test/e2e/e2e-cluster/test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.topo.yml b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.topo.yml similarity index 80% rename from test/e2e/e2e-cluster/test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.topo.yml rename to test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.topo.yml index 2e984d33c..26e497a13 100644 --- a/test/e2e/e2e-cluster/test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.topo.yml +++ b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.topo.yml @@ -19,10 +19,10 @@ nodes: name: User type: USER - id: not null - name: provider + name: e2e-cluster-with-gateway-provider type: Tomcat - id: not null - name: consumer + name: e2e-cluster-with-gateway-consumer type: Tomcat - id: not null name: "localhost:-1" @@ -33,13 +33,13 @@ nodes: calls: - id: not null source: ${User[0]} - target: ${consumer[0]} + target: ${e2e-cluster-with-gateway-consumer[0]} - id: not null source: ${127.0.0.1:9099[0]} - target: ${provider[0]} + target: ${e2e-cluster-with-gateway-provider[0]} - id: not null - source: ${consumer[0]} + source: ${e2e-cluster-with-gateway-consumer[0]} target: ${127.0.0.1:9099[0]} - id: not null - source: ${provider[0]} + source: ${e2e-cluster-with-gateway-provider[0]} target: ${localhost:-1[0]} diff --git a/test/e2e/e2e-cluster/test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.traces.yml b/test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.traces.yml similarity index 100% rename from test/e2e/e2e-cluster/test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.traces.yml rename to test/e2e/e2e-cluster-with-gateway/e2e-cluster-with-gateway-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.traces.yml diff --git a/test/e2e/e2e-cluster-with-gateway/pom.xml b/test/e2e/e2e-cluster-with-gateway/pom.xml new file mode 100644 index 000000000..464c50e0d --- /dev/null +++ b/test/e2e/e2e-cluster-with-gateway/pom.xml @@ -0,0 +1,39 @@ + + + + + + apache-skywalking-e2e + org.apache.skywalking + 1.0.0 + + 4.0.0 + + e2e-cluster-with-gateway + pom + + + e2e-cluster-with-gateway-provider + e2e-cluster-with-gateway-consumer + e2e-cluster-with-gateway-gateway + e2e-cluster-with-gateway-test-runner + + diff --git a/test/e2e/e2e-cluster/consumer/pom.xml b/test/e2e/e2e-cluster/e2e-cluster-consumer/pom.xml similarity index 97% rename from test/e2e/e2e-cluster/consumer/pom.xml rename to test/e2e/e2e-cluster/e2e-cluster-consumer/pom.xml index efaa53cb2..fd08eedd9 100644 --- a/test/e2e/e2e-cluster/consumer/pom.xml +++ b/test/e2e/e2e-cluster/e2e-cluster-consumer/pom.xml @@ -29,7 +29,7 @@ 4.0.0 jar - consumer + e2e-cluster-consumer diff --git a/test/e2e/e2e-cluster/e2e-cluster-consumer/src/main/java/org/apache/skywalking/e2e/cluster/Service1Application.java b/test/e2e/e2e-cluster/e2e-cluster-consumer/src/main/java/org/apache/skywalking/e2e/cluster/Service1Application.java new file mode 100644 index 000000000..7ba83aac7 --- /dev/null +++ b/test/e2e/e2e-cluster/e2e-cluster-consumer/src/main/java/org/apache/skywalking/e2e/cluster/Service1Application.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.cluster; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author kezhenxu94 + */ +@SpringBootApplication +public class Service1Application { + public static void main(String[] args) { + SpringApplication.run(Service1Application.class, args); + } +} diff --git a/test/e2e/e2e-cluster/e2e-cluster-consumer/src/main/java/org/apache/skywalking/e2e/cluster/TestController.java b/test/e2e/e2e-cluster/e2e-cluster-consumer/src/main/java/org/apache/skywalking/e2e/cluster/TestController.java new file mode 100644 index 000000000..7e99ff5ae --- /dev/null +++ b/test/e2e/e2e-cluster/e2e-cluster-consumer/src/main/java/org/apache/skywalking/e2e/cluster/TestController.java @@ -0,0 +1,55 @@ +/* + * 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.cluster; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; + +/** + * @author kezhenxu94 + */ +@RestController +@RequestMapping("/e2e") +public class TestController { + private final RestTemplate restTemplate = new RestTemplate(); + + private static final String[] URL = {"http://127.0.0.1:9090/e2e/users", "http://127.0.0.1:9091/e2e/users"}; + + @GetMapping("/health-check") + public String hello() { + return "healthy"; + } + + @PostMapping("/users") + public User createAuthor(@RequestBody final User user) throws InterruptedException { + Thread.sleep(1000L); + ResponseEntity response = null; + for (String url : URL) { + response = restTemplate.postForEntity( + url, user, User.class + ); + } + return response.getBody(); + } +} diff --git a/test/e2e/e2e-cluster/e2e-cluster-consumer/src/main/java/org/apache/skywalking/e2e/cluster/User.java b/test/e2e/e2e-cluster/e2e-cluster-consumer/src/main/java/org/apache/skywalking/e2e/cluster/User.java new file mode 100644 index 000000000..f62d1e7bc --- /dev/null +++ b/test/e2e/e2e-cluster/e2e-cluster-consumer/src/main/java/org/apache/skywalking/e2e/cluster/User.java @@ -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. + * + */ + +package org.apache.skywalking.e2e.cluster; + +/** + * @author kezhenxu94 + */ +public class User { + public User() { + } + + private Long id; + + private String name; + + public Long getId() { + return id; + } + + public void setId(final Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } +} diff --git a/test/e2e/e2e-cluster/e2e-cluster-consumer/src/main/resources/application.yml b/test/e2e/e2e-cluster/e2e-cluster-consumer/src/main/resources/application.yml new file mode 100644 index 000000000..ff8f0bf04 --- /dev/null +++ b/test/e2e/e2e-cluster/e2e-cluster-consumer/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: 9092 + +spring: + main: + banner-mode: 'off' diff --git a/test/e2e/e2e-cluster/provider/pom.xml b/test/e2e/e2e-cluster/e2e-cluster-provider/pom.xml similarity index 98% rename from test/e2e/e2e-cluster/provider/pom.xml rename to test/e2e/e2e-cluster/e2e-cluster-provider/pom.xml index 804e24c0b..dc6fed8a5 100644 --- a/test/e2e/e2e-cluster/provider/pom.xml +++ b/test/e2e/e2e-cluster/e2e-cluster-provider/pom.xml @@ -29,7 +29,7 @@ 4.0.0 jar - provider + e2e-cluster-provider diff --git a/test/e2e/e2e-cluster/e2e-cluster-provider/src/main/java/org/apache/skywalking/e2e/cluster/Service0Application.java b/test/e2e/e2e-cluster/e2e-cluster-provider/src/main/java/org/apache/skywalking/e2e/cluster/Service0Application.java new file mode 100644 index 000000000..791eab1a0 --- /dev/null +++ b/test/e2e/e2e-cluster/e2e-cluster-provider/src/main/java/org/apache/skywalking/e2e/cluster/Service0Application.java @@ -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. + * + */ + +package org.apache.skywalking.e2e.cluster; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; + +/** + * @author kezhenxu94 + */ +@EnableJpaRepositories +@SpringBootApplication +public class Service0Application { + public static void main(String[] args) { + SpringApplication.run(Service0Application.class, args); + } +} diff --git a/test/e2e/e2e-cluster/e2e-cluster-provider/src/main/java/org/apache/skywalking/e2e/cluster/TestController.java b/test/e2e/e2e-cluster/e2e-cluster-provider/src/main/java/org/apache/skywalking/e2e/cluster/TestController.java new file mode 100644 index 000000000..39076484f --- /dev/null +++ b/test/e2e/e2e-cluster/e2e-cluster-provider/src/main/java/org/apache/skywalking/e2e/cluster/TestController.java @@ -0,0 +1,48 @@ +/* + * 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.cluster; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author kezhenxu94 + */ +@RestController +@RequestMapping("/e2e") +public class TestController { + private final UserRepo userRepo; + + public TestController(final UserRepo userRepo) { + this.userRepo = userRepo; + } + + @GetMapping("/health-check") + public String hello() { + return "healthy"; + } + + @PostMapping("/users") + public User createAuthor(@RequestBody final User user) { + return userRepo.save(user); + } +} diff --git a/test/e2e/e2e-cluster/e2e-cluster-provider/src/main/java/org/apache/skywalking/e2e/cluster/User.java b/test/e2e/e2e-cluster/e2e-cluster-provider/src/main/java/org/apache/skywalking/e2e/cluster/User.java new file mode 100644 index 000000000..043127f21 --- /dev/null +++ b/test/e2e/e2e-cluster/e2e-cluster-provider/src/main/java/org/apache/skywalking/e2e/cluster/User.java @@ -0,0 +1,56 @@ +/* + * 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.cluster; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +/** + * @author kezhenxu94 + */ +@Entity +public class User { + public User() { + } + + @Id + @GeneratedValue + private Long id; + + @Column + private String name; + + public Long getId() { + return id; + } + + public void setId(final Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } +} diff --git a/test/e2e/e2e-cluster/e2e-cluster-provider/src/main/java/org/apache/skywalking/e2e/cluster/UserRepo.java b/test/e2e/e2e-cluster/e2e-cluster-provider/src/main/java/org/apache/skywalking/e2e/cluster/UserRepo.java new file mode 100644 index 000000000..f798f63e0 --- /dev/null +++ b/test/e2e/e2e-cluster/e2e-cluster-provider/src/main/java/org/apache/skywalking/e2e/cluster/UserRepo.java @@ -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. + * + */ + +package org.apache.skywalking.e2e.cluster; + +import org.springframework.data.jpa.repository.JpaRepository; + +/** + * @author kezhenxu94 + */ +public interface UserRepo extends JpaRepository { +} diff --git a/test/e2e/e2e-cluster/e2e-cluster-provider/src/main/resources/application.yml b/test/e2e/e2e-cluster/e2e-cluster-provider/src/main/resources/application.yml new file mode 100644 index 000000000..ef3ed01cd --- /dev/null +++ b/test/e2e/e2e-cluster/e2e-cluster-provider/src/main/resources/application.yml @@ -0,0 +1,35 @@ +# 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' + datasource: + url: jdbc:h2:mem:testdb + driver-class-name: org.h2.Driver + data-username: sa + password: sa + platform: org.hibernate.dialect.H2Dialect + jpa: + generate-ddl: true + hibernate: + ddl-auto: create-drop + properties: + hibernate.format_sql: true + show-sql: true diff --git a/test/e2e/e2e-cluster/test-runner/pom.xml b/test/e2e/e2e-cluster/e2e-cluster-test-runner/pom.xml similarity index 89% rename from test/e2e/e2e-cluster/test-runner/pom.xml rename to test/e2e/e2e-cluster/e2e-cluster-test-runner/pom.xml index 4cda3ba8f..485ac8402 100755 --- a/test/e2e/e2e-cluster/test-runner/pom.xml +++ b/test/e2e/e2e-cluster/e2e-cluster-test-runner/pom.xml @@ -28,7 +28,7 @@ 4.0.0 - test-runner + e2e-cluster-test-runner @@ -40,28 +40,21 @@ org.apache.skywalking - gateway + e2e-cluster-provider ${project.version} org.apache.skywalking - provider - ${project.version} - - - - org.apache.skywalking - consumer + e2e-cluster-consumer ${project.version} - provider - consumer - gateway + e2e-cluster-provider + e2e-cluster-consumer 1.1 skywalking-e2e-container-${build.id}-cluster @@ -128,21 +121,26 @@ -DSW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800 -DSW_AGENT_NAME=${provider.name} + -Dserver.port=9090 - - --server.port=9090 - - ${consumer.name}-${project.version}.jar + ${provider.name}-${project.version}.jar -DSW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11801 - -DSW_AGENT_NAME=${consumer.name} + -DSW_AGENT_NAME=${provider.name} + -Dserver.port=9091 - - --server.port=9091 - + + + ${consumer.name}-${project.version}.jar + + + -DSW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11801 + -DSW_AGENT_NAME=${consumer.name} + -Dserver.port=9092 + ${e2e.container.name.prefix}-elasticsearch @@ -150,7 +148,7 @@ +webapp.host:webapp.port:8081 - +service.host:service.port:9091 + +service.host:service.port:9092 ${e2e.container.name.prefix}-elasticsearch @@ -159,9 +157,6 @@ ${sw.home}:/sw - - ../${gateway.name}/target/${gateway.name}-${project.version}.jar:/home/${gateway.name}-${project.version}.jar - ../${provider.name}/target/${provider.name}-${project.version}.jar:/home/${provider.name}-${project.version}.jar @@ -196,6 +191,8 @@ ${webapp.port} ${service.host} ${service.port} + ${provider.name} + ${consumer.name} diff --git a/test/e2e/e2e-cluster/e2e-cluster-test-runner/src/docker/clusterize.awk b/test/e2e/e2e-cluster/e2e-cluster-test-runner/src/docker/clusterize.awk new file mode 100644 index 000000000..830ede77e --- /dev/null +++ b/test/e2e/e2e-cluster/e2e-cluster-test-runner/src/docker/clusterize.awk @@ -0,0 +1,96 @@ +# 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. + +#!/usr/bin/awk -f + +BEGIN { + in_cluster_section=0; + in_cluster_zk_section=0; + + in_storage_section=0; + in_storage_es_section=0; + in_storage_h2_section=0; +} + +{ + if (in_cluster_section == 0) { + in_cluster_section=$0 ~ /^cluster:$/ + } else { + in_cluster_section=$0 ~ /^(#|\s{2})/ + } + if (in_storage_section == 0) { + in_storage_section=$0 ~ /^storage:$/ + } else { + in_storage_section=$0 ~ /^(#|\s{2})/ + } + + if (in_cluster_section == 1) { + # in the cluster: section now + # disable standalone module + if ($0 ~ /^ standalone:$/) { + print "#" $0 + } else { + if (in_cluster_zk_section == 0) { + in_cluster_zk_section=$0 ~ /^#?\s+zookeeper:$/ + } else { + in_cluster_zk_section=$0 ~ /^(#\s{4}|\s{2})/ + } + if (in_cluster_zk_section == 1) { + # in the cluster.zookeeper section now + # uncomment zk config + gsub("^#", "", $0) + print + } else { + print + } + } + } else if (in_storage_section == 1) { + # in the storage: section now + # disable h2 module + if (in_storage_es_section == 0) { + if (ENVIRON["ES_VERSION"] ~ /^6.+/) { + in_storage_es_section=$0 ~ /^#?\s+elasticsearch:$/ + } else if (ENVIRON["ES_VERSION"] ~ /^7.+/) { + in_storage_es_section=$0 ~ /^#?\s+elasticsearch7:$/ + } + } else { + in_storage_es_section=$0 ~ /^#?\s{4}/ + } + if (in_storage_h2_section == 0) { + in_storage_h2_section=$0 ~ /^#?\s+h2:$/ + } else { + in_storage_h2_section=$0 ~ /^#?\s{4}/ + } + if (in_storage_es_section == 1) { + # in the storage.elasticsearch section now + # uncomment es config + gsub("^#", "", $0) + print + } else if (in_storage_h2_section == 1) { + # comment out h2 config + if ($0 !~ /^#/) { + print "#" $0 + } else { + print + } + } else { + print + } + } else { + print + } +} + diff --git a/test/e2e/e2e-cluster/e2e-cluster-test-runner/src/docker/rc.d/rc0-prepare.sh b/test/e2e/e2e-cluster/e2e-cluster-test-runner/src/docker/rc.d/rc0-prepare.sh new file mode 100755 index 000000000..f50baca02 --- /dev/null +++ b/test/e2e/e2e-cluster/e2e-cluster-test-runner/src/docker/rc.d/rc0-prepare.sh @@ -0,0 +1,36 @@ +#!/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. + +apt-get update && apt-get install -y gawk + +if test "${MODE}" = "cluster"; then + original_wd=$(pwd) + + # substitute application.yml to be capable of cluster mode + cd ${SW_HOME}/config \ + && gawk -f /clusterize.awk application.yml > clusterized_app.yml \ + && mv clusterized_app.yml application.yml \ + && sed '//a\ + \n' log4j2.xml > log4j2debuggable.xml \ + && mv log4j2debuggable.xml log4j2.xml + + cd ${SW_HOME}/webapp \ + && gawk '/^\s+listOfServers:/ {gsub("listOfServers:.*", "listOfServers: 127.0.0.1:12800,127.0.0.1:12801", $0)} {print}' webapp.yml > clusterized_webapp.yml \ + && mv clusterized_webapp.yml webapp.yml + + cd ${original_wd} +fi diff --git a/test/e2e/e2e-cluster/test-runner/src/docker/rc.d/rc1-startup.sh b/test/e2e/e2e-cluster/e2e-cluster-test-runner/src/docker/rc.d/rc1-startup.sh similarity index 88% rename from test/e2e/e2e-cluster/test-runner/src/docker/rc.d/rc1-startup.sh rename to test/e2e/e2e-cluster/e2e-cluster-test-runner/src/docker/rc.d/rc1-startup.sh index de408ee57..f09aee76c 100755 --- a/test/e2e/e2e-cluster/test-runner/src/docker/rc.d/rc1-startup.sh +++ b/test/e2e/e2e-cluster/e2e-cluster-test-runner/src/docker/rc.d/rc1-startup.sh @@ -15,21 +15,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -echo 'starting gateway service...' \ - && java -jar /home/gateway-1.0.0.jar 2>&1 > /tmp/gateway.log & - -check_tcp 127.0.0.1 \ - 9099 \ - 60 \ - 10 \ - 'waiting for the gateway service to be ready' - -if [[ $? -ne 0 ]]; then - echo "gateway service failed to start in 60 * 10 seconds: " - cat /tmp/gateway.log - exit 1 -fi - echo 'starting OAP server...' \ && SW_STORAGE_ES_BULK_ACTIONS=1 \ SW_STORAGE_ES_FLUSH_INTERVAL=1 \ @@ -78,10 +63,22 @@ if [[ $? -ne 0 ]]; then exit 1 fi +check_tcp 127.0.0.1 \ + 9092 \ + 60 \ + 10 \ + "waiting for the instrumented service 2 to be ready" + +if [[ $? -ne 0 ]]; then + echo "instrumented service 2 failed to start in 24 * 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/elasticsearch.log \ - ${ES_HOME}/logs/stdout.log + ${ES_HOME}/logs/stdout.log \ No newline at end of file diff --git a/test/e2e/e2e-cluster/e2e-cluster-test-runner/src/test/java/org/apache/skywalking/e2e/ClusterVerificationITCase.java b/test/e2e/e2e-cluster/e2e-cluster-test-runner/src/test/java/org/apache/skywalking/e2e/ClusterVerificationITCase.java new file mode 100755 index 000000000..8661a0e20 --- /dev/null +++ b/test/e2e/e2e-cluster/e2e-cluster-test-runner/src/test/java/org/apache/skywalking/e2e/ClusterVerificationITCase.java @@ -0,0 +1,408 @@ +/* + * 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 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.*; +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.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 java.io.InputStream; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import static org.apache.skywalking.e2e.metrics.MetricsQuery.*; +import static org.assertj.core.api.Assertions.fail; + +/** + * @author kezhenxu94 + */ +@RunWith(SpringJUnit4ClassRunner.class) +public class ClusterVerificationITCase { + private static final Logger LOGGER = LoggerFactory.getLogger(ClusterVerificationITCase.class); + + private final RestTemplate restTemplate = new RestTemplate(); + private final Yaml yaml = new Yaml(); + + private SimpleQueryClient queryClient; + private String instrumentedServiceUrl; + private long retryInterval = TimeUnit.SECONDS.toMillis(30); + + private String providerName; + private String consumerName; + + @Before + public void setUp() { + providerName = System.getProperty("provider.name", "e2e-cluster-provider"); + consumerName = System.getProperty("consumer.name", "e2e-cluster-consumer"); + + final String swWebappHost = System.getProperty("sw.webapp.host", "127.0.0.1"); + final String swWebappPort = System.getProperty("sw.webapp.port", "32791"); + final String instrumentedServiceHost = System.getProperty("service.host", "127.0.0.1"); + final String instrumentedServicePort = System.getProperty("service.port", "32790"); + queryClient = new SimpleQueryClient(swWebappHost, swWebappPort); + instrumentedServiceUrl = "http://" + instrumentedServiceHost + ":" + instrumentedServicePort; + } + + @Test(timeout = 1200000) + @DirtiesContext + public void verify() throws Exception { + LocalDateTime startTime = LocalDateTime.now(ZoneOffset.UTC); + + // minimum guarantee that the instrumented services registered + // which is the prerequisite of following verifications(service instance, service metrics, etc.) + List services = Collections.emptyList(); + while (services.size() < 2) { + try { + services = queryClient.services( + new ServicesQuery() + .start(startTime) + .end(LocalDateTime.now(ZoneOffset.UTC).plusMinutes(1)) + ); + Thread.sleep(500); // take a nap to avoid high payload + } catch (Throwable ignored) { + } + } + + verifyTraces(startTime); + + verifyServices(startTime); + + verifyTopo(startTime); + } + + private void verifyTopo(LocalDateTime minutesAgo) throws Exception { + String clientServiceId = null, serverServiceId = null; + boolean valid = false; + while (!valid) { + try { + final TopoData topoData = queryClient.topo( + new TopoQuery() + .stepByMinute() + .start(minutesAgo) + .end(LocalDateTime.now(ZoneOffset.UTC).plusMinutes(1)) + ); + LOGGER.info("Actual topology: {}", topoData); + + InputStream expectedInputStream = + new ClassPathResource("expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.topo.yml").getInputStream(); + + final TopoMatcher topoMatcher = yaml.loadAs(expectedInputStream, TopoMatcher.class); + topoMatcher.verify(topoData); + valid = true; + for (Node node : topoData.getNodes()) { + if (node.getName().equals(providerName)) { + serverServiceId = node.getId(); + } else if (node.getName().equals(consumerName)) { + clientServiceId = node.getId(); + } + } + } catch (Throwable t) { + LOGGER.warn(t.getMessage(), t); + generateTraffic(); + Thread.sleep(retryInterval); + } + } + verifyServiceInstanceTopo(minutesAgo, clientServiceId, serverServiceId); + } + + private void verifyServiceInstanceTopo(LocalDateTime minutesAgo, String clientServiceId, String serverServiceId) throws Exception { + if (clientServiceId == null || serverServiceId == null) { + fail("clientService or serverService not found"); + } + boolean valid = false; + while (!valid) { + try { + final ServiceInstanceTopoData topoData = queryClient.serviceInstanceTopo( + new ServiceInstanceTopoQuery() + .stepByMinute() + .start(minutesAgo.minusDays(1)) + .end(LocalDateTime.now(ZoneOffset.UTC).plusMinutes(1)) + .clientServiceId(clientServiceId) + .serverServiceId(serverServiceId) + ); + + LOGGER.info("Actual service instance topology: {}", topoData); + InputStream expectedInputStream = + new ClassPathResource("expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.serviceInstanceTopo.yml").getInputStream(); + final ServiceInstanceTopoMatcher topoMatcher = yaml.loadAs(expectedInputStream, ServiceInstanceTopoMatcher.class); + topoMatcher.verify(topoData); + valid = true; + } catch (Throwable t) { + LOGGER.warn(t.getMessage(), t); + generateTraffic(); + Thread.sleep(retryInterval); + } + } + } + + private void verifyServices(LocalDateTime minutesAgo) throws Exception { + List services = queryClient.services( + new ServicesQuery() + .start(minutesAgo) + .end(LocalDateTime.now(ZoneOffset.UTC).plusMinutes(1)) + ); + while (services.isEmpty()) { + LOGGER.warn("services is null, will retry to query"); + services = queryClient.services( + new ServicesQuery() + .start(minutesAgo) + .end(LocalDateTime.now(ZoneOffset.UTC)) + ); + Thread.sleep(retryInterval); + } + + InputStream expectedInputStream = + new ClassPathResource("expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.services.yml").getInputStream(); + + final ServicesMatcher servicesMatcher = yaml.loadAs(expectedInputStream, ServicesMatcher.class); + servicesMatcher.verify(services); + + for (Service service : services) { + LOGGER.info("verifying service instances: {}", service); + + verifyServiceMetrics(service, minutesAgo); + + Instances instances = verifyServiceInstances(minutesAgo, service); + + verifyInstancesMetrics(instances, minutesAgo); + + Endpoints endpoints = verifyServiceEndpoints(minutesAgo, service); + + verifyEndpointsMetrics(endpoints, minutesAgo); + } + } + + private Instances verifyServiceInstances(LocalDateTime minutesAgo, Service service) throws Exception { + Instances instances = queryClient.instances( + new InstancesQuery() + .serviceId(service.getKey()) + .start(minutesAgo) + .end(LocalDateTime.now(ZoneOffset.UTC).plusMinutes(1)) + ); + while (instances == null) { + LOGGER.warn("instances is null, will send traffic data and retry to query"); + generateTraffic(); + Thread.sleep(retryInterval); + instances = queryClient.instances( + new InstancesQuery() + .serviceId(service.getKey()) + .start(minutesAgo) + .end(LocalDateTime.now(ZoneOffset.UTC).plusMinutes(1)) + ); + } + + InputStream expectedInputStream; + if (providerName.equals(service.getLabel())) { + expectedInputStream = + new ClassPathResource("expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.providerInstances.yml").getInputStream(); + } else { + expectedInputStream = + new ClassPathResource("expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.instances.yml").getInputStream(); + } + + final InstancesMatcher instancesMatcher = yaml.loadAs(expectedInputStream, InstancesMatcher.class); + instancesMatcher.verify(instances); + return instances; + } + + private Endpoints verifyServiceEndpoints(LocalDateTime minutesAgo, Service service) throws Exception { + Endpoints endpoints = queryClient.endpoints( + new EndpointQuery().serviceId(service.getKey()) + ); + while (endpoints == null) { + LOGGER.warn("endpoints is null, will send traffic data and retry to query"); + generateTraffic(); + Thread.sleep(retryInterval); + endpoints = queryClient.endpoints( + new EndpointQuery().serviceId(service.getKey()) + ); + } + InputStream expectedInputStream = + new ClassPathResource("expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.endpoints.yml").getInputStream(); + final EndpointsMatcher endpointsMatcher = yaml.loadAs(expectedInputStream, EndpointsMatcher.class); + endpointsMatcher.verify(endpoints); + return endpoints; + } + + private void verifyInstancesMetrics(Instances instances, final LocalDateTime minutesAgo) throws Exception { + for (Instance instance : instances.getInstances()) { + for (String metricsName : ALL_INSTANCE_METRICS) { + LOGGER.info("verifying service instance response time: {}", instance); + + boolean valid = false; + while (!valid) { + LOGGER.warn("instanceMetrics is null, will retry to query"); + Metrics instanceMetrics = queryClient.metrics( + new MetricsQuery() + .stepByMinute() + .metricsName(metricsName) + .start(minutesAgo) + .end(LocalDateTime.now(ZoneOffset.UTC).plusMinutes(1)) + .id(instance.getKey()) + ); + AtLeastOneOfMetricsMatcher instanceRespTimeMatcher = new AtLeastOneOfMetricsMatcher(); + MetricsValueMatcher greaterThanZero = new MetricsValueMatcher(); + greaterThanZero.setValue("gt 0"); + instanceRespTimeMatcher.setValue(greaterThanZero); + try { + instanceRespTimeMatcher.verify(instanceMetrics); + valid = true; + } catch (Throwable ignored) { + generateTraffic(); + Thread.sleep(retryInterval); + } + LOGGER.info("{}: {}", metricsName, instanceMetrics); + } + } + } + } + + private void verifyEndpointsMetrics(Endpoints endpoints, final LocalDateTime minutesAgo) throws Exception { + for (Endpoint endpoint : endpoints.getEndpoints()) { + if (!endpoint.getLabel().equals("/e2e/users")) { + continue; + } + for (String metricName : ALL_ENDPOINT_METRICS) { + LOGGER.info("verifying endpoint {}, metrics: {}", endpoint, metricName); + + boolean valid = false; + while (!valid) { + Metrics endpointMetrics = queryClient.metrics( + new MetricsQuery() + .stepByMinute() + .metricsName(metricName) + .start(minutesAgo) + .end(LocalDateTime.now(ZoneOffset.UTC)) + .id(endpoint.getKey()) + ); + AtLeastOneOfMetricsMatcher instanceRespTimeMatcher = new AtLeastOneOfMetricsMatcher(); + MetricsValueMatcher greaterThanZero = new MetricsValueMatcher(); + greaterThanZero.setValue("gt 0"); + instanceRespTimeMatcher.setValue(greaterThanZero); + try { + instanceRespTimeMatcher.verify(endpointMetrics); + valid = true; + } catch (Throwable ignored) { + generateTraffic(); + Thread.sleep(retryInterval); + } + LOGGER.info("{}: {}", metricName, endpointMetrics); + } + } + } + } + + private void verifyServiceMetrics(Service service, final LocalDateTime minutesAgo) throws Exception { + for (String metricName : ALL_SERVICE_METRICS) { + LOGGER.info("verifying service {}, metrics: {}", service, metricName); + + boolean valid = false; + while (!valid) { + Metrics serviceMetrics = queryClient.metrics( + new MetricsQuery() + .stepByMinute() + .metricsName(metricName) + .start(minutesAgo) + .end(LocalDateTime.now(ZoneOffset.UTC)) + .id(service.getKey()) + ); + AtLeastOneOfMetricsMatcher instanceRespTimeMatcher = new AtLeastOneOfMetricsMatcher(); + MetricsValueMatcher greaterThanZero = new MetricsValueMatcher(); + greaterThanZero.setValue("gt 0"); + instanceRespTimeMatcher.setValue(greaterThanZero); + try { + instanceRespTimeMatcher.verify(serviceMetrics); + valid = true; + } catch (Throwable ignored) { + generateTraffic(); + Thread.sleep(retryInterval); + } + LOGGER.info("{}: {}", metricName, serviceMetrics); + } + } + } + + private void verifyTraces(LocalDateTime minutesAgo) throws Exception { + final TracesQuery query = new TracesQuery() + .stepBySecond() + .start(minutesAgo) + .orderByStartTime(); + + List traces = queryClient.traces(query.end(LocalDateTime.now(ZoneOffset.UTC))); + while (traces.isEmpty()) { + LOGGER.warn("traces is empty, will generate traffic data and retry"); + generateTraffic(); + Thread.sleep(retryInterval); + traces = queryClient.traces(query.end(LocalDateTime.now(ZoneOffset.UTC))); + } + + InputStream expectedInputStream = + new ClassPathResource("expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.traces.yml").getInputStream(); + + final TracesMatcher tracesMatcher = yaml.loadAs(expectedInputStream, TracesMatcher.class); + tracesMatcher.verifyLoosely(traces); + } + + private void generateTraffic() { + try { + final Map user = new HashMap<>(); + user.put("name", "SkyWalking"); + final ResponseEntity responseEntity = restTemplate.postForEntity( + instrumentedServiceUrl + "/e2e/users", + user, + String.class + ); + LOGGER.info("responseEntity: {}, {}", responseEntity.getStatusCode(), responseEntity.getBody()); + } catch (Throwable t) { + LOGGER.warn(t.getMessage(), t); + } + } +} diff --git a/test/e2e/e2e-cluster/e2e-cluster-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.endpoints.yml b/test/e2e/e2e-cluster/e2e-cluster-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.endpoints.yml new file mode 100644 index 000000000..3ddf42e49 --- /dev/null +++ b/test/e2e/e2e-cluster/e2e-cluster-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.endpoints.yml @@ -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. + +# 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/users diff --git a/test/e2e/e2e-cluster/e2e-cluster-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.instances.yml b/test/e2e/e2e-cluster/e2e-cluster-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.instances.yml new file mode 100644 index 000000000..1c48fc4ff --- /dev/null +++ b/test/e2e/e2e-cluster/e2e-cluster-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.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: gt 0 + 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-cluster/e2e-cluster-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.providerInstances.yml b/test/e2e/e2e-cluster/e2e-cluster-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.providerInstances.yml new file mode 100644 index 000000000..a9e60c2a1 --- /dev/null +++ b/test/e2e/e2e-cluster/e2e-cluster-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.providerInstances.yml @@ -0,0 +1,45 @@ +# 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: gt 0 + 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 + - key: gt 0 + 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-cluster/e2e-cluster-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.serviceInstanceTopo.yml b/test/e2e/e2e-cluster/e2e-cluster-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.serviceInstanceTopo.yml new file mode 100644 index 000000000..be3d18d4f --- /dev/null +++ b/test/e2e/e2e-cluster/e2e-cluster-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.serviceInstanceTopo.yml @@ -0,0 +1,48 @@ +# 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: not null + name: not null + type: null + serviceId: not null + serviceName: e2e-cluster-consumer + isReal: true + - id: not null + name: not null + serviceId: not null + serviceName: e2e-cluster-provider + type: Tomcat + isReal: true + - id: not null + name: not null + serviceId: not null + serviceName: e2e-cluster-provider + type: Tomcat + isReal: true +calls: + - id: not null + source: ${e2e-cluster-consumer(.*)[0]} + target: ${e2e-cluster-provider(.*)[0]} + - id: not null + source: ${e2e-cluster-consumer(.*)[0]} + target: ${e2e-cluster-provider(.*)[1]} diff --git a/test/e2e/e2e-cluster/test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.services.yml b/test/e2e/e2e-cluster/e2e-cluster-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.services.yml similarity index 94% rename from test/e2e/e2e-cluster/test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.services.yml rename to test/e2e/e2e-cluster/e2e-cluster-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.services.yml index f0fc18418..e697dd23d 100644 --- a/test/e2e/e2e-cluster/test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.services.yml +++ b/test/e2e/e2e-cluster/e2e-cluster-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.services.yml @@ -22,7 +22,7 @@ services: - key: not null - label: provider + label: e2e-cluster-provider - key: not null - label: consumer + label: e2e-cluster-consumer diff --git a/test/e2e/e2e-cluster/e2e-cluster-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.topo.yml b/test/e2e/e2e-cluster/e2e-cluster-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.topo.yml new file mode 100644 index 000000000..a53471ed5 --- /dev/null +++ b/test/e2e/e2e-cluster/e2e-cluster-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.topo.yml @@ -0,0 +1,39 @@ +# 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. + +nodes: + - id: not null + name: User + type: USER + - id: not null + name: e2e-cluster-provider + type: Tomcat + - id: not null + name: e2e-cluster-consumer + type: Tomcat + - id: not null + name: "localhost:-1" + type: H2 +calls: + - id: not null + source: ${User[0]} + target: ${e2e-cluster-consumer[0]} + - id: not null + source: ${e2e-cluster-consumer[0]} + target: ${e2e-cluster-provider[0]} + - id: not null + source: ${e2e-cluster-provider[0]} + target: ${localhost:-1[0]} diff --git a/test/e2e/e2e-cluster/e2e-cluster-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.traces.yml b/test/e2e/e2e-cluster/e2e-cluster-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.traces.yml new file mode 100644 index 000000000..4542e5d86 --- /dev/null +++ b/test/e2e/e2e-cluster/e2e-cluster-test-runner/src/test/resources/expected-data/org.apache.skywalking.e2e.ClusterVerificationITCase.traces.yml @@ -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. + +traces: + - key: not null + endpointNames: + - /e2e/users + duration: ge 0 + start: gt 0 + isError: false + traceIds: + - not null diff --git a/test/e2e/e2e-cluster/pom.xml b/test/e2e/e2e-cluster/pom.xml index f9c489554..513baf0c6 100644 --- a/test/e2e/e2e-cluster/pom.xml +++ b/test/e2e/e2e-cluster/pom.xml @@ -31,9 +31,8 @@ pom - provider - consumer - gateway - test-runner + e2e-cluster-provider + e2e-cluster-consumer + e2e-cluster-test-runner diff --git a/test/e2e/e2e-mysql/src/test/java/org/apache/skywalking/e2e/SampleVerificationITCase.java b/test/e2e/e2e-mysql/src/test/java/org/apache/skywalking/e2e/SampleVerificationITCase.java index 68a407089..fb381d542 100644 --- a/test/e2e/e2e-mysql/src/test/java/org/apache/skywalking/e2e/SampleVerificationITCase.java +++ b/test/e2e/e2e-mysql/src/test/java/org/apache/skywalking/e2e/SampleVerificationITCase.java @@ -33,9 +33,7 @@ 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.TopoData; -import org.apache.skywalking.e2e.topo.TopoMatcher; -import org.apache.skywalking.e2e.topo.TopoQuery; +import org.apache.skywalking.e2e.topo.*; import org.apache.skywalking.e2e.trace.Trace; import org.apache.skywalking.e2e.trace.TracesMatcher; import org.apache.skywalking.e2e.trace.TracesQuery; @@ -87,7 +85,7 @@ public class SampleVerificationITCase { instrumentedServiceUrl = "http://" + instrumentedServiceHost + ":" + instrumentedServicePort; } - @Test + @Test(timeout = 1200000) @DirtiesContext public void verify() throws Exception { final LocalDateTime minutesAgo = LocalDateTime.now(ZoneOffset.UTC); @@ -140,6 +138,14 @@ public class SampleVerificationITCase { LOGGER.warn(e.getMessage(), e); } }); + + doRetryableVerification(() -> { + try{ + verifyServiceInstanceTopo(minutesAgo); + }catch (Exception e) { + LOGGER.warn(e.getMessage(), e); + } + }); } private void verifyTopo(LocalDateTime minutesAgo) throws Exception { @@ -160,6 +166,26 @@ public class SampleVerificationITCase { topoMatcher.verify(topoData); } + 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("2") + ); + 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); + } + private void verifyServices(LocalDateTime minutesAgo) throws Exception { final LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC); diff --git a/test/e2e/e2e-mysql/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.serviceInstanceTopo.yml b/test/e2e/e2e-mysql/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.serviceInstanceTopo.yml new file mode 100644 index 000000000..d312bd8a7 --- /dev/null +++ b/test/e2e/e2e-mysql/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: 2 + name: not null + serviceId: 2 + serviceName: Your_ApplicationName + type: Tomcat + isReal: true +calls: + - id: 1_2 + source: 1 + detectPoints: + - SERVER + target: 2 diff --git a/test/e2e/e2e-single-service/src/test/java/org/apache/skywalking/e2e/SampleVerificationITCase.java b/test/e2e/e2e-single-service/src/test/java/org/apache/skywalking/e2e/SampleVerificationITCase.java index 68a407089..fb381d542 100644 --- a/test/e2e/e2e-single-service/src/test/java/org/apache/skywalking/e2e/SampleVerificationITCase.java +++ b/test/e2e/e2e-single-service/src/test/java/org/apache/skywalking/e2e/SampleVerificationITCase.java @@ -33,9 +33,7 @@ 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.TopoData; -import org.apache.skywalking.e2e.topo.TopoMatcher; -import org.apache.skywalking.e2e.topo.TopoQuery; +import org.apache.skywalking.e2e.topo.*; import org.apache.skywalking.e2e.trace.Trace; import org.apache.skywalking.e2e.trace.TracesMatcher; import org.apache.skywalking.e2e.trace.TracesQuery; @@ -87,7 +85,7 @@ public class SampleVerificationITCase { instrumentedServiceUrl = "http://" + instrumentedServiceHost + ":" + instrumentedServicePort; } - @Test + @Test(timeout = 1200000) @DirtiesContext public void verify() throws Exception { final LocalDateTime minutesAgo = LocalDateTime.now(ZoneOffset.UTC); @@ -140,6 +138,14 @@ public class SampleVerificationITCase { LOGGER.warn(e.getMessage(), e); } }); + + doRetryableVerification(() -> { + try{ + verifyServiceInstanceTopo(minutesAgo); + }catch (Exception e) { + LOGGER.warn(e.getMessage(), e); + } + }); } private void verifyTopo(LocalDateTime minutesAgo) throws Exception { @@ -160,6 +166,26 @@ public class SampleVerificationITCase { topoMatcher.verify(topoData); } + 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("2") + ); + 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); + } + private void verifyServices(LocalDateTime minutesAgo) throws Exception { final LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC); diff --git a/test/e2e/e2e-single-service/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.serviceInstanceTopo.yml b/test/e2e/e2e-single-service/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.serviceInstanceTopo.yml new file mode 100644 index 000000000..d312bd8a7 --- /dev/null +++ b/test/e2e/e2e-single-service/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: 2 + name: not null + serviceId: 2 + serviceName: Your_ApplicationName + type: Tomcat + isReal: true +calls: + - id: 1_2 + source: 1 + detectPoints: + - SERVER + target: 2 diff --git a/test/e2e/e2e-ttl/e2e-ttl-es/pom.xml b/test/e2e/e2e-ttl/e2e-ttl-es/pom.xml index e6295cd99..30b6b8419 100644 --- a/test/e2e/e2e-ttl/e2e-ttl-es/pom.xml +++ b/test/e2e/e2e-ttl/e2e-ttl-es/pom.xml @@ -116,6 +116,7 @@ ${sw.home}:/sw ${project.basedir}/src/docker/rc.d:/rc.d:ro ${project.basedir}/src/docker/es_storage.awk:/es_storage.awk + ${project.basedir}/src/docker/ttl_official_analysis.oal:/ttl_official_analysis.oal diff --git a/test/e2e/e2e-ttl/e2e-ttl-es/src/docker/rc.d/rc0-prepare.sh b/test/e2e/e2e-ttl/e2e-ttl-es/src/docker/rc.d/rc0-prepare.sh index 2b9b242a2..1ded6dd1b 100755 --- a/test/e2e/e2e-ttl/e2e-ttl-es/src/docker/rc.d/rc0-prepare.sh +++ b/test/e2e/e2e-ttl/e2e-ttl-es/src/docker/rc.d/rc0-prepare.sh @@ -23,6 +23,7 @@ original_wd=$(pwd) cd ${SW_HOME}/config \ && gawk -f /es_storage.awk application.yml > es_storage_app.yml \ && mv es_storage_app.yml application.yml \ + && cp /ttl_official_analysis.oal official_analysis.oal \ && sed '//a' log4j2.xml > log4j2debuggable.xml \ && mv log4j2debuggable.xml log4j2.xml diff --git a/test/e2e/e2e-ttl/e2e-ttl-es/src/docker/ttl_official_analysis.oal b/test/e2e/e2e-ttl/e2e-ttl-es/src/docker/ttl_official_analysis.oal new file mode 100755 index 000000000..52a0ccde4 --- /dev/null +++ b/test/e2e/e2e-ttl/e2e-ttl-es/src/docker/ttl_official_analysis.oal @@ -0,0 +1,23 @@ +/* + * 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. + * + */ + + +// In TTL ES7 testing, using full oal config makes the test very unstable, so only service_p99 is reserved for testing. + +// Service scope metrics +service_p99 = from(Service.latency).p99(10); \ No newline at end of file diff --git a/test/e2e/e2e-ttl/e2e-ttl-es/src/test/java/org/apache/skywalking/e2e/StorageTTLITCase.java b/test/e2e/e2e-ttl/e2e-ttl-es/src/test/java/org/apache/skywalking/e2e/StorageTTLITCase.java index 5c538de67..ecd778c2c 100644 --- a/test/e2e/e2e-ttl/e2e-ttl-es/src/test/java/org/apache/skywalking/e2e/StorageTTLITCase.java +++ b/test/e2e/e2e-ttl/e2e-ttl-es/src/test/java/org/apache/skywalking/e2e/StorageTTLITCase.java @@ -51,6 +51,9 @@ import static org.assertj.core.api.Assertions.assertThat; */ @Slf4j public class StorageTTLITCase { + + //TODO Make TTL ES7 test stable. Ref https://github.com/apache/skywalking/pull/3978, https://github.com/apache/skywalking/issues/4018 + private static final int SW_STORAGE_ES_MONTH_METRIC_DATA_TTL = 4; private static final int SW_STORAGE_ES_OTHER_METRIC_DATA_TTL = 5; @@ -190,7 +193,6 @@ public class StorageTTLITCase { final LocalDateTime queryEnd, final String step ) throws Exception { - boolean prepared = false; while (!prepared) { sendMetrics( @@ -199,7 +201,6 @@ public class StorageTTLITCase { .setEndTime(endTime) .build() ); - final Metrics serviceMetrics = queryMetrics(queryStart, queryEnd, step); final AtLeastOneOfMetricsMatcher instanceRespTimeMatcher = new AtLeastOneOfMetricsMatcher(); final MetricsValueMatcher greaterThanZero = new MetricsValueMatcher(); diff --git a/test/e2e/pom.xml b/test/e2e/pom.xml index 204be6e92..1d21ed82a 100644 --- a/test/e2e/pom.xml +++ b/test/e2e/pom.xml @@ -36,6 +36,7 @@ e2e-single-service e2e-mysql e2e-cluster + e2e-cluster-with-gateway e2e-agent-reboot e2e-ttl