未验证 提交 099adcf5 编写于 作者: K kezhenxu94 提交者: GitHub

Set up agent tests (#117)

上级 64bb6469
......@@ -17,11 +17,8 @@ jobs:
node-version: [8, 9, 10]
steps:
- uses: actions/checkout@v2
- name: checkout submodules
shell: bash
run: |
git submodule sync --recursive
git -c protocol.version=2 submodule update --init --force --recursive --depth=1
with:
submodules: true
- name: Cache node modules
uses: actions/cache@v1
with:
......
name: Test
on:
pull_request:
push:
branches:
- master
tags:
- 'v*'
jobs:
Test:
runs-on: ubuntu-18.04
timeout-minutes: 30
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Use NodeJS 10
uses: actions/setup-node@v1
with:
node-version: 10
- name: Test
run: cd modules/nodejs-agent && npm i && npm run test
......@@ -11,9 +11,11 @@ SkyAPM Node.js is the Node.js instrumentation agent, which is compatible with [A
## Support List
1. [Http](https://nodejs.org/api/http.html)
2. [Mysql](https://github.com/mysqljs/mysql)
3. [Egg](https://github.com/eggjs/egg)
1. [Mysql](https://github.com/mysqljs/mysql)
1. [Egg](https://github.com/eggjs/egg)
## Plugin Test
Read [plugin test](docs/PluginTest.md) if you want to contribute a new plugin to the community.
# Contact Us
* Submit an issue
......
# Plugin Test
Plugin test is required and should pass before a new plugin is able to be merged into the master branch.
## Mock Collector
Mock Collector respects the same protocol as the SkyWalking backend, and thus receives the report data from the agent side,
besides, it also exposes some http endpoints for verification.
## Tested Service
A tested service is a service involving the plugin that is to be tested, and exposes some endpoints to trigger the instrumentation
and report data to the mock collector.
## Docker Compose
`docker-compose` is used to orchestrate the mock collector and the tested service(s), the `docker-compose.yml` should be
able to run with `docker-compose -f docker-compose.yml up` in the standalone mode, which can be used in debugging too.
## Expected Data
The `expected.data.yml` file contains the expected segment data after we have triggered the instrumentation and report to mock collector,
since the mock collector received the segment data then, we can post the expected data to the mock collector and verify whether
they match. This can be done through the `/dataValidate` of the mock collector, say `http://collector:12800/dataValidate`, for example.
## Example
If we want to test the plugin for the built-in library `http`, we will:
1. Build a tested service, which sets up an http server by `http` library, and exposes an http endpoint to be triggered in the test codes, say `/trigger`, take [this service](../modules/nodejs-agent/test/plugins/http/serverA.js) as example.
1. Compose a `docker-compose.yml` file, orchestrating the service built in step 1 and the mock collector, take [this `docker-compose.yml`](../modules/nodejs-agent/test/plugins/http/docker-compose.yml) as example.
1. Write test codes to trigger the endpoint int step 1, and send the expected data file to the mock collector to verify, take [this test](../modules/nodejs-agent/test/plugins/http/http.test.js) as example.
......@@ -46,8 +46,8 @@ AgentConfig.prototype.setDirectServices = function(directServices) {
AgentConfig.prototype.initConfig = function(agentOptions) {
this._serviceName = process.env.SW_SERVICE_NAME || (agentOptions && agentOptions.serviceName) || "You Application";
this._instanceName = process.env.SW_INSTANCE_NAME || (agentOptions && agentOptions.instanceName) || "You Instance";
this._serviceName = process.env.SW_SERVICE_NAME || (agentOptions && agentOptions.serviceName) || "Your Application";
this._instanceName = process.env.SW_INSTANCE_NAME || (agentOptions && agentOptions.instanceName) || "Your Instance";
this._directServices = process.env.SW_DIRECT_SERVERS || (agentOptions && agentOptions.directServers) || "localhost:11800";
this._authentication = process.env.SW_AUTHENTICATION || (agentOptions && agentOptions.authentication) || "";
};
......
......@@ -94,7 +94,7 @@ module.exports = function(httpModule, instrumentation, contextManager) {
function wrapRequest(original) {
return function(options, callback) {
let contextCarrier = new ContextCarrier();
let span = contextManager.createExitSpan(options.path, options.hostname + ":" + options.port, contextCarrier);
let span = contextManager.createExitSpan(options.path, (options.hostname || options.host) + ":" + options.port, contextCarrier);
contextCarrier.pushBy(function(key, value) {
if (!options.hasOwnProperty("headers") || !options.headers) {
options.headers = {};
......
......@@ -40,8 +40,8 @@ ContextCarrier.prototype.serialize = function() {
traceContextArray.push(Base64.encode(this._traceId));
traceContextArray.push(Base64.encode(this._traceSegmentId));
traceContextArray.push(this._spanId);
traceContextArray.push(this._parentService);
traceContextArray.push(this._parentServiceInstance);
traceContextArray.push(Base64.encode(this._parentService));
traceContextArray.push(Base64.encode(this._parentServiceInstance));
traceContextArray.push(Base64.encode(this._parentEndpoint));
traceContextArray.push(Base64.encode(this._addressUsedAtClient));
return traceContextArray.join("-");
......@@ -53,7 +53,7 @@ ContextCarrier.prototype.deserialize = function(traceContext) {
}
let traceContextSegment = traceContext.split("-");
if (traceContextSegment.length != 8) {
if (traceContextSegment.length !== 8) {
return this;
}
......
......@@ -8,7 +8,8 @@
"generate-source": "run-script-os",
"generate-source:win32": "rd lib/network && md lib/network && cd proto && grpc_tools_node_protoc --js_out=import_style=commonjs,binary:../lib/network/ --grpc_out=../lib/network/ common/*.proto language-agent/Tracing.proto management/*.proto",
"generate-source:darwin:linux": "rm -rf lib/network && mkdir -p lib/network && cd proto && grpc_tools_node_protoc --js_out=import_style=commonjs,binary:../lib/network/ --grpc_out=../lib/network/ common/*.proto language-agent/Tracing.proto management/*.proto",
"check": "eslint lib/ && eslint index.js"
"check": "eslint lib/ && eslint index.js",
"test": "mocha test/**/*.test.js --recursive"
},
"repository": {
"type": "git",
......@@ -46,14 +47,18 @@
"semver": "^5.5.1"
},
"devDependencies": {
"axios": "^0.19.2",
"cz-conventional-changelog": "^2.1.0",
"eslint": "^4.19.1",
"eslint-config-google": "^0.9.1",
"eslint-plugin-header": "^1.2.0",
"grpc-tools": "1.7.1",
"mocha": "^7.2.0",
"mysql": "^2.15.0",
"require-self": "^0.2.1",
"run-script-os": "^1.0.3"
"run-script-os": "^1.0.3",
"wait-until": "0.0.2",
"yaml": "^1.10.0"
},
"eslintIgnore": [
"lib/network/common/*.js",
......
#
# 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.
#
FROM openjdk:8
WORKDIR /tests
ARG COMMIT_HASH=3c9d7099f05dc4a4b937c8a47506e56c130b6221
ADD https://github.com/apache/skywalking-agent-test-tool/archive/${COMMIT_HASH}.tar.gz .
RUN tar -xf ${COMMIT_HASH}.tar.gz --strip 1
RUN rm ${COMMIT_HASH}.tar.gz
RUN ./mvnw -B -DskipTests package
FROM openjdk:8
EXPOSE 19876 12800
WORKDIR /tests
COPY --from=0 /tests/dist/skywalking-mock-collector.tar.gz /tests
RUN tar -xf skywalking-mock-collector.tar.gz --strip 1
RUN chmod +x bin/collector-startup.sh
ENTRYPOINT bin/collector-startup.sh
#
# 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:
base_collector:
build:
context: .
dockerfile: Dockerfile.tool
ports:
- 12800:12800
networks:
- beyond
healthcheck:
test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/12800"]
interval: 5s
timeout: 60s
retries: 120
networks:
beyond:
/*
* Licensed to the SkyAPM under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const wait = require("wait-until");
const {spawn, exec} = require("child_process");
const ifRebuild = process.env.BUILD !== "false";
module.exports = {
/**
* Spawn the containers defined in [composeFile].
*
* @param {string} composeFile
* @param {function(function(boolean)): void} ready readiness callback.
* @return {Promise<void>}
*/
setUp: (composeFile, ready) => new Promise((resolve, reject) => {
const options = ["--force-recreate", "-d"];
ifRebuild && options.push("--build");
const composeUp = spawn("docker-compose", ["-f", composeFile, "up", ...options]);
composeUp.stdout.on("data", (data) => process.stdout.write(data.toString()));
composeUp.stderr.on("data", (data) => process.stderr.write(data.toString()));
wait()
.times(200)
.interval(3000)
.condition(ready)
.done((result) => result ? resolve() : reject());
}),
/**
* Destroy the containers defined in [composeFile].
*
* @param {string} composeFile
* @return {Promise<void>}
*/
tearDown: (composeFile) => new Promise((resolve, reject) => {
exec(`docker-compose -f ${composeFile} down`, (err, stdout, stderr) => {
console.info(stdout);
console.error(stderr);
resolve();
});
}),
};
#
# 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.
#
FROM node:10
WORKDIR /agent
ADD . /agent
RUN npm i
WORKDIR /app
RUN npm init -y && npm i /agent
#
# 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:
collector:
extends:
file: ../../common/base-compose.yml
service: base_collector
networks:
- beyond
service-a:
build:
context: ../../..
dockerfile: test/plugins/http/Dockerfile.agent
ports:
- 3001:3001
networks:
- beyond
volumes:
- ./serverA.js:/app/index.js
environment:
SW_SERVICE_NAME: service-a
SW_INSTANCE_NAME: service-a
SW_DIRECT_SERVERS: collector:19876
DEBUG: skyapm:*
depends_on:
collector:
condition: service_healthy
service-b:
condition: service_healthy
entrypoint: ['node', '/app/index.js']
service-b:
build:
context: ../../..
dockerfile: test/plugins/http/Dockerfile.agent
networks:
- beyond
volumes:
- ./serverB.js:/app/index.js
environment:
SW_SERVICE_NAME: service-b
SW_INSTANCE_NAME: service-b
SW_DIRECT_SERVERS: collector:19876
DEBUG: skyapm:*
depends_on:
collector:
condition: service_healthy
entrypoint: ['node', '/app/index.js']
healthcheck:
test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/3000"]
interval: 5s
timeout: 60s
retries: 120
networks:
beyond:
#
# 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.
#
segmentItems:
- serviceName: service-a
segmentSize: 1
segments:
- segmentId: not null
spans:
- {operationName: /1, operationId: 0, parentSpanId: 0, spanId: 1, spanLayer: Http,
startTime: gt 0, endTime: gt 0, componentId: 2, isError: false,
spanType: Exit, peer: 'service-b:3000', skipAnalysis: false}
- {operationName: /test, operationId: 0, parentSpanId: -1, spanId: 0, spanLayer: Http,
startTime: gt 0, endTime: gt 0, componentId: 2, isError: false,
spanType: Entry, peer: '', skipAnalysis: false}
- serviceName: service-b
segmentSize: 1
segments:
- segmentId: not null
spans:
- operationName: /1
operationId: 0
parentSpanId: -1
spanId: 0
spanLayer: Http
startTime: gt 0
endTime: gt 0
componentId: 2
isError: false
spanType: Entry
peer: ''
skipAnalysis: false
refs:
- {parentEndpoint: /test, networkAddress: 'service-b:3000', refType: CrossProcess,
parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: c2VydmljZS1h,
parentService: c2VydmljZS1h, traceId: not null}
/*
* Licensed to the SkyAPM under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const axios = require("axios");
const assert = require("assert");
const path = require("path");
const yaml = require("yaml");
const fs = require("fs");
const wait = require("wait-until");
const {setUp, tearDown} = require("../../common");
const composeFile = path.resolve(__dirname, "docker-compose.yml");
const expectedFile = path.resolve(__dirname, "expected.data.yml");
describe("PluginTests", function() {
before(function() {
this.timeout(600000);
return setUp(composeFile, (ready) => {
axios.get("http://127.0.0.1:3001/test")
.then((res) => ready(res.status === 200))
.catch(() => ready(false));
});
});
after(function() {
this.timeout(120000);
return tearDown(composeFile);
});
it("plugin test for http", function(done) {
this.timeout(30000);
wait()
.times(10)
.interval(3000)
.condition((cb) => {
axios.get("http://127.0.0.1:12800/receiveData").then((res) => {
cb(yaml.parse(res.data).segmentItems.length === 2);
}).catch(() => cb(false));
})
.done((result) => {
if (!result) {
assert.fail("Received data is empty");
}
const content = fs.readFileSync(expectedFile, {encoding: "utf8", flag: "r"});
axios.post("http://127.0.0.1:12800/dataValidate", content).then((response) => {
if (response.status === 200) {
done();
} else {
done(new Error("Verification failed"));
}
}).catch((err) => done(err));
});
});
});
/*
* Licensed to the SkyAPM under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
require("skyapm-nodejs").start({});
const http = require("http");
const port = 3001;
callback = function(response) {
let str = "";
response.on("data", function(chunk) {
str += chunk;
});
response.on("end", function() {
console.log(str);
});
};
const requestHandler = (request, response) => {
let options = {
host: "service-b",
path: "/1",
port: "3000",
headers: {
custom: "Custom Header Demo works",
},
};
let req = http.request(options, callback);
req.end();
response.end("success");
};
const server = http.createServer(requestHandler);
server.listen(port, (err) => {
if (err) {
return console.log("something bad happened", err);
}
console.log(`server is listening on ${port}`);
});
/*
* Licensed to the SkyAPM under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
require("skyapm-nodejs").start({});
const http = require("http");
const port = 3000;
const requestHandler = (request, response) => {
response.end("test");
};
const serverB = http.createServer(requestHandler);
serverB.listen(port, (err) => {
if (err) {
return console.log("something bad happened", err);
}
console.log(`server is listening on ${port}`);
});
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册