Skip to content

chore: Add docker compose and test services scripts #2886

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .github/workflows/test-all-versions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,6 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- name: Set MySQL variables
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note for reviewer: this is required only for testing @opentelemetry/instrumentation-mysql and can be executed as a query using the client. Hence is removed from there and added in plugins/node/opentelemetry-instrumentation-mysql/test/mysql.test.ts

run: mysql --user=root --password=${MYSQL_ROOT_PASSWORD} --host=${MYSQL_HOST} --port=${MYSQL_PORT} -e "SET GLOBAL log_output='TABLE'; SET GLOBAL general_log = 1;" mysql
- name: Install
run: npm ci
- name: Download Build Artifacts
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/unit-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,6 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- name: Set MySQL variables
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note for reviewer: same here

run: mysql --user=root --password=${MYSQL_ROOT_PASSWORD} --host=${MYSQL_HOST} --port=${MYSQL_PORT} -e "SET GLOBAL log_output='TABLE'; SET GLOBAL general_log = 1;" mysql
- name: Install
run: npm ci
- name: Download Build Artifacts
Expand Down
28 changes: 28 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,34 @@ The conventional commit type (in PR title) is very important to automatically bu

There is no need to update the CHANGELOG in a PR because it will be updated as part of the release process (see [RELEASING.md](RELEASING.md) for more details).

### Testing

Most unit tests case be run via:

```sh
npm test
```

However, some instrumentations require test-services to be running (e.g. the `instrumentation-mongodb` package requires a MongoDB server). Use the `test-services`-related npm scripts to start all required services in Docker and then run the tests with the appropriate configuration to use those services:

```sh
npm run test-services:start # starts services in Docker
npm run test:with-services-config # runs 'npm test' with envvars from test/test-services.env
npm run test-services:stop # stops services in Docker
```

If you only want to test a sigle package (e.g. the `instrumentation-mongodb`) you can `cd` into it and run the tests after you started the services.

```sh
npm run test-services:start # starts services in Docker
cd plugins/node/opentelemetry-instrumentation-mongodb # get into the instrumenation folder
RUN_MONGODB_TESTS=1 npm test # run the test with the proper config (check each package)
cd ../../.. # go back to root folder
npm run test-services:stop # stops services in Docker
```

NOTE: scripts for each package will be added to avoid extra consumption of resources and improve the development experience.

### Benchmarks

When two or more approaches must be compared, please write a benchmark in the benchmark/index.js module so that we can keep track of the most efficient algorithm.
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
"test:browser": "nx run-many -t test:browser",
"test:ci:changed": "nx affected -t test --base=origin/main --head=HEAD",
"test-all-versions": "nx run-many -t test-all-versions",
"test-services:start": "docker compose -f ./test/docker-compose.yaml up -d --wait",
"test-services:stop": "docker compose -f ./test/docker-compose.yaml down",
"test:with-services-config": "NODE_OPTIONS='-r dotenv/config' DOTENV_CONFIG_PATH=./test/test-services.env npm test",
Comment on lines +22 to +24

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably use cross-env or something similar to not bail on Windows.

"changelog": "lerna-changelog",
"lint": "nx run-many -t lint && npm run lint:deps && npm run lint:readme && npm run lint:markdown && npm run lint:semconv-deps",
"lint:fix": "nx run-many -t lint:fix && npm run lint:markdown:fix",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import {
} from '@opentelemetry/sdk-metrics';
import * as assert from 'assert';
import { MySQLInstrumentation } from '../src';
import * as testUtils from '@opentelemetry/contrib-test-utils';
import { registerInstrumentationTesting } from '@opentelemetry/contrib-test-utils';

