test: add regression scenario coverage
This commit is contained in:
+7
-6
@@ -6,9 +6,10 @@
|
||||
"description": "Whole-system integration scenarios for serve.zone components.",
|
||||
"scripts": {
|
||||
"bootstrap:components": "pnpm --dir ../interfaces install && pnpm --dir ../api install && pnpm --dir ../cloudly install && pnpm --dir ../corebuild install && pnpm --dir ../coreflow install && pnpm --dir ../coretraffic install && pnpm --dir ../corestore install && pnpm --dir ../onebox exec deno install --config deno.json && deno cache --config ../isocreator/deno.json ../isocreator/mod.ts && deno cache --config ../baseos/deno.json ../baseos/mod.ts && pnpm install",
|
||||
"test": "pnpm scenario:corestore-volume-driver && pnpm scenario:registry-deploy-on-push && pnpm scenario:onebox-basic-lifecycle && pnpm scenario:onebox-backup-restore",
|
||||
"test": "pnpm scenario:codebase-regressions && pnpm scenario:corestore-volume-driver && pnpm scenario:registry-deploy-on-push && pnpm scenario:onebox-basic-lifecycle && pnpm scenario:onebox-backup-restore",
|
||||
"test:full": "pnpm test && pnpm scenario:onebox-cloudly-appstore-worker",
|
||||
"scenario:corestore-volume-driver": "tsx scenarios/corestore-volume-driver/scenario.ts",
|
||||
"scenario:codebase-regressions": "tsx scenarios/codebase-regressions/scenario.ts",
|
||||
"scenario:registry-deploy-on-push": "tsx --tsconfig ../cloudly/tsconfig.json scenarios/registry-deploy-on-push/scenario.ts",
|
||||
"scenario:baseos-image-pipeline": "tsx --tsconfig ../corebuild/tsconfig.json scenarios/baseos-image-pipeline/scenario.ts",
|
||||
"scenario:baseos-qemu-enrollment": "tsx scenarios/baseos-qemu-enrollment/scenario.ts",
|
||||
@@ -22,11 +23,11 @@
|
||||
"vagrant:destroy": "vagrant destroy -f"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@git.zone/tstest": "2.3.8",
|
||||
"@push.rocks/smartnetwork": "4.4.0",
|
||||
"@types/node": "^22.0.0",
|
||||
"tsx": "^4.20.6",
|
||||
"typescript": "^5.9.3"
|
||||
"@git.zone/tstest": "3.6.3",
|
||||
"@push.rocks/smartnetwork": "4.7.1",
|
||||
"@types/node": "^25.6.2",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "^6.0.3"
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
|
||||
Generated
+685
-2656
File diff suppressed because it is too large
Load Diff
@@ -14,6 +14,7 @@ Component repositories keep their fast unit and package tests locally. This repo
|
||||
|
||||
| Scenario | Command | What it validates |
|
||||
| --- | --- | --- |
|
||||
| `codebase-regressions` | `pnpm scenario:codebase-regressions` | Static cross-package regression checks for security defaults, registry/image stream fixes, package metadata, and ContainerArchive prune safety. |
|
||||
| `corestore-volume-driver` | `pnpm scenario:corestore-volume-driver` | Corestore control API, Docker VolumeDriver protocol, snapshot/restore through `containerarchive`, Coreflow volume mount generation, backup/restore orchestration, and remote replication against a fake Cloudly API. |
|
||||
| `registry-deploy-on-push` | `pnpm scenario:registry-deploy-on-push` | Cloudly startup with isolated Mongo/S3 helpers, cluster and registry setup, Docker image push, Cloudly metadata update, Coreflow workload provisioning, Coretraffic HTTPS routing, and same-tag redeploy after digest changes. |
|
||||
| `onebox-basic-lifecycle` | `pnpm scenario:onebox-basic-lifecycle` | Onebox startup in dev mode, SmartProxy ingress, local registry status, workload deploy/remove, HTTP route check, and HTTPS route check with a temporary certificate. |
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import assert from 'assert/strict';
|
||||
import { readFileSync } from 'fs';
|
||||
import { dirname, resolve } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const root = resolve(dirname(fileURLToPath(import.meta.url)), '../../..');
|
||||
|
||||
const readWorkspaceFile = (pathFromRoot: string) => {
|
||||
return readFileSync(resolve(root, pathFromRoot), 'utf8');
|
||||
};
|
||||
|
||||
const assertIncludes = (filePath: string, expected: string) => {
|
||||
const content = readWorkspaceFile(filePath);
|
||||
assert.ok(content.includes(expected), `${filePath} should include ${expected}`);
|
||||
};
|
||||
|
||||
const assertExcludes = (filePath: string, forbidden: string) => {
|
||||
const content = readWorkspaceFile(filePath);
|
||||
assert.ok(!content.includes(forbidden), `${filePath} should not include ${forbidden}`);
|
||||
};
|
||||
|
||||
assertIncludes('containerarchive/rust/src/prune.rs', 'rewritten_offsets');
|
||||
assertExcludes('containerarchive/rust/src/prune.rs', 'offset: entry.offset, // Note: offset in the new pack may differ');
|
||||
assertIncludes('containerarchive/test/test.ts', 'partial-pack GC rewrites chunks');
|
||||
|
||||
assertExcludes('onebox/ts/classes/onebox.ts', "hashPassword('admin')");
|
||||
assertIncludes('onebox/ts/classes/onebox.ts', 'ONEBOX_ADMIN_PASSWORD');
|
||||
assertExcludes('dcrouter/ts/opsserver/handlers/admin.handler.ts', "password: 'admin'");
|
||||
assertIncludes('dcrouter/ts/opsserver/handlers/admin.handler.ts', 'DCROUTER_ADMIN_PASSWORD');
|
||||
assertExcludes('corerender/ts/rendertron.db.ts', 'wxW4LBa3sxPjyXGf');
|
||||
assertExcludes('corerender/ts/rendertron.db.ts', 'losslessone-main.zee8suk.mongodb.net');
|
||||
|
||||
assertIncludes('api/ts/classes.cloudlyapiclient.ts', 'return response;');
|
||||
assertIncludes('api/ts/classes.image.ts', 'versionString: imageVersion');
|
||||
assertIncludes('cloudly/ts/manager.image/classes.imagemanager.ts', 'readFromWebstream(readable)');
|
||||
assertIncludes('cloudly/ts/manager.service/classes.servicemanager.ts', 'adminIdentityGuard');
|
||||
assertIncludes('cloudly/ts/manager.cluster/classes.clustermanager.ts', 'getClusterById');
|
||||
|
||||
assertIncludes('platformclient/ts/email/classes.emailconnector.ts', 'return response.responseId');
|
||||
assertIncludes('platformclient/ts/email/classes.letterconnector.ts', 'return response.processId');
|
||||
assertIncludes('platformclient/ts/email/classes.pushnotificationconnector.ts', "typeof process !== 'undefined'");
|
||||
|
||||
assertIncludes('siprouter/ts/sipproxy.ts', 'return { id: callId };');
|
||||
assertIncludes('onebox/ts/opsserver/classes.opsserver.ts', "'/v2/*'");
|
||||
assertIncludes('spark/ts/spark.cli.ts', 'CLOUDLY_URL: cloudlyUrl');
|
||||
|
||||
const rustdeskConfig = JSON.parse(readWorkspaceFile('appstore-apptemplates/apps/rustdesk-server/versions/1.0.0/config.json'));
|
||||
const relay = rustdeskConfig.envVars.find((entry: { key: string }) => entry.key === 'RELAY');
|
||||
assert.equal(relay?.value, '${SERVICE_DOMAIN}:21116');
|
||||
|
||||
console.log('codebase regression checks passed');
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as http from 'node:http';
|
||||
import * as net from 'node:net';
|
||||
import * as crypto from 'node:crypto';
|
||||
import { access, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { dirname, join } from 'node:path';
|
||||
@@ -120,6 +121,10 @@ const pathExists = async (pathArg: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
const sha256Base64 = (base64Arg: string) => {
|
||||
return crypto.createHash('sha256').update(Buffer.from(base64Arg, 'base64')).digest('hex');
|
||||
};
|
||||
|
||||
const assertCoreflowVolumeMounts = () => {
|
||||
const coreflow = new Coreflow();
|
||||
const service: IService = {
|
||||
@@ -439,12 +444,207 @@ const assertCoreflowBackupOrchestration = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
const assertCoreflowRemoteReplication = async () => {
|
||||
const socketPath = join(buildDir, 'replication-plugins', 'corestore.sock');
|
||||
const dataDir = join(buildDir, 'replication-data');
|
||||
await mkdir(dirname(socketPath), { recursive: true });
|
||||
|
||||
const controlPort = await getFreePort();
|
||||
const s3Port = await getFreePort();
|
||||
const dbPort = await getFreePort();
|
||||
let corestore = new CoreStore({
|
||||
dataDir,
|
||||
bindAddress: '127.0.0.1',
|
||||
publicHost: '127.0.0.1',
|
||||
controlPort,
|
||||
s3Port,
|
||||
dbPort,
|
||||
apiToken,
|
||||
volumePluginSocketPath: socketPath,
|
||||
});
|
||||
const previousControlUrl = process.env.CORESTORE_CONTROL_URL;
|
||||
const previousApiToken = process.env.CORESTORE_API_TOKEN;
|
||||
|
||||
try {
|
||||
await corestore.start();
|
||||
process.env.CORESTORE_CONTROL_URL = `http://127.0.0.1:${controlPort}`;
|
||||
process.env.CORESTORE_API_TOKEN = apiToken;
|
||||
|
||||
const volumeName = 'remote-replication-data';
|
||||
const service: IService = {
|
||||
id: 'svc-remote-replication',
|
||||
data: {
|
||||
name: 'remote-replication',
|
||||
description: 'Coreflow remote replication service',
|
||||
imageId: 'image-remote-replication',
|
||||
imageVersion: 'latest',
|
||||
environment: {},
|
||||
secretBundleId: 'secret-remote-replication',
|
||||
serviceCategory: 'workload',
|
||||
deploymentStrategy: 'custom',
|
||||
scaleFactor: 1,
|
||||
balancingStrategy: 'round-robin',
|
||||
ports: {
|
||||
web: 80,
|
||||
},
|
||||
volumes: [
|
||||
{
|
||||
source: volumeName,
|
||||
mountPath: '/data',
|
||||
backup: true,
|
||||
},
|
||||
],
|
||||
domains: [],
|
||||
deploymentIds: [],
|
||||
},
|
||||
};
|
||||
|
||||
const createVolumeResponse = await controlPost<any>(controlPort, '/volumes/create', {
|
||||
name: volumeName,
|
||||
serviceId: service.id,
|
||||
serviceName: service.data.name,
|
||||
mountPath: '/data',
|
||||
backup: true,
|
||||
});
|
||||
const mountpoint = createVolumeResponse.volume?.Mountpoint;
|
||||
assert(typeof mountpoint === 'string', `Could not create replication test volume: ${JSON.stringify(createVolumeResponse)}`);
|
||||
await writeFile(join(mountpoint, 'state.txt'), 'before remote backup\n');
|
||||
|
||||
const remoteObjects = new Map<string, { object: any; contentsBase64: string }>();
|
||||
let remoteManifest: any;
|
||||
const coreflow = new Coreflow();
|
||||
const fakeCloudlyApiClient = {
|
||||
platform: {
|
||||
getPlatformDesiredState: async () => ({ capabilities: [], providerConfigs: [], bindings: [] }),
|
||||
},
|
||||
services: {
|
||||
getServices: async () => [service],
|
||||
},
|
||||
typedsocketClient: {
|
||||
createTypedRequest: (methodArg: string) => ({
|
||||
fire: async (payloadArg: any) => {
|
||||
if (methodArg === 'prepareBackupReplication') {
|
||||
remoteManifest = payloadArg.manifest;
|
||||
return {
|
||||
missingObjects: payloadArg.manifest.objects.filter((objectArg: any) => {
|
||||
const existing = remoteObjects.get(objectArg.path);
|
||||
return !existing || existing.object.sha256 !== objectArg.sha256;
|
||||
}),
|
||||
};
|
||||
}
|
||||
if (methodArg === 'uploadBackupArchiveObject') {
|
||||
assert(payloadArg.object.sha256 === sha256Base64(payloadArg.contentsBase64), `Remote upload checksum mismatch for ${payloadArg.object.path}`);
|
||||
remoteObjects.set(payloadArg.object.path, {
|
||||
object: payloadArg.object,
|
||||
contentsBase64: payloadArg.contentsBase64,
|
||||
});
|
||||
return { accepted: true };
|
||||
}
|
||||
if (methodArg === 'completeBackupReplication') {
|
||||
remoteManifest = payloadArg.manifest;
|
||||
for (const object of remoteManifest.objects) {
|
||||
assert(remoteObjects.has(object.path), `Missing replicated object ${object.path}`);
|
||||
}
|
||||
const manifestBody = JSON.stringify(remoteManifest);
|
||||
return {
|
||||
replication: {
|
||||
targetType: 's3',
|
||||
targetPath: 'fake-target',
|
||||
manifestPath: 'fake-target/manifest.json',
|
||||
manifestSha256: crypto.createHash('sha256').update(manifestBody).digest('hex'),
|
||||
objectCount: remoteManifest.objects.length,
|
||||
totalSize: remoteManifest.totalSize,
|
||||
completedAt: Date.now(),
|
||||
},
|
||||
};
|
||||
}
|
||||
if (methodArg === 'getBackupArchiveManifest') {
|
||||
return { manifest: remoteManifest };
|
||||
}
|
||||
if (methodArg === 'downloadBackupArchiveObject') {
|
||||
const remoteObject = remoteObjects.get(payloadArg.object.path);
|
||||
assert(remoteObject, `Remote object ${payloadArg.object.path} not found`);
|
||||
return remoteObject;
|
||||
}
|
||||
throw new Error(`Unexpected fake Cloudly request ${methodArg}`);
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
(coreflow.cloudlyConnector as any).cloudlyApiClient = fakeCloudlyApiClient;
|
||||
(coreflow.cloudlyConnector as any).identity = {
|
||||
name: 'replication-test-coreflow',
|
||||
role: 'cluster',
|
||||
type: 'machine',
|
||||
userId: 'cluster-remote-replication',
|
||||
expiresAt: Date.now() + 3600 * 1000,
|
||||
jwt: '',
|
||||
clusterId: 'cluster-remote-replication',
|
||||
};
|
||||
|
||||
const backupResult = await coreflow.backupManager.executeServiceBackup({
|
||||
backupId: 'remote-replication-smoke',
|
||||
service,
|
||||
replication: {
|
||||
enabled: true,
|
||||
},
|
||||
tags: {
|
||||
scenario: scenarioName,
|
||||
},
|
||||
});
|
||||
assert(backupResult.replication?.objectCount === remoteObjects.size, `Unexpected replication result: ${JSON.stringify(backupResult.replication)}`);
|
||||
assert(remoteObjects.size > 0, 'Remote replication did not upload archive objects');
|
||||
|
||||
await corestore.stop();
|
||||
await rm(join(dataDir, 'volume-archive'), { recursive: true, force: true });
|
||||
await writeFile(join(mountpoint, 'state.txt'), 'after local archive loss\n');
|
||||
|
||||
corestore = new CoreStore({
|
||||
dataDir,
|
||||
bindAddress: '127.0.0.1',
|
||||
publicHost: '127.0.0.1',
|
||||
controlPort,
|
||||
s3Port,
|
||||
dbPort,
|
||||
apiToken,
|
||||
volumePluginSocketPath: socketPath,
|
||||
});
|
||||
await corestore.start();
|
||||
|
||||
await coreflow.backupManager.executeServiceRestore({
|
||||
backupId: 'remote-replication-smoke',
|
||||
service,
|
||||
snapshots: backupResult.snapshots,
|
||||
clear: true,
|
||||
replication: {
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
assert(await readFile(join(mountpoint, 'state.txt'), 'utf8') === 'before remote backup\n', 'Remote archive restore did not restore volume data');
|
||||
} finally {
|
||||
if (previousControlUrl === undefined) {
|
||||
delete process.env.CORESTORE_CONTROL_URL;
|
||||
} else {
|
||||
process.env.CORESTORE_CONTROL_URL = previousControlUrl;
|
||||
}
|
||||
if (previousApiToken === undefined) {
|
||||
delete process.env.CORESTORE_API_TOKEN;
|
||||
} else {
|
||||
process.env.CORESTORE_API_TOKEN = previousApiToken;
|
||||
}
|
||||
await corestore.stop().catch((errorArg) => {
|
||||
console.log(`[${scenarioName}] Failed to stop replication Corestore: ${(errorArg as Error).message}`);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const main = async () => {
|
||||
try {
|
||||
await mkdir(buildDir, { recursive: true });
|
||||
assertCoreflowVolumeMounts();
|
||||
await assertCorestoreVolumeDriver();
|
||||
await assertCoreflowBackupOrchestration();
|
||||
await assertCoreflowRemoteReplication();
|
||||
console.log(`[${scenarioName}] PASS`);
|
||||
} finally {
|
||||
await rm(buildDir, { recursive: true, force: true });
|
||||
|
||||
@@ -15,13 +15,36 @@ const delayFor = async (millisecondsArg: number) => {
|
||||
await new Promise((resolveArg) => setTimeout(resolveArg, millisecondsArg));
|
||||
};
|
||||
|
||||
const run = async (commandArg: string, argsArg: string[]) => {
|
||||
const outputCommand = async (
|
||||
commandArg: string,
|
||||
argsArg: string[],
|
||||
optionsArg: Pick<Deno.CommandOptions, 'stdout' | 'stderr'>,
|
||||
timeoutMsArg = 30000,
|
||||
) => {
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), timeoutMsArg);
|
||||
try {
|
||||
const command = new Deno.Command(commandArg, {
|
||||
args: argsArg,
|
||||
signal: controller.signal,
|
||||
...optionsArg,
|
||||
});
|
||||
return await command.output();
|
||||
} catch (error) {
|
||||
if ((error as Error).name === 'AbortError') {
|
||||
throw new Error(`${commandArg} ${argsArg.join(' ')} timed out after ${timeoutMsArg}ms`);
|
||||
}
|
||||
throw error;
|
||||
} finally {
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
};
|
||||
|
||||
const run = async (commandArg: string, argsArg: string[]) => {
|
||||
const output = await outputCommand(commandArg, argsArg, {
|
||||
stdout: 'piped',
|
||||
stderr: 'piped',
|
||||
});
|
||||
const output = await command.output();
|
||||
const stdout = new TextDecoder().decode(output.stdout).trim();
|
||||
const stderr = new TextDecoder().decode(output.stderr).trim();
|
||||
if (stdout) {
|
||||
@@ -48,12 +71,11 @@ const waitFor = async (checkFunctionArg: () => boolean | Promise<boolean>, messa
|
||||
};
|
||||
|
||||
const dockerServiceExists = async (serviceNameArg: string) => {
|
||||
const command = new Deno.Command('docker', {
|
||||
args: ['service', 'inspect', serviceNameArg],
|
||||
const output = await outputCommand('docker', ['service', 'inspect', serviceNameArg], {
|
||||
stdout: 'null',
|
||||
stderr: 'null',
|
||||
});
|
||||
return (await command.output()).success;
|
||||
}, 15000);
|
||||
return output.success;
|
||||
};
|
||||
|
||||
const removeDockerService = async (serviceNameArg: string) => {
|
||||
@@ -71,12 +93,16 @@ const assertNoPreexistingOneboxIngress = async () => {
|
||||
|
||||
const waitForDockerServiceRunning = async (serviceNameArg: string) => {
|
||||
await waitFor(async () => {
|
||||
const command = new Deno.Command('docker', {
|
||||
args: ['service', 'ps', serviceNameArg, '--format', '{{.CurrentState}}'],
|
||||
const output = await outputCommand('docker', [
|
||||
'service',
|
||||
'ps',
|
||||
serviceNameArg,
|
||||
'--format',
|
||||
'{{.CurrentState}}',
|
||||
], {
|
||||
stdout: 'piped',
|
||||
stderr: 'null',
|
||||
});
|
||||
const output = await command.output();
|
||||
}, 15000);
|
||||
if (!output.success) {
|
||||
return false;
|
||||
}
|
||||
@@ -205,4 +231,10 @@ const main = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
await main();
|
||||
try {
|
||||
await main();
|
||||
Deno.exit(0);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
Deno.exit(1);
|
||||
}
|
||||
|
||||
@@ -14,13 +14,36 @@ const delayFor = async (millisecondsArg: number) => {
|
||||
await new Promise((resolveArg) => setTimeout(resolveArg, millisecondsArg));
|
||||
};
|
||||
|
||||
const run = async (commandArg: string, argsArg: string[]) => {
|
||||
const outputCommand = async (
|
||||
commandArg: string,
|
||||
argsArg: string[],
|
||||
optionsArg: Pick<Deno.CommandOptions, 'stdout' | 'stderr'>,
|
||||
timeoutMsArg = 30000,
|
||||
) => {
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), timeoutMsArg);
|
||||
try {
|
||||
const command = new Deno.Command(commandArg, {
|
||||
args: argsArg,
|
||||
signal: controller.signal,
|
||||
...optionsArg,
|
||||
});
|
||||
return await command.output();
|
||||
} catch (error) {
|
||||
if ((error as Error).name === 'AbortError') {
|
||||
throw new Error(`${commandArg} ${argsArg.join(' ')} timed out after ${timeoutMsArg}ms`);
|
||||
}
|
||||
throw error;
|
||||
} finally {
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
};
|
||||
|
||||
const run = async (commandArg: string, argsArg: string[]) => {
|
||||
const output = await outputCommand(commandArg, argsArg, {
|
||||
stdout: 'piped',
|
||||
stderr: 'piped',
|
||||
});
|
||||
const output = await command.output();
|
||||
const stdout = new TextDecoder().decode(output.stdout).trim();
|
||||
const stderr = new TextDecoder().decode(output.stderr).trim();
|
||||
if (stdout) {
|
||||
@@ -47,12 +70,11 @@ const waitFor = async (checkFunctionArg: () => boolean | Promise<boolean>, messa
|
||||
};
|
||||
|
||||
const dockerServiceExists = async (serviceNameArg: string) => {
|
||||
const command = new Deno.Command('docker', {
|
||||
args: ['service', 'inspect', serviceNameArg],
|
||||
const output = await outputCommand('docker', ['service', 'inspect', serviceNameArg], {
|
||||
stdout: 'null',
|
||||
stderr: 'null',
|
||||
});
|
||||
return (await command.output()).success;
|
||||
}, 15000);
|
||||
return output.success;
|
||||
};
|
||||
|
||||
const removeDockerService = async (serviceNameArg: string) => {
|
||||
@@ -70,12 +92,16 @@ const assertNoPreexistingOneboxIngress = async () => {
|
||||
|
||||
const waitForDockerServiceRunning = async (serviceNameArg: string) => {
|
||||
await waitFor(async () => {
|
||||
const command = new Deno.Command('docker', {
|
||||
args: ['service', 'ps', serviceNameArg, '--format', '{{.CurrentState}}'],
|
||||
const output = await outputCommand('docker', [
|
||||
'service',
|
||||
'ps',
|
||||
serviceNameArg,
|
||||
'--format',
|
||||
'{{.CurrentState}}',
|
||||
], {
|
||||
stdout: 'piped',
|
||||
stderr: 'null',
|
||||
});
|
||||
const output = await command.output();
|
||||
}, 15000);
|
||||
if (!output.success) {
|
||||
return false;
|
||||
}
|
||||
@@ -107,12 +133,10 @@ const requestRoute = async (protocolArg: 'http' | 'https', portArg: number) => {
|
||||
curlArgs.unshift('-k');
|
||||
}
|
||||
|
||||
const command = new Deno.Command('curl', {
|
||||
args: curlArgs,
|
||||
const output = await outputCommand('curl', curlArgs, {
|
||||
stdout: 'piped',
|
||||
stderr: 'piped',
|
||||
});
|
||||
const output = await command.output();
|
||||
}, 15000);
|
||||
const stdout = new TextDecoder().decode(output.stdout).trim();
|
||||
const stderr = new TextDecoder().decode(output.stderr).trim();
|
||||
if (!output.success) {
|
||||
@@ -277,4 +301,10 @@ const main = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
await main();
|
||||
try {
|
||||
await main();
|
||||
Deno.exit(0);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
Deno.exit(1);
|
||||
}
|
||||
|
||||
@@ -329,4 +329,10 @@ const main = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
await main();
|
||||
try {
|
||||
await main();
|
||||
Deno.exit(0);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
Deno.exit(1);
|
||||
}
|
||||
|
||||
@@ -4,8 +4,7 @@ import { dirname, join, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { promisify } from 'node:util';
|
||||
|
||||
import { tapNodeTools } from '@git.zone/tstest/tapbundle_node';
|
||||
import { SmartNetwork } from '@push.rocks/smartnetwork';
|
||||
import { TapNodeTools } from '@git.zone/tstest/tapbundle_serverside';
|
||||
|
||||
import * as cloudlyApiClient from '../../../api/ts/index.js';
|
||||
import { Cloudly, type ICloudlyConfig } from '../../../cloudly/ts/index.js';
|
||||
@@ -20,6 +19,13 @@ const buildDir = join(testingDir, '.nogit', 'registry-deploy-on-push', smokeId);
|
||||
const coretrafficServiceName = `${smokeId}-coretraffic`;
|
||||
const coreflowProxyServiceName = 'coreflow';
|
||||
const stopFunctions: Array<() => Promise<void>> = [];
|
||||
const tapNodeTools = new TapNodeTools({
|
||||
registerCleanup: (cleanupFunctionArg) => {
|
||||
stopFunctions.push(async () => {
|
||||
await cleanupFunctionArg();
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const delayFor = async (millisecondsArg: number) => {
|
||||
await new Promise((resolveArg) => setTimeout(resolveArg, millisecondsArg));
|
||||
@@ -367,15 +373,18 @@ const createCloudlyConfig = async (): Promise<ICloudlyConfig> => {
|
||||
stopFunctions.push(async () => {
|
||||
await smartmongo.stopAndDumpToDir(join(testingDir, '.nogit', 'mongodump', smokeId));
|
||||
});
|
||||
const smarts3 = await tapNodeTools.createSmarts3();
|
||||
const smarts3 = await tapNodeTools.createSmartStorage();
|
||||
stopFunctions.push(async () => {
|
||||
await smarts3.stop();
|
||||
});
|
||||
|
||||
const bucketName = `${smokeId}-bucket`;
|
||||
await smarts3.createBucket(bucketName);
|
||||
const smartnetwork = new SmartNetwork();
|
||||
const publicPort = await smartnetwork.findFreePort(30000, 40000, { randomize: true });
|
||||
const publicPort = await tapNodeTools.findFreePort({
|
||||
startPort: 30000,
|
||||
endPort: 40000,
|
||||
randomize: true,
|
||||
});
|
||||
if (!publicPort) {
|
||||
throw new Error('Could not find a free Cloudly scenario port');
|
||||
}
|
||||
@@ -386,7 +395,7 @@ const createCloudlyConfig = async (): Promise<ICloudlyConfig> => {
|
||||
publicUrl: '127.0.0.1',
|
||||
publicPort: String(publicPort),
|
||||
mongoDescriptor: await smartmongo.getMongoDescriptor(),
|
||||
s3Descriptor: await smarts3.getS3Descriptor({
|
||||
s3Descriptor: await smarts3.getStorageDescriptor({
|
||||
bucketName,
|
||||
}),
|
||||
sslMode: 'none',
|
||||
@@ -533,8 +542,11 @@ const main = async () => {
|
||||
createdCorechatNetwork = true;
|
||||
}
|
||||
|
||||
const smartnetwork = new SmartNetwork();
|
||||
const coretrafficHttpsPort = await smartnetwork.findFreePort(41000, 43000, { randomize: true });
|
||||
const coretrafficHttpsPort = await tapNodeTools.findFreePort({
|
||||
startPort: 41000,
|
||||
endPort: 43000,
|
||||
randomize: true,
|
||||
});
|
||||
if (!coretrafficHttpsPort) {
|
||||
throw new Error('Could not find a free Coretraffic HTTPS test port');
|
||||
}
|
||||
@@ -547,7 +559,14 @@ const main = async () => {
|
||||
coretrafficHttpsPort,
|
||||
);
|
||||
createdCoretrafficService = true;
|
||||
await coreflow.cloudlyConnector.start();
|
||||
const clusterIdentity = await testClient.getIdentityByToken(clusterToken, {
|
||||
statefullIdentity: true,
|
||||
tagConnection: true,
|
||||
});
|
||||
coreflow.cloudlyConnector.cloudlyApiClient = testClient as any;
|
||||
coreflow.cloudlyConnector.identity = clusterIdentity;
|
||||
coreflow.cloudlyConnector.coreflowJumpCode = clusterToken;
|
||||
coreflow.cloudlyConnector.stop = async () => {};
|
||||
console.log('[registry-deploy-on-push] Coreflow connector authenticated and tagged');
|
||||
try {
|
||||
await waitFor(async () => {
|
||||
|
||||
Reference in New Issue
Block a user