Skip to content

Commit d13f53a

Browse files
authored
test(api7): deploy test components by docker compose (#429)
1 parent b3ef55f commit d13f53a

6 files changed

Lines changed: 143 additions & 51 deletions

File tree

.github/workflows/e2e.yaml

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,16 +115,46 @@ jobs:
115115
api7:
116116
runs-on: ubuntu-latest
117117
if: contains(github.event.pull_request.labels.*.name, 'test/api7') || github.event_name == 'push'
118+
permissions:
119+
contents: read
120+
packages: read
118121
strategy:
122+
fail-fast: false
119123
matrix:
120-
version: [3.5.5, 3.6.1, 3.7.8, 3.8.22, 3.9.2]
121-
env:
122-
BACKEND_API7_VERSION: ${{ matrix.version }}
123-
BACKEND_API7_DOWNLOAD_URL: https://run.api7.ai/api7-ee/api7-ee-v${{ matrix.version }}.tar.gz
124-
BACKEND_API7_LICENSE: ${{ secrets.BACKEND_API7_LICENSE }}
124+
version: [3.5.5, 3.6.1, 3.7.8, 3.8.23, 3.9.9, dev]
125125
steps:
126+
- name: Determine API7 image and license
127+
run: |
128+
if [ "${{ matrix.version }}" = "dev" ]; then
129+
echo "BACKEND_API7_VERSION=999.999.999" >> $GITHUB_ENV
130+
echo "API7_DASHBOARD_IMAGE=ghcr.io/api7/api7-ee-3-integrated" >> $GITHUB_ENV
131+
echo "API7_IMAGE_TAG=dev" >> $GITHUB_ENV
132+
{
133+
echo "BACKEND_API7_LICENSE<<EOLICENSE"
134+
echo "${{ secrets.BACKEND_API7_DEV_LICENSE }}"
135+
echo "EOLICENSE"
136+
} >> $GITHUB_ENV
137+
else
138+
echo "BACKEND_API7_VERSION=${{ matrix.version }}" >> $GITHUB_ENV
139+
echo "API7_DASHBOARD_IMAGE=api7/api7-ee-3-integrated" >> $GITHUB_ENV
140+
echo "API7_IMAGE_TAG=v${{ matrix.version }}" >> $GITHUB_ENV
141+
{
142+
echo "BACKEND_API7_LICENSE<<EOLICENSE"
143+
echo "${{ secrets.BACKEND_API7_LICENSE }}"
144+
echo "EOLICENSE"
145+
} >> $GITHUB_ENV
146+
fi
147+
126148
- uses: actions/checkout@v4
127149

150+
- name: Login to GHCR
151+
if: matrix.version == 'dev'
152+
uses: docker/login-action@v3
153+
with:
154+
registry: ghcr.io
155+
username: ${{ github.actor }}
156+
password: ${{ secrets.GITHUB_TOKEN }}
157+
128158
# Build and test ADC CLI
129159
- uses: actions/setup-node@v4
130160
with:
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
server:
2+
listen:
3+
disable: true
4+
tls:
5+
host: "0.0.0.0"
6+
port: 7443
7+
log:
8+
level: warn
9+
output: stderr
10+
database:
11+
dsn: "postgres://api7ee:changeme@postgresql:5432/api7ee"
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
services:
2+
postgresql:
3+
image: api7/postgresql:15.4.0-debian-11-r45
4+
environment:
5+
POSTGRESQL_USERNAME: api7ee
6+
POSTGRESQL_PASSWORD: changeme
7+
POSTGRESQL_DATABASE: api7ee
8+
healthcheck:
9+
test: ["CMD", "pg_isready", "-U", "api7ee", "-d", "api7ee"]
10+
interval: 5s
11+
retries: 10
12+
networks:
13+
api7:
14+
15+
dashboard:
16+
image: ${API7_DASHBOARD_IMAGE:-api7/api7-ee-3-integrated}:${API7_IMAGE_TAG:-dev}
17+
depends_on:
18+
postgresql:
19+
condition: service_healthy
20+
volumes:
21+
- ./dashboard-conf.yaml:/app/conf/config.yaml:ro
22+
ports:
23+
- "7443:7443"
24+
networks:
25+
api7:
26+
27+
networks:
28+
api7:
29+
driver: bridge

libs/backend-api7/e2e/support/global-setup.ts

Lines changed: 68 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import axios from 'axios';
2-
import { spawn, spawnSync } from 'node:child_process';
2+
import { spawnSync } from 'node:child_process';
33
import { randomUUID } from 'node:crypto';
4-
import { readFileSync, writeFileSync } from 'node:fs';
4+
import { resolve as pathResolve } from 'node:path';
55
import { Agent } from 'node:https';
66
import * as semver from 'semver';
77

@@ -31,49 +31,65 @@ httpClient.interceptors.request.use(
3131
(error) => Promise.reject(error),
3232
);
3333

34-
const setupAPI7 = async () => {
35-
return new Promise<void>((resolve, reject) => {
36-
const download = spawnSync(
37-
'sh',
38-
[
39-
'-c',
40-
`curl -O ${process.env.BACKEND_API7_DOWNLOAD_URL} && tar xf api7-ee-*.tar.gz`,
41-
],
42-
{ cwd: `/tmp` },
43-
);
34+
const assetsDir = pathResolve(__dirname, '../../e2e/assets');
35+
36+
const waitForDashboard = async (
37+
maxRetries = 60,
38+
intervalMs = 2000,
39+
): Promise<void> => {
40+
console.log('Waiting for Dashboard to be ready...');
41+
for (let i = 0; i < maxRetries; i++) {
42+
try {
43+
await httpClient.get('/api/status', { timeout: 2000 });
44+
console.log('Dashboard is ready');
45+
return;
46+
} catch {
47+
// Also try login endpoint as a fallback health check
48+
try {
49+
await httpClient.post(
50+
'/api/login',
51+
{ username: '', password: '' },
52+
{ timeout: 2000, validateStatus: () => true },
53+
);
54+
console.log('Dashboard is ready');
55+
return;
56+
} catch {
57+
// not ready yet
58+
}
59+
}
60+
await new Promise((r) => setTimeout(r, intervalMs));
61+
}
62+
throw new Error(
63+
`Dashboard not ready after ${maxRetries * intervalMs / 1000}s`,
64+
);
65+
};
4466

45-
console.log('stdout: ' + download.stdout.toString('utf-8'));
46-
console.log('stderr: ' + download.stderr.toString('utf-8'));
67+
const runCompose = (...args: string[]) => {
68+
const result = spawnSync('docker', ['compose', ...args], {
69+
cwd: assetsDir,
70+
stdio: 'inherit',
71+
});
4772

48-
const dockerComposePath = `/tmp/api7-ee/docker-compose.yaml`;
49-
const dockerCompose = readFileSync(dockerComposePath, 'utf-8').replaceAll(
50-
': bitnami/',
51-
': bitnamilegacy/',
73+
if (result.error) throw result.error;
74+
if (result.signal)
75+
throw new Error(
76+
`docker compose ${args.join(' ')} terminated by signal ${result.signal}`,
5277
);
53-
writeFileSync(dockerComposePath, dockerCompose, 'utf-8');
54-
55-
const setup = spawn('sh', ['-c', `cd api7-ee && bash run.sh start`], {
56-
cwd: `/tmp`,
57-
});
58-
59-
console.log('\nSetup API7 Instance\n');
60-
61-
setup.stdout.on('data', function (data) {
62-
console.log('stdout: ' + data.toString());
63-
});
64-
65-
setup.stderr.on('data', function (data) {
66-
console.log('stderr: ' + data.toString());
67-
});
78+
if (result.status !== 0)
79+
throw new Error(
80+
`docker compose ${args.join(' ')} failed with code ${result.status}`,
81+
);
82+
};
6883

69-
setup.on('exit', function (code) {
70-
if (code)
71-
reject(`child process exited with non-zero code: ${code?.toString()}`);
84+
const setupAPI7 = async () => {
85+
console.log('\nSetup API7 Instance via Docker Compose\n');
86+
if (process.env.API7_IMAGE_TAG === 'dev') {
87+
runCompose('pull');
88+
}
89+
runCompose('up', '-d');
7290

73-
console.log('Successful deployment');
74-
resolve();
75-
});
76-
});
91+
console.log('Docker Compose started');
92+
await waitForDashboard();
7793
};
7894

7995
const initUser = async (
@@ -162,7 +178,7 @@ const generateToken = async () => {
162178
process.env.TOKEN = resp.data.value.token;
163179
};
164180

165-
export default async () => {
181+
export async function setup() {
166182
if (process.env['SKIP_API7_SETUP'] !== 'true') await setupAPI7();
167183
try {
168184
await initUser();
@@ -181,4 +197,14 @@ export default async () => {
181197
process.env.GATEWAY_GROUP = 'adc';
182198
process.env.BACKEND_API7_VERSION = '0.0.0'; */
183199
// ONLY FOR LOCAL TEST //
184-
};
200+
}
201+
202+
export async function teardown() {
203+
if (process.env['SKIP_API7_SETUP'] === 'true') return;
204+
console.log('Tearing down API7 via Docker Compose');
205+
try {
206+
runCompose('down', '-v');
207+
} catch (err) {
208+
console.error('Teardown failed:', err);
209+
}
210+
}

libs/backend-api7/e2e/support/global-teardown.ts

Lines changed: 0 additions & 3 deletions
This file was deleted.

libs/backend-api7/e2e/sync-and-dump-2.e2e-spec.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@ describe('Sync and Dump - 2', () => {
8686
timeout: 1,
8787
concurrency: 10,
8888
http_path: '/',
89-
https_verify_certificate: true,
9089
healthy: {
9190
interval: 1,
9291
http_statuses: [200, 302],

0 commit comments

Comments
 (0)