Skip to content
This repository was archived by the owner on Nov 23, 2022. It is now read-only.

Commit 748dd4a

Browse files
committed
Merge branch 'develop', prepare 4.1.0
2 parents 6d7df7a + 51880d1 commit 748dd4a

File tree

8 files changed

+72
-36
lines changed

8 files changed

+72
-36
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "exoframe-server",
3-
"version": "4.0.3",
3+
"version": "4.0.4-dev",
44
"description": "Exoframe is a self-hosted tool that allows simple one-command deployments using Docker",
55
"main": "bin/server-core.js",
66
"bin": "bin/exoframe-server.js",

src/docker/start.js

Lines changed: 5 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,11 @@
11
// our modules
22
const docker = require('./docker');
33
const {initNetwork, createNetwork} = require('../docker/network');
4-
const {getProjectConfig, nameFromImage, projectFromConfig, writeStatus} = require('../util');
5-
const {getSecretsCollection} = require('../db/secrets');
4+
const {getProjectConfig, nameFromImage, projectFromConfig, writeStatus, getHost, getEnv} = require('../util');
65
const {getConfig} = require('../config');
76
const {getPlugins} = require('../plugins');
87
const logger = require('../logger');
98

10-
// try to find secret with current value name and return secret value if present
11-
const valueOrSecret = (value, secrets) => {
12-
const secret = secrets.find(s => `@${s.name}` === value);
13-
if (secret) {
14-
return secret.value;
15-
}
16-
return value;
17-
};
18-
199
exports.startFromParams = async ({
2010
image,
2111
deploymentName,
@@ -166,29 +156,13 @@ exports.start = async ({image, username, folder, resultStream, existing = []}) =
166156
// get project info
167157
const config = getProjectConfig(folder);
168158

169-
// generate host
170-
// construct base domain from config, prepend with "." if it's not there
171-
const baseDomain = serverConfig.baseDomain ? serverConfig.baseDomain.replace(/^(\.?)/, '.') : undefined;
172-
// construc default domain using given base domain
173-
const defaultDomain = baseDomain ? `${name}${baseDomain}` : undefined;
174-
// construct host
175-
const host = config.domain === undefined ? defaultDomain : config.domain;
176159
// generate project name
177160
const project = projectFromConfig({username, config});
178161

179-
// replace env vars values with secrets if needed
180-
const secrets = getSecretsCollection().find({user: username});
181-
// generate env vars (with secrets)
182-
const userEnv = config.env
183-
? Object.keys(config.env).map(key => `${key}=${valueOrSecret(config.env[key], secrets)}`)
184-
: [];
185-
const exoEnv = [
186-
`EXOFRAME_DEPLOYMENT=${name}`,
187-
`EXOFRAME_USER=${username}`,
188-
`EXOFRAME_PROJECT=${project}`,
189-
`EXOFRAME_HOST=${host}`,
190-
];
191-
const Env = userEnv.concat(exoEnv);
162+
// generate host
163+
const host = getHost({serverConfig, name, config});
164+
165+
const Env = getEnv({username, config, name, project, host}).map((pair) => pair.join('='));
192166

193167
// construct restart policy
194168
let RestartPolicy = {};

src/docker/templates/compose.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const path = require('path');
55
const yaml = require('js-yaml');
66
const uuid = require('uuid');
77
const {spawn} = require('child_process');
8+
const {getHost, getEnv} = require('../../util');
89

910
// generates new base name for deployment
1011
const generateBaseName = ({username, config}) =>
@@ -63,9 +64,9 @@ const updateCompose = ({username, baseName, serverConfig, composePath}) => {
6364
};
6465

6566
// function to execute docker-compose file and return the output
66-
const executeCompose = ({cmd, resultStream, tempDockerDir, folder, writeStatus}) =>
67+
const executeCompose = ({cmd, resultStream, tempDockerDir, folder, writeStatus, env = {}}) =>
6768
new Promise(resolve => {
68-
const dc = spawn('docker-compose', cmd, {cwd: path.join(tempDockerDir, folder)});
69+
const dc = spawn('docker-compose', cmd, {cwd: path.join(tempDockerDir, folder), env: {...process.env, ...env}});
6970
const log = [];
7071

7172
dc.stdout.on('data', data => {
@@ -137,9 +138,15 @@ exports.executeTemplate = async ({
137138
util.logger.debug('Compose modified:', composeConfig);
138139
util.writeStatus(resultStream, {message: 'Compose file modified', data: composeConfig, level: 'verbose'});
139140

141+
// generate host
142+
const host = getHost({serverConfig, name: baseName, config});
143+
const env = getEnv({username, config, name: baseName, host})
144+
.reduce((merged, [key, value]) => ({...merged, [key]: value}), {});
145+
140146
// re-build images if needed
141147
const {code: buildExitCode, log: buildLog} = await executeCompose({
142148
cmd: ['--project-name', baseName, 'build'],
149+
env,
143150
resultStream,
144151
tempDockerDir,
145152
folder,
@@ -179,6 +186,7 @@ exports.executeTemplate = async ({
179186
// execute compose 'up -d'
180187
const exitCode = await executeCompose({
181188
cmd: ['--project-name', baseName, 'up', '-d'],
189+
env,
182190
resultStream,
183191
tempDockerDir,
184192
folder,

src/util/index.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,20 @@ const {spawn} = require('child_process');
66
const tar = require('tar-fs');
77
const rimraf = require('rimraf');
88
const uuid = require('uuid');
9+
const {getSecretsCollection} = require('../db/secrets');
910

1011
// our modules
1112
const {tempDockerDir: tempDir} = require('../config');
1213

14+
// try to find secret with current value name and return secret value if present
15+
const valueOrSecret = (value, secrets) => {
16+
const secret = secrets.find(s => `@${s.name}` === value);
17+
if (secret) {
18+
return secret.value;
19+
}
20+
return value;
21+
};
22+
1323
// cleanup temp folder
1424
exports.cleanTemp = folder => new Promise(resolve => rimraf(path.join(tempDir, folder), resolve));
1525

@@ -81,3 +91,29 @@ exports.compareNames = (nameOne = '', nameTwo = '') => {
8191
const nameTwoClean = nameTwoParts.slice(0, nameTwoParts.length - 2).join('-');
8292
return nameOneClean === nameTwoClean;
8393
};
94+
95+
exports.getHost = ({serverConfig, name, config}) => {
96+
// construct base domain from config, prepend with "." if it's not there
97+
const baseDomain = serverConfig.baseDomain ? serverConfig.baseDomain.replace(/^(\.?)/, '.') : undefined;
98+
// construct default domain using given base domain
99+
const defaultDomain = baseDomain ? `${name}${baseDomain}` : undefined;
100+
// construct host
101+
const host = config.domain === undefined ? defaultDomain : config.domain;
102+
return host;
103+
};
104+
105+
exports.getEnv = ({username, config, name, host, project = config.project || name}) => {
106+
// replace env vars values with secrets if needed
107+
const secrets = getSecretsCollection().find({ user: username });
108+
// generate env vars (with secrets)
109+
const userEnv = config.env
110+
? Object.entries(config.env).map(([key, value]) => [key, valueOrSecret(value, secrets)])
111+
: [];
112+
return [
113+
...userEnv,
114+
['EXOFRAME_DEPLOYMENT', name],
115+
['EXOFRAME_USER', username],
116+
['EXOFRAME_PROJECT', project],
117+
['EXOFRAME_HOST', host],
118+
];
119+
};

test/__mocks__/util.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
/* eslint-env jest */
22
// mock util module
3+
const {runYarn, getProjectConfig, ...actualUtil} = jest.requireActual('../../src/util/index.js');
34
const util = jest.genMockFromModule('../../src/util/index.js');
45

56
util.runYarn = () => new Promise(r => r());
67
util.getProjectConfig = folder => folder;
78

8-
module.exports = util;
9+
module.exports = {...util, ...actualUtil};

test/deploy.test.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const authToken = require('./fixtures/authToken');
1515
const {startServer} = require('../src');
1616
const docker = require('../src/docker/docker');
1717
const {initNetwork} = require('../src/docker/network');
18+
const {getSecretsCollection, secretsInited} = require('../src/db/secrets')
1819

1920
// create tar streams
2021
const streamDockerImage = tar.pack(path.join(__dirname, 'fixtures', 'docker-image-project'));
@@ -400,7 +401,12 @@ test('Should update simple HTML project', async done => {
400401
done();
401402
});
402403

404+
const testSecret = {user: 'admin', name: 'test-secret', value: 'custom-secret-value'};
405+
403406
test('Should deploy simple compose project', async done => {
407+
await secretsInited;
408+
getSecretsCollection().insert(testSecret);
409+
404410
const options = Object.assign(optionsBase, {
405411
payload: streamCompose,
406412
});
@@ -451,6 +457,8 @@ test('Should deploy simple compose project', async done => {
451457
expect(containerOne.Labels['traefik.enable']).toEqual('true');
452458
expect(containerTwo.Labels['traefik.enable']).toEqual('true');
453459
expect(containerOne.Labels['traefik.frontend.rule']).toEqual('Host:test.dev');
460+
expect(containerOne.Labels['custom.envvar']).toEqual('custom-value');
461+
expect(containerOne.Labels['custom.secret']).toEqual('custom-secret-value');
454462
expect(containerOne.NetworkSettings.Networks.exoframe).toBeDefined();
455463
expect(containerTwo.NetworkSettings.Networks.exoframe).toBeDefined();
456464

@@ -513,6 +521,8 @@ test('Should update simple compose project', async done => {
513521
expect(containerOne.Labels['traefik.enable']).toEqual('true');
514522
expect(containerTwo.Labels['traefik.enable']).toEqual('true');
515523
expect(containerOne.Labels['traefik.frontend.rule']).toEqual('Host:test.dev');
524+
expect(containerOne.Labels['custom.envvar']).toEqual('custom-value');
525+
expect(containerOne.Labels['custom.secret']).toEqual('custom-secret-value');
516526
expect(containerOne.NetworkSettings.Networks.exoframe).toBeDefined();
517527
expect(containerTwo.NetworkSettings.Networks.exoframe).toBeDefined();
518528

@@ -535,6 +545,7 @@ test('Should update simple compose project', async done => {
535545
await instanceOne.remove({force: true});
536546
const instanceTwo = docker.getContainer(containerTwo.Id);
537547
await instanceTwo.remove({force: true});
548+
getSecretsCollection().remove(testSecret);
538549

539550
done();
540551
});

test/fixtures/compose-project/docker-compose.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,7 @@ services:
44
build: .
55
labels:
66
traefik.frontend.rule: 'Host:test.dev'
7+
custom.envvar: "${CUSTOM_LABEL}"
8+
custom.secret: "${CUSTOM_SECRET}"
79
redis:
810
image: "redis:alpine"
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
{
22
"name": "test-compose-deploy",
3-
"restart": "no"
3+
"restart": "no",
4+
"env": {
5+
"CUSTOM_LABEL": "custom-value",
6+
"CUSTOM_SECRET": "@test-secret"
7+
}
48
}

0 commit comments

Comments
 (0)