const instrumentation = registerInstrumentationTesting(
Expand Down Expand Up @@ -62,9 +61,9 @@ import * as mysqlTypes from 'mysql';
describe('mysql@2.x-Metrics', () => {
let otelTestingMeterProvider;
let inMemoryMetricsExporter: InMemoryMetricExporter;
const testMysql = process.env.RUN_MYSQL_TESTS; // For CI: assumes local mysql db is already available
const testMysqlLocally = process.env.RUN_MYSQL_TESTS_LOCAL; // For local: spins up local mysql db via docker
const shouldTest = testMysql || testMysqlLocally; // Skips these tests if false (default)
// assumes local mysql db is already available in CI or
// using `npm run test-services:start` script at the root folder
const shouldTest = process.env.RUN_MYSQL_TESTS;

function initMeterProvider() {
inMemoryMetricsExporter = new InMemoryMetricExporter(
Expand All @@ -90,22 +89,7 @@ describe('mysql@2.x-Metrics', () => {
console.log('Skipping test-mysql for metrics.');
this.skip();
}

if (testMysqlLocally) {
testUtils.startDocker('mysql');
// wait 15 seconds for docker container to start
this.timeout(20000);
setTimeout(done, 15000);
} else {
done();
}
});

after(function () {
if (testMysqlLocally) {
this.timeout(5000);
testUtils.cleanUpDocker('mysql');
}
done();
});

describe('#Pool - metrics', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import {
SEMATTRS_NET_PEER_NAME,
SEMATTRS_NET_PEER_PORT,
} from '@opentelemetry/semantic-conventions';
import * as testUtils from '@opentelemetry/contrib-test-utils';
import {
BasicTracerProvider,
InMemorySpanExporter,
Expand Down Expand Up @@ -54,9 +53,9 @@ describe('mysql@2.x-Tracing', () => {
let connection: mysqlTypes.Connection;
let pool: mysqlTypes.Pool;
let poolCluster: mysqlTypes.PoolCluster;
const testMysql = process.env.RUN_MYSQL_TESTS; // For CI: assumes local mysql db is already available
const testMysqlLocally = process.env.RUN_MYSQL_TESTS_LOCAL; // For local: spins up local mysql db via docker
const shouldTest = testMysql || testMysqlLocally; // Skips these tests if false (default)
// assumes local mysql db is already available in CI or
// using `npm run test-services:start` script at the root folder
const shouldTest = process.env.RUN_MYSQL_TESTS; // For CI: assumes local mysql db is already available
const memoryExporter = new InMemorySpanExporter();
const provider = new BasicTracerProvider({
spanProcessors: [new SimpleSpanProcessor(memoryExporter)],
Expand All @@ -68,25 +67,11 @@ describe('mysql@2.x-Tracing', () => {
// https://github.com/mochajs/mocha/issues/2683#issuecomment-375629901
this.test!.parent!.pending = true;
this.skip();
}

if (testMysqlLocally) {
testUtils.startDocker('mysql');
// wait 15 seconds for docker container to start
this.timeout(20000);
setTimeout(done, 15000);
} else {
done();
}
});

after(function () {
if (testMysqlLocally) {
this.timeout(5000);
testUtils.cleanUpDocker('mysql');
}
});

beforeEach(() => {
instrumentation.disable();
contextManager = new AsyncLocalStorageContextManager().enable();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,28 +64,38 @@ interface Result extends RowDataPacket {
solution: number;
}

describe('mysql2', () => {
const testMysql = process.env.RUN_MYSQL_TESTS; // For CI: assumes local mysql db is already available
const testMysqlLocally = process.env.RUN_MYSQL_TESTS_LOCAL; // For local: spins up local mysql db via docker
const shouldTest = testMysql || testMysqlLocally; // Skips these tests if false (default)

before(function (done) {
if (testMysqlLocally) {
testUtils.startDocker('mysql');
// wait 15 seconds for docker container to start
this.timeout(20000);
setTimeout(done, 15000);
} else {
done();
}
// Helper function to setup the database
const execPromise = (conn: Connection, command: string) => {
return new Promise<void>((res, rej) => {
conn.execute(command, err => {
if (err) rej(err);
else res();
});
});
};

after(function (done) {
if (testMysqlLocally) {
this.timeout(5000);
testUtils.cleanUpDocker('mysql');
describe('mysql2', () => {
// assumes local mysql db is already available in CI or
// using `npm run test-services:start` script at the root folder
const shouldTest = process.env.RUN_MYSQL_TESTS;

before(async function () {
const connection = createConnection({
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note for reviewer: enabling here the general_log setting instead of doing it in the workflows

port,
user: 'root',
host,
password: rootPassword,
database,
});
try {
await execPromise(connection, "SET GLOBAL log_output='TABLE'");
await execPromise(connection, 'SET GLOBAL general_log = 1');
} catch (execErr) {
console.error('MySQL seup error: ', execErr);
this.skip();
} finally {
connection.end();
}
done();
});

describe('callback API', () => {
Expand Down Expand Up @@ -358,7 +368,7 @@ describe('mysql2', () => {
it('should not add comment by default', done => {
const span = provider.getTracer('default').startSpan('test span');
context.with(trace.setSpan(context.active(), span), () => {
connection.query('SELECT 1+1 as solution', () => {
connection.query('SELECT 1+1 as solution', (e, r) => {
const spans = memoryExporter.getFinishedSpans();
assert.strictEqual(spans.length, 1);
getLastQueries(1).then(([query]) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const memoryExporter = new InMemorySpanExporter();
const CONFIG = {
user: process.env.POSTGRES_USER || 'postgres',
password: process.env.POSTGRES_PASSWORD || 'postgres',
database: process.env.POSTGRES_DB || 'postgres',
database: process.env.POSTGRES_DB || 'otel_pg_database',
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note for reviewer: align the value with the one in workflow services

host: process.env.POSTGRES_HOST || 'localhost',
port: process.env.POSTGRES_PORT
? parseInt(process.env.POSTGRES_PORT, 10)
Expand Down
119 changes: 119 additions & 0 deletions test/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# A `docker compose` config file to run tests services for testing
# `@opentelemetry/instrumentation-*` locally.
#
# Note: This isn't used in CI. CI uses GitHub Actions' `services: ...` for
# defining test services.
#
# Usage:
# npm run test-services:start [services...]
# npm run test-services:stop [services...]

name: opentelemetry-nodejs-test-services

services:
cassandra:
image: cassandra:3
environment:
MAX_HEAP_SIZE: "1G"
HEAP_NEWSIZE: 400m
ports:
- "9042:9042"
healthcheck:
test: ["CMD-SHELL", "[ $$(nodetool statusgossip) = running ]"]
interval: 1s
timeout: 10s
retries: 30

memcached:
image: memcached:1.6.37-alpine
ports:
- 11211:11211
mongodb:
image: mongo:7
ports:
- "27017:27017"
healthcheck:
test: ["CMD", "mongosh", "--eval", "db.runCommand('ping').ok", "--quiet"]
interval: 1s
timeout: 10s
retries: 30

mssql:
# Tags listed at https://hub.docker.com/r/microsoft/mssql-server
# Docs: https://learn.microsoft.com/en-us/sql/linux/quickstart-install-connect-docker
image: mcr.microsoft.com/mssql/server:2022-CU14-ubuntu-22.04
platform: linux/amd64
environment:
- ACCEPT_EULA=Y
- MSSQL_SA_PASSWORD=mssql_passw0rd
ports:
- "1433:1433"
healthcheck:
test: ["CMD", "/opt/mssql-tools18/bin/sqlcmd", "-C", "-S", "mssql", "-U", "sa", "-P", "mssql_passw0rd", "-Q", "select 1"]
interval: 1s
timeout: 10s
retries: 30

mysql:
image: mysql:5.7
# No ARM64 image layer. See https://stackoverflow.com/a/65592942
platform: linux/x86_64
environment:
MYSQL_USER: "otel"
MYSQL_PASSWORD: "secret"
MYSQL_DATABASE: "otel_mysql_database"
MYSQL_ROOT_PASSWORD: "rootpw"
ports:
- "33306:3306"
healthcheck:
test: ["CMD", "mysqladmin" ,"ping"]
interval: 1s
timeout: 10s
retries: 30

oracledb:
image: gvenzl/oracle-free:slim
environment:
APP_USER: "otel"
APP_USER_PASSWORD: "secret"
ORACLE_PASSWORD: "oracle"
ports:
- 1521:1521
healthcheck:
test: ["CMD", "sqlplus" ,"system/oracle@//localhost/FREEPDB1"]
interval: 10s
timeout: 5s
retries: 30
postgres:
# https://github.com/docker-library/docs/blob/master/postgres/README.md#how-to-extend-this-image
image: postgres:16-alpine
ports:
- "54320:5432"
environment:
POSTGRES_HOST_AUTH_METHOD: "trust"
POSTGRES_USER: "postgres"
POSTGRES_DB: "otel_pg_database"
POSTGRES_PASSWORD: "postgres"
healthcheck:
test: ["CMD", "pg_isready"]
interval: 1s
timeout: 10s
retries: 30

rabbitmq:
image: rabbitmq:3
environment:
RABBITMQ_DEFAULT_USER: "username"
RABBITMQ_DEFAULT_PASS: "password"
ports:
- "22221:5672"

redis:
image: redis:7
ports:
- "6379:6379"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 1s
timeout: 10s
retries: 30
35 changes: 35 additions & 0 deletions test/test-services.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
RUN_CASSANDRA_TESTS=1
RUN_MEMCACHED_TESTS=1
RUN_MONGODB_TESTS=1
RUN_MYSQL_TESTS=1
RUN_MSSQL_TESTS=1
RUN_ORACLEDB_TESTS=1
RUN_POSTGRES_TESTS=1
RUN_REDIS_TESTS=1
RUN_RABBIT_TESTS=1
CASSANDRA_HOST=localhost
MONGODB_DB=opentelemetry-tests
MONGODB_HOST=127.0.0.1
MONGODB_PORT=27017
MSSQL_PASSWORD=mssql_passw0rd
MYSQL_DATABASE=otel_mysql_database
MYSQL_HOST=127.0.0.1
MYSQL_PASSWORD=secret
MYSQL_ROOT_PASSWORD=rootpw
MYSQL_PORT=33306
MYSQL_USER=otel
OPENTELEMETRY_MEMCACHED_HOST=localhost
OPENTELEMETRY_MEMCACHED_PORT=11211
OPENTELEMETRY_REDIS_HOST=localhost
OPENTELEMETRY_REDIS_PORT=6379
ORACLE_HOSTNAME=localhost
ORACLE_PORT=1521
ORACLE_CONNECTSTRING=localhost:1521/freepdb1
ORACLE_USER=otel
ORACLE_PASSWORD=secret
ORACLE_SERVICENAME=FREEPDB1
POSTGRES_DB=otel_pg_database
POSTGRES_HOST=localhost
POSTGRES_PORT=54320
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres