未验证 提交 32f9ab01 编写于 作者: W wankai123 提交者: GitHub

E2e immigrate to e2e-v2 finished, remove old e2e. (#8037)

上级 fea9ce36
......@@ -48,7 +48,7 @@ jobs:
name: Build and Test / Java ${{ matrix.java-version }} / ${{ matrix.os }}
needs: [ check-license-header ]
runs-on: ${{ matrix.os }}-latest
timeout-minutes: 60
timeout-minutes: 90
strategy:
matrix:
os: [ ubuntu, macos, windows ]
......
......@@ -7,9 +7,6 @@
[submodule "skywalking-ui"]
path = skywalking-ui
url = https://github.com/apache/skywalking-rocketbot-ui.git
[submodule "test/e2e/e2e-protocol/src/main/proto"]
path = test/e2e/e2e-protocol/src/main/proto
url = https://github.com/apache/skywalking-data-collect-protocol.git
[submodule "test/e2e-v2/java-test-service/e2e-protocol/src/main/proto"]
path = test/e2e-v2/java-test-service/e2e-protocol/src/main/proto
url = git@github.com:apache/skywalking-data-collect-protocol.git
......@@ -7,27 +7,7 @@ Release Notes.
#### Project
* Replace e2e cases to e2e-v2:
- Simple: JDK, Auth, SSL, mTLS
- Lua Nginx
- SelfObservability
- Gateway
- Meter
- Nodejs
- PHP
- VM: Prometheus Node Exporter, Zabbix
- go2sky
- log
- Python
- Storage
- Cluster
- Event
- Profile
- Kafka: Base, Meter, Log, Profile
- Client-JS
- Istio: ALS, Metrics
- TTL
- Alarm
* E2E tests immigrate to e2e-v2.
* Support JDK 16 and 17.
#### OAP Server
......
......@@ -50,16 +50,15 @@ Since version 6.3.0, we have introduced more automatic tests to perform software
> End-to-end testing is a methodology used to test whether the flow of an application is performing as designed from start to finish.
The purpose of carrying out end-to-end tests is to identify system dependencies and to ensure that the right information is passed between various system components and systems.
The E2E test involves some/all of the OAP server, storage, coordinator, webapp, and the instrumented services, all of which are orchestrated by `docker-compose`. Besides, there is a test controller (JUnit test) running outside of the container that sends traffic to the instrumented service,
and then verifies the corresponding results after those requests have been made through GraphQL API of the SkyWalking Web App.
The E2E test involves some/all of the OAP server, storage, coordinator, webapp, and the instrumented services, all of which are orchestrated by `docker-compose` or `KinD`.
Since version 8.9.0, we immigrate to e2e-v2 which leverage [skywalking-infra-e2e](https://github.com/apache/skywalking-infra-e2e) and [skywalking-cli](https://github.com/apache/skywalking-cli) to do the whole e2e process.
`skywalking-infra-e2e` is used to control the e2e process and `skywalking-cli` is used to interact with the OAP such as request and get response metris from OAP.
#### Writing E2E Cases
- Set up the environment in IntelliJ IDEA
The E2E test is a separate project under the SkyWalking root directory and the IDEA cannot recognize it by default. Right click
on the file `test/e2e/pom.xml` and click `Add as Maven Project`. We recommend opening the directory `skywalking/test/e2e`
in a separate IDE window for better experience, since there may be shaded classes issues.
- Set up the environment
1. Set up `skywalking-infra-e2e`
1. Set up `skywalking-cli`, `yq` (generally these 2 are enough) and others tools if your cases need. Can reference the script under `skywalking/test/e2e-v2/script/prepare/setup-e2e-shell`.
- Orchestrate the components
......@@ -71,17 +70,15 @@ To make the orchestration process easier, we're using a [docker-compose](https:/
Follow these steps:
1. Decide what (and how many) containers will be needed. For example, for cluster testing, you'll need > 2 OAP nodes, coordinators (e.g. zookeeper), storage (e.g. ElasticSearch), and instrumented services;
1. Define the containers in `docker-compose.yml`, and carefully specify the dependencies, starting orders, and most importantly, link them together, e.g. set the correct OAP address on the agent end, and set the correct coordinator address in OAP, etc.
1. Write (or hopefully reuse) the test codes to verify that the results are correct.
As for the final step, we have a user-friendly framework to help you get started more quickly. This framework provides the annotation `@DockerCompose("docker-compose.yml")` to load/parse and start up all the containers in the proper order.
`@ContainerHost`/`@ContainerPort` obtains the real host/port of the container. `@ContainerHostAndPort` obtains both. `@DockerContainer` obtains the running container.
- Write test controller
1. Define the e2e case [config](https://skywalking.apache.org/docs/skywalking-infra-e2e/latest/en/setup/configuration-file/) in `e2e.yaml`.
1. Write the expected data(yml) for verify.
Put it simply, test controllers are tests that can be bound to the maven `integration-test/verify` phase.
They send **design** requests to the instrumented services, and anticipate corresponding traces/metrics/metadata from the SkyWalking webapp GraphQL API.
- [Run e2e test](https://skywalking.apache.org/docs/skywalking-infra-e2e/latest/en/setup/run-e2e-tests/)
In the test framework, we provide a `TrafficController` that periodically sends traffic data to the instrumented services. You can simply enable it by providing a url and traffic data. Refer to [this](../../../test/e2e/e2e-test/src/test/java/org/apache/skywalking/e2e/base/TrafficController.java).
All e2e cases should under `skywalking/test/e2e-v2/cases`. You could execute e2e run command in `skywalking/` e.g.
```
e2e run -c test/e2e-v2/cases/alarm/h2/e2e.yaml
```
- Troubleshooting
......
......@@ -12,7 +12,7 @@ We define the VM entity as a `Service` in OAP, and use `vm::` as a prefix to ide
## Setup
1. Setup [Prometheus node-exporter](https://prometheus.io/docs/guides/node-exporter/).
2. Setup [OpenTelemetry Collector ](https://opentelemetry.io/docs/collector/). This is an example for OpenTelemetry Collector configuration [otel-collector-config.yaml](../../../../test/e2e/e2e-test/docker/promOtelVM/otel-collector-config.yaml).
2. Setup [OpenTelemetry Collector ](https://opentelemetry.io/docs/collector/). This is an example for OpenTelemetry Collector configuration [otel-collector-config.yaml](../../../../test/e2e-v2/cases/vm/prometheus-node-exporter/otel-collector-config.yaml).
3. Config SkyWalking [OpenTelemetry receiver](backend-receivers.md#opentelemetry-receiver).
## Supported Metrics
......
......@@ -24,7 +24,7 @@ are located at `$CLASSPATH/zabbix-rules`.
The file is written in YAML format, defined by the scheme described below. Square brackets indicate that a parameter is optional.
An example for Zabbix agent configuration could be found [here](../../../../test/e2e/e2e-test/docker/zabbix/zabbix_agentd.conf).
An example for Zabbix agent configuration could be found [here](../../../../test/e2e-v2/cases/vm/zabbix/zabbix_agentd.conf).
You could find details on Zabbix agent items from [Zabbix Agent documentation](https://www.zabbix.com/documentation/current/manual/config/items/itemtypes/zabbix_agent).
### Configuration file
......
......@@ -14,20 +14,20 @@ or [HTTP JSON array](../../protocols/Log-Data-Protocol.md#http-api).
Filebeat supports using Kafka to transport logs. Open [kafka-fetcher](backend-fetcher.md#kafka-fetcher) and enable configs `enableNativeJsonLog`.
Take the following filebeat config yaml as an example to set up Filebeat:
- [filebeat.yml](../../../../test/e2e/e2e-test/docker/kafka/filebeat.yml)
- [filebeat.yml](../../../../test/e2e-v2/cases/kafka/log/filebeat.yml)
#### Fluentd
Fluentd supports using Kafka to transport logs. Open [kafka-fetcher](backend-fetcher.md#kafka-fetcher) and enable configs `enableNativeJsonLog`.
Take the following fluentd config file as an example to set up Fluentd:
- [fluentd.conf](../../../../test/e2e/e2e-test/docker/kafka/fluentd.conf)
- [fluentd.conf](../../../../test/e2e-v2/cases/kafka/log/fluentd.conf)
#### Fluent-bit
Fluent-bit sends logs to OAP directly through HTTP(rest port).
Point the output address to `restHost`:`restPort` of `receiver-sharing-server` or `core`(if `receiver-sharing-server` is inactivated)
Take the following fluent-bit config files as an example to set up Fluent-bit:
- [fluent-bit.conf](../../../../test/e2e/e2e-test/docker/log/fluent-bit)
- [fluent-bit.conf](../../../../test/e2e-v2/cases/log/fluent-bit/fluent-bit.conf)
### Java agent's toolkits
Java agent provides toolkits for
......@@ -47,9 +47,9 @@ Java agent provides toolkits for
to report logs through files with automatically injected trace context.
Log framework config examples:
- [log4j1.x fileAppender](../../../../test/e2e/e2e-service-provider/src/main/resources/log4j.properties)
- [log4j2.x fileAppender](../../../../test/e2e/e2e-service-provider/src/main/resources/log4j2.xml)
- [logback fileAppender](../../../../test/e2e/e2e-service-provider/src/main/resources/logback.xml)
- [log4j1.x fileAppender](../../../../test/e2e-v2/java-test-service/e2e-service-provider/src/main/resources/log4j.properties)
- [log4j2.x fileAppender](../../../../test/e2e-v2/java-test-service/e2e-service-provider/src/main/resources/log4j2.xml)
- [logback fileAppender](../../../../test/e2e-v2/java-test-service/e2e-service-provider/src/main/resources/logback.xml)
### Python agent log reporter
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
verify:
retry:
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
verify:
retry:
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
verify:
retry:
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
verify:
retry:
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
verify:
retry:
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
verify:
retry:
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
verify:
retry:
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
verify:
retry:
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
verify:
retry:
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......@@ -128,4 +126,4 @@ verify:
swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql trace ls --order startTime --service-name "e2e-service-provider" --endpoint-name "POST:/users" \
| yq e '.traces[0].traceids[0]' - \
)
expected: ../expected/trace-users-detail.yml
\ No newline at end of file
expected: ../expected/trace-users-detail.yml
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......@@ -128,4 +126,4 @@ verify:
swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql trace ls --order startTime --service-name "e2e-service-provider" --endpoint-name "POST:/users" \
| yq e '.traces[0].traceids[0]' - \
)
expected: ../expected/trace-users-detail.yml
\ No newline at end of file
expected: ../expected/trace-users-detail.yml
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......@@ -128,4 +126,4 @@ verify:
swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql trace ls --order startTime --service-name "e2e-service-provider" --endpoint-name "POST:/users" \
| yq e '.traces[0].traceids[0]' - \
)
expected: ../expected/trace-users-detail.yml
\ No newline at end of file
expected: ../expected/trace-users-detail.yml
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......@@ -128,4 +126,4 @@ verify:
swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql trace ls --order startTime --service-name "e2e-service-provider" --endpoint-name "POST:/users" \
| yq e '.traces[0].traceids[0]' - \
)
expected: ../expected/trace-users-detail.yml
\ No newline at end of file
expected: ../expected/trace-users-detail.yml
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......@@ -128,4 +126,4 @@ verify:
swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql trace ls --order startTime --service-name "e2e-service-provider" --endpoint-name "POST:/users" \
| yq e '.traces[0].traceids[0]' - \
)
expected: ../expected/trace-users-detail.yml
\ No newline at end of file
expected: ../expected/trace-users-detail.yml
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......@@ -128,4 +126,4 @@ verify:
swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql trace ls --order startTime --service-name "e2e-service-provider" --endpoint-name "POST:/users" \
| yq e '.traces[0].traceids[0]' - \
)
expected: ../expected/trace-users-detail.yml
\ No newline at end of file
expected: ../expected/trace-users-detail.yml
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
trigger:
action: http
......@@ -128,4 +126,4 @@ verify:
swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql trace ls --order startTime --service-name "e2e-service-provider" --endpoint-name "POST:/users" \
| yq e '.traces[0].traceids[0]' - \
)
expected: ../expected/trace-users-detail.yml
\ No newline at end of file
expected: ../expected/trace-users-detail.yml
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
verify:
retry:
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
verify:
retry:
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
verify:
retry:
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
verify:
retry:
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
verify:
retry:
......
......@@ -25,8 +25,6 @@ setup:
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
- name: install swctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
- name: install etcdctl
command: bash test/e2e-v2/script/prepare/setup-e2e-shell/install.sh etcdctl
verify:
retry:
......
<?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-common</artifactId>
<description>Some fundamental classes used in E2E tests</description>
<properties>
<version.system-rules>1.19.0</version.system-rules>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
/*
* 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 com.google.common.base.Strings;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.extern.slf4j.Slf4j;
import org.apache.skywalking.e2e.annotation.ContainerHost;
import org.apache.skywalking.e2e.annotation.ContainerHostAndPort;
import org.apache.skywalking.e2e.annotation.ContainerPort;
import org.apache.skywalking.e2e.annotation.DockerCompose;
import org.apache.skywalking.e2e.annotation.DockerContainer;
import org.apache.skywalking.e2e.common.HostAndPort;
import org.apache.skywalking.e2e.docker.DockerComposeFile;
import org.apache.skywalking.e2e.logging.ContainerLogger;
import org.apache.skywalking.e2e.utils.Envs;
import org.testcontainers.containers.ContainerState;
import org.testcontainers.containers.DockerComposeContainer;
import org.testcontainers.containers.output.Slf4jLogConsumer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.containers.wait.strategy.WaitStrategy;
import static java.util.stream.Collectors.joining;
import static org.apache.skywalking.e2e.utils.Yamls.load;
/**
* {@link #init(Object) SkyWalkingAnnotations.init(this)} initializes fields annotated with SkyWalking annotations. You
* don't usually need to call this method to initialize if the {@link SkyWalkingExtension} is already used, which does
* the work for you automatically:
*
* <pre>{@code
* @ExtendWith(SkyWalkingExtension.class)
* @TestInstance(TestInstance.Lifecycle.PER_CLASS)
* public class SomeTest {
* @DockerCompose("docker-compose.yml")
* private DockerComposeContainer justForSideEffects;
*
* @ContainerHostAndPort(name = "service-name1-in-docker-compose.yml", port = 8080)
* private HostAndPort someService1HostPort;
*
* @ContainerHostAndPort(name = "service-name2-in-docker-compose.yml", port = 9090)
* private HostAndPort someService2HostPort;
* }
* }</pre>
*
* but if you don't use the extension for some reasons (which is rare), here is an example:
*
* <pre>{@code
* public class SomeTest {
* @DockerCompose("docker/simple/docker-compose.yml")
* private DockerComposeContainer justForSideEffects;
*
* @ContainerHostAndPort(name = "service-name1-in-docker-compose.yml", port = 8080)
* private HostAndPort someService1HostPort;
*
* @ContainerHostAndPort(name = "service-name2-in-docker-compose.yml", port = 9090)
* private HostAndPort someService2HostPort;
*
* @BeforeAll
* public void setUp() throws Exception {
* SkyWalkingAnnotations.init(this);
* }
* }
* }</pre>
*/
@Slf4j
public final class SkyWalkingAnnotations {
private static final boolean IS_CI = !Strings.isNullOrEmpty(System.getenv("GITHUB_RUN_ID"));
private static final String IDENTIFIER =
!Strings.isNullOrEmpty(System.getenv("GITHUB_RUN_ID"))
? System.getenv("GITHUB_RUN_ID") : "skywalking-e2e-";
private static final String LOG_DIR_ENV =
!Strings.isNullOrEmpty(System.getenv("GITHUB_WORKSPACE"))
? (System.getenv("GITHUB_WORKSPACE") + "/logs") : "/tmp/skywalking/logs";
private static final Path LOG_DIR = Paths.get(LOG_DIR_ENV);
private static final List<String> REMOTE_SERVICE_NAMES = new LinkedList<>();
private static final int REMOTE_DEBUG_PORT = 5005;
static {
LOGGER.info("IDENTIFIER={}", IDENTIFIER);
LOGGER.info("LOG_DIR={}", LOG_DIR);
}
public static void init(final Object testClass) throws Exception {
Objects.requireNonNull(testClass, "testClass");
final DockerComposeContainer<?> compose = initDockerComposeField(testClass).orElseThrow(RuntimeException::new);
compose.start();
initHostAndPort(testClass, compose);
initDockerContainers(testClass, compose);
}
/**
* Destroy the containers started by the docker compose in the given test class, this should be typically called in
* the corresponding {@code @AfterAll} or {@code @AfterEach} method.
*
* @param testClass in which the containers should be destroyed
*/
public static void destroy(final Object testClass) {
Stream.of(testClass.getClass().getDeclaredFields())
.filter(SkyWalkingAnnotations::isAnnotatedWithDockerCompose)
.findFirst()
.ifPresent(field -> {
try {
field.setAccessible(true);
((DockerComposeContainer<?>) field.get(testClass)).stop();
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
});
}
private static void initHostAndPort(final Object testClass,
final DockerComposeContainer<?> compose) throws Exception {
final Field[] fields = testClass.getClass().getDeclaredFields();
for (final Field field : fields) {
if (field.isAnnotationPresent(ContainerHost.class) && field.isAnnotationPresent(ContainerPort.class)) {
throw new RuntimeException(
"field cannot be annotated with both ContainerHost and ContainerPort: " + field.getName()
);
}
if (field.isAnnotationPresent(ContainerHost.class)) {
final ContainerHost host = field.getAnnotation(ContainerHost.class);
field.setAccessible(true);
field.set(testClass, compose.getServiceHost(host.name(), host.port()));
}
if (field.isAnnotationPresent(ContainerPort.class)) {
final ContainerPort host = field.getAnnotation(ContainerPort.class);
field.setAccessible(true);
field.set(testClass, compose.getServicePort(host.name(), host.port()));
}
if (field.isAnnotationPresent(ContainerHostAndPort.class)) {
final ContainerHostAndPort hostAndPort = field.getAnnotation(ContainerHostAndPort.class);
final String host = compose.getServiceHost(hostAndPort.name(), hostAndPort.port());
final int port = compose.getServicePort(hostAndPort.name(), hostAndPort.port());
field.setAccessible(true);
field.set(testClass, HostAndPort.builder().host(host).port(port).build());
}
}
if (!IS_CI) {
File portFile = new File("remote_real_port");
portFile.createNewFile();
FileWriter fileWriter = new FileWriter(portFile.getName());
for (String service : REMOTE_SERVICE_NAMES) {
fileWriter.write(String.format("%s-%s:%s\n", service, compose.getServiceHost(service, REMOTE_DEBUG_PORT),
compose.getServicePort(service, REMOTE_DEBUG_PORT)));
}
fileWriter.flush();
fileWriter.close();
}
}
private static Optional<DockerComposeContainer<?>> initDockerComposeField(final Object testClass) throws Exception {
final Field[] fields = testClass.getClass().getDeclaredFields();
final List<Field> dockerComposeFields = Stream.of(fields)
.filter(SkyWalkingAnnotations::isAnnotatedWithDockerCompose)
.collect(Collectors.toList());
if (dockerComposeFields.isEmpty()) {
return Optional.empty();
}
if (dockerComposeFields.size() > 1) {
throw new RuntimeException("can only have one field annotated with @DockerCompose");
}
final Field dockerComposeField = dockerComposeFields.get(0);
final DockerCompose dockerCompose = dockerComposeField.getAnnotation(DockerCompose.class);
final List<File> files = Stream.of(dockerCompose.value()).map(Envs::resolve)
.map(File::new).collect(Collectors.toList());
final DockerComposeContainer<?> compose = new DockerComposeContainer<>(IDENTIFIER, files);
if (!IS_CI) {
List<String> filePathList = files.stream().map(File::getAbsolutePath).collect(Collectors.toList());
try {
LOGGER.info("Parse files:{}", filePathList.stream().collect(joining(" ", " ", "")));
DockerComposeFile dockerComposeFile = DockerComposeFile.getAllConfigInfo(filePathList);
dockerComposeFile.getServices().forEach((service, ignored) -> {
if (dockerComposeFile.isExposedPort(service, REMOTE_DEBUG_PORT)) {
REMOTE_SERVICE_NAMES.add(service);
compose.withExposedService(service, REMOTE_DEBUG_PORT, Wait.forListeningPort());
}
});
} catch (IOException | InterruptedException e) {
LOGGER.error(e.getMessage(), e);
}
}
for (final Field field : fields) {
if (field.isAnnotationPresent(ContainerHost.class) && field.isAnnotationPresent(ContainerPort.class)) {
throw new RuntimeException(
"field cannot be annotated with both ContainerHost and ContainerPort: " + field.getName()
);
}
final WaitStrategy waitStrategy = Wait.forListeningPort().withStartupTimeout(Duration.ofMinutes(5));
if (field.isAnnotationPresent(ContainerHost.class)) {
final ContainerHost host = field.getAnnotation(ContainerHost.class);
compose.withExposedService(host.name(), host.port(), waitStrategy);
}
if (field.isAnnotationPresent(ContainerPort.class)) {
final ContainerPort port = field.getAnnotation(ContainerPort.class);
compose.withExposedService(port.name(), port.port(), waitStrategy);
}
if (field.isAnnotationPresent(ContainerHostAndPort.class)) {
final ContainerHostAndPort hostAndPort = field.getAnnotation(ContainerHostAndPort.class);
compose.withExposedService(hostAndPort.name(), hostAndPort.port(), waitStrategy);
}
}
compose.withPull(true)
.withLocalCompose(true)
.withTailChildContainers(true)
.withRemoveImages(
IS_CI ? DockerComposeContainer.RemoveImages.ALL : DockerComposeContainer.RemoveImages.LOCAL
);
if (IS_CI) {
initLoggers(files, compose);
}
dockerComposeField.setAccessible(true);
dockerComposeField.set(testClass, compose);
return Optional.of(compose);
}
private static void initLoggers(final List<File> files, final DockerComposeContainer<?> compose) {
files.forEach(file -> {
try {
load(file).as(DockerComposeFile.class).getServices().forEach(
(service, ignored) -> compose.withLogConsumer(
service, new Slf4jLogConsumer(new ContainerLogger(LOG_DIR, service + ".log"))
)
);
} catch (IOException e) {
LOGGER.error(e.getMessage(), e);
}
});
}
@SuppressWarnings("unchecked")
private static void initDockerContainers(final Object testClass,
final DockerComposeContainer<?> compose) throws Exception {
final List<Field> containerFields = Stream.of(testClass.getClass().getDeclaredFields())
.filter(SkyWalkingAnnotations::isAnnotatedWithDockerContainer)
.collect(Collectors.toList());
if (containerFields.isEmpty()) {
return;
}
final Field serviceMap = DockerComposeContainer.class.getDeclaredField("serviceInstanceMap");
serviceMap.setAccessible(true);
final Map<String, ContainerState> serviceInstanceMap = (Map<String, ContainerState>) serviceMap.get(compose);
for (final Field containerField : containerFields) {
if (containerField.getType() != ContainerState.class) {
throw new IllegalArgumentException(
"@DockerContainer can only be annotated on fields of type " + ContainerState.class.getName()
+ " but was " + containerField.getType() + "; field \"" + containerField.getName() + "\""
);
}
final DockerContainer dockerContainer = containerField.getAnnotation(DockerContainer.class);
final String serviceName = dockerContainer.value();
final Optional<ContainerState> container =
serviceInstanceMap.entrySet()
.stream()
.filter(e -> e.getKey().startsWith(serviceName + "_"))
.findFirst()
.map(Map.Entry::getValue);
containerField.setAccessible(true);
containerField.set(
testClass,
container.orElseThrow(
() -> new NoSuchElementException("cannot find container with name " + serviceName)
)
);
}
}
private static boolean isAnnotatedWithDockerCompose(final Field field) {
return field.isAnnotationPresent(DockerCompose.class);
}
private static boolean isAnnotatedWithDockerContainer(final Field field) {
return field.isAnnotationPresent(DockerContainer.class);
}
}
/*
* 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.annotation.ContainerHost;
import org.apache.skywalking.e2e.annotation.ContainerHostAndPort;
import org.apache.skywalking.e2e.annotation.ContainerPort;
import org.apache.skywalking.e2e.annotation.DockerCompose;
import org.junit.jupiter.api.extension.AfterAllCallback;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.Extension;
import org.junit.jupiter.api.extension.ExtensionContext;
/**
* {@link Extension} that supports the {@link DockerCompose @DockerCompose}, {@link ContainerHost @ContainerHost} and
* {@link ContainerPort @ContainerPort}, {@link ContainerHostAndPort @ContainerHostAndPort} annotations.
*
* <pre>{@code
* @ExtendWith(SkyWalkingExtension.class)
* @TestInstance(TestInstance.Lifecycle.PER_CLASS)
* public class SomeTest {
* @DockerCompose("docker-compose.yml")
* private DockerComposeContainer justForSideEffects;
*
* @ContainerHostAndPort(name = "service-name1-in-docker-compose.yml", port = 8080)
* private HostAndPort someService1HostPort;
*
* @ContainerHostAndPort(name = "service-name2-in-docker-compose.yml", port = 9090)
* private HostAndPort someService2HostPort;
* }
* }</pre>
*/
public final class SkyWalkingExtension implements BeforeAllCallback, AfterAllCallback {
@Override
public void beforeAll(final ExtensionContext context) throws Exception {
SkyWalkingAnnotations.init(context.getRequiredTestInstance());
}
@Override
public void afterAll(final ExtensionContext context) {
SkyWalkingAnnotations.destroy(context.getRequiredTestInstance());
}
}
/*
* 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.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import org.apache.skywalking.e2e.SkyWalkingExtension;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Fields of type {@link String} annotated with {@link ContainerHost @ContainerHost} can be initialized by {@link
* SkyWalkingExtension} with the real host of the docker container, whose original {@link #name() service name} and
* {@link #port() exposed port} defined in {@code docker-compose.yml} are given, for more details and examples, refer to
* the JavaDoc of {@link SkyWalkingExtension}.
*/
@Documented
@Target(FIELD)
@Retention(RUNTIME)
public @interface ContainerHost {
/**
* @return the original name that is defined in {@code docker-compose.yml}.
*/
String name();
/**
* @return the original port that is exposed in {@code docker-compose.yml}.
*/
int port();
}
/*
* 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.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import org.apache.skywalking.e2e.SkyWalkingExtension;
import org.apache.skywalking.e2e.common.HostAndPort;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Fields of type {@link HostAndPort} annotated with {@link ContainerHostAndPort @ContainerHostAndPort} can be
* initialized by {@link SkyWalkingExtension} with the real host and port of the docker container, whose original {@link
* #name() service name} and {@link #port() exposed port} defined in {@code docker-compose.yml} are given, for more
* details and examples, refer to the JavaDoc of {@link SkyWalkingExtension}.
*/
@Documented
@Target(FIELD)
@Retention(RUNTIME)
public @interface ContainerHostAndPort {
/**
* @return the original name that is defined in {@code docker-compose.yml}.
*/
String name();
/**
* @return the original port that is exposed in {@code docker-compose.yml}.
*/
int port();
}
/*
* 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.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import org.apache.skywalking.e2e.SkyWalkingExtension;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Fields of type {@code int} annotated with {@link ContainerPort @ContainerPort} can be initialized by {@link
* SkyWalkingExtension} with the real port of the docker container, whose original {@link #name() service name} and
* {@link #port() exposed port} defined in {@code docker-compose.yml} are given, for more details and examples, refer
* to the JavaDoc of {@link SkyWalkingExtension}.
*/
@Documented
@Target(FIELD)
@Retention(RUNTIME)
public @interface ContainerPort {
/**
* @return the original name that is defined in {@code docker-compose.yml}.
*/
String name();
/**
* @return the original port that is exposed in {@code docker-compose.yml}.
*/
int port();
}
/*
* 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.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import org.apache.skywalking.e2e.SkyWalkingExtension;
import org.testcontainers.containers.DockerComposeContainer;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Fields of type {@link DockerComposeContainer} annotated with {@link DockerCompose @DockerCompose} can be initialized
* by {@link SkyWalkingExtension} with the given {@link #value() docker-compose.yml} files, for more details and
* exampless, refer to the JavaDoc of {@link SkyWalkingExtension}.
*/
@Documented
@Target(FIELD)
@Retention(RUNTIME)
public @interface DockerCompose {
/**
* @return the {@code docker-compose.yml} files
*/
String[] value();
}
/*
* 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.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import org.apache.skywalking.e2e.SkyWalkingExtension;
import org.testcontainers.containers.ContainerState;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Fields of type {@link ContainerState} annotated with {@link DockerContainer @DockerContainer} can be initialized by
* {@link SkyWalkingExtension} with the docker container, whose {@link #value() service name} defined in {@code
* docker-compose.yml} are given, for more details and examples, refer to the JavaDoc of {@link SkyWalkingExtension}.
*/
@Documented
@Target(FIELD)
@Retention(RUNTIME)
public @interface DockerContainer {
String value();
}
/*
* 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.common;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* An immutable representation of a host and port.
*/
@Data
@Builder
@Accessors(fluent = true)
public final class HostAndPort {
private final String host;
private final int port;
@Override
public String toString() {
return host + ":" + port;
}
}
/*
* 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.docker;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import lombok.Data;
import static java.util.stream.Collectors.joining;
import static org.apache.skywalking.e2e.utils.Yamls.load;
@Data
public final class DockerComposeFile {
private String version;
private Map<String, Map<String, Object>> services;
private Map<String, Map<String, Object>> networks;
public static DockerComposeFile getAllConfigInfo(List<String> composeFiles) throws IOException, InterruptedException {
String shStr = String.format("docker-compose %s config",
composeFiles.stream().collect(joining(" -f", " -f", "")));
Process process = Runtime.getRuntime().exec(shStr, null, null);
InputStreamReader ir = new InputStreamReader(process.getInputStream());
LineNumberReader input = new LineNumberReader(ir);
String line;
StringBuilder result = new StringBuilder();
process.waitFor();
while ((line = input.readLine()) != null) {
result.append(line).append("\n");
}
return load(result).as(DockerComposeFile.class);
}
public List<String> getServiceExposedPorts(String serviceName) {
Map<String, Object> service = services.get(serviceName);
List tmp = (List) service.get("expose");
if (tmp == null) {
return new LinkedList<>();
}
List<String> ports = new LinkedList<>();
for (Object item: tmp) {
ports.add(item.toString());
}
return ports;
}
public boolean isExposedPort(String serviceName, Integer port) {
List<String> ports = getServiceExposedPorts(serviceName);
return ports.contains(String.valueOf(port));
}
}
/*
* 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.logging;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.FileAppender;
import java.nio.file.Path;
import lombok.experimental.Delegate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class ContainerLogger implements Logger {
@Delegate
private final Logger delegate;
public ContainerLogger(final Path logDirectory, final String container) {
final LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
final PatternLayoutEncoder encoder = new PatternLayoutEncoder();
encoder.setPattern("%d{HH:mm:ss.SSS} %-5level %logger{36}.%M - %msg%n");
encoder.setContext(context);
encoder.start();
final FileAppender<ILoggingEvent> fileAppender = new FileAppender<>();
fileAppender.setFile(logDirectory.resolve(container).toAbsolutePath().toString());
fileAppender.setEncoder(encoder);
fileAppender.setContext(context);
fileAppender.start();
final ch.qos.logback.classic.Logger logger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(container);
logger.addAppender(fileAppender);
logger.setLevel(Level.DEBUG);
logger.setAdditive(false);
this.delegate = logger;
}
}
/*
* 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.naming;
import java.lang.reflect.Method;
import org.junit.jupiter.api.DisplayNameGenerator;
public final class CamelCaseNameGenerator extends DisplayNameGenerator.Standard {
@Override
public String generateDisplayNameForMethod(final Class<?> testClass, final Method testMethod) {
return "verify " + String.join(" ", testMethod.getName().split("(?=\\p{Upper})")).toLowerCase();
}
}
/*
* 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.retryable;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
import org.junit.jupiter.api.extension.ConditionEvaluationResult;
import org.junit.jupiter.api.extension.ExecutionCondition;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.TestExecutionExceptionHandler;
import org.opentest4j.TestAbortedException;
import static org.apache.skywalking.e2e.retryable.RetryableTestExtension.TEST_SHOULD_RETRY;
@RequiredArgsConstructor
final class OneShotExtension
implements ExecutionCondition, AfterTestExecutionCallback, TestExecutionExceptionHandler {
private final Class<? extends Throwable> throwableClass;
private final int invocation;
private final int times;
@Override
public ConditionEvaluationResult evaluateExecutionCondition(final ExtensionContext context) {
final boolean shouldRetry = Stores.get(context, TEST_SHOULD_RETRY, Boolean.class) != Boolean.FALSE;
if (!shouldRetry) {
return ConditionEvaluationResult.disabled("test passed after " + invocation + " attempts");
}
Stores.put(context, TEST_SHOULD_RETRY, invocation < times || retryInfinitely());
return ConditionEvaluationResult.enabled("test is retried " + times + " times and is still failed");
}
@Override
public void afterTestExecution(final ExtensionContext context) {
final Optional<Throwable> throwable = context.getExecutionException();
Stores.put(
context, TEST_SHOULD_RETRY,
throwable.isPresent() && throwable.get().getClass() == TestAbortedException.class
);
}
@Override
public void handleTestExecutionException(final ExtensionContext context,
final Throwable throwable) throws Throwable {
if (Stores.get(context, TEST_SHOULD_RETRY, Boolean.class) == Boolean.FALSE) {
throw throwable;
}
if (retryOnAnyException()) {
throw new TestAbortedException("test failed, will retry", throwable);
}
if (throwableClass == throwable.getClass()) {
throw new TestAbortedException("test failed, will retry", throwable);
}
throw throwable;
}
private boolean retryOnAnyException() {
return throwableClass == Throwable.class;
}
private boolean retryInfinitely() {
return times < 0;
}
}
/*
* 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.retryable;
import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.ExtendWith;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Tests annotated with {@link RetryableTest @RetryableTest} can be retried automatically on failure.
*/
@Inherited
@Documented
@TestTemplate
@Retention(RUNTIME)
@Target({METHOD, TYPE})
@ExtendWith(RetryableTestExtension.class)
public @interface RetryableTest {
/**
* @return the {@link Throwable} class, when this type of throwable is thrown, the test should be retried; if {@link
* Throwable Throwable.class} is specified, the failed test will be retried when any exception is thrown. {@code
* Throwable.class} by default
*/
Class<? extends Throwable> throwable() default Throwable.class;
/**
* @return maximum times to retry, or -1 for infinite retries. {@code -1} by default.
*/
int value() default 60;
/**
* @return the interval between any two retries, in millisecond. {@code 1000} by default.
*/
long interval() default 10000;
}
/*
* 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.retryable;
import java.util.Collections;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.junit.jupiter.api.extension.Extension;
import org.junit.jupiter.api.extension.TestTemplateInvocationContext;
@RequiredArgsConstructor
final class RetryableTestContext implements TestTemplateInvocationContext {
private final Class<? extends Throwable> throwable;
private final int invocation;
private final int times;
@Override
public List<Extension> getAdditionalExtensions() {
return Collections.singletonList(new OneShotExtension(throwable, invocation, times));
}
@Override
public String getDisplayName(final int invocationIndex) {
return String.format("test attempt #%d", invocationIndex);
}
}
/*
* 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.retryable;
import java.util.Iterator;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import lombok.SneakyThrows;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.TestTemplateInvocationContext;
import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider;
final class RetryableTestExtension implements TestTemplateInvocationContextProvider {
public static final String TEST_SHOULD_RETRY = "TEST_PASSED";
@Override
public boolean supportsTestTemplate(final ExtensionContext context) {
return context.getTestMethod().map(m -> m.isAnnotationPresent(RetryableTest.class)).orElse(false);
}
@Override
public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(final ExtensionContext context) {
final RetryableTest retryableTest = context.getRequiredTestMethod().getAnnotation(RetryableTest.class);
final Class<? extends Throwable> throwable = retryableTest.throwable();
final long interval = retryableTest.interval();
final int times = retryableTest.value();
if (interval <= 0) {
throw new IllegalArgumentException(
"RetryableTest#interval must be a positive integer, but was " + interval
);
}
return StreamSupport.stream(
Spliterators.spliteratorUnknownSize(new Iterator<Integer>() {
final AtomicInteger count = new AtomicInteger(0);
@Override
@SneakyThrows
public boolean hasNext() {
final boolean shouldRetry = Stores.get(context, TEST_SHOULD_RETRY, Boolean.class) != Boolean.FALSE;
final boolean hasNext = (times < 0 || count.get() <= times) && shouldRetry;
if (hasNext) {
Thread.sleep(interval);
}
return hasNext;
}
@Override
public Integer next() {
return count.getAndIncrement();
}
}, Spliterator.NONNULL), false
).map(time -> new RetryableTestContext(throwable, time, times));
}
}
/*
* 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.retryable;
import org.junit.jupiter.api.extension.ExtensionContext;
final class Stores {
public static ExtensionContext.Store store(final ExtensionContext context) {
final ExtensionContext.Namespace namespace = ExtensionContext.Namespace.create(
context.getRequiredTestClass(),
context.getRequiredTestMethod()
);
return context.getRoot().getStore(namespace);
}
public static <T> T get(final ExtensionContext context, final Object key, final Class<T> requiredType) {
return store(context).get(key, requiredType);
}
public static void put(final ExtensionContext context, final Object key, final Object value) {
store(context).put(key, value);
}
}
/*
* 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.utils;
import com.google.common.base.Strings;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class Envs {
public static String resolve(final String text) {
if (Strings.isNullOrEmpty(text)) {
return "";
}
final Pattern pattern = Pattern.compile("\\$\\{(?<name>\\w+)}");
final Matcher matcher = pattern.matcher(text);
final StringBuffer buffer = new StringBuffer();
while (matcher.find()) {
final String name = matcher.group("name");
final String value = System.getenv(name);
matcher.appendReplacement(buffer, Matcher.quoteReplacement(Strings.nullToEmpty(value)));
}
matcher.appendTail(buffer);
return buffer.toString();
}
}
/*
* 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.utils;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import lombok.RequiredArgsConstructor;
import org.springframework.core.io.ClassPathResource;
import org.testcontainers.shaded.org.yaml.snakeyaml.Yaml;
/**
* A helper class to load YAML content as a type-safe object.
*/
@RequiredArgsConstructor
public final class Yamls {
public interface Builder {
}
public interface AsTypeBuilder extends Builder {
<T> T as(final Class<T> klass);
}
public static boolean exists(final String file) {
return new ClassPathResource(Envs.resolve(file)).exists();
}
public static AsTypeBuilder load(final String file) throws IOException {
final InputStream inputStream = new ClassPathResource(Envs.resolve(file)).getInputStream();
return new AsTypeBuilder() {
@Override
public <T> T as(final Class<T> klass) {
return new Yaml().loadAs(inputStream, klass);
}
};
}
public static AsTypeBuilder load(final File file) throws IOException {
final InputStream inputStream = new FileInputStream(file);
return new AsTypeBuilder() {
@Override
public <T> T as(final Class<T> klass) {
return new Yaml().loadAs(inputStream, klass);
}
};
}
public static AsTypeBuilder load(final StringBuilder content) {
return new AsTypeBuilder() {
@Override
public <T> T as(final Class<T> klass) {
return new Yaml().loadAs(content.toString(), klass);
}
};
}
}
/*
* 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.docker;
import org.apache.skywalking.e2e.utils.Yamls;
import org.junit.Assert;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
class DockerComposeFileTest {
private static DockerComposeFile COMPOSE_FILE_ONE = null;
private static DockerComposeFile COMPOSE_FILE_TWO = null;
@BeforeAll
static void setUp() throws Exception {
COMPOSE_FILE_ONE = Yamls.load("docker-compose.one.yml").as(DockerComposeFile.class);
COMPOSE_FILE_TWO = Yamls.load("docker-compose.two.yml").as(DockerComposeFile.class);
}
@Test
void getAllConfigInfo() throws IOException, InterruptedException, URISyntaxException {
File composeOne = new File(DockerComposeFileTest.class
.getClassLoader()
.getResource("docker-compose.one.yml")
.toURI());
File composeTwo = new File(DockerComposeFileTest.class
.getClassLoader()
.getResource("docker-compose.two.yml")
.toURI());
List<String> filePathList = new ArrayList<>();
filePathList.add(composeOne.getAbsolutePath());
filePathList.add(composeTwo.getAbsolutePath());
DockerComposeFile testFile = DockerComposeFile.getAllConfigInfo(filePathList);
Assert.assertNotNull(testFile);
Assert.assertNotNull(testFile.getServices());
Assert.assertEquals(COMPOSE_FILE_ONE.getServices().size() + COMPOSE_FILE_TWO.getServices().size(),
testFile.getServices().size());
}
@Test
void getServiceExposedPort() {
List<String> ports = COMPOSE_FILE_TWO.getServiceExposedPorts("oap");
Assert.assertNotNull(ports);
Assert.assertEquals(3, ports.size());
}
@Test
void isExposedPort() {
boolean result = COMPOSE_FILE_TWO.isExposedPort("oap", 5005);
Assert.assertTrue(result);
result = COMPOSE_FILE_TWO.isExposedPort("oap", 5006);
Assert.assertFalse(result);
}
}
\ No newline at end of file
/*
* 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.retryable;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Timeout;
import static org.junit.jupiter.api.Assertions.fail;
@SuppressWarnings("ResultOfMethodCallIgnored")
class RetryableTestTest {
private static final AtomicInteger TIMES1 = new AtomicInteger(1);
private static final AtomicInteger TIMES2 = new AtomicInteger(1);
private static final AtomicInteger TIMES3 = new AtomicInteger(1);
@Timeout(30)
@RetryableTest
@DisplayName("should retry on failure")
void shouldRetryOnFailure() {
if (TIMES1.getAndIncrement() == 1) {
Integer.parseInt("abc");
}
}
@Timeout(30)
@RetryableTest(3)
@DisplayName("should retry specific times")
void shouldRetrySpecificTimes() {
if (TIMES3.getAndIncrement() < 3) {
Integer.parseInt("abc");
}
}
@Timeout(30)
@DisplayName("should retry specific exception")
@RetryableTest(throwable = NumberFormatException.class)
void shouldRetrySpecificException() {
final int retriedTimes = TIMES2.getAndIncrement();
if (retriedTimes == 1) {
throw new NumberFormatException("not NumberFormatException");
}
if (retriedTimes > 2) {
fail("should never happen");
}
}
}
# 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.
version: '2.1'
services:
ui:
depends_on:
oap:
condition: service_healthy
environment:
SW_OAP_ADDRESS: http://oap:12800
expose:
- '8080'
image: skywalking/ui:latest
networks:
- e2e
# 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.
version: '2.1'
services:
oap:
image: skywalking/oap:latest
expose:
- 11800
- 12800
- 5005
networks:
- e2e
restart: on-failure
environment:
SW_CLUSTER_ZK_HOST_PORT: zk:2181
SW_STORAGE_ES_CLUSTER_NODES: es:9200
SW_JDBC_URL: jdbc:mysql://mysql:3306/swtest
SW_STORAGE_INFLUXDB_URL: http://influxdb:8086
healthcheck:
test: ["CMD", "sh", "-c", "nc -zn 127.0.0.1 11800"]
interval: 5s
timeout: 60s
retries: 120
networks:
e2e: {}
<?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-data</artifactId>
</project>
/*
* 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.springframework.util.StringUtils;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
@SuppressWarnings("unchecked")
public abstract class AbstractQuery<T extends AbstractQuery<T>> {
private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HHmmss");
private static final DateTimeFormatter MINUTE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HHmm");
private static final DateTimeFormatter DAY_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private static final DateTimeFormatter MONTH_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM");
private String start;
private String end;
private String step = "SECOND";
private String name;
public String start() {
if (start != null) {
return start;
}
if ("SECOND".equals(step())) {
return LocalDateTime.now(ZoneOffset.UTC).minusMinutes(15).format(TIME_FORMATTER);
}
if ("MINUTE".equals(step())) {
return LocalDateTime.now(ZoneOffset.UTC).minusMinutes(15).format(MINUTE_TIME_FORMATTER);
}
if ("DAY".equals(step())) {
return LocalDateTime.now(ZoneOffset.UTC).minusMinutes(15).format(DAY_TIME_FORMATTER);
}
if ("MONTH".equals(step())) {
return LocalDateTime.now(ZoneOffset.UTC).minusMinutes(15).format(MONTH_TIME_FORMATTER);
}
return null;
}
public T start(String start) {
this.start = start;
return (T) this;
}
public T start(LocalDateTime start) {
if ("MINUTE".equals(step())) {
this.start = start.format(MINUTE_TIME_FORMATTER);
} else if ("SECOND".equals(step())) {
this.start = start.format(TIME_FORMATTER);
} else if ("DAY".equals(step())) {
this.start = start.format(DAY_TIME_FORMATTER);
} else if ("MONTH".equals(step())) {
this.start = start.format(MONTH_TIME_FORMATTER);
}
return (T) this;
}
public String end() {
if (end != null) {
return end;
}
if ("SECOND".equals(step())) {
return LocalDateTime.now(ZoneOffset.UTC).format(TIME_FORMATTER);
}
if ("MINUTE".equals(step())) {
return LocalDateTime.now(ZoneOffset.UTC).format(MINUTE_TIME_FORMATTER);
}
if ("DAY".equals(step())) {
return LocalDateTime.now(ZoneOffset.UTC).format(DAY_TIME_FORMATTER);
}
if ("MONTH".equals(step())) {
return LocalDateTime.now(ZoneOffset.UTC).format(MONTH_TIME_FORMATTER);
}
return null;
}
public T end(String end) {
this.end = end;
return (T) this;
}
public T end(LocalDateTime end) {
if ("MINUTE".equals(step())) {
this.end = end.format(MINUTE_TIME_FORMATTER);
} else if ("SECOND".equals(step())) {
this.end = end.format(TIME_FORMATTER);
} else if ("DAY".equals(step())) {
this.end = end.format(DAY_TIME_FORMATTER);
} else if ("MONTH".equals(step())) {
this.end = end.format(MONTH_TIME_FORMATTER);
}
return (T) this;
}
public String step() {
return step;
}
public T step(String step) {
this.step = step;
return (T) this;
}
public T stepByMonth() {
this.step = "MONTH";
return (T) this;
}
public T stepByDay() {
this.step = "DAY";
return (T) this;
}
public T stepByMinute() {
this.step = "MINUTE";
return (T) this;
}
public T stepBySecond() {
this.step = "SECOND";
return (T) this;
}
public String name() {
if (!StringUtils.isEmpty(name)) {
return name;
}
return null;
}
public T name(String name) {
this.name = name;
return (T) this;
}
}
/*
* 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 lombok.Data;
/**
* GraphQL response for easily test
*/
@Data
public class GQLResponse<T> {
private T data;
}
/*
* 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 com.google.common.io.Resources;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.apache.skywalking.e2e.profile.creation.ProfileTaskCreationRequest;
import org.apache.skywalking.e2e.profile.creation.ProfileTaskCreationResult;
import org.apache.skywalking.e2e.profile.creation.ProfileTaskCreationResultWrapper;
import org.apache.skywalking.e2e.profile.query.ProfileAnalyzation;
import org.apache.skywalking.e2e.profile.query.ProfileTaskQuery;
import org.apache.skywalking.e2e.profile.query.ProfileTasks;
import org.apache.skywalking.e2e.profile.query.ProfiledSegment;
import org.apache.skywalking.e2e.profile.query.Traces;
import org.apache.skywalking.e2e.trace.Trace;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
@SuppressWarnings("UnstableApiUsage")
public class ProfileClient extends SimpleQueryClient {
public ProfileClient(String host, int port) {
super(host, port);
}
public ProfileTaskCreationResult createProfileTask(final ProfileTaskCreationRequest creationRequest) throws Exception {
final URL queryFileUrl = Resources.getResource("profileTaskCreation.gql");
final String queryString =
Resources.readLines(queryFileUrl, StandardCharsets.UTF_8)
.stream()
.filter(it -> !it.startsWith("#"))
.collect(Collectors.joining())
.replace("{serviceId}", String.valueOf(creationRequest.getServiceId()))
.replace("{endpointName}", creationRequest.getEndpointName())
.replace("{duration}", String.valueOf(creationRequest.getDuration()))
.replace("{startTime}", String.valueOf(creationRequest.getStartTime()))
.replace("{minDurationThreshold}", String.valueOf(creationRequest.getMinDurationThreshold()))
.replace("{dumpPeriod}", String.valueOf(creationRequest.getDumpPeriod()))
.replace("{maxSamplingCount}", String.valueOf(creationRequest.getMaxSamplingCount()));
final ResponseEntity<GQLResponse<ProfileTaskCreationResultWrapper>> responseEntity = restTemplate.exchange(
new RequestEntity<>(queryString, HttpMethod.POST, URI.create(endpointUrl)),
new ParameterizedTypeReference<GQLResponse<ProfileTaskCreationResultWrapper>>() {
}
);
if (responseEntity.getStatusCode() != HttpStatus.OK) {
throw new RuntimeException("Response status != 200, actual: " + responseEntity.getStatusCode());
}
return Objects.requireNonNull(responseEntity.getBody()).getData().getCreationResult();
}
public ProfileTasks getProfileTaskList(final ProfileTaskQuery query) throws IOException {
final URL queryFileUrl = Resources.getResource("profileTaskList.gql");
final String queryString = Resources.readLines(queryFileUrl, StandardCharsets.UTF_8)
.stream()
.filter(it -> !it.startsWith("#"))
.collect(Collectors.joining())
.replace("{serviceId}", String.valueOf(query.serviceId()))
.replace("{endpointName}", query.endpointName());
final ResponseEntity<GQLResponse<ProfileTasks>> responseEntity = restTemplate.exchange(
new RequestEntity<>(queryString, HttpMethod.POST, URI.create(endpointUrl)),
new ParameterizedTypeReference<GQLResponse<ProfileTasks>>() {
}
);
if (responseEntity.getStatusCode() != HttpStatus.OK) {
throw new RuntimeException("Response status != 200, actual: " + responseEntity.getStatusCode());
}
return Objects.requireNonNull(responseEntity.getBody()).getData();
}
public List<Trace> getProfiledTraces(final String taskId) throws Exception {
final URL queryFileUrl = Resources.getResource("profileTaskSegmentList.gql");
final String queryString = Resources.readLines(queryFileUrl, StandardCharsets.UTF_8)
.stream()
.filter(it -> !it.startsWith("#"))
.collect(Collectors.joining())
.replace("{taskID}", taskId);
final ResponseEntity<GQLResponse<Traces>> responseEntity = restTemplate.exchange(
new RequestEntity<>(queryString, HttpMethod.POST, URI.create(endpointUrl)),
new ParameterizedTypeReference<GQLResponse<Traces>>() {
}
);
if (responseEntity.getStatusCode() != HttpStatus.OK) {
throw new RuntimeException("Response status != 200, actual: " + responseEntity.getStatusCode());
}
return Objects.requireNonNull(responseEntity.getBody()).getData().getTraces();
}
public ProfiledSegment.ProfiledSegmentData getProfiledSegment(final String segmentId) throws IOException {
final URL queryFileUrl = Resources.getResource("profiledSegment.gql");
final String queryString = Resources.readLines(queryFileUrl, StandardCharsets.UTF_8)
.stream()
.filter(it -> !it.startsWith("#"))
.collect(Collectors.joining())
.replace("{segmentId}", segmentId);
final ResponseEntity<GQLResponse<ProfiledSegment>> responseEntity = restTemplate.exchange(
new RequestEntity<>(queryString, HttpMethod.POST, URI.create(endpointUrl)),
new ParameterizedTypeReference<GQLResponse<ProfiledSegment>>() {
}
);
if (responseEntity.getStatusCode() != HttpStatus.OK) {
throw new RuntimeException("Response status != 200, actual: " + responseEntity.getStatusCode());
}
return Objects.requireNonNull(responseEntity.getBody()).getData().getSegment();
}
public ProfileAnalyzation getProfileAnalyzation(final String segmentId, long start, long end) throws IOException {
final URL queryFileUrl = Resources.getResource("profileAnalyzation.gql");
final String queryString = Resources.readLines(queryFileUrl, StandardCharsets.UTF_8)
.stream()
.filter(it -> !it.startsWith("#"))
.collect(Collectors.joining())
.replace("{segmentId}", segmentId)
.replace("{start}", String.valueOf(start))
.replace("{end}", String.valueOf(end));
final ResponseEntity<GQLResponse<ProfileAnalyzation>> responseEntity = restTemplate.exchange(
new RequestEntity<>(queryString, HttpMethod.POST, URI.create(endpointUrl)),
new ParameterizedTypeReference<GQLResponse<ProfileAnalyzation>>() {
}
);
if (responseEntity.getStatusCode() != HttpStatus.OK) {
throw new RuntimeException("Response status != 200, actual: " + responseEntity.getStatusCode());
}
return Objects.requireNonNull(responseEntity.getBody()).getData();
}
}
/*
* 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 com.google.common.io.Resources;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.apache.skywalking.e2e.dashboard.DashboardConfiguration;
import org.apache.skywalking.e2e.dashboard.DashboardConfigurationListWrapper;
import org.apache.skywalking.e2e.dashboard.DashboardSetting;
import org.apache.skywalking.e2e.dashboard.TemplateChangeStatus;
import org.apache.skywalking.e2e.dashboard.TemplateChangeStatusWrapper;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
public class UIConfigurationManagementClient extends SimpleQueryClient {
public UIConfigurationManagementClient(final String endpointUrl) {
super(endpointUrl);
}
public UIConfigurationManagementClient(final String host, final int port) {
super(host, port);
}
public TemplateChangeStatus addTemplate(DashboardSetting setting) throws IOException {
final URL queryFileUrl = Resources.getResource("ui-addTemplate.gql");
final String queryString = Resources.readLines(queryFileUrl, StandardCharsets.UTF_8)
.stream()
.filter(it -> !it.startsWith("#"))
.collect(Collectors.joining())
.replace("{name}", setting.name())
.replace("{type}", String.valueOf(setting.type()))
.replace("{configuration}", setting.configuration())
.replace("{active}", String.valueOf(setting.active()));
final ResponseEntity<GQLResponse<TemplateChangeStatusWrapper>> responseEntity = restTemplate.exchange(
new RequestEntity<>(queryString, HttpMethod.POST, URI.create(endpointUrl)),
new ParameterizedTypeReference<GQLResponse<TemplateChangeStatusWrapper>>() {
}
);
if (responseEntity.getStatusCode() != HttpStatus.OK) {
throw new RuntimeException("Response status != 200, actual: " + responseEntity.getStatusCode());
}
return Objects.requireNonNull(responseEntity.getBody()).getData().getChangeStatusResult();
}
public List<DashboardConfiguration> getAllTemplates(Boolean includingDisabled) throws IOException {
final URL queryFileUrl = Resources.getResource("ui-getTemplates.gql");
final String queryString =
Resources.readLines(queryFileUrl, StandardCharsets.UTF_8)
.stream()
.filter(it -> !it.startsWith("#"))
.collect(Collectors.joining())
.replace("{includingDisabled}", String.valueOf(includingDisabled));
final ResponseEntity<GQLResponse<DashboardConfigurationListWrapper>> responseEntity = restTemplate.exchange(
new RequestEntity<>(queryString, HttpMethod.POST, URI.create(endpointUrl)),
new ParameterizedTypeReference<GQLResponse<DashboardConfigurationListWrapper>>() {
}
);
if (responseEntity.getStatusCode() != HttpStatus.OK) {
throw new RuntimeException("Response status != 200, actual: " + responseEntity.getStatusCode());
}
return Objects.requireNonNull(responseEntity.getBody()).getData().getConfigurations();
}
public TemplateChangeStatus changeTemplate(DashboardSetting setting) throws IOException {
final URL queryFileUrl = Resources.getResource("ui-changeTemplate.gql");
final String queryString = Resources.readLines(queryFileUrl, StandardCharsets.UTF_8)
.stream()
.filter(it -> !it.startsWith("#"))
.collect(Collectors.joining())
.replace("{name}", setting.name())
.replace("{type}", String.valueOf(setting.type()))
.replace("{configuration}", setting.configuration())
.replace("{active}", String.valueOf(setting.active()));
final ResponseEntity<GQLResponse<TemplateChangeStatusWrapper>> responseEntity = restTemplate.exchange(
new RequestEntity<>(queryString, HttpMethod.POST, URI.create(endpointUrl)),
new ParameterizedTypeReference<GQLResponse<TemplateChangeStatusWrapper>>() {
}
);
if (responseEntity.getStatusCode() != HttpStatus.OK) {
throw new RuntimeException("Response status != 200, actual: " + responseEntity.getStatusCode());
}
return Objects.requireNonNull(responseEntity.getBody()).getData().getChangeStatusResult();
}
public TemplateChangeStatus disableTemplate(String name) throws IOException {
final URL queryFileUrl = Resources.getResource("ui-disableTemplate.gql");
final String queryString =
Resources.readLines(queryFileUrl, StandardCharsets.UTF_8)
.stream()
.filter(it -> !it.startsWith("#"))
.collect(Collectors.joining())
.replace("{name}", name);
final ResponseEntity<GQLResponse<TemplateChangeStatusWrapper>> responseEntity = restTemplate.exchange(
new RequestEntity<>(queryString, HttpMethod.POST, URI.create(endpointUrl)),
new ParameterizedTypeReference<GQLResponse<TemplateChangeStatusWrapper>>() {
}
);
if (responseEntity.getStatusCode() != HttpStatus.OK) {
throw new RuntimeException("Response status != 200, actual: " + responseEntity.getStatusCode());
}
return Objects.requireNonNull(responseEntity.getBody()).getData().getChangeStatusResult();
}
}
/*
* 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.alarm;
import lombok.Data;
import lombok.experimental.Accessors;
import org.apache.skywalking.e2e.common.KeyValue;
import org.apache.skywalking.e2e.event.Event;
import java.util.List;
@Data
@Accessors(chain = true)
public class Alarm {
private String startTime;
private String scope;
private String id;
private String message;
private List<KeyValue> tags;
private List<Event> events;
}
/*
* 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.alarm;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.skywalking.e2e.common.KeyValue;
import org.apache.skywalking.e2e.common.KeyValueMatcher;
import org.apache.skywalking.e2e.event.Event;
import org.apache.skywalking.e2e.event.EventMatcher;
import org.apache.skywalking.e2e.verification.AbstractMatcher;
import org.springframework.util.CollectionUtils;
import java.util.List;
import static java.util.Objects.nonNull;
import static org.assertj.core.api.Assertions.fail;
@Slf4j
@Data
public class AlarmMatcher extends AbstractMatcher<Alarm> {
private String startTime;
private String scope;
private String id;
private String message;
private List<KeyValueMatcher> tags;
private List<EventMatcher> events;
@Override
public void verify(Alarm alarm) {
doVerify(this.scope, alarm.getScope());
doVerify(this.id, alarm.getId());
doVerify(this.message, alarm.getMessage());
if (nonNull(getTags())) {
for (final KeyValueMatcher matcher : getTags()) {
boolean matched = false;
for (final KeyValue keyValue : alarm.getTags()) {
try {
matcher.verify(keyValue);
matched = true;
} catch (Throwable ignore) {
}
}
if (!matched) {
fail("\nExpected: %s\n Actual: %s", getTags(), alarm.getTags());
}
}
}
if (!CollectionUtils.isEmpty(getEvents())) {
for (final EventMatcher matcher : getEvents()) {
boolean matched = false;
for (final Event event : alarm.getEvents()) {
try {
matcher.verify(event);
matched = true;
} catch (Throwable ignore) {
//ignore.
}
}
if (!matched) {
fail("\nExpected: %s\n Actual: %s", getEvents(), alarm.getEvents());
}
}
}
}
}
/*
* 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.alarm;
import org.apache.skywalking.e2e.AbstractQuery;
import org.apache.skywalking.e2e.event.Event;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
public class AlarmQuery extends AbstractQuery<AlarmQuery> {
private List<Map<String, String>> tags = Collections.emptyList();
private List<Event> events = Collections.emptyList();
public List<Map<String, String>> tags() {
return tags;
}
public List<Event> events() {
return events;
}
public AlarmQuery tags(List<Map<String, String>> tags) {
this.tags = tags;
return this;
}
public AlarmQuery events(List<Event> events) {
this.events = events;
return this;
}
public AlarmQuery addTag(String key, String value) {
if (Collections.EMPTY_LIST.equals(tags)) {
tags = new ArrayList<>();
}
Map<String, String> tag = new HashMap<>();
tag.put("key", key);
tag.put("value", value);
tags.add(tag);
return this;
}
public AlarmQuery addEvents(List<Event> events) {
if (Collections.EMPTY_LIST.equals(events)) {
events = new ArrayList<>();
}
events.addAll(events);
return this;
}
}
/*
* 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.alarm;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.junit.Assert;
import java.util.LinkedList;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
@Data
@Slf4j
public class AlarmsMatcher {
private int total;
private List<AlarmMatcher> matchers;
public AlarmsMatcher() {
this.matchers = new LinkedList<>();
}
public void verify(final GetAlarm alarms) {
LOGGER.info("alarms:{} matchers:{}", alarms, this.matchers);
Assert.assertEquals(this.total, alarms.getTotal());
assertThat(this.matchers).hasSameSizeAs(alarms.getMsgs());
for (int i = 0; i < this.matchers.size(); i++) {
boolean matched = false;
for (Alarm alarm : alarms.getMsgs()) {
try {
this.matchers.get(i).verify(alarm);
matched = true;
} catch (Throwable ignored) {
}
}
if (!matched) {
fail("\nExpected: %s\nActual: %s", this.matchers, alarms);
}
}
}
}
/*
* 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.alarm;
import lombok.Data;
import java.util.List;
@Data
public class GetAlarm {
private int total;
private List<Alarm> msgs;
}
/*
* 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.alarm;
import lombok.Data;
@Data
public class GetAlarmData {
private GetAlarm getAlarm;
}
/*
* 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.alarm;
import lombok.Data;
import org.apache.skywalking.e2e.common.KeyValue;
import java.util.List;
@Data
public class HookAlarm {
private String scopeId;
private String scope;
private String name;
private String id0;
private String id1;
private String ruleName;
private String alarmMessage;
private String startTime;
private List<KeyValue> tags;
}
/*
* 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.alarm;
import lombok.Data;
import org.apache.skywalking.e2e.common.KeyValue;
import org.apache.skywalking.e2e.common.KeyValueMatcher;
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;
@Data
public class HookAlarmMatcher extends AbstractMatcher<HookAlarm> {
private String scopeId;
private String scope;
private String name;
private String id0;
private String id1;
private String ruleName;
private String alarmMessage;
private String startTime;
private List<KeyValueMatcher> tags;
@Override
public void verify(HookAlarm hookAlarm) {
doVerify(this.scopeId, hookAlarm.getScopeId());
doVerify(this.scope, hookAlarm.getScope());
doVerify(this.name, hookAlarm.getName());
doVerify(this.id0, hookAlarm.getId0());
doVerify(this.id1, hookAlarm.getId1());
doVerify(this.ruleName, hookAlarm.getRuleName());
doVerify(this.alarmMessage, hookAlarm.getAlarmMessage());
doVerify(this.startTime, hookAlarm.getStartTime());
if (nonNull(getTags())) {
for (final KeyValueMatcher matcher : getTags()) {
boolean matched = false;
for (final KeyValue keyValue : hookAlarm.getTags()) {
try {
matcher.verify(keyValue);
matched = true;
} catch (Throwable ignore) {
}
}
if (!matched) {
fail("\nExpected: %s\n Actual: %s", getTags(), hookAlarm.getTags());
}
}
}
}
}
/*
* 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.alarm;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class HookAlarms {
private ArrayList<HookAlarm> messages;
}
/*
* 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.alarm;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.skywalking.e2e.verification.AbstractMatcher;
import java.util.ArrayList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class HookAlarmsMatcher extends AbstractMatcher<HookAlarms> {
private ArrayList<HookAlarmMatcher> messages;
@Override
public void verify(HookAlarms hookAlarms) {
assertThat(this.messages).hasSameSizeAs(hookAlarms.getMessages());
for (int i = 0; i < this.messages.size(); i++) {
boolean matched = false;
for (HookAlarm hookAlarm : hookAlarms.getMessages()) {
try {
this.messages.get(i).verify(hookAlarm);
matched = true;
} catch (Throwable ignored) {
}
}
if (!matched) {
fail("\nExpected: %s\nActual: %s", this.messages.get(i), hookAlarms.getMessages());
}
}
}
}
/*
* 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.assertor;
import java.util.List;
import java.util.function.Function;
import java.util.regex.Pattern;
import org.apache.skywalking.e2e.assertor.exception.VariableNotFoundException;
import static java.util.Objects.isNull;
public class VariableExpressParser {
public static <T> T parse(final String express, List<T> actual, Function<T, String> getFiled) {
String variable = express.trim();
if (!(variable.startsWith("${") && variable.endsWith("}"))) {
return null;
}
variable = variable.substring(2, variable.length() - 1);
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;
for (T t : actual) {
if (Pattern.matches(regex, getFiled.apply(t))) {
if (mappingIndex++ == expectedIndex) {
mapping = t;
break;
}
}
}
if (isNull(mapping)) {
throw new VariableNotFoundException(express);
}
return mapping;
}
}
/*
* 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.assertor.exception;
import org.apache.skywalking.e2e.exception.AssertFailedException;
public class VariableNotFoundException extends AssertFailedException {
private static final long serialVersionUID = 1337142072507388456L;
private final String express;
public VariableNotFoundException(String express) {
this.express = express;
}
@Override
public String getCauseMessage() {
return String.format("VariableNotFoundException\nexpected: %s\nactual: %s\n", express, "NOT FOUND");
}
}
/*
* 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.browser;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
public class BrowserErrorLog {
private String service;
private String serviceVersion;
private Long time;
private String pagePath;
private String category;
private String grade;
private String message;
private Integer line;
private Integer col;
private String stack;
private String errorUrl;
private boolean firstReportedError;
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册