未验证 提交 13107196 编写于 作者: J Jared Tan 提交者: GitHub

add token authentication between agent and oap receiver. (#4197)

* add token authentication between agent and oap receiver.

* revert ui.

* fix code style.

* update docs.

* Update docs/en/setup/backend/backend-token-auth.md
Co-Authored-By: Nkezhenxu94 <kezhenxu94@apache.org>

* fix hits.

* filter

* revert yml.

* update logical.

* fix codestyle check.

* fix ci and add e2e test case.

* revert mistake commit.

* fix ci

* remove duplicate maven dependency.

* revert pom

* revert unchanged.

* update javadocs.

* update logical.
Co-authored-by: Nkezhenxu94 <kezhenxu94@163.com>
上级 9149de2a
......@@ -338,6 +338,7 @@ receiver-sharing-server:
maxMessageSize: \${SW_RECEIVER_SHARING_MAX_MESSAGE_SIZE:0}
gRPCThreadPoolSize: \${SW_RECEIVER_SHARING_GRPC_THREAD_POOL_SIZE:0}
gRPCThreadPoolQueueSize: \${SW_RECEIVER_SHARING_GRPC_THREAD_POOL_QUEUE_SIZE:0}
authentication: \${SW_AUTHENTICATION:""}
receiver-register:
default:
receiver-trace:
......
......@@ -339,6 +339,7 @@ receiver-sharing-server:
maxMessageSize: \${SW_RECEIVER_SHARING_MAX_MESSAGE_SIZE:0}
gRPCThreadPoolSize: \${SW_RECEIVER_SHARING_GRPC_THREAD_POOL_SIZE:0}
gRPCThreadPoolQueueSize: \${SW_RECEIVER_SHARING_GRPC_THREAD_POOL_QUEUE_SIZE:0}
authentication: \${SW_AUTHENTICATION:""}
receiver-register:
default:
receiver-trace:
......
......@@ -66,6 +66,7 @@ DB. But clearly, it doesn't fit the product env. In here, you could find what ot
Choose the one you like, we are also welcome anyone to contribute new storage implementor,
1. [Set receivers](backend-receivers.md). You could choose receivers by your requirements, most receivers
are harmless, at least our default receivers are. You would set and active all receivers provided.
1. [Token authentication](backend-token-auth.md). You could add token authentication mechanisms to avoid `OAP` receiving untrusted data.
1. Do [trace sampling](trace-sampling.md) at backend. This sample keep the metrics accurate, only don't save some of traces
in storage based on rate.
1. Follow [slow DB statement threshold](slow-db-statement.md) config document to understand that,
......
# Token Authentication
## Supported version
7.0.0+
## Why need token authentication after we have TLS?
TLS is about transport security, which makes sure the network can be trusted.
The token authentication is about monitoring application data **can be trusted**.
## Token
In current version, Token is considered as a simple string.
### Set Token
1. Set token in agent.config file
```properties
# Authentication active is based on backend setting, see application.yml for more details.
agent.authentication = ${SW_AGENT_AUTHENTICATION:xxxx}
```
2. Set token in `application.yml` file
```yaml
······
receiver-sharing-server:
default:
authentication: ${SW_AUTHENTICATION:""}
······
```
## Authentication fails
The Skywalking OAP verifies every request from agent, only allows requests whose token matches the one configured in `application.yml`.
If the token is not right, you will see the following log in agent
```
org.apache.skywalking.apm.dependencies.io.grpc.StatusRuntimeException: PERMISSION_DENIED
```
## FAQ
### Can I use token authentication instead of TLS?
No, you shouldn't. In tech way, you can of course, but token and TLS are used for untrusted network env. In that circumstance,
TLS has higher priority than this. Token can be trusted only under TLS protection.Token can be stolen easily if you
send it through a non-TLS network.
### Do you support other authentication mechanisms? Such as ak/sk?
For now, no. But we appreciate someone contributes this feature.
......@@ -137,6 +137,7 @@ storage:
# metadataQueryMaxSize: ${SW_STORAGE_MYSQL_QUERY_MAX_SIZE:5000}
receiver-sharing-server:
default:
authentication: ${SW_AUTHENTICATION:""}
receiver-register:
default:
receiver-trace:
......
......@@ -22,11 +22,13 @@ import io.grpc.*;
import org.apache.skywalking.oap.server.library.module.Service;
/**
* @author peng-yongsheng
* @author peng-yongsheng, jian.tan
*/
public interface GRPCHandlerRegister extends Service {
void addHandler(BindableService handler);
void addHandler(ServerServiceDefinition definition);
void addFilter(ServerInterceptor interceptor);
}
......@@ -18,11 +18,13 @@
package org.apache.skywalking.oap.server.core.server;
import io.grpc.*;
import io.grpc.BindableService;
import io.grpc.ServerInterceptor;
import io.grpc.ServerServiceDefinition;
import org.apache.skywalking.oap.server.library.server.grpc.GRPCServer;
/**
* @author peng-yongsheng
* @author peng-yongsheng, jian.tan
*/
public class GRPCHandlerRegisterImpl implements GRPCHandlerRegister {
......@@ -39,4 +41,8 @@ public class GRPCHandlerRegisterImpl implements GRPCHandlerRegister {
@Override public void addHandler(ServerServiceDefinition definition) {
server.addHandler(definition);
}
@Override public void addFilter(ServerInterceptor interceptor) {
}
}
/*
* 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.server.auth;
import io.grpc.Metadata;
import io.grpc.ServerCall;
import io.grpc.ServerCallHandler;
import io.grpc.ServerInterceptor;
import io.grpc.Status;
/**
* Active the authentication between agent and oap receiver. token checker if expected token exists in application.yml
*
* @author jian.tan
*/
public class AuthenticationInterceptor implements ServerInterceptor {
private String expectedToken = "";
private ServerCall.Listener listener = new ServerCall.Listener() {
};
public AuthenticationInterceptor(String expectedToken) {
this.expectedToken = expectedToken;
}
public void setExpectedToken(String expectedToken) {
this.expectedToken = expectedToken;
}
private static final Metadata.Key<String> AUTH_HEAD_HEADER_NAME = Metadata.Key.of("Authentication", Metadata.ASCII_STRING_MARSHALLER);
/**
* intercept point of call.
*
* @param serverCall call of server.
* @param metadata of call.
* @param serverCallHandler handler of call.
* @param <REQUEST> of call.
* @param <RESPONSE> of call.
* @return lister of call.
*/
@Override
public <REQUEST, RESPONSE> ServerCall.Listener<REQUEST> interceptCall(ServerCall<REQUEST, RESPONSE> serverCall,
Metadata metadata,
ServerCallHandler<REQUEST, RESPONSE> serverCallHandler) {
String token = metadata.get(AUTH_HEAD_HEADER_NAME);
if (expectedToken.equals(token)) {
return serverCallHandler.startCall(serverCall, metadata);
} else {
serverCall.close(Status.PERMISSION_DENIED, new Metadata());
return listener;
}
}
}
......@@ -18,22 +18,48 @@
package org.apache.skywalking.oap.server.receiver.sharing.server;
import io.grpc.*;
import io.grpc.BindableService;
import io.grpc.ServerInterceptor;
import io.grpc.ServerInterceptors;
import io.grpc.ServerServiceDefinition;
import java.util.LinkedList;
import java.util.List;
import lombok.Setter;
import org.apache.skywalking.oap.server.core.server.GRPCHandlerRegister;
/**
* @author peng-yongsheng
* @author peng-yongsheng, jian.tan
*/
public class ReceiverGRPCHandlerRegister implements GRPCHandlerRegister {
@Setter private GRPCHandlerRegister grpcHandlerRegister;
private List<ServerInterceptor> interceptors = new LinkedList<>();
@Override public void addHandler(BindableService handler) {
grpcHandlerRegister.addHandler(handler);
if (interceptors.isEmpty()) {
grpcHandlerRegister.addHandler(handler);
} else {
interceptors.forEach(interceptor -> {
grpcHandlerRegister.addHandler(handlerInterceptorBind(handler, interceptor));
});
}
}
@Override public void addHandler(ServerServiceDefinition definition) {
grpcHandlerRegister.addHandler(definition);
}
/**
* If you want to bind @{io.grpc.ServerInterceptor} on a handler,
* you must call this method before register a handler.
*
* @param interceptor of @{io.grpc.ServerInterceptor}
*/
@Override public void addFilter(ServerInterceptor interceptor) {
this.interceptors.add(interceptor);
}
private ServerServiceDefinition handlerInterceptorBind(BindableService handler, ServerInterceptor interceptor) {
return ServerInterceptors.intercept(handler, interceptor);
}
}
......@@ -22,7 +22,7 @@ import lombok.*;
import org.apache.skywalking.oap.server.library.module.ModuleConfig;
/**
* @author peng-yongsheng,yantaowu
* @author peng-yongsheng,yantaowu,jian.tan
*/
@Getter
@Setter
......@@ -36,4 +36,5 @@ public class SharingServerConfig extends ModuleConfig {
private int maxMessageSize;
private int gRPCThreadPoolSize;
private int gRPCThreadPoolQueueSize;
private String authentication;
}
......@@ -20,16 +20,24 @@ package org.apache.skywalking.oap.server.receiver.sharing.server;
import java.util.Objects;
import org.apache.logging.log4j.util.Strings;
import org.apache.skywalking.apm.util.StringUtil;
import org.apache.skywalking.oap.server.core.CoreModule;
import org.apache.skywalking.oap.server.core.remote.health.HealthCheckServiceHandler;
import org.apache.skywalking.oap.server.core.server.*;
import org.apache.skywalking.oap.server.library.module.*;
import org.apache.skywalking.oap.server.core.server.GRPCHandlerRegister;
import org.apache.skywalking.oap.server.core.server.GRPCHandlerRegisterImpl;
import org.apache.skywalking.oap.server.core.server.JettyHandlerRegister;
import org.apache.skywalking.oap.server.core.server.JettyHandlerRegisterImpl;
import org.apache.skywalking.oap.server.core.server.auth.AuthenticationInterceptor;
import org.apache.skywalking.oap.server.library.module.ModuleConfig;
import org.apache.skywalking.oap.server.library.module.ModuleDefine;
import org.apache.skywalking.oap.server.library.module.ModuleProvider;
import org.apache.skywalking.oap.server.library.module.ModuleStartException;
import org.apache.skywalking.oap.server.library.server.ServerException;
import org.apache.skywalking.oap.server.library.server.grpc.GRPCServer;
import org.apache.skywalking.oap.server.library.server.jetty.JettyServer;
/**
* @author peng-yongsheng
* @author peng-yongsheng, jian.tan
*/
public class SharingServerModuleProvider extends ModuleProvider {
......@@ -86,6 +94,9 @@ public class SharingServerModuleProvider extends ModuleProvider {
this.registerServiceImplementation(GRPCHandlerRegister.class, new GRPCHandlerRegisterImpl(grpcServer));
} else {
this.receiverGRPCHandlerRegister = new ReceiverGRPCHandlerRegister();
if (StringUtil.isNotEmpty(config.getAuthentication())) {
receiverGRPCHandlerRegister.addFilter(new AuthenticationInterceptor(config.getAuthentication()));
}
this.registerServiceImplementation(GRPCHandlerRegister.class, receiverGRPCHandlerRegister);
}
}
......
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
~
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
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">
<parent>
<artifactId>apache-skywalking-e2e</artifactId>
<groupId>org.apache.skywalking</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>e2e-agent-auth</artifactId>
<properties>
<e2e.container.version>1.1</e2e.container.version>
<e2e.container.name.prefix>skywalking-e2e-container-${build.id}-agent-auth</e2e.container.name.prefix>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>${h2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>e2e-base</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
<configuration>
<executable>true</executable>
<addResources>true</addResources>
<excludeDevtools>true</excludeDevtools>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<configuration>
<containerNamePattern>%a-%t-%i</containerNamePattern>
<images>
<image>
<name>skyapm/e2e-container:${e2e.container.version}</name>
<alias>${e2e.container.name.prefix}</alias>
<run>
<env>
<INSTRUMENTED_SERVICE>${project.build.finalName}.jar</INSTRUMENTED_SERVICE>
<INSTRUMENTED_SERVICE_OPTS>-Dskywalking.agent.cool_down_threshold=1
</INSTRUMENTED_SERVICE_OPTS>
</env>
<ports>
<port>+webapp.host:webapp.port:8080</port>
<port>+client.host:client.port:9090</port>
<port>+reboot.host:reboot.port:9091</port>
</ports>
<volumes>
<bind>
<volume>${sw.home}:/sw</volume>
<volume>${project.build.directory}:/home</volume>
<volume>${project.basedir}/src/docker/rc.d:/rc.d:ro</volume>
</bind>
</volumes>
<wait>
<log>SkyWalking e2e container is ready for tests</log>
<time>2400000</time>
</wait>
</run>
</image>
</images>
</configuration>
</plugin>
<!-- set the system properties that can be used in test codes -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<sw.webapp.host>
${webapp.host}
</sw.webapp.host>
<sw.webapp.port>
${webapp.port}
</sw.webapp.port>
<client.host>
${client.host}
</client.host>
<client.port>
${client.port}
</client.port>
</systemPropertyVariables>
</configuration>
<executions>
<execution>
<goals>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
#!/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.
# in order to make it easier to restart the OAP (by executing the restart script) from outside (container),
# we'll expose a tcp port and whenever we receive a message on that port, we'll restart the OAP server,
# socat will help on this to execute the script when receiving a message on that port
apt-get update && apt-get -y install socat
# socat will execute the command in a new shell, thus won't catch the original functions' declarations
# so we'll put the restart command in a script file
echo '
ps -ef | grep -v grep | grep oap.logDir | awk '"'"'{print $2}'"'"' | xargs --no-run-if-empty kill -9
rm -rf /tmp/oap/trace_buffer1
rm -rf /tmp/oap/mesh_buffer1
echo "restarting OAP server..." \
&& SW_RECEIVER_BUFFER_PATH=/tmp/oap/trace_buffer1 \
&& SW_SERVICE_MESH_BUFFER_PATH=/tmp/oap/mesh_buffer1 \
&& cd /sw \
&& bash bin/oapService.sh > /dev/null 2>&1 &
' > /usr/bin/restart_oap
sync
chmod +x /usr/bin/restart_oap
sync
#!/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.
# set up the tcp server to listen for the restart command
socat -u tcp-l:9091,fork system:'bash restart_oap' &
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 \
&& SW_AUTHENTICATION="test-token" \
&& start_oap 'init'
echo 'starting Web app...' \
&& start_webapp '0.0.0.0' 8080
echo 'starting instrumented services...' \
&& start_instrumented_services
check_tcp 127.0.0.1 \
9090 \
60 \
10 \
"waiting for the instrumented service to be ready"
if [[ $? -ne 0 ]]; then
echo "instrumented service failed to start in 30 * 10 seconds: "
cat ${SERVICE_LOG}/*
exit 1
fi
echo "SkyWalking e2e container is ready for tests"
tail -f ${OAP_LOG_DIR}/* \
${WEBAPP_LOG_DIR}/* \
${SERVICE_LOG}/* \
${ES_HOME}/logs/stdout.log
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.skywalking.e2e.sample.client;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
/**
* @author kezhenxu94, jian.tan
*/
@EnableJpaRepositories
@SpringBootApplication
public class SampleClientApplication {
public static void main(String[] args) {
SpringApplication.run(SampleClientApplication.class, args);
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.skywalking.e2e.sample.client;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author kezhenxu94, jian.tan
*/
@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) throws InterruptedException {
Thread.sleep(1000L);
return userRepo.save(user);
}
}
......@@ -16,56 +16,41 @@
*
*/
package org.apache.skywalking.oap.server.library.server.grpc;
package org.apache.skywalking.e2e.sample.client;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
/**
* @author peng-yongsheng
* @author kezhenxu94, jian.tan
*/
public abstract class GRPCServerConfig {
private String host;
private int port;
private String sslCertChainFilePath;
private String sslPrivateKeyFilePath;
private String authentication;
public String getHost() {
return host;
@Entity
public class User {
public User() {
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
@Id
@GeneratedValue
private Long id;
public void setPort(int port) {
this.port = port;
}
public String getSslCertChainFilePath() {
return sslCertChainFilePath;
}
public void setSslCertChainFilePath(String sslCertChainFilePath) {
this.sslCertChainFilePath = sslCertChainFilePath;
}
@Column
private String name;
public String getSslPrivateKeyFilePath() {
return sslPrivateKeyFilePath;
public Long getId() {
return id;
}
public void setSslPrivateKeyFilePath(String sslPrivateKeyFilePath) {
this.sslPrivateKeyFilePath = sslPrivateKeyFilePath;
public void setId(final Long id) {
this.id = id;
}
public String getAuthentication() {
return authentication;
public String getName() {
return name;
}
public void setAuthentication(String authentication) {
this.authentication = authentication;
public void setName(final String name) {
this.name = name;
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.skywalking.e2e.sample.client;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* @author kezhenxu94, jian.tan
*/
public interface UserRepo extends JpaRepository<User, Long> {
}
# 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
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.skywalking.e2e;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.skywalking.e2e.metrics.AtLeastOneOfMetricsMatcher;
import org.apache.skywalking.e2e.metrics.Metrics;
import org.apache.skywalking.e2e.metrics.MetricsQuery;
import org.apache.skywalking.e2e.metrics.MetricsValueMatcher;
import org.apache.skywalking.e2e.service.Service;
import org.apache.skywalking.e2e.service.ServicesMatcher;
import org.apache.skywalking.e2e.service.ServicesQuery;
import org.apache.skywalking.e2e.service.endpoint.Endpoint;
import org.apache.skywalking.e2e.service.endpoint.EndpointQuery;
import org.apache.skywalking.e2e.service.endpoint.Endpoints;
import org.apache.skywalking.e2e.service.endpoint.EndpointsMatcher;
import org.apache.skywalking.e2e.service.instance.Instance;
import org.apache.skywalking.e2e.service.instance.Instances;
import org.apache.skywalking.e2e.service.instance.InstancesMatcher;
import org.apache.skywalking.e2e.service.instance.InstancesQuery;
import org.apache.skywalking.e2e.topo.Call;
import org.apache.skywalking.e2e.topo.ServiceInstanceTopoData;
import org.apache.skywalking.e2e.topo.ServiceInstanceTopoMatcher;
import org.apache.skywalking.e2e.topo.ServiceInstanceTopoQuery;
import org.apache.skywalking.e2e.topo.TopoData;
import org.apache.skywalking.e2e.topo.TopoMatcher;
import org.apache.skywalking.e2e.topo.TopoQuery;
import org.apache.skywalking.e2e.trace.Trace;
import org.apache.skywalking.e2e.trace.TracesMatcher;
import org.apache.skywalking.e2e.trace.TracesQuery;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.client.RestTemplate;
import org.yaml.snakeyaml.Yaml;
import static org.apache.skywalking.e2e.metrics.MetricsMatcher.verifyMetrics;
import static org.apache.skywalking.e2e.metrics.MetricsQuery.ALL_ENDPOINT_METRICS;
import static org.apache.skywalking.e2e.metrics.MetricsQuery.ALL_INSTANCE_METRICS;
import static org.apache.skywalking.e2e.metrics.MetricsQuery.ALL_SERVICE_INSTANCE_RELATION_CLIENT_METRICS;
import static org.apache.skywalking.e2e.metrics.MetricsQuery.ALL_SERVICE_INSTANCE_RELATION_SERVER_METRICS;
import static org.apache.skywalking.e2e.metrics.MetricsQuery.ALL_SERVICE_METRICS;
import static org.apache.skywalking.e2e.metrics.MetricsQuery.ALL_SERVICE_RELATION_CLIENT_METRICS;
import static org.apache.skywalking.e2e.metrics.MetricsQuery.ALL_SERVICE_RELATION_SERVER_METRICS;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author kezhenxu94, jian.tan
*/
@RunWith(SpringJUnit4ClassRunner.class)
public class SampleVerificationITCase {
private static final Logger LOGGER = LoggerFactory.getLogger(SampleVerificationITCase.class);
private final RestTemplate restTemplate = new RestTemplate();
private final int retryInterval = 30;
private SimpleQueryClient queryClient;
private String instrumentedServiceUrl;
@Before
public void setUp() {
final String swWebappHost = System.getProperty("sw.webapp.host", "127.0.0.1");
final String swWebappPort = System.getProperty("sw.webapp.port", "32783");
final String instrumentedServiceHost = System.getProperty("client.host", "127.0.0.1");
final String instrumentedServicePort = System.getProperty("client.port", "32782");
queryClient = new SimpleQueryClient(swWebappHost, swWebappPort);
instrumentedServiceUrl = "http://" + instrumentedServiceHost + ":" + instrumentedServicePort;
}
@Test(timeout = 1200000)
@DirtiesContext
public void verify() throws Exception {
final LocalDateTime minutesAgo = LocalDateTime.now(ZoneOffset.UTC);
while (true) {
try {
final Map<String, String> user = new HashMap<>();
user.put("name", "SkyWalking");
final ResponseEntity<String> responseEntity = restTemplate.postForEntity(
instrumentedServiceUrl + "/e2e/users",
user,
String.class
);
LOGGER.info("responseEntity: {}", responseEntity);
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
final List<Trace> traces = queryClient.traces(
new TracesQuery()
.start(minutesAgo)
.end(LocalDateTime.now())
.orderByDuration()
);
if (!traces.isEmpty()) {
break;
}
Thread.sleep(10000L);
} catch (Exception ignored) {
}
}
doRetryableVerification(() -> {
try {
verifyTraces(minutesAgo);
} catch (Exception e) {
LOGGER.warn(e.getMessage(), e);
}
});
doRetryableVerification(() -> {
try {
verifyServices(minutesAgo);
} catch (Exception e) {
LOGGER.warn(e.getMessage(), e);
}
});
doRetryableVerification(() -> {
try {
verifyTopo(minutesAgo);
} catch (Exception e) {
LOGGER.warn(e.getMessage(), e);
}
});
doRetryableVerification(() -> {
try{
verifyServiceInstanceTopo(minutesAgo);
}catch (Exception e) {
LOGGER.warn(e.getMessage(), e);
}
});
}
private void verifyTopo(LocalDateTime minutesAgo) throws Exception {
final LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);
final TopoData topoData = queryClient.topo(
new TopoQuery()
.stepByMinute()
.start(minutesAgo.minusDays(1))
.end(now)
);
LOGGER.info("topoData: {}", topoData);
InputStream expectedInputStream =
new ClassPathResource("expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.topo.yml").getInputStream();
final TopoMatcher topoMatcher = new Yaml().loadAs(expectedInputStream, TopoMatcher.class);
topoMatcher.verify(topoData);
verifyServiceRelationMetrics(topoData.getCalls(), minutesAgo);
}
private void verifyServiceInstanceTopo(LocalDateTime minutesAgo) throws Exception {
final LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);
final ServiceInstanceTopoData topoData = queryClient.serviceInstanceTopo(
new ServiceInstanceTopoQuery()
.stepByMinute()
.start(minutesAgo.minusDays(1))
.end(now)
.clientServiceId("1")
.serverServiceId("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);
verifyServiceInstanceRelationMetrics(topoData.getCalls(), minutesAgo);
}
private void verifyServices(LocalDateTime minutesAgo) throws Exception {
final LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);
final List<Service> services = queryClient.services(
new ServicesQuery()
.start(minutesAgo)
.end(now)
);
LOGGER.info("services: {}", services);
InputStream expectedInputStream =
new ClassPathResource("expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.services.yml").getInputStream();
final ServicesMatcher servicesMatcher = new Yaml().loadAs(expectedInputStream, ServicesMatcher.class);
servicesMatcher.verify(services);
for (Service service : services) {
LOGGER.info("verifying service instances: {}", service);
verifyServiceMetrics(service);
Instances instances = verifyServiceInstances(minutesAgo, now, service);
verifyInstancesMetrics(instances);
Endpoints endpoints = verifyServiceEndpoints(minutesAgo, now, service);
verifyEndpointsMetrics(endpoints);
}
}
private Instances verifyServiceInstances(LocalDateTime minutesAgo, LocalDateTime now,
Service service) throws Exception {
InputStream expectedInputStream;
Instances instances = queryClient.instances(
new InstancesQuery()
.serviceId(service.getKey())
.start(minutesAgo)
.end(now)
);
LOGGER.info("instances: {}", instances);
expectedInputStream =
new ClassPathResource("expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.instances.yml").getInputStream();
final InstancesMatcher instancesMatcher = new Yaml().loadAs(expectedInputStream, InstancesMatcher.class);
instancesMatcher.verify(instances);
return instances;
}
private Endpoints verifyServiceEndpoints(LocalDateTime minutesAgo, LocalDateTime now,
Service service) throws Exception {
Endpoints instances = queryClient.endpoints(
new EndpointQuery().serviceId(service.getKey())
);
LOGGER.info("instances: {}", instances);
InputStream expectedInputStream =
new ClassPathResource("expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.endpoints.yml").getInputStream();
final EndpointsMatcher endpointsMatcher = new Yaml().loadAs(expectedInputStream, EndpointsMatcher.class);
endpointsMatcher.verify(instances);
return instances;
}
private void verifyInstancesMetrics(Instances instances) throws Exception {
for (Instance instance : instances.getInstances()) {
for (String metricsName : ALL_INSTANCE_METRICS) {
LOGGER.info("verifying service instance response time: {}", instance);
final Metrics instanceMetrics = queryClient.metrics(
new MetricsQuery()
.stepByMinute()
.metricsName(metricsName)
.id(instance.getKey())
);
LOGGER.info("instanceMetrics: {}", instanceMetrics);
AtLeastOneOfMetricsMatcher instanceRespTimeMatcher = new AtLeastOneOfMetricsMatcher();
MetricsValueMatcher greaterThanZero = new MetricsValueMatcher();
greaterThanZero.setValue("gt 0");
instanceRespTimeMatcher.setValue(greaterThanZero);
instanceRespTimeMatcher.verify(instanceMetrics);
LOGGER.info("{}: {}", metricsName, instanceMetrics);
}
}
}
private void verifyEndpointsMetrics(Endpoints endpoints) throws Exception {
for (Endpoint endpoint : endpoints.getEndpoints()) {
if (!endpoint.getLabel().equals("/e2e/users")) {
continue;
}
for (String metricName : ALL_ENDPOINT_METRICS) {
LOGGER.info("verifying endpoint {}, metrics: {}", endpoint, metricName);
final Metrics metrics = queryClient.metrics(
new MetricsQuery()
.stepByMinute()
.metricsName(metricName)
.id(endpoint.getKey())
);
LOGGER.info("metrics: {}", metrics);
AtLeastOneOfMetricsMatcher instanceRespTimeMatcher = new AtLeastOneOfMetricsMatcher();
MetricsValueMatcher greaterThanZero = new MetricsValueMatcher();
greaterThanZero.setValue("gt 0");
instanceRespTimeMatcher.setValue(greaterThanZero);
instanceRespTimeMatcher.verify(metrics);
LOGGER.info("{}: {}", metricName, metrics);
}
}
}
private void verifyServiceMetrics(Service service) throws Exception {
for (String metricName : ALL_SERVICE_METRICS) {
LOGGER.info("verifying service {}, metrics: {}", service, metricName);
final Metrics serviceMetrics = queryClient.metrics(
new MetricsQuery()
.stepByMinute()
.metricsName(metricName)
.id(service.getKey())
);
LOGGER.info("serviceMetrics: {}", serviceMetrics);
AtLeastOneOfMetricsMatcher instanceRespTimeMatcher = new AtLeastOneOfMetricsMatcher();
MetricsValueMatcher greaterThanZero = new MetricsValueMatcher();
greaterThanZero.setValue("gt 0");
instanceRespTimeMatcher.setValue(greaterThanZero);
instanceRespTimeMatcher.verify(serviceMetrics);
LOGGER.info("{}: {}", metricName, serviceMetrics);
}
}
private void verifyTraces(LocalDateTime minutesAgo) throws Exception {
final LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);
final List<Trace> traces = queryClient.traces(
new TracesQuery()
.start(minutesAgo)
.end(now)
.orderByDuration()
);
LOGGER.info("traces: {}", traces);
InputStream expectedInputStream =
new ClassPathResource("expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.traces.yml").getInputStream();
final TracesMatcher tracesMatcher = new Yaml().loadAs(expectedInputStream, TracesMatcher.class);
tracesMatcher.verify(traces);
}
private void verifyServiceInstanceRelationMetrics(List<Call> calls, final LocalDateTime minutesAgo) throws Exception {
verifyRelationMetrics(calls, minutesAgo, ALL_SERVICE_INSTANCE_RELATION_CLIENT_METRICS, ALL_SERVICE_INSTANCE_RELATION_SERVER_METRICS);
}
private void verifyServiceRelationMetrics(List<Call> calls, final LocalDateTime minutesAgo) throws Exception {
verifyRelationMetrics(calls, minutesAgo, ALL_SERVICE_RELATION_CLIENT_METRICS, ALL_SERVICE_RELATION_SERVER_METRICS);
}
private void verifyRelationMetrics(List<Call> calls, final LocalDateTime minutesAgo, String[] relationClientMetrics, String[] relationServerMetrics) throws Exception {
for (Call call : calls) {
for (String detectPoint : call.getDetectPoints()) {
switch (detectPoint) {
case "CLIENT": {
for (String metricName : relationClientMetrics) {
verifyMetrics(queryClient, metricName, call.getId(), minutesAgo);
}
break;
}
case "SERVER": {
for (String metricName : relationServerMetrics) {
verifyMetrics(queryClient, metricName, call.getId(), minutesAgo);
}
break;
}
}
}
}
}
private void doRetryableVerification(Runnable runnable) throws InterruptedException {
while (true) {
try {
runnable.run();
break;
} catch (Throwable ignored) {
Thread.sleep(retryInterval);
}
}
}
}
# 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/health-check
- key: not null
label: /e2e/users
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# 1 health-check by docker-maven-plugin
# 1 drop table if exists, because we have `ddl-auto: create-drop`
# 1 drop sequence
# 1 create sequence
# 1 create table statement
instances:
- key: 2
label: not null
attributes:
- name: os_name
value: not null
- name: host_name
value: not null
- name: process_no
value: gt 0
- name: ipv4s
value: not null
# 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
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# 1 health-check by docker-maven-plugin
# 1 drop table if exists, because we have `ddl-auto: create-drop`
# 1 drop sequence
# 1 create sequence
# 1 create table statement
services:
- key: 2
label: "Your_ApplicationName"
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# 1 health-check by docker-maven-plugin
# 1 drop table if exists, because we have `ddl-auto: create-drop`
# 1 drop sequence
# 1 create sequence
# 1 create table statement
nodes:
- id: 1
name: User
type: USER
isReal: false
- id: 2
name: Your_ApplicationName
type: Tomcat
isReal: true
- id: 3
name: "localhost:-1"
type: H2
isReal: false
calls:
- id: 2_3
source: 2
detectPoints:
- CLIENT
target: 3
- id: 1_2
source: 1
detectPoints:
- SERVER
target: 2
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# 1 health-check by docker-maven-plugin
# 1 drop table if exists, because we have `ddl-auto: create-drop`
# 1 drop sequence
# 1 create sequence
# 1 create table statement
traces:
- key: not null
endpointNames:
- /e2e/users
duration: ge 0
start: gt 0
isError: false
traceIds:
- not null
......@@ -38,6 +38,7 @@
<module>e2e-cluster</module>
<module>e2e-cluster-with-gateway</module>
<module>e2e-agent-reboot</module>
<module>e2e-agent-auth</module>
<module>e2e-ttl</module>
<module>e2e-6.x-agent-7.x-oap-compatibility</module>
<module>e2e-profile</module>
......
......@@ -69,6 +69,7 @@ export agent_opts="-javaagent:${SCENARIO_HOME}/agent/skywalking-agent.jar
-Dskywalking.collector.backend_service=localhost:19876
-Dskywalking.agent.service_name=${SCENARIO_NAME}
-Dskywalking.logging.dir=/usr/local/skywalking/scenario/logs
-Dskywalking.agent.authentication=test-token
-Xms256m -Xmx256m ${agent_opts}"
exec /var/run/${SCENARIO_NAME}/${SCENARIO_START_SCRIPT} 1>/dev/null &
......
......@@ -113,6 +113,7 @@ if [ -f "${AGENT_FILE_PATH}/skywalking-agent.jar" ]; then
-Dskywalking.collector.discovery_check_interval=2
-Dskywalking.collector.backend_service=localhost:19876
-Dskywalking.agent.service_name=${SCENARIO_NAME}
-Dskywalking.agent.authentication=test-token
-Dskywalking.logging.dir=/usr/local/skywalking/scenario/logs
-Xms256m -Xmx256m -XX:PermSize=64M -XX:MaxPermSize=64"
fi
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册