From e5b40af3352e743b215e85fa242e3ce15e45abab Mon Sep 17 00:00:00 2001 From: Viktor Varland Date: Mon, 1 Jul 2019 14:34:43 +0200 Subject: [PATCH] feat: decouple configs from cluster (#53) * fix: each cluster has its own compose cache * fix: use multipe cluster caches * fix: store a cache obj * fix: use the dirname of the cache loc * fix: log cache obj * refactor: extra constant for cache config * fix: shorten the composeProject name after isolation * fix: simplify command cache handling * test: fix tests * docs: fix desc for changed arg * chore: new yarn.lock file after master was merged in * feat: allow cluster configuration to be stored in ~/.config/d2/config.js * docs: update readme for cluster * docs: update readme * docs: fix broken syntax * refactor: break up path and clean up imports * refactor: clean up imports * docs: add note about config order * chore: add repo config files * refactor: address pr comments * refactor: prefix docker-compose projects * refactor: rearrange noop change * chore: correct log variable --- .commitlintrc.js | 3 + .dependabot/config.yml | 16 ++ .eslintrc.js | 39 +++ .github/semantic.yml | 4 + package.json | 3 +- packages/cluster/README.md | 301 +++++++++++++++++++- packages/cluster/src/commands/down.js | 33 ++- packages/cluster/src/commands/logs.js | 24 +- packages/cluster/src/commands/restart.js | 20 +- packages/cluster/src/commands/seed.js | 25 +- packages/cluster/src/commands/up.js | 10 +- packages/cluster/src/common.js | 81 ++++-- packages/cluster/tests/setup-environment.js | 102 +++++-- yarn.lock | 6 + 14 files changed, 554 insertions(+), 113 deletions(-) create mode 100644 .commitlintrc.js create mode 100644 .dependabot/config.yml create mode 100644 .eslintrc.js create mode 100644 .github/semantic.yml diff --git a/.commitlintrc.js b/.commitlintrc.js new file mode 100644 index 00000000..3e16e7f1 --- /dev/null +++ b/.commitlintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: ['@commitlint/config-conventional'], +} diff --git a/.dependabot/config.yml b/.dependabot/config.yml new file mode 100644 index 00000000..1bfa1cf9 --- /dev/null +++ b/.dependabot/config.yml @@ -0,0 +1,16 @@ +version: 1 + +update_configs: + - package_manager: "javascript" + directory: "/" + update_schedule: "live" + version_requirement_updates: "increase_versions" + - package_manager: "java:maven" + directory: "/" + update_schedule: "monthly" + - package_manager: "docker" + directory: "/" + update_schedule: "weekly" + - package_manager: "submodules" + directory: "/" + update_schedule: "weekly" diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..74c8bab3 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,39 @@ +const SEVERITY = 2 + +module.exports = { + root: true, + + parser: 'babel-eslint', + + env: { + browser: true, + node: true, + jest: true, + }, + + parserOptions: { + // latest standard is ok, eq. to 9 + ecmaVersion: 2018, + ecmaFeatures: { + jsx: true, + modules: true, + }, + }, + + rules: { + 'max-params': [ + SEVERITY, + { + max: 3, + }, + ], + 'prefer-const': [ + SEVERITY, + { + destructuring: 'any', + ignoreReadBeforeAssign: false, + }, + ], + 'no-mixed-spaces-and-tabs': [SEVERITY], + }, +} diff --git a/.github/semantic.yml b/.github/semantic.yml new file mode 100644 index 00000000..756b8b5a --- /dev/null +++ b/.github/semantic.yml @@ -0,0 +1,4 @@ +titleOnly: true +commitsOnly: false +titleAndCommits: false +allowMergeCommits: false diff --git a/package.json b/package.json index 1d99d6ed..9fd3f712 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "devDependencies": { "@dhis2/cli-style": "3.3.4", "husky": "^2.7.0", - "tape": "^4.10.2" + "tape": "^4.10.2", + "tape-await": "^0.1.2" }, "husky": { "hooks": { diff --git a/packages/cluster/README.md b/packages/cluster/README.md index f7891e8c..f3c85c30 100644 --- a/packages/cluster/README.md +++ b/packages/cluster/README.md @@ -19,21 +19,298 @@ While you can install and run `d2-cluster` from `@dhis2/cli-cluster`, the prefer Depending on your installation method, the following examples which use `d2 cluster` may need to be modified to use `d2-cluster`, or `npx @dhis2/cli-cluster`. -## Spin up a development cluster +## Common concepts -This sequence of commands will install the latest bleeding-edge DHIS2 core instance in a Docker container and seed it with the relevant Sierra Leone [demo database](https://github.com/dhis2/dhis2-demo-db/) +### Release channels -```sh -> d2 cluster up dev --seed -> d2 cluster logs dev core -# Wait for the line "Catalina.start Server startup in XXXX ms", then ctrl+c / cmd+c to terminate -> d2 cluster restart dev gateway # this is a hack necessary to rehup the gateway -# DHIS2 is available with Sierra Leone db at http://localhost:8080 -# Run the Analytics Table export task from the Data Administration app +DHIS2 has several release channels, such as **dev** and **stable**. + +To each channel several artifacts can be published, so the **stable** +channel contains all the stable releases of DHIS2, such as 2.32.0, +2.32.1, etc. + +The **dev** channel is the latest build straight from the development +branches. There is one development branch per supported release of +DHIS2. + +For our Docker images, that means that we have one repo on Docker Hub +per channel: + +- Stable: https://cloud.docker.com/u/dhis2/repository/docker/dhis2/core +- Dev: https://cloud.docker.com/u/dhis2/repository/docker/dhis2/core-dev + +### Tags + +Within each Docker repo, we have multiple tags. The channel coupled with +the tag uniquely identifies a built DHIS2 Docker image. This is +important for how the `cluster` command works. + +For the **stable channel** each tag represents a formally released version +of DHIS2. + +- [2.32.0](https://github.com/dhis2/dhis2-core/tree/2.32.0) +- [2.31.3](https://github.com/dhis2/dhis2-core/tree/2.31.3) + +For the **dev channel**, each tag represents the last build from the +development branches in +[dhis2/dhis2-core](https://github.com/dhis2/dhis2-core): + +- [master](https://github.com/dhis2/dhis2-core/tree/master) +- [2.32](https://github.com/dhis2/dhis2-core/tree/2.32) +- [2.31](https://github.com/dhis2/dhis2-core/tree/2.31) +- [2.30](https://github.com/dhis2/dhis2-core/tree/2.30) + +### Database dumps + +For development DHIS2 provides a [set of database +dumps](https://github.com/dhis2/dhis2-demo-db) which are essential in +getting a usable environment up and running quickly. + +Most often we use the [Sierra +Leone](https://github.com/dhis2/dhis2-demo-db/tree/master/sierra-leone) +dumps. + +## Usage + +### Getting help + +Remember that the help is your friend: + +```bash +d2 cluster --help +``` + +### Command layout + +There are two arguments that are always required for the `cluster` to +command to be able to do anything at all: `{command}` and `{name}`. + +```bash +d2 cluster {command} {name} ``` -Note that any Docker image in [amcgee/dhis2-core](https://cloud.docker.com/u/amcgee/repository/docker/amcgee/dhis2-core) can be used, just omit the `-alpine` suffix. Sierra Leone demo databases are automatically downloaded from the [dhis2-demo-db](https://github.com/dhis2/dhis2-demo-db) repository. +The command refers to an action, like `up` or `down` and the name is the +name of the cluster to operate on, which can be anything you like, like +`mydev`, `superfly`, or `2.32`. + +### Arguments + +In addition to the command and name, there are more arguments you can +pass to `cluster` to customize your environment. If the arguments are +omitted there is some fallback logic, so even if they are not used, they +are important to know about. + +- `--channel`: This matches to the Docker Hub repository mentioned above + in [Release channels](#release-channels). E.g. `dev`. + +- `--dhis2-version`: This matches to the [tag name within a Docker + Hub repo](#tags). E.g. + [`2.32`](https://cloud.docker.com/u/dhis2/repository/docker/dhis2/core-dev/tags) + +- `--db-version`: This matches to the database dumps mentioned in + [Database dumps](#database-dumps). E.g. `dev` or `2.32`. -## Known Issues +So through a combination of these arguments: `channel`, `dhis2-version`, +and `db-version` we can spin up a cluster. + +### Spin up a stable version + +First up, in the best case scenario where you want to run DHIS2 2.32.0 on +an empty database, you are able to run: + +```bash +d2 cluster up 2.32.0 + +# result +# --- +# channel: stable +# dhis2Version: 2.32.0 +# dbVersion: empty +``` + +Usually you want to `seed` your database with a database dump from +Sierra Leone to have an instance set up with data. If you add the +`--seed` command to the command above, it will try to find the database +dump 2.32.0 in the +[dhis2-db-demo](https://github.com/dhis2/dhis2-demo-db/tree/master/sierra-leone) +repo. That doesn't exist, but 2.32 does. + +```bash +d2 cluster up 2.32.0 --seed +# fail: there's no db dump with 2.32.0 + +d2 cluster up 2.32.0 --db-version 2.32 --seed + +# result +# --- +# channel: stable +# dhis2Version: 2.32.0 +# dbVersion: 2.32 +``` + +### Spin up a development version + +Let's switch to the **dev** channel as we want the bleeding edge build +from 2.32. We want it seeded with a 2.32 dump so we are going to run it +with `--seed`. + +```bash +d2 cluster up 2.32 --channel dev --seed + +# result +# --- +# channel: dev +# dhis2Version: 2.32 +# dbVersion: 2.32 +``` + +Since the 2.32 branch exists in +[dhis2-core](https://github.com/dhis2/dhis2-core/tree/2.32) and the 2.32 +dump exists in +[dhis2-demo-db](https://github.com/dhis2/dhis2-demo-db/tree/master/sierra-leone/2.32) +the tool doesn't need more information to create an environment. + +Now, let's run a `master` build from the **dev** channel: + +```bash +d2 cluster up master --channel dev --db-version dev --seed + +# result +# --- +# channel: dev +# dhis2Version: master +# dbVersion: dev +``` + +Since the `--dhis2-version` argument was omitted, it used the `{name}` +as fallback. Since we used `master` as the name, and the `master` tag +exists in the +[dhis2/core-dev](https://cloud.docker.com/u/dhis2/repository/docker/dhis2/core-dev/tags) +it is able to resolve a complete environment. + +We could also have run: + +```bash +d2 cluster up master --channel dev --db-version dev --dhis2-version master --seed +``` + +The name can be anything you wish, but remember to specify `channel`, +`dhis2-version`, and `db-version` in that case. + +## Configuration + +### Cached configuration + +To avoid having to pass in all arguments over and over when using the +`up` and `down` commands often, the `cluster` command caches your +configuration per cluster in a `config.json` file. + +```bash +d2 debug cache list cluster/2.32.0 +┌────────────────┬──────┬─────────────────────┐ +│ Name │ Size │ Modified │ +├────────────────┼──────┼─────────────────────┤ +│ config.json │ 171 │ 2019-06-06 11:07:37 │ +├────────────────┼──────┼─────────────────────┤ +│ docker-compose │ 512 │ 2019-06-06 11:07:32 │ +└────────────────┴──────┴─────────────────────┘ +``` + +And it looks like this: + +```bash +cat ~/.cache/d2/cache/cluster/2.32.0/config.json +{ + "channel": "dev", + "dbVersion": "2.32", + "dhis2Version": "2.32.0", + "customContext": false, + "image": "dhis2/core{channel}:{version}", + "port": 8080 +} +``` + +This means that if you run a command sequence like: + +```bash +d2 cluster up superfly --db-version 2.31 --dhis2-version master --seed --custom-context --port 9999 --channel dev + +d2 cluster down superfly + +d2 cluster up superfly +``` + +The second time you run `up superfly` it will use the configuration from +the first run: + +```bash +cat ~/.cache/d2/cache/cluster/superfly/config.json +{ + "channel": "dev", + "dbVersion": "2.31", + "dhis2Version": "master", + "customContext": true, + "image": "dhis2/core{channel}:{version}", + "port": "9999" +} +``` + +To purge the `config.json` file you can use: + +```bash +d2 debug cache purge cluster/superfly/config.json +? Are you sure you want to remove cache item "cluster/superfly/config.json"? Yes +Purged cache item cluster/superfly/config.json +``` + +### Persistent configuration + +It is also possible to set up your clusters in the `d2` configuration +file, e.g. `~/.config/d2/config.js`: + +```js +module.exports = { + cluster: { + channel: 'stable', + clusters: { + superfly: { + channel: 'dev', + dbVersion: '2.31', + dhis2Version: 'master', + customContext: true, + image: 'dhis2/core{channel}:{version}', + port: 9999 + } + } + } +} +``` + +```bash +d2 cluster up superfly + +# saves the configuration to `config.json` +cat ~/.cache/d2/cache/cluster/superfly/config.json +{ + "channel": "dev", + "dbVersion": "2.31", + "dhis2Version": "master", + "customContext": true, + "image": "dhis2/core{channel}:{version}", + "port": 9999 +} +``` + +From here it's possible to override the configuration file properties +for a cluster as well: + +``` +# port is 9999 in ~/.config/d2/config.js:clusters.superfly.port +d2 cluster up superfly --port 8888 + +# port is saved as 8888 in ~/.cache/d2/cache/cluster/superfly/config.json:port +``` -- The 2.31 patch releases (2.31.0, 2.31.1, etc.) are nested, so they cannot be downloaded automatically. You can download any .sql.gz file manually and specify it with the `--seedFile` option to the `d2 cluster up` command or the `--path` option to the `d2 cluster seed` command. +Now for each subsequence `down` and `up` command, the cached config will +take priority over the persistent configuration. When you clear the +cache, the persistent configuration will come into effect again. diff --git a/packages/cluster/src/commands/down.js b/packages/cluster/src/commands/down.js index 4a7bb4f5..335ffa2a 100644 --- a/packages/cluster/src/commands/down.js +++ b/packages/cluster/src/commands/down.js @@ -3,21 +3,21 @@ const path = require('path') const { exec, reporter } = require('@dhis2/cli-helpers-engine') const { initDockerComposeCache, - makeComposeProject, resolveConfiguration, + makeComposeProject, + makeEnvironment, + cleanCache, } = require('../common') -const defaults = require('../defaults') +const run = async function(argv) { + const { name, clean, getCache } = argv + const cfg = await resolveConfiguration(argv) -const run = async function({ name, clean, getCache, cluster, ...argv }) { - const { - dockerComposeRepository, - dockerComposeDirectory, - } = resolveConfiguration(argv, {}, cluster) const cacheLocation = await initDockerComposeCache({ + composeProjectName: name, cache: getCache(), - dockerComposeRepository, - dockerComposeDirectory, + dockerComposeRepository: cfg.dockerComposeRepository, + dockerComposeDirectory: cfg.dockerComposeDirectory, force: false, }) @@ -26,7 +26,7 @@ const run = async function({ name, clean, getCache, cluster, ...argv }) { process.exit(1) } - console.log(`Winding down cluster ${chalk.cyan(name)}`) + reporter.info(`Winding down cluster ${chalk.cyan(name)}`) try { await exec({ cmd: 'docker-compose', @@ -37,12 +37,15 @@ const run = async function({ name, clean, getCache, cluster, ...argv }) { path.join(cacheLocation, 'docker-compose.yml'), 'down', ].concat(clean ? ['--volumes'] : []), - env: { - DHIS2_CORE_IMAGE: defaults.image, - DHIS2_CORE_NAME: name, - DHIS2_CORE_PORT: defaults.port, - }, + env: makeEnvironment(cfg), }) + + if (clean) { + cleanCache({ + name, + cache: getCache(), + }) + } } catch (e) { reporter.error('Failed to execute docker-compose', e) process.exit(1) diff --git a/packages/cluster/src/commands/logs.js b/packages/cluster/src/commands/logs.js index ff643f65..f2bcfd19 100644 --- a/packages/cluster/src/commands/logs.js +++ b/packages/cluster/src/commands/logs.js @@ -4,30 +4,33 @@ const { reporter, exec, tryCatchAsync } = require('@dhis2/cli-helpers-engine') const { initDockerComposeCache, makeComposeProject, + makeEnvironment, resolveConfiguration, } = require('../common') -const defaults = require('../defaults') -const run = async function({ service, name, cluster, ...argv }) { - const { - dockerComposeRepository, - dockerComposeDirectory, - } = resolveConfiguration(argv, {}, cluster) +const run = async function(argv) { + const { service, name } = argv + const cfg = await resolveConfiguration(argv) + const cacheLocation = await initDockerComposeCache({ + composeProjectName: name, cache: argv.getCache(), - dockerComposeRepository, - dockerComposeDirectory, + dockerComposeRepository: cfg.dockerComposeRepository, + dockerComposeDirectory: cfg.dockerComposeDirectory, force: false, }) + if (!cacheLocation) { reporter.error('Failed to initialize cache...') process.exit(1) } + reporter.info( `Reading logs from cluster version ${chalk.cyan(name)}${ service ? ` <${service}>` : '' }` ) + const res = await tryCatchAsync( 'exec(docker-compose)', exec({ @@ -40,10 +43,7 @@ const run = async function({ service, name, cluster, ...argv }) { 'logs', '-f', ].concat(service ? [service] : []), - env: { - DHIS2_CORE_NAME: name, - DHIS2_CORE_PORT: 8000, // doesn't matter - }, + env: makeEnvironment(cfg), pipe: true, }) ) diff --git a/packages/cluster/src/commands/restart.js b/packages/cluster/src/commands/restart.js index 4d20a336..c9e17fcc 100644 --- a/packages/cluster/src/commands/restart.js +++ b/packages/cluster/src/commands/restart.js @@ -4,20 +4,21 @@ const { reporter, exec, tryCatchAsync } = require('@dhis2/cli-helpers-engine') const { initDockerComposeCache, makeComposeProject, + makeEnvironment, resolveConfiguration, } = require('../common') const defaults = require('../defaults') -const run = async function({ service, name, port, cluster, ...argv }) { - const { - dockerComposeRepository, - dockerComposeDirectory, - } = resolveConfiguration(argv, {}, cluster) +const run = async function(argv) { + const { name, service } = argv + const cfg = await resolveConfiguration(argv) + const cacheLocation = await initDockerComposeCache({ + composeProjectName: name, cache: argv.getCache(), - dockerComposeRepository, - dockerComposeDirectory, + dockerComposeRepository: cfg.dockerComposeRepository, + dockerComposeDirectory: cfg.dockerComposeDirectory, force: false, }) if (!cacheLocation) { @@ -36,10 +37,7 @@ const run = async function({ service, name, port, cluster, ...argv }) { path.join(cacheLocation, 'docker-compose.yml'), 'restart', ].concat(service ? [service] : []), - env: { - DHIS2_CORE_NAME: name, - DHIS2_CORE_PORT: port, - }, + env: makeEnvironment(cfg), pipe: true, }) ) diff --git a/packages/cluster/src/commands/seed.js b/packages/cluster/src/commands/seed.js index 7d0bc826..25ccbeaf 100644 --- a/packages/cluster/src/commands/seed.js +++ b/packages/cluster/src/commands/seed.js @@ -2,20 +2,16 @@ const { reporter } = require('@dhis2/cli-helpers-engine') const { initDockerComposeCache, resolveConfiguration } = require('../common') const { seed } = require('../db') -const defaults = require('../defaults') +const run = async function(argv) { + const { name, getCache } = argv -const run = async function({ dhis2Version, ...argv }) { - const { cluster } = argv - const { - dockerComposeRepository, - dockerComposeDirectory, - dbVersion, - } = resolveConfiguration(argv, {}, cluster) + const cfg = await resolveConfiguration(argv) const cacheLocation = await initDockerComposeCache({ - cache: argv.getCache(), - dockerComposeRepository, - dockerComposeDirectory, + composeProjectName: name, + cache: getCache(), + dockerComposeRepository: cfg.dockerComposeRepository, + dockerComposeDirectory: cfg.dockerComposeDirectory, force: false, }) @@ -26,7 +22,8 @@ const run = async function({ dhis2Version, ...argv }) { return await seed({ cacheLocation, - dbVersion, + dbVersion: cfg.dbVersion, + url: cfg.demoDatabaseURL, ...argv, }) } @@ -41,8 +38,8 @@ module.exports = { type: 'boolean', default: false, }, - dhis2Version: { - desc: 'DHIS2 version to use', + dbVersion: { + desc: 'Version of the Database dump to use', type: 'string', }, }, diff --git a/packages/cluster/src/commands/up.js b/packages/cluster/src/commands/up.js index aff51218..271d0f98 100644 --- a/packages/cluster/src/commands/up.js +++ b/packages/cluster/src/commands/up.js @@ -3,8 +3,8 @@ const path = require('path') const { reporter, exec, tryCatchAsync } = require('@dhis2/cli-helpers-engine') const { initDockerComposeCache, - makeComposeProject, makeEnvironment, + makeComposeProject, resolveConfiguration, } = require('../common') @@ -12,11 +12,13 @@ const defaults = require('../defaults') const { seed: doSeed } = require('../db') const run = async function(argv) { - const { cluster, name, seed, seedFile, update } = argv - const cfg = resolveConfiguration(argv, {}, cluster) + const { name, seed, seedFile, update, getCache } = argv + + const cfg = await resolveConfiguration(argv) const cacheLocation = await initDockerComposeCache({ - cache: argv.getCache(), + composeProjectName: name, + cache: getCache(), dockerComposeRepository: cfg.dockerComposeRepository, dockerComposeDirectory: cfg.dockerComposeDirectory, force: update, diff --git a/packages/cluster/src/common.js b/packages/cluster/src/common.js index f6260fd9..1bde7090 100644 --- a/packages/cluster/src/common.js +++ b/packages/cluster/src/common.js @@ -1,19 +1,30 @@ const path = require('path') +const fs = require('fs') const { reporter } = require('@dhis2/cli-helpers-engine') const defaults = require('./defaults') -const dockerComposeCacheName = 'd2-cluster-docker-compose-v2' +const clusterDir = 'clusters' +const dockerComposeCacheName = 'docker-compose' +const cacheFile = 'config.json' module.exports.initDockerComposeCache = async ({ + composeProjectName, cache, dockerComposeRepository, dockerComposeDirectory, force, }) => { - const cacheDir = path.join(dockerComposeCacheName, dockerComposeDirectory) - const exists = await cache.exists(cacheDir) + const cacheDir = path.join( + clusterDir, + composeProjectName, + dockerComposeCacheName + ) + + const cachePath = path.join(cacheDir, dockerComposeDirectory) + + const exists = await cache.exists(cachePath) if (exists && !force) { reporter.debug( @@ -23,18 +34,14 @@ module.exports.initDockerComposeCache = async ({ reporter.info('Initializing Docker Compose repository...') try { - const repoDir = await cache.get( - dockerComposeRepository, - dockerComposeCacheName, - { - force: true, - } - ) + await cache.get(dockerComposeRepository, cacheDir, { + force: true, + }) - const created = await cache.exists(cacheDir) + const created = await cache.exists(cachePath) if (created) { - reporter.debug(`Cache created at: ${cacheDir}`) + reporter.debug(`Cache created at: ${cachePath}`) } } catch (e) { reporter.error('Initialization failed!') @@ -42,11 +49,9 @@ module.exports.initDockerComposeCache = async ({ } } - return cache.getCacheLocation(cacheDir) + return cache.getCacheLocation(cachePath) } -module.exports.makeComposeProject = version => `d2-cluster-${version}` - module.exports.substituteVersion = (string, version) => replacer(string, 'version', version) @@ -80,8 +85,29 @@ function replacer(string, token, value) { return string.replace(regexp, value) } -function resolveConfiguration(argv = {}, cache = {}, config = {}) { - const resolved = Object.assign({}, defaults, config, cache, argv) +async function resolveConfiguration(argv = {}) { + const file = path.join(clusterDir, argv.name, cacheFile) + + let currentCache + try { + currentCache = JSON.parse(await argv.getCache().read(file)) + } catch (e) { + reporter.debug('JSON parse of cache file failed', e) + } + + let currentConfig + if (argv.cluster && argv.cluster.clusters) { + currentConfig = argv.cluster.clusters[argv.name] + } + + // order matters! it defines the precedence of configuration + const cache = Object.assign({}, currentConfig, currentCache) + + const config = argv.cluster + const args = argv + + // order matters! it defines the precedence of configuration + const resolved = Object.assign({}, defaults, config, cache, args) // resolve specials... resolved.dhis2Version = resolved.dhis2Version || resolved.name @@ -99,9 +125,28 @@ function resolveConfiguration(argv = {}, cache = {}, config = {}) { reporter.debug('Resolved configuration\n', resolved) + await argv.getCache().write( + file, + JSON.stringify( + { + channel: resolved.channel, + dbVersion: resolved.dbVersion, + dhis2Version: resolved.dhis2Version, + customContext: resolved.customContext, + image: resolved.image, + port: resolved.port, + }, + null, + 4 + ) + ) + return resolved } +module.exports.cleanCache = async ({ cache, name }) => + await cache.purge(path.join(clusterDir, name)) + module.exports.makeEnvironment = cfg => { const env = { DHIS2_CORE_NAME: cfg.name, @@ -117,6 +162,8 @@ module.exports.makeEnvironment = cfg => { return env } +module.exports.makeComposeProject = name => `d2-cluster-${name}` + module.exports.getLocalClusters = async () => {} module.exports.makeDockerImage = makeDockerImage diff --git a/packages/cluster/tests/setup-environment.js b/packages/cluster/tests/setup-environment.js index 5b75eacc..5da1fd7c 100644 --- a/packages/cluster/tests/setup-environment.js +++ b/packages/cluster/tests/setup-environment.js @@ -1,19 +1,24 @@ -const test = require('tape') +const test = require('tape-await') const { makeEnvironment, resolveConfiguration } = require('../src/common.js') const defaults = require('../src/defaults.js') -test('build runtime environment based on defaults', function(t) { +const cache = obj => ({ + read: () => JSON.stringify(obj), + write: () => {}, +}) + +test('build runtime environment based on defaults', async function(t) { t.plan(1) const argv = { name: 'dev', + getCache: () => cache(null), } - const cache = {} - const config = {} - const actual = makeEnvironment(resolveConfiguration(argv, cache, config)) + const cfg = await resolveConfiguration(argv) + const actual = makeEnvironment(cfg) const expected = { DHIS2_CORE_NAME: 'dev', @@ -27,7 +32,7 @@ test('build runtime environment based on defaults', function(t) { t.deepEqual(actual, expected, 'default environment') }) -test('build runtime environment based on args', function(t) { +test('build runtime environment based on args', async function(t) { t.plan(1) const argv = { @@ -38,11 +43,11 @@ test('build runtime environment based on args', function(t) { channel: 'canary', variant: 'jetty-slackware', port: 8233, + getCache: () => cache(null), } - const cache = {} - const config = {} - const actual = makeEnvironment(resolveConfiguration(argv, cache, config)) + const cfg = await resolveConfiguration(argv) + const actual = makeEnvironment(cfg) const expected = { DHIS2_CORE_NAME: 'dev', @@ -56,16 +61,9 @@ test('build runtime environment based on args', function(t) { t.deepEqual(actual, expected, 'args environment') }) -test('build runtime environment based on mixed args and config', function(t) { +test('build runtime environment based on mixed args and config', async function(t) { t.plan(1) - const argv = { - name: 'mydev', - customContext: true, - } - - const cache = {} - const config = { dhis2Version: 'master', port: 8233, @@ -73,7 +71,15 @@ test('build runtime environment based on mixed args and config', function(t) { dbVersion: 'dev', } - const actual = makeEnvironment(resolveConfiguration(argv, cache, config)) + const argv = { + name: 'mydev', + customContext: true, + cluster: config, + getCache: () => cache(null), + } + + const cfg = await resolveConfiguration(argv) + const actual = makeEnvironment(cfg) const expected = { DHIS2_CORE_NAME: 'mydev', @@ -87,32 +93,74 @@ test('build runtime environment based on mixed args and config', function(t) { t.deepEqual(actual, expected, 'args and config environment') }) -test('build runtime environment based on mixed args, cache, config and defaults', function(t) { +test('build runtime environment based on mixed args, cache, config and defaults', async function(t) { t.plan(1) + const config = { + port: 8233, + dhis2Version: 'dev', + } + const argv = { name: 'mydev', + cluster: config, + getCache: () => + cache({ + customContext: true, + image: 'dhis2/core-canary:master-20190523-alpine', + }), } - const cache = { - customContext: true, - image: 'dhis2/core-canary:master-20190523-alpine', + const cfg = await resolveConfiguration(argv) + const actual = makeEnvironment(cfg) + + const expected = { + DHIS2_CORE_NAME: 'mydev', + DHIS2_CORE_CONTEXT_PATH: '/mydev', + DHIS2_CORE_IMAGE: 'dhis2/core-canary:master-20190523-alpine', + DHIS2_CORE_VERSION: 'dev', + DHIS2_CORE_DB_VERSION: 'dev', + DHIS2_CORE_PORT: 8233, } + t.deepEqual(actual, expected, 'merged environment') +}) + +test('build runtime environment based on mixed args, cache, config, custom per-cluster config and defaults', async function(t) { + t.plan(1) + const config = { port: 8233, dhis2Version: 'dev', + dbVersion: 'dev', + clusters: { + '2330': { + port: 9999, + dhis2Version: 'apa', + }, + }, + } + + const argv = { + name: '2330', + cluster: config, + getCache: () => + cache({ + customContext: true, + image: 'dhis2/core-canary:master-20190523-alpine', + }), } - const actual = makeEnvironment(resolveConfiguration(argv, cache, config)) + const cfg = await resolveConfiguration(argv) + const actual = makeEnvironment(cfg) const expected = { - DHIS2_CORE_NAME: 'mydev', - DHIS2_CORE_CONTEXT_PATH: '/mydev', + DHIS2_CORE_NAME: '2330', + DHIS2_CORE_CONTEXT_PATH: '/2330', DHIS2_CORE_IMAGE: 'dhis2/core-canary:master-20190523-alpine', - DHIS2_CORE_VERSION: 'dev', + DHIS2_CORE_VERSION: 'apa', DHIS2_CORE_DB_VERSION: 'dev', - DHIS2_CORE_PORT: 8233, + DHIS2_CORE_PORT: 9999, } t.deepEqual(actual, expected, 'merged environment') diff --git a/yarn.lock b/yarn.lock index b8571ff1..6b1dd840 100644 --- a/yarn.lock +++ b/yarn.lock @@ -182,6 +182,7 @@ "@dhis2/cli-helpers-engine@1.3.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@dhis2/cli-helpers-engine/-/cli-helpers-engine-1.3.0.tgz#e12f7a1bfd6223054cf9254e675b2b660b0690a5" + integrity sha512-S8Bcl9MbEGadDcJ9kknrXf7WWd+aq7lp4v1mTBBLxgluZyq5tfdJyxsyNxYmk0y2uFWQ+xvnCA7gpl9fEugbsA== dependencies: chalk "^2.4.2" fs-extra "^8.0.1" @@ -4743,6 +4744,11 @@ table@^5.2.3: slice-ansi "^2.1.0" string-width "^3.0.0" +tape-await@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/tape-await/-/tape-await-0.1.2.tgz#41f99110a2bc4728732d8bc058278b2fbf3c0bec" + integrity sha512-Gt1bXilp9uRTVj+DecLDs37tP1XwGXfFzWVqQEfW7foO9TNacy+aN5TdT0Kv6LI5t/9l3iOE4nX2hr2SQ4+OSg== + tape@^4.10.2: version "4.10.2" resolved "https://registry.yarnpkg.com/tape/-/tape-4.10.2.tgz#129fcf62f86df92687036a52cce7b8ddcaffd7a6"