mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-01-08 06:22:53 +00:00
feat: tests
This commit is contained in:
@@ -20,8 +20,8 @@
|
||||
"scripts": {
|
||||
"build": "tsup --dts",
|
||||
"dev": "tsup --watch",
|
||||
"format": "prettier .",
|
||||
"format:fix": "prettier --write .",
|
||||
"format": "prettier . --ignore-path ../../.gitignore",
|
||||
"format:fix": "prettier --write . --ignore-path ../../.gitignore",
|
||||
"lint": "eslint --ext .ts src/",
|
||||
"lint:fix": "eslint --fix --ext .ts src/",
|
||||
"start": "NODE_CONFIG_ENV=elasticsearch ALLOW_NO_TRANSPORT=true node ./lib/cli.js",
|
||||
|
||||
@@ -13,9 +13,10 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {ConfigFile} from '../src/common.js';
|
||||
|
||||
const config: ConfigFile = {
|
||||
// ESM is not supported, and cts is not detected, so we use type-checked cjs instead.
|
||||
/** @type {import('../src/common').ConfigFile} */
|
||||
const configFile = {
|
||||
activeVersions: ['1\\.0\\.\\d+', '2\\.0\\.\\d+'],
|
||||
hiddenRoutes: ['/bulk'],
|
||||
logFormat: 'default',
|
||||
@@ -31,4 +32,4 @@ const config: ConfigFile = {
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
export default configFile;
|
||||
@@ -17,11 +17,11 @@
|
||||
"bin": "app.js",
|
||||
"scripts": {
|
||||
"build": "tsup --dts",
|
||||
"format": "prettier .",
|
||||
"format:fix": "prettier --write .",
|
||||
"format": "prettier . --ignore-path ../../.gitignore",
|
||||
"format:fix": "prettier --write . --ignore-path ../../.gitignore",
|
||||
"lint": "eslint --ext .ts src/",
|
||||
"lint:fix": "eslint --fix --ext .ts src/",
|
||||
"test": "nyc mocha 'test/**/*.spec.ts'"
|
||||
"test": "c8 mocha"
|
||||
},
|
||||
"dependencies": {
|
||||
"@openstapps/logger": "workspace:*",
|
||||
@@ -33,38 +33,36 @@
|
||||
"dockerode": "3.3.5",
|
||||
"is-cidr": "4.0.2",
|
||||
"mustache": "4.2.0",
|
||||
"node-port-scanner": "3.0.1",
|
||||
"semver": "7.3.8"
|
||||
"semver": "7.3.8",
|
||||
"typescript": "4.8.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@openstapps/eslint-config": "workspace:*",
|
||||
"@openstapps/nyc-config": "workspace:*",
|
||||
"@openstapps/prettier-config": "workspace:*",
|
||||
"@openstapps/tsconfig": "workspace:*",
|
||||
"@testdeck/mocha": "0.3.3",
|
||||
"@types/chai": "4.3.5",
|
||||
"@types/chai-spies": "1.0.3",
|
||||
"@types/sinon-chai": "3.2.9",
|
||||
"@types/config": "3.3.0",
|
||||
"@types/sinon": "10.0.14",
|
||||
"@types/dockerode": "3.3.14",
|
||||
"@types/mustache": "4.2.2",
|
||||
"@types/mocha": "10.0.1",
|
||||
"@types/node": "18.15.3",
|
||||
"@types/proxyquire": "1.3.28",
|
||||
"@types/semver": "7.3.13",
|
||||
"@types/sha1": "1.1.3",
|
||||
"chai": "4.3.7",
|
||||
"chai-spies": "1.0.0",
|
||||
"sinon": "15.0.4",
|
||||
"sinon-chai": "3.7.0",
|
||||
"mocha": "10.2.0",
|
||||
"nyc": "15.1.0",
|
||||
"proxyquire": "2.1.3",
|
||||
"rimraf": "3.0.2",
|
||||
"c8": "7.13.0",
|
||||
"ts-node": "10.9.1",
|
||||
"tsup": "6.7.0",
|
||||
"typedoc": "0.23.28",
|
||||
"typescript": "4.8.4"
|
||||
"typedoc": "0.23.28"
|
||||
},
|
||||
"tsup": {
|
||||
"entry": [
|
||||
"src/app.ts",
|
||||
"src/app.ts"
|
||||
],
|
||||
"sourcemap": true,
|
||||
@@ -77,8 +75,5 @@
|
||||
"extends": [
|
||||
"@openstapps"
|
||||
]
|
||||
},
|
||||
"nyc": {
|
||||
"extends": "@openstapps/nyc-config"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,11 @@
|
||||
*/
|
||||
import {Logger} from '@openstapps/logger';
|
||||
import {execSync} from 'child_process';
|
||||
import * as Dockerode from 'dockerode';
|
||||
import type {ContainerInfo} from 'dockerode';
|
||||
import mustache from 'mustache';
|
||||
import {asyncReadFile, asyncWriteFile} from './common.js';
|
||||
import {getContainers, getTemplateView} from './main.js';
|
||||
import {readFile, writeFile} from 'fs/promises';
|
||||
import {configFile} from './common.js';
|
||||
|
||||
/* eslint-disable unicorn/prefer-module */
|
||||
|
||||
@@ -39,14 +40,11 @@ async function updateNginxConfig() {
|
||||
const containers = await getContainers();
|
||||
|
||||
const containerHash = containers
|
||||
.map((container: Dockerode.ContainerInfo) => {
|
||||
.map((container: ContainerInfo) => {
|
||||
return container.Id;
|
||||
})
|
||||
.join(',');
|
||||
|
||||
delete require.cache[require.resolve('config')];
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const configFile = require('config');
|
||||
const configHash = JSON.stringify(configFile);
|
||||
|
||||
// if containers changed -> write config file, reload nginx
|
||||
@@ -57,7 +55,7 @@ async function updateNginxConfig() {
|
||||
|
||||
// render nginx config file
|
||||
const nginxConfig = mustache.render(
|
||||
await asyncReadFile('nginx.conf.template', 'utf8'),
|
||||
await readFile('nginx.conf.template', 'utf8'),
|
||||
await getTemplateView(containers),
|
||||
);
|
||||
|
||||
@@ -67,7 +65,7 @@ async function updateNginxConfig() {
|
||||
Logger.log(`Writing new config file "${configFile.output}"`);
|
||||
|
||||
// overwrite nginx config file with our rendered one
|
||||
await asyncWriteFile(configFile.output, nginxConfig, 'utf8');
|
||||
await writeFile(configFile.output, nginxConfig, 'utf8');
|
||||
|
||||
Logger.log('Executing "nginx -s reload" to tell nginx to reload the configuration file');
|
||||
|
||||
|
||||
@@ -15,15 +15,11 @@
|
||||
*/
|
||||
import {Logger, SMTP} from '@openstapps/logger';
|
||||
import config from 'config';
|
||||
import {existsSync, readFile, writeFile} from 'fs';
|
||||
import {promisify} from 'util';
|
||||
import {existsSync} from 'fs';
|
||||
|
||||
// set transport on logger
|
||||
Logger.setTransport(SMTP.getInstance());
|
||||
|
||||
export const asyncReadFile = promisify(readFile);
|
||||
export const asyncWriteFile = promisify(writeFile);
|
||||
|
||||
/**
|
||||
* A representation of the file paths of the needed ssl certificates
|
||||
*/
|
||||
|
||||
@@ -20,8 +20,7 @@ import mustache from 'mustache';
|
||||
import path from 'path';
|
||||
import * as semver from 'semver';
|
||||
import {
|
||||
asyncReadFile,
|
||||
ConfigFile,
|
||||
configFile,
|
||||
isFileType,
|
||||
protocolHardeningParameters,
|
||||
SSLFilePaths,
|
||||
@@ -29,10 +28,9 @@ import {
|
||||
SupportedLogFormats,
|
||||
TemplateView,
|
||||
} from './common.js';
|
||||
// @ts-expect-error missing type defs
|
||||
import nodePortScanner from 'node-port-scanner';
|
||||
import {readFile} from 'fs/promises';
|
||||
import PortScanner from './port-scanner.js';
|
||||
|
||||
/* eslint-disable unicorn/prefer-module */
|
||||
/* eslint-disable unicorn/no-await-expression-member */
|
||||
|
||||
/**
|
||||
@@ -90,9 +88,7 @@ Please expose a port if the container should be accessible by NGINX.`);
|
||||
|
||||
// Get a routable network connection
|
||||
for (const network in container.NetworkSettings.Networks) {
|
||||
const scan = await nodePortScanner(container.NetworkSettings.Networks[network].IPAddress, [port]);
|
||||
|
||||
if ((scan.ports.open as Array<number>).includes(port)) {
|
||||
if (await PortScanner.isPortFree(port, container.NetworkSettings.Networks[network].IPAddress)) {
|
||||
Logger.info(
|
||||
`${container.Names[0]} reachable via ${container.NetworkSettings.Networks[network].IPAddress}:${port}`,
|
||||
);
|
||||
@@ -260,7 +256,7 @@ export async function generateMetricsServer(logFormat: string, enableMetrics?: b
|
||||
* @param view Data to render template with
|
||||
*/
|
||||
async function renderTemplate(path: string, view: unknown): Promise<string> {
|
||||
const content = await asyncReadFile(path, 'utf8');
|
||||
const content = await readFile(path, 'utf8');
|
||||
|
||||
return mustache.render(content, view);
|
||||
}
|
||||
@@ -283,12 +279,7 @@ function generateRateLimitAllowList(entries: string[]): string {
|
||||
* @param containers List of container info
|
||||
*/
|
||||
export async function getTemplateView(containers: Dockerode.ContainerInfo[]): Promise<TemplateView> {
|
||||
delete require.cache[require.resolve('config')];
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const config = require('config');
|
||||
const configFile = config as ConfigFile;
|
||||
|
||||
const cors = await asyncReadFile('./fixtures/cors.template', 'utf8');
|
||||
const cors = await readFile('./fixtures/cors.template', 'utf8');
|
||||
|
||||
const visibleRoutesPromises = ['/'].map(async route => {
|
||||
return renderTemplate(path.join('fixtures', 'visibleRoute.template'), {
|
||||
|
||||
22
backend/proxy/src/port-scanner.ts
Normal file
22
backend/proxy/src/port-scanner.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import {createServer, Server} from 'net';
|
||||
|
||||
/**
|
||||
* Checks if a port is in use
|
||||
*/
|
||||
async function isPortFree(port: number, hostname?: string): Promise<boolean> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const server: Server = createServer()
|
||||
.once('error', error => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
if ((error as any).code === 'EADDRINUSE') {
|
||||
resolve(true);
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
})
|
||||
.once('listening', () => server.once('close', () => resolve(false)).close())
|
||||
.listen(port, hostname);
|
||||
});
|
||||
}
|
||||
|
||||
export default {isPortFree};
|
||||
@@ -13,17 +13,12 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// tslint:disable:no-implicit-dependencies
|
||||
// tslint:disable:no-magic-numbers
|
||||
// tslint:disable:completed-docs
|
||||
// tslint:disable:prefer-function-over-method
|
||||
// tslint:disable:newline-per-chained-call
|
||||
import {suite, test} from '@testdeck/mocha';
|
||||
import {Logger} from '@openstapps/logger';
|
||||
import {expect} from 'chai';
|
||||
import {mkdirSync, writeFileSync, unlinkSync, rmdirSync} from 'fs';
|
||||
import {resolve} from 'path';
|
||||
import path from 'path';
|
||||
import {isFileType} from '../src/common.js';
|
||||
import {fileURLToPath} from 'url';
|
||||
|
||||
process.on('unhandledRejection', async error => {
|
||||
await Logger.error(error);
|
||||
@@ -31,22 +26,20 @@ process.on('unhandledRejection', async error => {
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
@suite
|
||||
export class CommonSpec {
|
||||
@test
|
||||
async testSSLCert() {
|
||||
const testCertDir = resolve(__dirname, 'certs');
|
||||
mkdirSync(testCertDir);
|
||||
const notAnExptectedFileTypeFilePath = resolve(testCertDir, 'notAnExptectedFileType.txt');
|
||||
const anExptectedFileTypeFilePath = resolve(testCertDir, 'notARealCert.crt');
|
||||
writeFileSync(notAnExptectedFileTypeFilePath, 'Test');
|
||||
writeFileSync(anExptectedFileTypeFilePath, 'Test');
|
||||
describe('common', function () {
|
||||
it('should use ssl certs', function () {
|
||||
const testCertDirectory = path.resolve(path.dirname(fileURLToPath(import.meta.url)), 'certs');
|
||||
mkdirSync(testCertDirectory);
|
||||
const notAnExpectedFileTypeFilePath = path.resolve(testCertDirectory, 'notAnExpectedFileType.txt');
|
||||
const anExpectedFileTypeFilePath = path.resolve(testCertDirectory, 'notARealCert.crt');
|
||||
writeFileSync(notAnExpectedFileTypeFilePath, 'Test');
|
||||
writeFileSync(anExpectedFileTypeFilePath, 'Test');
|
||||
|
||||
expect(isFileType(notAnExptectedFileTypeFilePath, 'crt')).to.equal(false);
|
||||
expect(isFileType(anExptectedFileTypeFilePath, 'crt')).to.equal(true);
|
||||
expect(isFileType(notAnExpectedFileTypeFilePath, 'crt')).to.equal(false);
|
||||
expect(isFileType(anExpectedFileTypeFilePath, 'crt')).to.equal(true);
|
||||
|
||||
unlinkSync(notAnExptectedFileTypeFilePath);
|
||||
unlinkSync(anExptectedFileTypeFilePath);
|
||||
rmdirSync(testCertDir);
|
||||
}
|
||||
}
|
||||
unlinkSync(notAnExpectedFileTypeFilePath);
|
||||
unlinkSync(anExpectedFileTypeFilePath);
|
||||
rmdirSync(testCertDirectory);
|
||||
});
|
||||
});
|
||||
|
||||
44
backend/proxy/test/containers/any-with-exposed-ports.ts
Normal file
44
backend/proxy/test/containers/any-with-exposed-ports.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
/* eslint-disable unicorn/no-null */
|
||||
import type {ContainerInfo} from 'dockerode';
|
||||
|
||||
export const anyContainerWithExposedPorts: ContainerInfo = {
|
||||
Command: 'sh',
|
||||
Created: 1_524_669_882,
|
||||
HostConfig: {
|
||||
NetworkMode: 'default',
|
||||
},
|
||||
Id: 'e3d3f4d18aceac2780bdb95523845d066ed25c04fc65168a5ddbd37a85671bb7',
|
||||
Image: 'ubuntu:4',
|
||||
ImageID: 'sha256:ef9f0c8c4b6f99dd208948c7aae1d042590aa18e05ebeae4f586e4b4beebeac9',
|
||||
Labels: {},
|
||||
Mounts: [],
|
||||
Names: ['/container_name_1'],
|
||||
NetworkSettings: {
|
||||
Networks: {
|
||||
bridge: {
|
||||
Aliases: null,
|
||||
EndpointID: 'da17549a086ff2c9f622e80de833e6f334afda52c8f07080428640c1716dcd14',
|
||||
Gateway: '172.18.0.1',
|
||||
GlobalIPv6Address: '',
|
||||
GlobalIPv6PrefixLen: 0,
|
||||
IPAMConfig: null,
|
||||
IPAddress: '172.18.0.3',
|
||||
IPPrefixLen: 16,
|
||||
IPv6Gateway: '',
|
||||
Links: null,
|
||||
MacAddress: '03:41:ac:11:00:23',
|
||||
NetworkID: '947ea5247cc7429e1fdebd5404fa4d15f7c05e6765f2b93ddb3bdb6aaffd1193',
|
||||
},
|
||||
},
|
||||
},
|
||||
Ports: [
|
||||
{
|
||||
IP: '0.0.0.0',
|
||||
PrivatePort: 80,
|
||||
PublicPort: 80,
|
||||
Type: 'tcp',
|
||||
},
|
||||
],
|
||||
State: 'running',
|
||||
Status: 'Up 3 minutes',
|
||||
};
|
||||
52
backend/proxy/test/containers/backend-with-exposed-ports.ts
Normal file
52
backend/proxy/test/containers/backend-with-exposed-ports.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
/* eslint-disable unicorn/no-null */
|
||||
import type {ContainerInfo} from 'dockerode';
|
||||
|
||||
export const backendContainerWithExposedPorts: ContainerInfo = {
|
||||
Command: 'node ./bin/www',
|
||||
Created: 1_524_669_882,
|
||||
HostConfig: {
|
||||
NetworkMode: 'deployment_default',
|
||||
},
|
||||
Id: 'e3d3f4d18aceac2780bdb95523845d066ed25c04fc65168a5ddbd37a85671bb7',
|
||||
Image: 'registry.gitlab.com/openstapps/backend/b-tu-typescript-refactor-for-new-tslint-config',
|
||||
ImageID: 'sha256:ef9f0c8c4b6f99dd208948c7aae1d042590aa18e05ebeae4f586e4b4beebeac9',
|
||||
Labels: {
|
||||
'com.docker.compose.config-hash': '91c6e0cebad15951824162c93392b6880b69599692f07798ae8de659c1616a03',
|
||||
'com.docker.compose.container-number': '1',
|
||||
'com.docker.compose.oneoff': 'False',
|
||||
'com.docker.compose.project': 'deployment',
|
||||
'com.docker.compose.service': 'backend',
|
||||
'com.docker.compose.version': '1.21.0',
|
||||
'stapps.version': '1.0.0',
|
||||
},
|
||||
Mounts: [],
|
||||
Names: ['/deployment_backend_1'],
|
||||
NetworkSettings: {
|
||||
Networks: {
|
||||
deployment_default: {
|
||||
Aliases: null,
|
||||
EndpointID: 'da17549a086ff2c9f622e80de833e6f334afda52c8f07080428640c1716dcd14',
|
||||
Gateway: '172.18.0.1',
|
||||
GlobalIPv6Address: '',
|
||||
GlobalIPv6PrefixLen: 0,
|
||||
IPAMConfig: null,
|
||||
IPAddress: '172.18.0.3',
|
||||
IPPrefixLen: 16,
|
||||
IPv6Gateway: '',
|
||||
Links: null,
|
||||
MacAddress: '03:41:ac:11:00:23',
|
||||
NetworkID: '947ea5247cc7429e1fdebd5404fa4d15f7c05e6765f2b93ddb3bdb6aaffd1193',
|
||||
},
|
||||
},
|
||||
},
|
||||
Ports: [
|
||||
{
|
||||
IP: '127.0.0.1',
|
||||
PrivatePort: 3000,
|
||||
PublicPort: 3000,
|
||||
Type: 'tcp',
|
||||
},
|
||||
],
|
||||
State: 'running',
|
||||
Status: 'Up 3 minutes',
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
/* eslint-disable unicorn/no-null */
|
||||
import type {ContainerInfo} from 'dockerode';
|
||||
|
||||
export const swarmBackendContainerWithExposedPorts: ContainerInfo = {
|
||||
Command: 'node ./bin/www',
|
||||
Created: 1_524_669_882,
|
||||
HostConfig: {
|
||||
NetworkMode: 'swarm_default',
|
||||
},
|
||||
Id: 'e3d3f4d18aceac2780bdb95523845d066ed25c04fc65168a5ddbd37a85671bb7',
|
||||
Image: 'registry.gitlab.com/openstapps/backend/b-tu-typescript-refactor-for-new-tslint-config',
|
||||
ImageID: 'sha256:ef9f0c8c4b6f99dd208948c7aae1d042590aa18e05ebeae4f586e4b4beebeac9',
|
||||
Labels: {
|
||||
'com.docker.compose.config-hash': '91c6e0cebad15951824162c93392b6880b69599692f07798ae8de659c1616a03',
|
||||
'com.docker.compose.container-number': '1',
|
||||
'com.docker.compose.oneoff': 'False',
|
||||
'com.docker.stack.namespace': 'deployment',
|
||||
'com.docker.swarm.service.name': 'deployment_backend',
|
||||
'com.docker.compose.version': '1.21.0',
|
||||
'stapps.version': '1.0.0',
|
||||
},
|
||||
Mounts: [],
|
||||
Names: ['/deployment_backend_1'],
|
||||
NetworkSettings: {
|
||||
Networks: {
|
||||
ingress: {
|
||||
Aliases: null,
|
||||
EndpointID: 'da17549a086ff2c9f622e80de833e6f334afda52c8f07080428640c1716dcd14',
|
||||
Gateway: '172.18.0.1',
|
||||
GlobalIPv6Address: '',
|
||||
GlobalIPv6PrefixLen: 0,
|
||||
IPAMConfig: null,
|
||||
IPAddress: '172.18.0.3',
|
||||
IPPrefixLen: 16,
|
||||
IPv6Gateway: '',
|
||||
Links: null,
|
||||
MacAddress: '03:41:ac:11:00:23',
|
||||
NetworkID: '947ea5247cc7429e1fdebd5404fa4d15f7c05e6765f2b93ddb3bdb6aaffd1193',
|
||||
},
|
||||
},
|
||||
},
|
||||
Ports: [
|
||||
{
|
||||
IP: 'delete me',
|
||||
PrivatePort: 3000,
|
||||
PublicPort: 3000,
|
||||
Type: 'tcp',
|
||||
},
|
||||
],
|
||||
State: 'running',
|
||||
Status: 'Up 3 minutes',
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/*
|
||||
* Copyright (C) 2019 StApps
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@@ -13,18 +14,16 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// tslint:disable:no-implicit-dependencies
|
||||
// tslint:disable:no-magic-numbers
|
||||
// tslint:disable:completed-docs
|
||||
// tslint:disable:prefer-function-over-method
|
||||
// tslint:disable:newline-per-chained-call
|
||||
import {Logger} from '@openstapps/logger';
|
||||
import chai from 'chai';
|
||||
import {expect} from 'chai';
|
||||
import chaiSpies from 'chai-spies';
|
||||
import {ContainerInfo} from 'dockerode';
|
||||
import {slow, suite, test, timeout} from '@testdeck/mocha';
|
||||
import {sslHardeningParameters, protocolHardeningParameters, SSLFilePaths} from './../src/common.js';
|
||||
import {
|
||||
sslHardeningParameters,
|
||||
protocolHardeningParameters,
|
||||
SSLFilePaths,
|
||||
configFile,
|
||||
} from './../src/common.js';
|
||||
import {
|
||||
containerMatchesRegex,
|
||||
generateUpstreamMap,
|
||||
@@ -34,11 +33,15 @@ import {
|
||||
generateMetricsServer,
|
||||
getContainers,
|
||||
} from '../src/main.js';
|
||||
import {resolve} from 'path';
|
||||
import path from 'path';
|
||||
import {mkdirSync, writeFileSync, unlinkSync, rmdirSync} from 'fs';
|
||||
import proxyquire from 'proxyquire';
|
||||
|
||||
proxyquire.callThru().preserveCache();
|
||||
import {anyContainerWithExposedPorts} from './containers/any-with-exposed-ports.js';
|
||||
import {backendContainerWithExposedPorts} from './containers/backend-with-exposed-ports.js';
|
||||
import {swarmBackendContainerWithExposedPorts} from './containers/swarm-backend-with-exposed-ports.js';
|
||||
import {fileURLToPath} from 'url';
|
||||
import sinon from 'sinon';
|
||||
import sinonChai from 'sinon-chai';
|
||||
import portScannerModule from '../src/port-scanner.js';
|
||||
|
||||
process.on('unhandledRejection', async error => {
|
||||
await Logger.error(error);
|
||||
@@ -47,196 +50,47 @@ process.on('unhandledRejection', async error => {
|
||||
});
|
||||
|
||||
chai.should();
|
||||
chai.use(chaiSpies);
|
||||
chai.use(sinonChai);
|
||||
|
||||
@suite(timeout(1000), slow(500))
|
||||
export class MainSpec {
|
||||
static 'anyContainerWithExposedPorts': ContainerInfo = {
|
||||
Command: 'sh',
|
||||
Created: 1_524_669_882,
|
||||
HostConfig: {
|
||||
NetworkMode: 'default',
|
||||
},
|
||||
Id: 'e3d3f4d18aceac2780bdb95523845d066ed25c04fc65168a5ddbd37a85671bb7',
|
||||
Image: 'ubuntu:4',
|
||||
ImageID: 'sha256:ef9f0c8c4b6f99dd208948c7aae1d042590aa18e05ebeae4f586e4b4beebeac9',
|
||||
Labels: {},
|
||||
Mounts: [],
|
||||
Names: ['/container_name_1'],
|
||||
NetworkSettings: {
|
||||
Networks: {
|
||||
bridge: {
|
||||
Aliases: null,
|
||||
EndpointID: 'da17549a086ff2c9f622e80de833e6f334afda52c8f07080428640c1716dcd14',
|
||||
Gateway: '172.18.0.1',
|
||||
GlobalIPv6Address: '',
|
||||
GlobalIPv6PrefixLen: 0,
|
||||
IPAMConfig: null,
|
||||
IPAddress: '172.18.0.3',
|
||||
IPPrefixLen: 16,
|
||||
IPv6Gateway: '',
|
||||
Links: null,
|
||||
MacAddress: '03:41:ac:11:00:23',
|
||||
NetworkID: '947ea5247cc7429e1fdebd5404fa4d15f7c05e6765f2b93ddb3bdb6aaffd1193',
|
||||
},
|
||||
},
|
||||
},
|
||||
Ports: [
|
||||
{
|
||||
IP: '0.0.0.0',
|
||||
PrivatePort: 80,
|
||||
PublicPort: 80,
|
||||
Type: 'tcp',
|
||||
},
|
||||
],
|
||||
State: 'running',
|
||||
Status: 'Up 3 minutes',
|
||||
};
|
||||
console.log(configFile);
|
||||
|
||||
static 'backendContainerWithExposedPorts': ContainerInfo = {
|
||||
Command: 'node ./bin/www',
|
||||
Created: 1524669882,
|
||||
HostConfig: {
|
||||
NetworkMode: 'deployment_default',
|
||||
},
|
||||
Id: 'e3d3f4d18aceac2780bdb95523845d066ed25c04fc65168a5ddbd37a85671bb7',
|
||||
Image: 'registry.gitlab.com/openstapps/backend/b-tu-typescript-refactor-for-new-tslint-config',
|
||||
ImageID: 'sha256:ef9f0c8c4b6f99dd208948c7aae1d042590aa18e05ebeae4f586e4b4beebeac9',
|
||||
Labels: {
|
||||
'com.docker.compose.config-hash': '91c6e0cebad15951824162c93392b6880b69599692f07798ae8de659c1616a03',
|
||||
'com.docker.compose.container-number': '1',
|
||||
'com.docker.compose.oneoff': 'False',
|
||||
'com.docker.compose.project': 'deployment',
|
||||
'com.docker.compose.service': 'backend',
|
||||
'com.docker.compose.version': '1.21.0',
|
||||
'stapps.version': '1.0.0',
|
||||
},
|
||||
Mounts: [],
|
||||
Names: ['/deployment_backend_1'],
|
||||
NetworkSettings: {
|
||||
Networks: {
|
||||
deployment_default: {
|
||||
Aliases: null,
|
||||
EndpointID: 'da17549a086ff2c9f622e80de833e6f334afda52c8f07080428640c1716dcd14',
|
||||
Gateway: '172.18.0.1',
|
||||
GlobalIPv6Address: '',
|
||||
GlobalIPv6PrefixLen: 0,
|
||||
IPAMConfig: null,
|
||||
IPAddress: '172.18.0.3',
|
||||
IPPrefixLen: 16,
|
||||
IPv6Gateway: '',
|
||||
Links: null,
|
||||
MacAddress: '03:41:ac:11:00:23',
|
||||
NetworkID: '947ea5247cc7429e1fdebd5404fa4d15f7c05e6765f2b93ddb3bdb6aaffd1193',
|
||||
},
|
||||
},
|
||||
},
|
||||
Ports: [
|
||||
{
|
||||
IP: '127.0.0.1',
|
||||
PrivatePort: 3000,
|
||||
PublicPort: 3000,
|
||||
Type: 'tcp',
|
||||
},
|
||||
],
|
||||
State: 'running',
|
||||
Status: 'Up 3 minutes',
|
||||
};
|
||||
describe('main', function () {
|
||||
this.timeout(1000);
|
||||
this.slow(500);
|
||||
|
||||
static 'swarmBackendContainerWithExposedPorts': ContainerInfo = {
|
||||
Command: 'node ./bin/www',
|
||||
Created: 1524669882,
|
||||
HostConfig: {
|
||||
NetworkMode: 'swarm_default',
|
||||
},
|
||||
Id: 'e3d3f4d18aceac2780bdb95523845d066ed25c04fc65168a5ddbd37a85671bb7',
|
||||
Image: 'registry.gitlab.com/openstapps/backend/b-tu-typescript-refactor-for-new-tslint-config',
|
||||
ImageID: 'sha256:ef9f0c8c4b6f99dd208948c7aae1d042590aa18e05ebeae4f586e4b4beebeac9',
|
||||
Labels: {
|
||||
'com.docker.compose.config-hash': '91c6e0cebad15951824162c93392b6880b69599692f07798ae8de659c1616a03',
|
||||
'com.docker.compose.container-number': '1',
|
||||
'com.docker.compose.oneoff': 'False',
|
||||
'com.docker.stack.namespace': 'deployment',
|
||||
'com.docker.swarm.service.name': 'deployment_backend',
|
||||
'com.docker.compose.version': '1.21.0',
|
||||
'stapps.version': '1.0.0',
|
||||
},
|
||||
Mounts: [],
|
||||
Names: ['/deployment_backend_1'],
|
||||
NetworkSettings: {
|
||||
Networks: {
|
||||
ingress: {
|
||||
Aliases: null,
|
||||
EndpointID: 'da17549a086ff2c9f622e80de833e6f334afda52c8f07080428640c1716dcd14',
|
||||
Gateway: '172.18.0.1',
|
||||
GlobalIPv6Address: '',
|
||||
GlobalIPv6PrefixLen: 0,
|
||||
IPAMConfig: null,
|
||||
IPAddress: '172.18.0.3',
|
||||
IPPrefixLen: 16,
|
||||
IPv6Gateway: '',
|
||||
Links: null,
|
||||
MacAddress: '03:41:ac:11:00:23',
|
||||
NetworkID: '947ea5247cc7429e1fdebd5404fa4d15f7c05e6765f2b93ddb3bdb6aaffd1193',
|
||||
},
|
||||
},
|
||||
},
|
||||
Ports: [
|
||||
{
|
||||
IP: 'delete me',
|
||||
PrivatePort: 3000,
|
||||
PublicPort: 3000,
|
||||
Type: 'tcp',
|
||||
},
|
||||
],
|
||||
State: 'running',
|
||||
Status: 'Up 3 minutes',
|
||||
};
|
||||
const sandbox = sinon.createSandbox();
|
||||
|
||||
static 'sandbox' = chai.spy.sandbox();
|
||||
beforeEach(function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
'before'() {
|
||||
MainSpec.sandbox.restore();
|
||||
}
|
||||
it('should check if container does not match any container', function () {
|
||||
expect(containerMatchesRegex('anyName', new RegExp('d+'), anyContainerWithExposedPorts)).to.be.equal(
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
@test
|
||||
'check if container does not match any container'() {
|
||||
it('should check if container does not match if version is incorrect', function () {
|
||||
expect(
|
||||
containerMatchesRegex('anyName', new RegExp('d+'), MainSpec.anyContainerWithExposedPorts),
|
||||
containerMatchesRegex('backend', new RegExp('1\\.4\\.\\d+'), backendContainerWithExposedPorts),
|
||||
).to.be.equal(false);
|
||||
}
|
||||
});
|
||||
|
||||
@test
|
||||
'check if container does not match if version is incorrect'() {
|
||||
it('should check if container matches', function () {
|
||||
expect(
|
||||
containerMatchesRegex('backend', new RegExp('1\\.4\\.\\d+'), MainSpec.backendContainerWithExposedPorts),
|
||||
).to.be.equal(false);
|
||||
}
|
||||
|
||||
@test
|
||||
'check if container matches'() {
|
||||
expect(
|
||||
containerMatchesRegex('backend', new RegExp('1\\.0\\.\\d+'), MainSpec.backendContainerWithExposedPorts),
|
||||
containerMatchesRegex('backend', new RegExp('1\\.0\\.\\d+'), backendContainerWithExposedPorts),
|
||||
).to.be.equal(true);
|
||||
expect(
|
||||
containerMatchesRegex(
|
||||
'backend',
|
||||
new RegExp('1\\.0\\.\\d+'),
|
||||
MainSpec.swarmBackendContainerWithExposedPorts,
|
||||
),
|
||||
containerMatchesRegex('backend', new RegExp('1\\.0\\.\\d+'), swarmBackendContainerWithExposedPorts),
|
||||
).to.be.equal(true);
|
||||
}
|
||||
});
|
||||
|
||||
@test
|
||||
async 'get gateway of any container with exposed ports'() {
|
||||
expect(await getGatewayOfStAppsBackend(MainSpec.anyContainerWithExposedPorts)).to.be.equal('0.0.0.0:80');
|
||||
}
|
||||
it('should get gateway of any container with exposed ports', async function () {
|
||||
expect(await getGatewayOfStAppsBackend(anyContainerWithExposedPorts)).to.be.equal('0.0.0.0:80');
|
||||
});
|
||||
|
||||
@test
|
||||
async 'get gateway of backend container'() {
|
||||
const spy = MainSpec.sandbox.on(console, 'error', () => {
|
||||
// noop
|
||||
});
|
||||
it('should get gateway of backend container', async function () {
|
||||
const spy = sandbox.stub(console, 'error');
|
||||
|
||||
const containerWithoutPorts: Partial<ContainerInfo> = {
|
||||
Id: 'Foo',
|
||||
@@ -245,112 +99,68 @@ export class MainSpec {
|
||||
};
|
||||
|
||||
expect(await getGatewayOfStAppsBackend(containerWithoutPorts as ContainerInfo)).to.be.equal('');
|
||||
expect(spy.__spy.calls[0][0]).to.contain('Container /container_name_1 does not advertise any port.');
|
||||
}
|
||||
expect(spy).to.have.been.calledWithMatch('Container /container_name_1 does not advertise any port.');
|
||||
});
|
||||
|
||||
@test
|
||||
async 'get gateway of backend container without ports'() {
|
||||
expect(await getGatewayOfStAppsBackend(MainSpec.backendContainerWithExposedPorts)).to.be.equal(
|
||||
'127.0.0.1:3000',
|
||||
);
|
||||
}
|
||||
it('should get gateway of backend container without ports', async function () {
|
||||
expect(await getGatewayOfStAppsBackend(backendContainerWithExposedPorts)).to.be.equal('127.0.0.1:3000');
|
||||
});
|
||||
|
||||
@test
|
||||
async 'get gateway of backend container within docker swarm'() {
|
||||
const backendContainer = MainSpec.swarmBackendContainerWithExposedPorts as any;
|
||||
it('should get gateway of backend container within docker swarm', async function () {
|
||||
const backendContainer = swarmBackendContainerWithExposedPorts as any;
|
||||
delete backendContainer.Ports[0].IP;
|
||||
|
||||
const main = proxyquire('../src/main', {
|
||||
'node-port-scanner': (_host: unknown, _ports: unknown) => {
|
||||
return new Promise((resolve, _reject) => {
|
||||
resolve({
|
||||
ports: {
|
||||
open: [3000],
|
||||
},
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
expect(await main.getGatewayOfStAppsBackend(backendContainer)).to.be.equal('172.18.0.3:3000');
|
||||
}
|
||||
const spy = sandbox.stub(portScannerModule, 'isPortFree').resolves(true);
|
||||
expect(await getGatewayOfStAppsBackend(backendContainer)).to.be.equal('172.18.0.3:3000');
|
||||
expect(spy).to.have.been.called;
|
||||
});
|
||||
|
||||
@test
|
||||
async 'fail to get gateway of backend container if unreachable'() {
|
||||
const backendContainer = MainSpec.swarmBackendContainerWithExposedPorts as any;
|
||||
it('should fail to get gateway of backend container if unreachable', async function () {
|
||||
const backendContainer = swarmBackendContainerWithExposedPorts as any;
|
||||
delete backendContainer.Ports[0].IP;
|
||||
|
||||
const spy = MainSpec.sandbox.on(console, 'error', () => {
|
||||
// noop
|
||||
});
|
||||
const spy = sandbox.stub(console, 'error');
|
||||
|
||||
const main = proxyquire('../src/main', {
|
||||
'node-port-scanner': (_host: unknown, _ports: unknown) => {
|
||||
return new Promise((resolve, _reject) => {
|
||||
resolve({
|
||||
ports: {
|
||||
open: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
const scanner = sandbox.stub(portScannerModule, 'isPortFree').resolves(false);
|
||||
|
||||
expect(await main.getGatewayOfStAppsBackend(MainSpec.swarmBackendContainerWithExposedPorts)).to.be.equal(
|
||||
'',
|
||||
);
|
||||
expect(spy.__spy.calls[0][0]).to.contain(
|
||||
expect(await getGatewayOfStAppsBackend(swarmBackendContainerWithExposedPorts)).to.be.equal('');
|
||||
expect(scanner.calledOnce).to.be.true;
|
||||
expect(spy).to.have.been.calledWithMatch(
|
||||
"It's possible your current Docker network setup isn't supported yet.",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@test
|
||||
async 'fail to get gateway of backend container network mode is unsupported'() {
|
||||
const backendContainer = MainSpec.swarmBackendContainerWithExposedPorts as any;
|
||||
it('should fail to get gateway of backend container network mode is unsupported', async function () {
|
||||
const backendContainer = swarmBackendContainerWithExposedPorts as any;
|
||||
delete backendContainer.Ports[0].IP;
|
||||
delete backendContainer.Ports[0].PublicPort;
|
||||
delete backendContainer.Ports[0].PrivatePort;
|
||||
|
||||
const spy = MainSpec.sandbox.on(console, 'error', () => {
|
||||
// noop
|
||||
});
|
||||
const spy = sandbox.stub(console, 'error');
|
||||
|
||||
expect(await getGatewayOfStAppsBackend(MainSpec.swarmBackendContainerWithExposedPorts)).to.be.equal('');
|
||||
expect(spy.__spy.calls[0][0]).to.contain(
|
||||
expect(await getGatewayOfStAppsBackend(swarmBackendContainerWithExposedPorts)).to.be.equal('');
|
||||
expect(spy).to.have.been.calledWithMatch(
|
||||
"It's possible your current Docker network setup isn't supported yet.",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@test
|
||||
async 'upstream map calls logger error when no matching container is found'() {
|
||||
const spy = MainSpec.sandbox.on(console, 'warn', () => {
|
||||
// noop
|
||||
});
|
||||
it('should upstream map calls logger error when no matching container is found', async function () {
|
||||
const spy = sandbox.stub(console, 'warn');
|
||||
|
||||
expect(
|
||||
await generateUpstreamMap(
|
||||
['0\\.8\\.\\d+'],
|
||||
['1\\.1\\.\\d+'],
|
||||
[MainSpec.backendContainerWithExposedPorts],
|
||||
),
|
||||
).to.be.equal(`map $http_x_stapps_version $proxyurl {
|
||||
expect(await generateUpstreamMap(['0\\.8\\.\\d+'], ['1\\.1\\.\\d+'], [backendContainerWithExposedPorts]))
|
||||
.to.be.equal(`map $http_x_stapps_version $proxyurl {
|
||||
default unsupported;
|
||||
"~0\\.8\\.\\d+" unavailable;
|
||||
"~1\\.1\\.\\d+" outdated;
|
||||
}
|
||||
`);
|
||||
|
||||
expect(spy.__spy.calls[0][0]).to.contain('No backend for version');
|
||||
}
|
||||
expect(spy).to.have.been.calledWithMatch('No backend for version');
|
||||
});
|
||||
|
||||
@test
|
||||
async 'upstream map with one active version and no outdated ones'() {
|
||||
expect(
|
||||
await generateUpstreamMap(
|
||||
['1\\.0\\.\\d+'],
|
||||
['0\\.8\\.\\d+'],
|
||||
[MainSpec.backendContainerWithExposedPorts],
|
||||
),
|
||||
).to.be.equal(`map $http_x_stapps_version $proxyurl {
|
||||
it('should upstream map with one active version and no outdated ones', async function () {
|
||||
expect(await generateUpstreamMap(['1\\.0\\.\\d+'], ['0\\.8\\.\\d+'], [backendContainerWithExposedPorts]))
|
||||
.to.be.equal(`map $http_x_stapps_version $proxyurl {
|
||||
default unsupported;
|
||||
"~1\\.0\\.\\d+" 1__0___d_;
|
||||
"~0\\.8\\.\\d+" outdated;
|
||||
@@ -359,17 +169,16 @@ upstream 1__0___d_ {
|
||||
server 127.0.0.1:3000;
|
||||
}
|
||||
`);
|
||||
}
|
||||
});
|
||||
|
||||
@test
|
||||
async 'get containers'() {
|
||||
it('should get containers', async function () {
|
||||
try {
|
||||
await getContainers();
|
||||
return false;
|
||||
} catch (e) {
|
||||
if ((e as Error).message.startsWith('No')) {
|
||||
} catch (error) {
|
||||
if ((error as Error).message.startsWith('No')) {
|
||||
// Result, if docker is installed
|
||||
expect((e as Error).message).to.equal(`No running docker containers found.
|
||||
expect((error as Error).message).to.equal(`No running docker containers found.
|
||||
|
||||
Please check if docker is running and Node.js can access the docker socket (/var/run/docker.sock)`);
|
||||
} else {
|
||||
@@ -377,41 +186,34 @@ Please check if docker is running and Node.js can access the docker socket (/var
|
||||
expect([
|
||||
new Error(`connect ENOENT /var/run/docker.sock`).message,
|
||||
new Error('connect EACCES /var/run/docker.sock').message,
|
||||
]).to.include((e as Error).message);
|
||||
]).to.include((error as Error).message);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
@test
|
||||
async 'get template view'() {
|
||||
it('should get template view', async function () {
|
||||
try {
|
||||
let containersWithSameVersion = [
|
||||
MainSpec.backendContainerWithExposedPorts,
|
||||
MainSpec.backendContainerWithExposedPorts,
|
||||
];
|
||||
const containersWithSameVersion = [backendContainerWithExposedPorts, backendContainerWithExposedPorts];
|
||||
await getTemplateView(containersWithSameVersion);
|
||||
return false;
|
||||
} catch (e) {
|
||||
expect((e as Error).message).to.equal(`Multiple backends for one version found.`);
|
||||
} catch (error) {
|
||||
expect((error as Error).message).to.equal(`Multiple backends for one version found.`);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
@test
|
||||
async 'include metrics config'() {
|
||||
it('should include metrics config', async function () {
|
||||
expect(await generateMetricsServer('test', true)).length.to.be.greaterThan(1);
|
||||
}
|
||||
});
|
||||
|
||||
@test
|
||||
async 'omit metrics config'() {
|
||||
it('should omit metrics config', async function () {
|
||||
expect(await generateMetricsServer('test', false)).to.equal('');
|
||||
}
|
||||
});
|
||||
|
||||
@test
|
||||
'create listener faulty config'() {
|
||||
it('should create listener faulty config', async function () {
|
||||
expect(
|
||||
generateListener({
|
||||
certificate: 'faultyTest',
|
||||
@@ -423,17 +225,16 @@ Please check if docker is running and Node.js can access the docker socket (/var
|
||||
|
||||
${protocolHardeningParameters}
|
||||
`);
|
||||
}
|
||||
});
|
||||
|
||||
@test
|
||||
'create listener correct config'() {
|
||||
const testCertDir = resolve(__dirname, 'certs');
|
||||
mkdirSync(testCertDir);
|
||||
it('should create listener correct config', async function () {
|
||||
const testCertDirectory = path.resolve(path.dirname(fileURLToPath(import.meta.url)), 'certs');
|
||||
mkdirSync(testCertDirectory);
|
||||
|
||||
const certificateFile = resolve(testCertDir, 'ssl.crt');
|
||||
const certificateKeyFile = resolve(testCertDir, 'ssl.key');
|
||||
const certificateChainFile = resolve(testCertDir, 'chain.crt');
|
||||
const dhparamFile = resolve(testCertDir, 'dhparam.pem');
|
||||
const certificateFile = path.resolve(testCertDirectory, 'ssl.crt');
|
||||
const certificateKeyFile = path.resolve(testCertDirectory, 'ssl.key');
|
||||
const certificateChainFile = path.resolve(testCertDirectory, 'chain.crt');
|
||||
const dhparamFile = path.resolve(testCertDirectory, 'dhparam.pem');
|
||||
|
||||
writeFileSync(certificateFile, 'Test');
|
||||
writeFileSync(certificateKeyFile, 'Test');
|
||||
@@ -461,6 +262,6 @@ ${protocolHardeningParameters}
|
||||
unlinkSync(certificateKeyFile);
|
||||
unlinkSync(certificateChainFile);
|
||||
unlinkSync(dhparamFile);
|
||||
rmdirSync(testCertDir);
|
||||
}
|
||||
}
|
||||
rmdirSync(testCertDirectory);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"extends": "@openstapps/tsconfig",
|
||||
"exclude": ["./config/", "./lib/", "./test/"]
|
||||
"exclude": ["./config/", "./lib/"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user