diff --git a/packages/core/lib/actions/execute/assert/index.js b/packages/core/lib/actions/execute/assert/index.js index 0422c4e85..6aeb30f31 100644 --- a/packages/core/lib/actions/execute/assert/index.js +++ b/packages/core/lib/actions/execute/assert/index.js @@ -13,24 +13,26 @@ export default { if (config.content && config.trim) { config.content = config.content.trim(); } + const configForExecute = utils.object.filter(config, ['code', 'content', 'not', 'trim']); // Command exit code - if (config.code != null) { - const {code} = await this.execute(config, { - $relax: true - }); - if (!config.not) { - if (!config.code.includes(code)) { - throw utils.error('NIKITA_EXECUTE_ASSERT_EXIT_CODE', ['an unexpected exit code was encountered,', `got ${JSON.stringify(code)}`, config.code.length === 1 ? `while expecting ${config.code}.` : `while expecting one of ${JSON.stringify(config.code)}.`]); - } - } else { - if (config.code.includes(code)) { - throw utils.error('NIKITA_EXECUTE_ASSERT_NOT_EXIT_CODE', ['an unexpected exit code was encountered,', `got ${JSON.stringify(code)}`, config.code.length === 1 ? `while expecting anything but ${config.code}.` : `while expecting anything but one of ${JSON.stringify(config.code)}.`]); - } + const res = await this.execute({ + ...configForExecute, + $relax: true + }); + const code = res.error ? res.error.exit_code : res.code + const expectedCodes = utils.array.flatten(config.code.true, config.code.false); + if (!config.not) { + if (!expectedCodes.includes(code)) { + throw utils.error('NIKITA_EXECUTE_ASSERT_EXIT_CODE', ['an unexpected exit code was encountered,', `got ${JSON.stringify(code)}`, expectedCodes.length === 1 ? `while expecting ${expectedCodes}.` : `while expecting one of ${JSON.stringify(expectedCodes)}.`]); + } + } else { + if (expectedCodes.includes(code)) { + throw utils.error('NIKITA_EXECUTE_ASSERT_NOT_EXIT_CODE', ['an unexpected exit code was encountered,', `got ${JSON.stringify(code)}`, expectedCodes.length === 1 ? `while expecting anything but ${expectedCodes}.` : `while expecting anything but one of ${JSON.stringify(expectedCodes)}.`]); } } // Content is a string or a buffer if ((config.content != null) && typeof config.content === 'string') { - let {stdout} = await this.execute(config); + let {stdout} = await this.execute(configForExecute); if (config.trim) { stdout = stdout.trim(); } @@ -46,7 +48,7 @@ export default { } // Content is a regexp if ((config.content != null) && utils.regexp.is(config.content)) { - let {stdout} = await this.execute(config); + let {stdout} = await this.execute(configForExecute); if (config.trim) { stdout = stdout.trim(); } @@ -64,7 +66,7 @@ export default { hooks: { on_action: function({config, metadata}) { if (!config.content) { - return config.code != null ? config.code : config.code = [0]; + return config.code != null ? config.code : config.code = {true: [0]}; } } }, diff --git a/packages/core/lib/actions/execute/assert/schema.json b/packages/core/lib/actions/execute/assert/schema.json index 91fe889f5..9319a031e 100644 --- a/packages/core/lib/actions/execute/assert/schema.json +++ b/packages/core/lib/actions/execute/assert/schema.json @@ -1,15 +1,8 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/core/actions/execute#/definitions/config", "properties": { - "code": { - "type": ["array", "integer"], - "coercion": true, - "items": { - "type": "integer" - }, - "description": "Expected exit code, activated by default unless content is provided." - }, "content": { "oneOf": [ { diff --git a/packages/core/lib/actions/execute/schema.json b/packages/core/lib/actions/execute/schema.json index 2b45f1c36..d57a6a3f7 100644 --- a/packages/core/lib/actions/execute/schema.json +++ b/packages/core/lib/actions/execute/schema.json @@ -6,6 +6,14 @@ "type": ["boolean", "string"], "description": "Run this command inside a root directory with the arc-chroot command or any provided string, require the \"arch_chroot_rootdir\" option if activated." }, + "arch_chroot_rootdir": { + "type": "string", + "description": "Path to the mount point corresponding to the root directory, required if the \"arch_chroot\" option is activated." + }, + "arch_chroot_tmpdir": { + "type": "string", + "description": "Temporary path used with arch_chroot." + }, "bash": { "type": ["boolean", "string"], "description": "Serialize the command into a file and execute it with bash." @@ -184,18 +192,8 @@ "description": "Unix user id." } }, - "dependencies": { - "arch_chroot": { - "properties": { - "arch_chroot_rootdir": { - "type": "string", - "description": "Path to the mount point corresponding to the root directory, required if the \"arch_chroot\" option is activated." - } - }, - "required": [ - "arch_chroot_rootdir" - ] - } + "dependentRequired": { + "arch_chroot": ["arch_chroot_rootdir"] }, "required": [ "command" diff --git a/packages/core/lib/actions/fs/assert/index.js b/packages/core/lib/actions/fs/assert/index.js index fda3e8a4d..6aa7886f4 100644 --- a/packages/core/lib/actions/fs/assert/index.js +++ b/packages/core/lib/actions/fs/assert/index.js @@ -36,25 +36,58 @@ const errors = { }); }, NIKITA_FS_ASSERT_CONTENT_UNMATCH: function({config, expect}) { - return utils.error('NIKITA_FS_ASSERT_CONTENT_UNMATCH', ['content does not match the provided regexp,', `expect ${JSON.stringify(expect.toString())}`, `to match ${config.content.toString()},`, `location is ${JSON.stringify(config.target)}.`], { + return utils.error('NIKITA_FS_ASSERT_CONTENT_UNMATCH', [ + 'content does not match the provided regexp,', + `expect ${JSON.stringify(expect.toString())}`, + `to match ${config.content.toString()},`, + `location is ${JSON.stringify(config.target)}.` + ], { target: config.target, message: config.error }); }, NIKITA_FS_ASSERT_CONTENT_MATCH: function({config, expect}) { - return utils.error('NIKITA_FS_ASSERT_CONTENT_MATCH', ['content is matching the provided regexp,', `got ${JSON.stringify(expect.toString())}`, `to match ${config.content.toString()},`, `location is ${JSON.stringify(config.target)}.`], { + return utils.error('NIKITA_FS_ASSERT_CONTENT_MATCH', [ + 'content is matching the provided regexp,', + `got ${JSON.stringify(expect.toString())}`, + `to match ${config.content.toString()},`, + `location is ${JSON.stringify(config.target)}.` + ], { target: config.target, message: config.error }); }, + NIKITA_FS_ASSERT_FILETYPE_INVALID_VALUE: function({config}) { + return utils.error('NIKITA_FS_ASSERT_FILETYPE_INVALID_VALUE', [ + 'provided filetype is not supported,', + `got ${JSON.stringify(config.filetype)}.` + ], { + target: config.target, + }); + }, + NIKITA_FS_ASSERT_FILETYPE_INVALID_TYPE: function({config}) { + return utils.error('NIKITA_FS_ASSERT_FILETYPE_INVALID_TYPE', [ + 'filetype must be a string or a number,', + `got ${JSON.stringify(config.filetype)}.` + ], { + target: config.target, + }); + }, NIKITA_FS_ASSERT_HASH_UNMATCH: function({config, algo, hash}) { - return utils.error('NIKITA_FS_ASSERT_HASH_UNMATCH', [`an invalid ${algo} signature was computed,`, `expect ${JSON.stringify(hash.expected)},`, `got ${JSON.stringify(hash.actual)}.`], { + return utils.error('NIKITA_FS_ASSERT_HASH_UNMATCH', [ + `an invalid ${algo} signature was computed,`, + `expect ${JSON.stringify(hash.expected)},`, + `got ${JSON.stringify(hash.actual)}.` + ], { target: config.target, message: config.error }); }, NIKITA_FS_ASSERT_HASH_MATCH: function({config, algo, hash}) { - return utils.error('NIKITA_FS_ASSERT_HASH_MATCH', [`the ${algo} signatures are matching,`, `not expecting to equal ${JSON.stringify(hash)}.`], { + return utils.error('NIKITA_FS_ASSERT_HASH_MATCH', [ + `the ${algo} signatures are matching,`, + `not expecting to equal ${JSON.stringify(hash)}.` + ], { target: config.target, message: config.error }); @@ -63,7 +96,11 @@ const errors = { const expect = config.mode.map(function(mode) { return pad(4, utils.mode.stringify(mode), '0'); }); - return utils.error("NIKITA_FS_ASSERT_MODE_UNMATCH", ['content permission don\'t match the provided mode,', `expect ${expect},`, `got ${utils.mode.stringify(mode).slice(-4)}.`], { + return utils.error("NIKITA_FS_ASSERT_MODE_UNMATCH", [ + 'content permission don\'t match the provided mode,', + `expect ${expect},`, + `got ${utils.mode.stringify(mode).slice(-4)}.` + ], { target: config.target, message: config.error }); @@ -72,31 +109,47 @@ const errors = { const expect = config.mode.map(function(mode) { return pad(4, utils.mode.stringify(mode), '0'); }); - return utils.error("NIKITA_FS_ASSERT_MODE_MATCH", ['the content permission match the provided mode,', `not expecting to equal ${expect}.`], { + return utils.error("NIKITA_FS_ASSERT_MODE_MATCH", [ + 'the content permission match the provided mode,', + `not expecting to equal ${expect}.` + ], { target: config.target, message: config.error }); }, NIKITA_FS_ASSERT_UID_UNMATCH: function({config, actual}) { - return utils.error('NIKITA_FS_ASSERT_UID_UNMATCH', ['the uid of the target does not match the expected value,', `expected ${JSON.stringify(config.uid)},`, `got ${JSON.stringify(actual)}.`], { + return utils.error('NIKITA_FS_ASSERT_UID_UNMATCH', [ + 'the uid of the target does not match the expected value,', + `expected ${JSON.stringify(config.uid)},`, `got ${JSON.stringify(actual)}.` + ], { target: config.target, message: config.error }); }, NIKITA_FS_ASSERT_UID_MATCH: function({config}) { - return utils.error('NIKITA_FS_ASSERT_UID_MATCH', ['the uid of the target match the provided value,', `not expecting to equal ${JSON.stringify(config.uid)}.`], { + return utils.error('NIKITA_FS_ASSERT_UID_MATCH', [ + 'the uid of the target match the provided value,', + `not expecting to equal ${JSON.stringify(config.uid)}.` + ], { target: config.target, message: config.error }); }, NIKITA_FS_ASSERT_GID_UNMATCH: function({config, actual}) { - return utils.error('NIKITA_FS_ASSERT_GID_UNMATCH', ['the gid of the target does not match the expected value,', `expected ${JSON.stringify(config.uid)},`, `got ${JSON.stringify(actual)}.`], { + return utils.error('NIKITA_FS_ASSERT_GID_UNMATCH', [ + 'the gid of the target does not match the expected value,', + `expected ${JSON.stringify(config.uid)},`, + `got ${JSON.stringify(actual)}.` + ], { target: config.target, message: config.error }); }, NIKITA_FS_ASSERT_GID_MATCH: function({config}) { - return utils.error('NIKITA_FS_ASSERT_GID_MATCH', ['the gid of the target match the provided value,', `not expecting to equal ${JSON.stringify(config.uid)}.`], { + return utils.error('NIKITA_FS_ASSERT_GID_MATCH', [ + 'the gid of the target match the provided value,', + `not expecting to equal ${JSON.stringify(config.uid)}.` + ], { target: config.target, message: config.error }); @@ -149,10 +202,12 @@ export default { results.push(fs.constants.S_IFSOCK); break; default: - results.push(filetype); + throw errors.NIKITA_FS_ASSERT_FILETYPE_INVALID_VALUE({config}); } - } else { + } else if (typeof filetype === 'number') { results.push(filetype); + } else { + throw errors.NIKITA_FS_ASSERT_FILETYPE_INVALID_TYPE({config}); } } return results; diff --git a/packages/core/lib/actions/fs/assert/schema.json b/packages/core/lib/actions/fs/assert/schema.json index bfe1f01be..8443f761c 100644 --- a/packages/core/lib/actions/fs/assert/schema.json +++ b/packages/core/lib/actions/fs/assert/schema.json @@ -21,6 +21,10 @@ "default": "utf8", "description": "Content encoding, see the Node.js supported Buffer encoding." }, + "error": { + "type": "string", + "description": "Error message to use with failed assertions." + }, "filetype": { "type": "array", "coercion": true, diff --git a/packages/core/lib/actions/fs/base/createReadStream/schema.json b/packages/core/lib/actions/fs/base/createReadStream/schema.json index 19114c401..91b3bdf97 100644 --- a/packages/core/lib/actions/fs/base/createReadStream/schema.json +++ b/packages/core/lib/actions/fs/base/createReadStream/schema.json @@ -25,6 +25,9 @@ "typeof": "function", "description": "User provided function receiving the newly created readable stream. The user is responsible for pumping new content from it." }, + "sudo": { + "$ref": "module://@nikitajs/core/actions/execute#/definitions/config/properties/sudo" + }, "target": { "oneOf": [ { diff --git a/packages/core/lib/actions/fs/base/createWriteStream/schema.json b/packages/core/lib/actions/fs/base/createWriteStream/schema.json index 98ccaae07..704feeb3c 100644 --- a/packages/core/lib/actions/fs/base/createWriteStream/schema.json +++ b/packages/core/lib/actions/fs/base/createWriteStream/schema.json @@ -18,6 +18,9 @@ "typeof": "function", "description": "User provided function receiving the newly created writable stream. The user is responsible for writing new content and for closing the stream." }, + "sudo": { + "$ref": "module://@nikitajs/core/actions/execute#/definitions/config/properties/sudo" + }, "target": { "oneOf": [ { diff --git a/packages/core/lib/actions/fs/base/readFile/schema.json b/packages/core/lib/actions/fs/base/readFile/schema.json index 74ed4d5a4..0dd24eac7 100644 --- a/packages/core/lib/actions/fs/base/readFile/schema.json +++ b/packages/core/lib/actions/fs/base/readFile/schema.json @@ -5,6 +5,17 @@ "encoding": { "$ref": "module://@nikitajs/core/actions/fs/base/createReadStream#/definitions/config/properties/encoding" }, + "format": { + "oneOf": [ + { + "type": "string" + }, + { + "typeof": "function" + } + ], + "description": "Parse the file content." + }, "target": { "oneOf": [ { diff --git a/packages/core/lib/actions/fs/base/stat/index.js b/packages/core/lib/actions/fs/base/stat/index.js index be7209345..85ebe508c 100644 --- a/packages/core/lib/actions/fs/base/stat/index.js +++ b/packages/core/lib/actions/fs/base/stat/index.js @@ -3,8 +3,7 @@ import dedent from 'dedent'; import utils from '@nikitajs/core/utils'; import { escapeshellarg as esa } from "@nikitajs/core/utils/string"; -import definitionsIn from './schema.in.json' assert { type: "json" }; -import definitionsOut from './schema.out.json' assert { type: "json" }; +import definitions from './schema.json' assert { type: "json" }; const errors = { NIKITA_FS_STAT_TARGET_ENOENT: ({config, error}) => @@ -57,7 +56,6 @@ export default { argument_to_config: 'target', log: false, raw_output: true, - definitions: definitionsIn + definitions: definitions }, - definitions_output: definitionsOut }; diff --git a/packages/core/lib/actions/fs/base/stat/schema.in.json b/packages/core/lib/actions/fs/base/stat/schema.json similarity index 96% rename from packages/core/lib/actions/fs/base/stat/schema.in.json rename to packages/core/lib/actions/fs/base/stat/schema.json index 9fd9ecdc8..85e4ede34 100644 --- a/packages/core/lib/actions/fs/base/stat/schema.in.json +++ b/packages/core/lib/actions/fs/base/stat/schema.json @@ -1,6 +1,7 @@ { "config": { "type": "object", + "additionalProperties": false, "properties": { "dereference": { "type": "boolean", @@ -25,6 +26,7 @@ }, "output": { "type": "object", + "additionalProperties": true, "properties": { "stats": { "type": "object", diff --git a/packages/core/lib/actions/fs/base/stat/schema.out.json b/packages/core/lib/actions/fs/base/stat/schema.out.json deleted file mode 100644 index 688dfbbe5..000000000 --- a/packages/core/lib/actions/fs/base/stat/schema.out.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "type": "object", - "properties": { - "stats": { - "type": "object", - "properties": { - "mode": { - "$ref": "module://@nikitajs/core/actions/fs/base/chmod#/definitions/config/properties/mode" - }, - "uid": { - "type": "integer", - "description": "The numeric user identifier of the user that owns the file (POSIX)." - }, - "gid": { - "type": "integer", - "description": "The numeric group identifier of the group that owns the file (POSIX)." - }, - "size": { - "type": "integer", - "description": "The size of the file in bytes." - }, - "atime": { - "type": "integer", - "description": "The timestamp indicating the last time this file was accessed expressed in milliseconds since the POSIX Epoch." - }, - "mtime": { - "type": "integer", - "description": "The timestamp indicating the last time this file was modified expressed in milliseconds since the POSIX Epoch." - } - } - } - } -} diff --git a/packages/core/lib/actions/fs/base/writeFile/index.js b/packages/core/lib/actions/fs/base/writeFile/index.js index 6b9b17d2f..be6daa424 100644 --- a/packages/core/lib/actions/fs/base/writeFile/index.js +++ b/packages/core/lib/actions/fs/base/writeFile/index.js @@ -4,7 +4,7 @@ import utils from '@nikitajs/core/utils'; import definitions from "./schema.json" assert { type: "json" }; const errors = { - NIKITA_FS_STAT_TARGET_ENOENT: ({config, err}) => + NIKITA_FS_TARGET_INVALID: ({config, err}) => utils.error('NIKITA_FS_TARGET_INVALID', [ 'the target location is absolute', 'but this is not suported in SSH mode,', @@ -28,7 +28,7 @@ export default { // Normalization config.target = config.cwd ? path.resolve(config.cwd, config.target) : path.normalize(config.target); if (ssh && !path.isAbsolute(config.target)) { - throw NIKITA_FS_STAT_TARGET_ENOENT({ + throw errors.NIKITA_FS_TARGET_INVALID({ config: config, err: err }); diff --git a/packages/core/lib/actions/fs/copy/index.js b/packages/core/lib/actions/fs/copy/index.js index 0457519d8..8167d0b0c 100644 --- a/packages/core/lib/actions/fs/copy/index.js +++ b/packages/core/lib/actions/fs/copy/index.js @@ -129,7 +129,7 @@ export default { return await this.fs.copy({ target: target, source: source, - source_stat: stats, + source_stats: stats, uid: uid, gid: gid, mode: mode, diff --git a/packages/core/lib/plugins/tools/schema.js b/packages/core/lib/plugins/tools/schema.js index c9817c5d1..fd1344cc3 100644 --- a/packages/core/lib/plugins/tools/schema.js +++ b/packages/core/lib/plugins/tools/schema.js @@ -7,7 +7,7 @@ object. import stream from "node:stream"; import dedent from "dedent"; import { merge, mutate } from "mixme"; -import Ajv from "ajv"; +import Ajv from "ajv/dist/2019.js"; import ajv_keywords from "ajv-keywords"; import ajv_formats from "ajv-formats"; import utils from "@nikitajs/core/utils"; @@ -137,35 +137,29 @@ export default { ajv.addSchema(schema, "nikita"); return true; }, - validate: async (action, schema) => { + validate: async (action, definitions) => { let validate; try { - if (schema == null) { - schema = action.metadata.definitions; + if (definitions == null) { + definitions = action.metadata.definitions; } - schema = { - definitions: schema, + const schema = { + definitions: definitions, + // definitions: {config: {}, ...definitions}, type: "object", - allOf: [ - { - properties: (() => { - const obj = {}; - for (const k in schema) { - obj[k] = { - $ref: `#/definitions/${k}`, - }; + properties: { + config: definitions?.config + ? { + type: "object", + // additionalProperties: false, + unevaluatedProperties: false, + $ref: "#/definitions/config", } - return obj; - })(), + : {}, + metadata: { + $ref: "nikita#/definitions/metadata", }, - { - properties: { - metadata: { - $ref: "nikita#/definitions/metadata", - }, - }, - }, - ], + }, }; validate = await ajv.compileAsync(schema); } catch (error) { diff --git a/packages/core/lib/session.js b/packages/core/lib/session.js index 51e275396..92f7bb9ff 100644 --- a/packages/core/lib/session.js +++ b/packages/core/lib/session.js @@ -149,7 +149,7 @@ const session = function(args, options = {}) { action = await action.plugins.call({ name: "nikita:normalize", args: action, - hooks: action.hooks?.on_normalize || action.on_normalize, + hooks: action.hooks?.on_normalize, // || action.hooks?.["nikita:normalize"] handler: (action) => action }); } catch (error) { @@ -175,7 +175,7 @@ const session = function(args, options = {}) { const output = action.plugins.call({ name: 'nikita:action', args: action, - hooks: action.hooks.on_action, + hooks: action.hooks.on_action, // || action.hooks.["nikita:action"] handler: function(action) { // Execution of an action handler return action.handler.call(action.context, action); @@ -207,7 +207,7 @@ const session = function(args, options = {}) { error: error, output: output }, - hooks: action.hooks.on_result, + hooks: action.hooks.on_result, // || action.hooks.["nikita:result"] handler: function({action, error, output}) { if (error) { throw error; diff --git a/packages/core/test/actions/execute/assert.coffee b/packages/core/test/actions/execute/assert.coffee index a66ddc7b3..20e84adc1 100644 --- a/packages/core/test/actions/execute/assert.coffee +++ b/packages/core/test/actions/execute/assert.coffee @@ -15,7 +15,7 @@ describe 'actions.execute.assert', -> content: 42 code: 1 ({config}) -> - config.code.should.eql [1] + config.code.should.eql {true: [1], false: []} config.content.should.eql "42" describe 'exit code', -> @@ -32,7 +32,7 @@ describe 'actions.execute.assert', -> .should.be.rejectedWith [ 'NIKITA_EXECUTE_ASSERT_EXIT_CODE:' 'an unexpected exit code was encountered,' - 'got undefined while expecting 0.' + 'got 1 while expecting 0.' ].join ' ' they 'assert custom code', ({ssh}) -> @@ -46,7 +46,7 @@ describe 'actions.execute.assert', -> .should.be.rejectedWith [ 'NIKITA_EXECUTE_ASSERT_EXIT_CODE:' 'an unexpected exit code was encountered,' - 'got undefined while expecting 1.' + 'got 0 while expecting 1.' ].join ' ' they 'assert custom code with negation', ({ssh}) -> diff --git a/packages/core/test/actions/execute/config.arch_linux.coffee b/packages/core/test/actions/execute/config.arch_linux.coffee index 16c419c7a..277e4e91c 100644 --- a/packages/core/test/actions/execute/config.arch_linux.coffee +++ b/packages/core/test/actions/execute/config.arch_linux.coffee @@ -20,7 +20,8 @@ describe 'actions.execute.config.arch_linux', -> message: [ 'NIKITA_SCHEMA_VALIDATION_CONFIG:' 'one error was found in the configuration of action `execute`:' - '#/dependencies/arch_chroot/required config must have required property \'arch_chroot_rootdir\'.' + '#/dependentRequired config must have property arch_chroot_rootdir when property arch_chroot is present,' + 'property is "arch_chroot", depsCount is 1, deps is "arch_chroot_rootdir".' ].join ' ' it 'arch_chroot_rootdir must exist', -> diff --git a/packages/core/test/actions/execute/index.coffee b/packages/core/test/actions/execute/index.coffee index 0525c9ee4..1736a2162 100644 --- a/packages/core/test/actions/execute/index.coffee +++ b/packages/core/test/actions/execute/index.coffee @@ -30,19 +30,21 @@ describe 'actions.execute', -> they 'as an action handler returning a string', ({ssh}) -> nikita $ssh: ssh, -> @execute - a_key: 'test context' + env: + a_key: 'test context' command: ({config}) -> - "text='#{config.a_key}'; echo $text" + "text='#{config.env.a_key}'; echo $text" .should.be.finally.containEql stdout: 'test context\n' they 'as an action handler returning a promise', ({ssh}) -> nikita $ssh: ssh, -> @execute - a_key: 'test context' + env: + a_key: 'test context' command: ({config}) -> new Promise (resolve, reject) -> - resolve "text='#{config.a_key}'; echo $text" + resolve "text='#{config.env.a_key}'; echo $text" .should.be.finally.containEql stdout: 'test context\n' diff --git a/packages/core/test/actions/fs/assert.coffee b/packages/core/test/actions/fs/assert.coffee index bb9168c01..5da13b5f7 100644 --- a/packages/core/test/actions/fs/assert.coffee +++ b/packages/core/test/actions/fs/assert.coffee @@ -88,6 +88,26 @@ describe 'actions.fs.assert', -> message: 'Got it' describe 'config `filetype`', -> + + they 'must match a registered string value', ({ssh}) -> + nikita + $ssh: ssh + , () -> + @fs.assert "/invalid/a_dir", filetype: 'INVALID' + .should.be.rejectedWith [ + 'NIKITA_FS_ASSERT_FILETYPE_INVALID_VALUE:' + 'provided filetype is not supported, got ["INVALID"].' + ].join(' ') + + they 'defined as string or number', ({ssh}) -> + nikita + $ssh: ssh + , () -> + await @fs.assert "/invalid/a_dir", $schema: false, filetype: [true] + .should.be.rejectedWith [ + 'NIKITA_FS_ASSERT_FILETYPE_INVALID_TYPE:' + 'filetype must be a string or a number, got [true].' + ].join(' ') they 'assert a file', ({ssh}) -> nikita @@ -479,7 +499,6 @@ describe 'actions.fs.assert', -> , ({metadata: {tmpdir}}) -> await @fs.base.mkdir target: "#{tmpdir}/a_file" - content: "are u here" mode: 0o0755 @fs.assert target: "#{tmpdir}/a_file" @@ -500,7 +519,6 @@ describe 'actions.fs.assert', -> , ({metadata: {tmpdir}}) -> await @fs.base.mkdir target: "#{tmpdir}/a_file" - content: "are u here" mode: 0o0755 @fs.assert target: "#{tmpdir}/a_file" @@ -530,7 +548,7 @@ describe 'actions.fs.assert', -> , ({metadata: {tmpdir}}) -> {stdout} = await @execute 'id -u && id -g' [uid, gid] = stdout.split '\n' - await @fs.base.writeFile "#{tmpdir}/a_file", content: '', uid: uid, gid: gid + await @fs.base.writeFile "#{tmpdir}/a_file", content: '' await @fs.base.chown "#{tmpdir}/a_file", gid: gid @fs.assert target: "#{tmpdir}/a_file", diff --git a/packages/core/test/actions/fs/base/createReadStream.coffee b/packages/core/test/actions/fs/base/createReadStream.coffee index 66e3a1ecf..185973891 100644 --- a/packages/core/test/actions/fs/base/createReadStream.coffee +++ b/packages/core/test/actions/fs/base/createReadStream.coffee @@ -46,6 +46,12 @@ describe 'actions.fs.base.createReadStream', -> describe 'errors', -> they 'schema', ({ssh}) -> + # Note, we encountered a weird behavior + # after introducing the schema definition `config.properties.sudo.$ref` + # the error message is altered: + # before, `#/definitions/config/required config must have required property 'target'` + # after, `#/required config must have required property 'target'` + # Note, switching from a ref to a hard-coded definition revert the error message nikita $ssh: ssh , -> @@ -55,7 +61,8 @@ describe 'actions.fs.base.createReadStream', -> message: [ 'NIKITA_SCHEMA_VALIDATION_CONFIG:' 'one error was found in the configuration of action `fs.base.createReadStream`:' - '#/definitions/config/required' + # '#/definitions/config/required' + '#/required' 'config must have required property \'target\'.' ].join ' ' diff --git a/packages/core/test/actions/fs/copy.coffee b/packages/core/test/actions/fs/copy.coffee index 5929c0cf4..07126ff8d 100644 --- a/packages/core/test/actions/fs/copy.coffee +++ b/packages/core/test/actions/fs/copy.coffee @@ -323,7 +323,6 @@ describe 'actions.fs.copy', -> mode: 0o0606 await @fs.mkdir target: "#{tmpdir}/a_source/a_dir" - content: '' mode: 0o0777 await @fs.base.writeFile target: "#{tmpdir}/a_source/a_dir/a_file" diff --git a/packages/core/test/actions/fs/link.coffee b/packages/core/test/actions/fs/link.coffee index 1ee61f12f..bee3a14b6 100644 --- a/packages/core/test/actions/fs/link.coffee +++ b/packages/core/test/actions/fs/link.coffee @@ -113,7 +113,7 @@ describe 'actions.fs.link', -> $status.should.be.true() await @fs.assert target: "#{tmpdir}/test/dir/link_test" - type: 'symlink' + filetype: 'symlink' {$status} = await @fs.link $ssh: ssh source: "#{tmpdir}/source_dir/merge.coffee" diff --git a/packages/core/test/actions/fs/remove.coffee b/packages/core/test/actions/fs/remove.coffee index 120a4049e..9f1582e25 100644 --- a/packages/core/test/actions/fs/remove.coffee +++ b/packages/core/test/actions/fs/remove.coffee @@ -106,7 +106,7 @@ describe 'actions.fs.remove', -> await @fs.assert "#{tmpdir}/a_dir.tar.gz", not: true await @fs.assert "#{tmpdir}/a_dir.tgz", not: true await @fs.assert "#{tmpdir}/a_dir.zip" - await @fs.assert "#{tmpdir}/a_dir", type: 'directory' + await @fs.assert "#{tmpdir}/a_dir", filetype: 'directory' they 'a dir', ({ssh}) -> nikita diff --git a/packages/core/test/actions/wait.coffee b/packages/core/test/actions/wait.coffee index 974d378eb..9f899f430 100644 --- a/packages/core/test/actions/wait.coffee +++ b/packages/core/test/actions/wait.coffee @@ -34,7 +34,8 @@ describe 'actions.wait', -> 'NIKITA_SCHEMA_VALIDATION_CONFIG: multiple errors were found in the configuration of action `wait`:' '#/definitions/config/oneOf config must match exactly one schema in oneOf, passingSchemas is null;' '#/definitions/config/oneOf/0/properties/time/type config/time must be integer,string, type is ["integer","string"];' - '#/definitions/config/oneOf/1/additionalProperties config must NOT have additional properties, additionalProperty is "time".' + '#/definitions/config/oneOf/1/additionalProperties config must NOT have additional properties, additionalProperty is "time";' + '#/properties/config/unevaluatedProperties config must NOT have unevaluated properties, unevaluatedProperty is "time".' ].join ' ' describe 'time', -> diff --git a/packages/core/test/plugins/execute.sudo.coffee b/packages/core/test/plugins/execute.sudo.coffee index 767497f71..438b85298 100644 --- a/packages/core/test/plugins/execute.sudo.coffee +++ b/packages/core/test/plugins/execute.sudo.coffee @@ -17,8 +17,6 @@ describe 'plugins.execute.sudo', -> $sudo: true target: "#{tmpdir}/a_file" content: 'hello' - uid: 0 - gid: 0 await @fs.base.chown target: "#{tmpdir}/a_file" uid: 0 @@ -50,8 +48,6 @@ describe 'plugins.execute.sudo', -> $sudo: true target: "#{tmpdir}/a_file" content: 'hello' - uid: 0 - gid: 0 await @fs.base.chown $sudo: true target: "#{tmpdir}/a_file" diff --git a/packages/core/test/plugins/metadata/definitions.coffee b/packages/core/test/plugins/metadata/definitions.coffee index 4f737ff08..f9381208a 100644 --- a/packages/core/test/plugins/metadata/definitions.coffee +++ b/packages/core/test/plugins/metadata/definitions.coffee @@ -118,7 +118,6 @@ describe 'plugins.metadata.definitions', -> type: 'object' properties: 'an_integer': type: 'integer', 'minimum': 1 - a_string: 1 an_integer: 0 , (->) .should.be.rejectedWith [ diff --git a/packages/db/lib/database/exists/index.js b/packages/db/lib/database/exists/index.js index ab865d995..03159cb60 100644 --- a/packages/db/lib/database/exists/index.js +++ b/packages/db/lib/database/exists/index.js @@ -1,4 +1,5 @@ // Dependencies +import { db } from "@nikitajs/db/utils"; import definitions from "./schema.json" assert { type: "json" }; // Action @@ -9,7 +10,8 @@ export default { : config.engine === 'mariadb' || config.engine === 'mysql' ? `SHOW DATABASES;` : undefined; - const {$status} = await this.db.query(config, { + const {$status} = await this.db.query({ + ...db.connection_config(config), command: cmd_list_tables, database: null, grep: config.database diff --git a/packages/db/lib/database/index.js b/packages/db/lib/database/index.js index 96ab5078d..6e468539e 100644 --- a/packages/db/lib/database/index.js +++ b/packages/db/lib/database/index.js @@ -1,118 +1,106 @@ // Dependencies import dedent from "dedent"; +import { db } from "@nikitajs/db/utils"; import definitions from "./schema.json" assert { type: "json" }; -import utils from "@nikitajs/db/utils"; // Action export default { handler: async function ({ config, tools: { log } }) { - if (config.user == null) { - config.user = []; - } - if (typeof config.user === "string") { - config.user = [config.user]; - } // Defines and check the engine type config.engine = config.engine.toLowerCase(); - log({ - message: `Database engine set to ${config.engine}`, - level: "DEBUG", - }); - const engine = config.engine === 'mysql' || config.engine === 'mariadb' - ? "mysql" - : "postgresql" + log("DEBUG", `Database engine set to ${config.engine}`); + const engine = + config.engine === "mysql" || config.engine === "mariadb" + ? "mysql" + : "postgresql"; if (engine === "mysql") { - if (config.character_set == null) { - config.character_set = "latin1"; // MySQL default - } + config.character_set ??= "latin1"; // MySQL default switch (config.character_set) { case "latin1": - if (config.collation == null) { - config.collation = "latin1_swedish_ci"; // MySQL default - } + config.collation ??= "latin1_swedish_ci"; // MySQL default break; case "utf8": - if (config.collation == null) { - config.collation = "utf8_general_ci"; - } + config.collation ??= "utf8_general_ci"; } } // Create the database if it does not exists - log({ - message: `Check if database ${config.database} exists`, - level: "DEBUG", - }); - const { exists } = await this.db.database.exists(config); + log("DEBUG", `Check if database ${config.database} exists`); + const { exists } = await this.db.database.exists( + db.connection_config(config) + ); if (!exists) { await this.execute({ - command: engine === "mysql" - ? utils.db.command( - config, - { - database: null, - }, - [ - `CREATE DATABASE ${config.database}`, - `DEFAULT CHARACTER SET ${config.character_set}`, - config.collation ? `DEFAULT COLLATE ${config.collation}` : void 0, - ";", - ].join(" ") - ) - : utils.db.command( - config, - { - database: null, - }, - `CREATE DATABASE ${config.database};` - ), - }); - log({ - message: `Database created: ${JSON.stringify(config.database)}`, - level: "WARN", + command: + engine === "mysql" + ? db.command( + config, + { + database: null, + }, + [ + `CREATE DATABASE ${config.database}`, + `DEFAULT CHARACTER SET ${config.character_set}`, + config.collation + ? `DEFAULT COLLATE ${config.collation}` + : void 0, + ";", + ].join(" ") + ) + : db.command( + config, + { + database: null, + }, + `CREATE DATABASE ${config.database};` + ), }); + log("WARN", `Database created: ${JSON.stringify(config.database)}`); } // Associate users to the database for (const user of config.user) { - log({ - message: `Check if user ${user} has PRIVILEGES on ${config.database} `, - level: "DEBUG", - }); - const { exists } = await this.db.user.exists(config, { + log( + "DEBUG", + `Check if user ${user} has PRIVILEGES on ${config.database} ` + ); + const { exists } = await this.db.user.exists({ + ...db.connection_config(config), username: user, }); if (!exists) { throw Error(`DB user does not exists: ${user}`); } - const command_has_privileges = engine === "mysql" - ? utils.db.command( - config, - { - database: "mysql", - }, - `SELECT user FROM db WHERE db='${config.database}';` - ) + ` | grep '${user}'` - : utils.db.command( - config, - { - database: config.database, - }, - "\\l" - ) + ` | egrep '^${user}='` - const command_grant_privileges = engine === "mysql" - ? utils.db.command( - config, - { - database: null, - }, - `GRANT ALL PRIVILEGES ON ${config.database}.* TO '${user}' WITH GRANT OPTION;` - ) - : utils.db.command( - config, - { - database: null, - }, - `GRANT ALL PRIVILEGES ON DATABASE ${config.database} TO ${user}` - ); + const command_has_privileges = + engine === "mysql" + ? db.command( + config, + { + database: "mysql", + }, + `SELECT user FROM db WHERE db='${config.database}';` + ) + ` | grep '${user}'` + : db.command( + config, + { + database: config.database, + }, + "\\l" + ) + ` | egrep '^${user}='`; + const command_grant_privileges = + engine === "mysql" + ? db.command( + config, + { + database: null, + }, + `GRANT ALL PRIVILEGES ON ${config.database}.* TO '${user}' WITH GRANT OPTION;` + ) + : db.command( + config, + { + database: null, + }, + `GRANT ALL PRIVILEGES ON DATABASE ${config.database} TO ${user}` + ); const { $status } = await this.execute({ command: dedent` if ${command_has_privileges}; then @@ -125,10 +113,7 @@ export default { code: [0, 3], }); if ($status) { - log({ - message: `Privileges granted: to ${user} on ${config.database}`, - level: "WARN", - }); + log("WARN", `Privileges granted: to ${user} on ${config.database}`); } } }, diff --git a/packages/db/lib/database/remove/index.js b/packages/db/lib/database/remove/index.js index 00b5ce0d2..39c02d6a1 100644 --- a/packages/db/lib/database/remove/index.js +++ b/packages/db/lib/database/remove/index.js @@ -1,4 +1,5 @@ // Dependencies +import { db } from "@nikitajs/db/utils"; import definitions from "./schema.json" assert { type: "json" }; // Action @@ -7,7 +8,8 @@ export default { // Avoid errors when database argument is provided in the command: // - Postgres: "ERROR: cannot drop the currently open database" // - MariaDB: "ERROR 1049 (42000): Unknown database 'my_db'" - await this.db.query(config, { + await this.db.query({ + ...db.connection_config(config), command: `DROP DATABASE IF EXISTS ${config.database};`, code: [0, 2], database: null, diff --git a/packages/db/lib/database/remove/schema.json b/packages/db/lib/database/remove/schema.json index cf5e2deca..b13217bb7 100644 --- a/packages/db/lib/database/remove/schema.json +++ b/packages/db/lib/database/remove/schema.json @@ -1,11 +1,7 @@ { "config": { "type": "object", - "allOf": [ - { - "$ref": "module://@nikitajs/db/query#/definitions/db" - } - ], + "$ref": "module://@nikitajs/db/query#/definitions/db", "properties": { "database": { "type": "string", diff --git a/packages/db/lib/database/schema.json b/packages/db/lib/database/schema.json index dd1f47cb9..5193c2b31 100644 --- a/packages/db/lib/database/schema.json +++ b/packages/db/lib/database/schema.json @@ -1,11 +1,7 @@ { "config": { "type": "object", - "allOf": [ - { - "$ref": "module://@nikitajs/db/query#/definitions/db" - } - ], + "$ref": "module://@nikitajs/db/query#/definitions/db", "properties": { "database": { "type": "string", @@ -14,6 +10,7 @@ "user": { "type": "array", "coercion": true, + "default": [], "items": { "type": "string" }, diff --git a/packages/db/lib/database/wait/index.js b/packages/db/lib/database/wait/index.js index f6ddfecb2..fabe63151 100644 --- a/packages/db/lib/database/wait/index.js +++ b/packages/db/lib/database/wait/index.js @@ -1,4 +1,5 @@ // Dependencies +import { db } from "@nikitajs/db/utils"; import definitions from "./schema.json" assert { type: "json" }; // Action @@ -13,7 +14,8 @@ export default { retry: config.retry, interval: config.interval, }, async function() { - const { stdout } = await this.db.query(config, { + const { stdout } = await this.db.query({ + ...db.connection_config(config), command: cmd_list_tables, database: null, grep: config.database, diff --git a/packages/db/lib/database/wait/schema.json b/packages/db/lib/database/wait/schema.json index 4c851f4f5..9ebae05e0 100644 --- a/packages/db/lib/database/wait/schema.json +++ b/packages/db/lib/database/wait/schema.json @@ -1,11 +1,7 @@ { "config": { "type": "object", - "allOf": [ - { - "$ref": "module://@nikitajs/db/query#/definitions/db" - } - ], + "$ref": "module://@nikitajs/db/query#/definitions/db", "properties": { "database": { "type": "string", diff --git a/packages/db/lib/query/index.js b/packages/db/lib/query/index.js index ff0142718..565edc600 100644 --- a/packages/db/lib/query/index.js +++ b/packages/db/lib/query/index.js @@ -1,11 +1,12 @@ // Dependencies -import definitions from "./schema.json" assert { type: "json" }; import utils from "@nikitajs/db/utils"; +import definitions from "./schema.json" assert { type: "json" }; // Action export default { handler: async function ({ config }) { const { $status, stdout } = await this.execute({ + code: config.code, command: utils.db.command(config), trim: config.trim, }); diff --git a/packages/db/lib/query/schema.json b/packages/db/lib/query/schema.json index 401073906..e2cd2343e 100644 --- a/packages/db/lib/query/schema.json +++ b/packages/db/lib/query/schema.json @@ -10,6 +10,10 @@ "type": "string", "description": "The password of the database administrator." }, + "code": { + "$ref": "module://@nikitajs/core/actions/execute#/definitions/config/properties/code", + "description": "Expected code returned by the SQL execute command." + }, "database": { "type": [ "null", diff --git a/packages/db/lib/schema/exists/index.js b/packages/db/lib/schema/exists/index.js index 8df56c01f..03dfe46f9 100644 --- a/packages/db/lib/schema/exists/index.js +++ b/packages/db/lib/schema/exists/index.js @@ -1,19 +1,22 @@ // Dependencies +import { db } from "@nikitajs/db/utils"; import definitions from "./schema.json" assert { type: "json" }; // Action export default { - handler: async function({config}) { - const {$status} = await this.db.query(config, { - command: `SELECT 1 FROM pg_namespace WHERE nspname = '${config.schema}';`, - grep: '1' - }); + handler: async function ({ config }) { return { - exists: $status + exists: await this.db + .query({ + ...db.connection_config(config), + command: `SELECT 1 FROM pg_namespace WHERE nspname = '${config.schema}';`, + grep: "1", + }) + .then(({ $status: exists }) => exists), }; }, metadata: { - global: 'db', - definitions: definitions - } + global: "db", + definitions: definitions, + }, }; diff --git a/packages/db/lib/schema/index.js b/packages/db/lib/schema/index.js index 4a7a01db4..38a6c2592 100644 --- a/packages/db/lib/schema/index.js +++ b/packages/db/lib/schema/index.js @@ -1,35 +1,45 @@ // Dependencies +import { db } from "@nikitajs/db/utils"; import definitions from "./schema.json" assert { type: "json" }; -import utils from "@nikitajs/db/utils"; // Action export default { - handler: async function({config}) { - const {$status} = await this.execute({ + handler: async function ({ config }) { + const { $status } = await this.execute({ $shy: true, code: [0, 2], - command: utils.db.command(config, '\\dt') + command: db.command(config, "\\dt"), }); if (!$status) { throw Error(`Database does not exist ${config.database}`); } - await this.db.query(config, { + await this.db.query({ + ...db.connection_config(config), command: `CREATE SCHEMA ${config.schema};`, - $unless_execute: utils.db.command(config, `SELECT 1 FROM pg_namespace WHERE nspname = '${config.schema}';`) + " | grep 1" + $unless_execute: + db.command( + config, + `SELECT 1 FROM pg_namespace WHERE nspname = '${config.schema}';` + ) + " | grep 1", }); // Check if owner is the good one - const {stderr} = await this.execute({ + const { stderr } = await this.execute({ $if: config.owner != null, - $unless_execute: utils.db.command(config, '\\dn') + ` | grep '${config.schema}|${config.owner}'`, - command: utils.db.command(config, `ALTER SCHEMA ${config.schema} OWNER TO ${config.owner};`), - code: [0, 1] + $unless_execute: + db.command(config, "\\dn") + + ` | grep '${config.schema}|${config.owner}'`, + command: db.command( + config, + `ALTER SCHEMA ${config.schema} OWNER TO ${config.owner};` + ), + code: [0, 1], }); if (/^ERROR:\s\srole.*does\snot\sexist/.test(stderr)) { throw Error(`Owner ${config.owner} does not exists`); } }, metadata: { - global: 'db', - definitions: definitions - } + global: "db", + definitions: definitions, + }, }; diff --git a/packages/db/lib/schema/list/index.js b/packages/db/lib/schema/list/index.js index f49ebf2cd..ee5d73f1a 100644 --- a/packages/db/lib/schema/list/index.js +++ b/packages/db/lib/schema/list/index.js @@ -1,28 +1,29 @@ // Dependencies import utils from "@nikitajs/core/utils"; +import { db } from "@nikitajs/db/utils"; import definitions from "./schema.json" assert { type: "json" }; // ## Exports export default { - handler: async function({config}) { - const {stdout} = await this.db.query(config, { - command: '\\dn', - trim: true - }); - const schemas = utils.string.lines(stdout).map(function(line) { - const [name, owner] = line.split('|'); - return { - name: name, - owner: owner - }; + handler: async function ({ config }) { + const { stdout } = await this.db.query({ + ...db.connection_config(config), + command: "\\dn", + trim: true, }); return { - schemas: schemas + schemas: utils.string.lines(stdout).map((line) => { + const [name, owner] = line.split("|"); + return { + name: name, + owner: owner, + }; + }), }; }, metadata: { - argument_to_config: 'database', - global: 'db', - definitions: definitions - } + argument_to_config: "database", + global: "db", + definitions: definitions, + }, }; diff --git a/packages/db/lib/schema/remove/index.js b/packages/db/lib/schema/remove/index.js index 32cc7483f..762fd611e 100644 --- a/packages/db/lib/schema/remove/index.js +++ b/packages/db/lib/schema/remove/index.js @@ -1,20 +1,25 @@ // Dependencies +import { db } from "@nikitajs/db/utils"; import definitions from "./schema.json" assert { type: "json" }; // Action export default { - handler: async function({config}) { - const {exists} = await this.db.schema.exists(config); + handler: async function ({ config }) { + const { exists } = await this.db.schema.exists({ + ...db.connection_config(config), + schema: config.schema, + }); if (!exists) { return false; } - return await this.db.query(config, { - command: `DROP SCHEMA IF EXISTS ${config.schema};` + await this.db.query({ + ...db.connection_config(config), + command: `DROP SCHEMA IF EXISTS ${config.schema};`, }); }, metadata: { - argument_to_config: 'schema', - global: 'db', - definitions: definitions - } + argument_to_config: "schema", + global: "db", + definitions: definitions, + }, }; diff --git a/packages/db/lib/schema/remove/schema.json b/packages/db/lib/schema/remove/schema.json index 14478967a..f823bf22e 100644 --- a/packages/db/lib/schema/remove/schema.json +++ b/packages/db/lib/schema/remove/schema.json @@ -1,11 +1,7 @@ { "config": { "type": "object", - "allOf": [ - { - "$ref": "module://@nikitajs/db/query#/definitions/db" - } - ], + "$ref": "module://@nikitajs/db/query#/definitions/db", "properties": { "database": { "type": "string", diff --git a/packages/db/lib/user/exists/index.js b/packages/db/lib/user/exists/index.js index 2aa3559dc..898366344 100644 --- a/packages/db/lib/user/exists/index.js +++ b/packages/db/lib/user/exists/index.js @@ -1,31 +1,32 @@ // Dependencies -import definitions from "./schema.json" assert { type: "json" }; import { db } from "@nikitajs/db/utils"; +import definitions from "./schema.json" assert { type: "json" }; // Action export default { - handler: async function({config}) { - const {stdout} = await this.db.query(db.connection_config(config), { + handler: async function ({ config }) { + const { stdout } = await this.db.query({ + ...db.connection_config(config), database: undefined, - command: (function() { + command: (function () { switch (config.engine) { - case 'mariadb': - case 'mysql': + case "mariadb": + case "mysql": return `SELECT User FROM mysql.user WHERE User = '${config.username}'`; - case 'postgresql': + case "postgresql": return `SELECT '${config.username}' FROM pg_roles WHERE rolname='${config.username}'`; } })(), - trim: true + trim: true, }); return { - exists: stdout === config.username + exists: stdout === config.username, }; }, metadata: { - argument_to_config: 'username', - global: 'db', + argument_to_config: "username", + global: "db", shy: true, - definitions: definitions - } + definitions: definitions, + }, }; diff --git a/packages/db/lib/user/exists/schema.json b/packages/db/lib/user/exists/schema.json index f4338b405..b59f1e989 100644 --- a/packages/db/lib/user/exists/schema.json +++ b/packages/db/lib/user/exists/schema.json @@ -1,11 +1,7 @@ { "config": { "type": "object", - "allOf": [ - { - "$ref": "module://@nikitajs/db/query#/definitions/db" - } - ], + "$ref": "module://@nikitajs/db/query#/definitions/db", "properties": { "username": { "type": "string", diff --git a/packages/db/lib/user/remove/index.js b/packages/db/lib/user/remove/index.js index a14dc9013..6cecf26ea 100644 --- a/packages/db/lib/user/remove/index.js +++ b/packages/db/lib/user/remove/index.js @@ -1,10 +1,12 @@ // Dependencies +import { db } from "@nikitajs/db/utils"; import definitions from "./schema.json" assert { type: "json" }; // Action export default { handler: async function({config}) { - await this.db.query(config, { + await this.db.query({ + ...db.connection_config(config), command: `DROP USER IF EXISTS ${config.username};` }); }, diff --git a/packages/db/lib/user/remove/schema.json b/packages/db/lib/user/remove/schema.json index 5cd3a449a..3c9f28620 100644 --- a/packages/db/lib/user/remove/schema.json +++ b/packages/db/lib/user/remove/schema.json @@ -1,11 +1,7 @@ { "config": { "type": "object", - "allOf": [ - { - "$ref": "module://@nikitajs/db/query#/definitions/db" - } - ], + "$ref": "module://@nikitajs/db/query#/definitions/db", "properties": { "username": { "type": "string", diff --git a/packages/db/lib/user/schema.json b/packages/db/lib/user/schema.json index 0db34c946..68e298613 100644 --- a/packages/db/lib/user/schema.json +++ b/packages/db/lib/user/schema.json @@ -1,11 +1,7 @@ { "config": { "type": "object", - "allOf": [ - { - "$ref": "module://@nikitajs/db/query#/definitions/db" - } - ], + "$ref": "module://@nikitajs/db/query#/definitions/db", "properties": { "username": { "type": "string", diff --git a/packages/db/test/query.coffee b/packages/db/test/query.coffee index ba570b11d..a2878c1a3 100644 --- a/packages/db/test/query.coffee +++ b/packages/db/test/query.coffee @@ -17,10 +17,10 @@ for engine, _ of test.db then do (engine) -> .should.be.rejectedWith [ "NIKITA_SCHEMA_VALIDATION_CONFIG:" "multiple errors were found in the configuration of action `db.query`:" - "module://@nikitajs/db/query#/definitions/db/required config must have required property 'admin_password';" - "module://@nikitajs/db/query#/definitions/db/required config must have required property 'admin_username';" - "module://@nikitajs/db/query#/definitions/db/required config must have required property 'engine';" - "module://@nikitajs/db/query#/definitions/db/required config must have required property 'host'." + "#/required config must have required property 'admin_password';" + "#/required config must have required property 'admin_username';" + "#/required config must have required property 'engine';" + "#/required config must have required property 'host'." ].join ' ' they 'config command', ({ssh}) -> diff --git a/packages/db/test/user/index.coffee b/packages/db/test/user/index.coffee index 381564af5..a3a4eead0 100644 --- a/packages/db/test/user/index.coffee +++ b/packages/db/test/user/index.coffee @@ -22,9 +22,9 @@ for engine, _ of test.db message: [ 'NIKITA_SCHEMA_VALIDATION_CONFIG:' 'multiple errors were found in the configuration of action `db.user`:' + '#/required config must have required property \'host\';' '#/required config must have required property \'password\';' - '#/required config must have required property \'username\';' - 'module://@nikitajs/db/query#/definitions/db/required config must have required property \'host\'.' + '#/required config must have required property \'username\'.' ].join ' ' they 'requires admin_username, password, username', ({ssh}) -> @@ -40,9 +40,9 @@ for engine, _ of test.db message: [ 'NIKITA_SCHEMA_VALIDATION_CONFIG:' 'multiple errors were found in the configuration of action `db.user`:' + '#/required config must have required property \'admin_username\';' '#/required config must have required property \'password\';' - '#/required config must have required property \'username\';' - 'module://@nikitajs/db/query#/definitions/db/required config must have required property \'admin_username\'.' + '#/required config must have required property \'username\'.' ].join ' ' they 'add new user', ({ssh}) -> diff --git a/packages/docker/lib/build/schema.json b/packages/docker/lib/build/schema.json index db7b8ae1b..b1e78621b 100644 --- a/packages/docker/lib/build/schema.json +++ b/packages/docker/lib/build/schema.json @@ -1,6 +1,7 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker", "properties": { "build_arg": { "oneOf": [ diff --git a/packages/docker/lib/compose/index.js b/packages/docker/lib/compose/index.js index f0915c4e1..ec6e58728 100644 --- a/packages/docker/lib/compose/index.js +++ b/packages/docker/lib/compose/index.js @@ -12,34 +12,17 @@ export default { } let clean_target = false; if (config.content && config.target == null) { - if (config.target == null) { - config.target = `/tmp/nikita_docker_compose_${Date.now()}/docker-compose.yml`; - } + config.target ??= `/tmp/nikita_docker_compose_${Date.now()}/docker-compose.yml`; clean_target = true; } - if (config.compose_env == null) { - config.compose_env = []; - } if (config.compose_env.length && config.target_env == null) { - if (config.target_env == null) { - config.target_env = `/tmp/nikita_docker_compose_${Date.now()}/.env`; - } + config.target_env ??= `/tmp/nikita_docker_compose_${Date.now()}/.env`; clean_target = true; } - if (config.recreate == null) { - config.recreate = false; // TODO: move to schema - } - if (config.services == null) { - config.services = []; - } - if (!Array.isArray(config.services)) { - config.services = [config.services]; - } await this.file.yaml({ $if: config.content != null, backup: config.backup, content: config.content, - eof: config.eof, target: config.target, }); await this.file({ @@ -51,7 +34,6 @@ export default { // .join('\n') // If compose_env is an array content: config.compose_env.join("\n"), - eof: config.eof, target: config.target_env, }); let { $status, stdout } = await this.docker.tools.execute({ @@ -63,16 +45,14 @@ export default { cwd: config.cwd, uid: config.uid, code: [0, 123], - stdout_log: false, + // stdout_log: false, }); if (!$status) { $status = true; } else { const containers = JSON.parse(stdout); $status = containers.some((container) => !container.State.Running); - if ($status) { - log("Docker created, need start"); - } + if ($status) log("Docker created, need start"); } try { return await this.docker.tools.execute({ diff --git a/packages/docker/lib/compose/schema.json b/packages/docker/lib/compose/schema.json index 7192dfc6b..e74278517 100644 --- a/packages/docker/lib/compose/schema.json +++ b/packages/docker/lib/compose/schema.json @@ -1,16 +1,12 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker", "properties": { "content": { "type": "object", "description": "The content of the docker-compose.yml to write if not exist." }, - "eof": { - "type": "boolean", - "default": true, - "description": "Inherited from nikita.file use when writing docker-compose.yml file." - }, "backup": { "type": [ "string", @@ -19,11 +15,19 @@ "default": false, "description": "Create a backup, append a provided string to the filename extension or\na timestamp if value is not a string, only apply if the target file\nexists and is modified." }, + "compose_env": { + "type": "array", + "default": [], + "description": "List of environment variable in the form of `key=value`." + }, "detached": { "type": "boolean", "default": true, "description": "Run containers in detached mode." }, + "docker": { + "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker" + }, "force": { "type": "boolean", "default": false, @@ -32,6 +36,7 @@ "services": { "type": "array", "coercion": true, + "default": [], "items": { "type": "string" }, diff --git a/packages/docker/lib/cp/schema.json b/packages/docker/lib/cp/schema.json index c4541a9a8..6dd0233ad 100644 --- a/packages/docker/lib/cp/schema.json +++ b/packages/docker/lib/cp/schema.json @@ -1,6 +1,7 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker", "properties": { "docker": { "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker" diff --git a/packages/docker/lib/exec/index.js b/packages/docker/lib/exec/index.js index 5dd42d64e..5a61d0474 100644 --- a/packages/docker/lib/exec/index.js +++ b/packages/docker/lib/exec/index.js @@ -6,25 +6,19 @@ import definitions from "./schema.json" assert { type: "json" }; export default { handler: async function({ config, - tools: {log} }) { - if (config.service == null) { - config.service = false; - } - // Construct exec command - let command = 'exec'; - if (config.uid != null) { - command += ` -u ${config.uid}`; - if (config.gid != null) { - command += `:${config.gid}`; - } - } else if (config.gid != null) { - log('WARN', 'config.gid ignored unless config.uid is provided'); - } - command += ` ${config.container} ${config.command}`; return await this.docker.tools.execute({ - command: command, - code: config.code + command: [ + "exec", + config.uid && + [`-u ${config.uid}`, config.gid && `:${config.gid}`] + .filter(Boolean) + .join(""), + `${config.container} ${config.command}`, + ] + .filter(Boolean) + .join(" "), + code: config.code, }); }, metadata: { diff --git a/packages/docker/lib/exec/schema.json b/packages/docker/lib/exec/schema.json index a5f397d18..9b3e95684 100644 --- a/packages/docker/lib/exec/schema.json +++ b/packages/docker/lib/exec/schema.json @@ -1,6 +1,7 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker", "properties": { "code": { "$ref": "module://@nikitajs/core/actions/execute#/definitions/config/properties/code", @@ -8,7 +9,11 @@ }, "container": { "type": "string", - "description": "Name/ID of the container" + "description": "Name/ID of the container." + }, + "command": { + "type": "string", + "description": "Command to execute." }, "docker": { "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker" diff --git a/packages/docker/lib/images/schema.json b/packages/docker/lib/images/schema.json index 9d34fb435..99f3432dc 100644 --- a/packages/docker/lib/images/schema.json +++ b/packages/docker/lib/images/schema.json @@ -1,6 +1,7 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker", "properties": { "filters": { "type": "object", @@ -26,7 +27,7 @@ "description": "Filter images created after the image with given id or reference." } }, - "description": "" + "description": "Filter output based on conditions provided." } } } diff --git a/packages/docker/lib/inspect/schema.json b/packages/docker/lib/inspect/schema.json index c3170f309..a97391e78 100644 --- a/packages/docker/lib/inspect/schema.json +++ b/packages/docker/lib/inspect/schema.json @@ -1,6 +1,7 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker", "properties": { "container": { "type": ["array", "string"], diff --git a/packages/docker/lib/kill/schema.json b/packages/docker/lib/kill/schema.json index 6f600f845..1d2bc7cdb 100644 --- a/packages/docker/lib/kill/schema.json +++ b/packages/docker/lib/kill/schema.json @@ -1,6 +1,7 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker", "properties": { "container": { "type": "string", diff --git a/packages/docker/lib/load/schema.json b/packages/docker/lib/load/schema.json index ff5c6d0d7..fa6183810 100644 --- a/packages/docker/lib/load/schema.json +++ b/packages/docker/lib/load/schema.json @@ -1,6 +1,7 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker", "properties": { "checksum": { "type": "string", diff --git a/packages/docker/lib/login/schema.json b/packages/docker/lib/login/schema.json index ad956f1d2..0b77f63f7 100644 --- a/packages/docker/lib/login/schema.json +++ b/packages/docker/lib/login/schema.json @@ -1,6 +1,7 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker", "properties": { "docker": { "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker" diff --git a/packages/docker/lib/logout/schema.json b/packages/docker/lib/logout/schema.json index 685326e69..047462b42 100644 --- a/packages/docker/lib/logout/schema.json +++ b/packages/docker/lib/logout/schema.json @@ -1,6 +1,7 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker", "properties": { "docker": { "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker" diff --git a/packages/docker/lib/pause/schema.json b/packages/docker/lib/pause/schema.json index 34e26641e..4f94295b2 100644 --- a/packages/docker/lib/pause/schema.json +++ b/packages/docker/lib/pause/schema.json @@ -1,6 +1,7 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker", "properties": { "container": { "type": "string", diff --git a/packages/docker/lib/pull/schema.json b/packages/docker/lib/pull/schema.json index 684d87237..da5bdf802 100644 --- a/packages/docker/lib/pull/schema.json +++ b/packages/docker/lib/pull/schema.json @@ -1,6 +1,7 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker", "properties": { "all": { "type": "boolean", diff --git a/packages/docker/lib/restart/schema.json b/packages/docker/lib/restart/schema.json index 8f74b9019..715ab31f6 100644 --- a/packages/docker/lib/restart/schema.json +++ b/packages/docker/lib/restart/schema.json @@ -1,6 +1,7 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker", "properties": { "container": { "type": "string", diff --git a/packages/docker/lib/rm/index.js b/packages/docker/lib/rm/index.js index fcceeb2c3..c641c9404 100644 --- a/packages/docker/lib/rm/index.js +++ b/packages/docker/lib/rm/index.js @@ -35,7 +35,8 @@ export default { }); }, metadata: { + argument_to_config: 'container', global: 'docker', - definitions: definitions + definitions: definitions, } }; diff --git a/packages/docker/lib/rm/schema.json b/packages/docker/lib/rm/schema.json index 53714f414..0969e90d6 100644 --- a/packages/docker/lib/rm/schema.json +++ b/packages/docker/lib/rm/schema.json @@ -1,6 +1,7 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker", "properties": { "container": { "type": "string", diff --git a/packages/docker/lib/rmi/schema.json b/packages/docker/lib/rmi/schema.json index 509c57d1c..08469fa70 100644 --- a/packages/docker/lib/rmi/schema.json +++ b/packages/docker/lib/rmi/schema.json @@ -1,6 +1,7 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker", "properties": { "cwd": { "type": "string", @@ -9,6 +10,10 @@ "docker": { "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker" }, + "force": { + "type": "boolean", + "description": "Force removal of the image." + }, "image": { "type": "string", "description": "Name of the Docker image present in the registry." diff --git a/packages/docker/lib/run/README.md b/packages/docker/lib/run/README.md index 42b639873..8aad5dfe8 100644 --- a/packages/docker/lib/run/README.md +++ b/packages/docker/lib/run/README.md @@ -17,11 +17,12 @@ Run Docker Containers ## Example ```js -const {$status} = await nikita.docker.run({ - name: 'myContainer' - image: 'test-image' - env: ["FOO=bar",] - entrypoint: '/bin/true' -}) -console.info(`Container was run: ${$status}`) +const { $status } = await nikita.docker.run({ + name: "myContainer", + image: "test-image", + env: ["FOO=bar"], + entrypoint: "/bin/true", + rm: true, +}); +console.info(`Container was run: ${$status}`); ``` diff --git a/packages/docker/lib/run/index.js b/packages/docker/lib/run/index.js index 7f332c676..f5f112b5f 100644 --- a/packages/docker/lib/run/index.js +++ b/packages/docker/lib/run/index.js @@ -6,10 +6,7 @@ import utils from "@nikitajs/docker/utils"; export default { handler: async function ({ config, tools: { log } }) { if (!(config.name != null || config.rm)) { - log({ - message: "Should specify a container name if rm is false", - level: "WARN", - }); + log("WARN", "Should specify a container name if rm is false"); } // Construct exec command let command = "run"; @@ -112,20 +109,14 @@ export default { code: [0, 1], }); if ($status) { - log({ - message: "Container already running. Skipping", - level: "INFO", - }); + log("Container already running. Skipping"); } const result = await this.docker.tools.execute({ $if: config.name == null || $status === false, command: command, }); if (result.$status) { - log({ - message: "Container now running", - level: "WARN", - }); + log("WARN", "Container now running"); } return result; }, diff --git a/packages/docker/lib/run/schema.json b/packages/docker/lib/run/schema.json index 2707f629a..f39b29ca2 100644 --- a/packages/docker/lib/run/schema.json +++ b/packages/docker/lib/run/schema.json @@ -1,6 +1,7 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker", "properties": { "add_host": { "type": "array", @@ -42,6 +43,10 @@ "type": "string", "description": "Alias of name." }, + "command": { + "type": "string", + "description": "The command to execute." + }, "cpuset_cpus": { "type": "string", "description": "CPUs in which to allow execution (ex: 0-3 0,1 ...)." @@ -167,7 +172,6 @@ }, "rm": { "type": "boolean", - "default": true, "description": "Delete the container when it ends. True by default." }, "ulimit": { diff --git a/packages/docker/lib/save/schema.json b/packages/docker/lib/save/schema.json index 120cb0b90..07e717580 100644 --- a/packages/docker/lib/save/schema.json +++ b/packages/docker/lib/save/schema.json @@ -1,6 +1,7 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker", "properties": { "docker": { "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker" diff --git a/packages/docker/lib/start/index.js b/packages/docker/lib/start/index.js index cd6d39a73..846ed2ff8 100644 --- a/packages/docker/lib/start/index.js +++ b/packages/docker/lib/start/index.js @@ -8,23 +8,20 @@ export default { config, tools: {log} }) { - const {$status} = await this.docker.tools.status(config, { + const {$status} = await this.docker.tools.status({ + container: config.container, $shy: true }); if ($status) { - log({ - message: `Container already started ${config.container} (Skipping)`, - level: 'INFO', - }); + log(`Container already started ${config.container} (Skipping)`); } else { - log({ - message: `Starting container ${config.container}`, - level: 'INFO', - }); + log(`Starting container ${config.container}`); } await this.docker.tools.execute({ $unless: $status, - command: ['start', config.attach ? '-a' : void 0, `${config.container}`].join(' ') + command: ["start", config.attach && "-a", `${config.container}`] + .filter(Boolean) + .join(" "), }); }, metadata: { diff --git a/packages/docker/lib/start/schema.json b/packages/docker/lib/start/schema.json index 400efe7c2..3fda5b648 100644 --- a/packages/docker/lib/start/schema.json +++ b/packages/docker/lib/start/schema.json @@ -1,6 +1,7 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker", "properties": { "attach": { "type": "boolean", diff --git a/packages/docker/lib/stop/schema.json b/packages/docker/lib/stop/schema.json index 5f52cf46c..5eb0fd52e 100644 --- a/packages/docker/lib/stop/schema.json +++ b/packages/docker/lib/stop/schema.json @@ -1,6 +1,7 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker", "properties": { "container": { "type": "string", diff --git a/packages/docker/lib/tools/checksum/schema.json b/packages/docker/lib/tools/checksum/schema.json index 7f8750c58..4f8080038 100644 --- a/packages/docker/lib/tools/checksum/schema.json +++ b/packages/docker/lib/tools/checksum/schema.json @@ -1,6 +1,7 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker", "properties": { "cwd": { "type": "string", diff --git a/packages/docker/lib/tools/execute/schema.json b/packages/docker/lib/tools/execute/schema.json index e00ec9480..80b465e0d 100644 --- a/packages/docker/lib/tools/execute/schema.json +++ b/packages/docker/lib/tools/execute/schema.json @@ -1,47 +1,41 @@ { "config": { "type": "object", - "allOf": [ - { - "$ref": "#/definitions/docker" + "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker", + "properties": { + "bash": { + "type": [ + "boolean", + "string" + ], + "description": "Serialize the command into a file and execute it with bash." }, - { - "properties": { - "bash": { - "type": [ - "boolean", - "string" - ], - "description": "Serialize the command into a file and execute it with bash." - }, - "command": { - "oneOf": [ - { - "type": "string" - }, - { - "typeof": "function" - } - ], - "description": "String, Object or array; Command to execute. A value provided as a\nfunction is interpreted as an action and will be called by forwarding\nthe config object. The result is the expected to be the command\nto execute." - }, - "cwd": { - "type": "string", - "description": "Current working directory from where to execute the command." - }, - "code": { - "$ref": "module://@nikitajs/core/actions/execute#/definitions/config/properties/code", - "default": {} - }, - "docker": { - "$ref": "#/definitions/docker" + "command": { + "oneOf": [ + { + "type": "string" }, - "format": { - "$ref": "module://@nikitajs/core/actions/execute#/definitions/config/properties/format" + { + "typeof": "function" } - } + ], + "description": "String, Object or array; Command to execute. A value provided as a\nfunction is interpreted as an action and will be called by forwarding\nthe config object. The result is the expected to be the command\nto execute." + }, + "cwd": { + "type": "string", + "description": "Current working directory from where to execute the command." + }, + "code": { + "$ref": "module://@nikitajs/core/actions/execute#/definitions/config/properties/code", + "default": {} + }, + "docker": { + "$ref": "#/definitions/docker" + }, + "format": { + "$ref": "module://@nikitajs/core/actions/execute#/definitions/config/properties/format" } - ], + }, "required": [ "command" ] diff --git a/packages/docker/lib/tools/service/index.js b/packages/docker/lib/tools/service/index.js index 218da1cd6..6f1965002 100644 --- a/packages/docker/lib/tools/service/index.js +++ b/packages/docker/lib/tools/service/index.js @@ -5,15 +5,11 @@ import definitions from "./schema.json" assert { type: "json" }; // Action export default { handler: async function ({ config }) { - // Normalization - if (config.detach == null) { - config.detach = true; - } - if (config.rm == null) { - config.rm = false; - } - // Execution - await this.docker.run(config); + await this.docker.run({ + detach: true, + rm: false, + ...config + }); }, metadata: { definitions: definitions, diff --git a/packages/docker/lib/tools/service/schema.json b/packages/docker/lib/tools/service/schema.json index 25b5e63ca..0955d2921 100644 --- a/packages/docker/lib/tools/service/schema.json +++ b/packages/docker/lib/tools/service/schema.json @@ -1,24 +1,19 @@ { "config": { "type": "object", - "allOf": [ - { - "properties": { - "detach": { - "default": true - }, - "rm": { - "default": false - } - } + "$ref": "module://@nikitajs/docker/run#/definitions/config", + "properties": { + "detach": { + "const": true, + "default": true }, - { - "$ref": "module://@nikitajs/docker/run" + "rm": { + "const": false, + "default": false } - ], + }, "required": [ - "container", - "image" + "container" ] } } diff --git a/packages/docker/lib/tools/status/schema.json b/packages/docker/lib/tools/status/schema.json index 3a09a9539..75b5b492d 100644 --- a/packages/docker/lib/tools/status/schema.json +++ b/packages/docker/lib/tools/status/schema.json @@ -1,6 +1,7 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker", "properties": { "container": { "type": "array", diff --git a/packages/docker/lib/unpause/schema.json b/packages/docker/lib/unpause/schema.json index 141144443..c7740825d 100644 --- a/packages/docker/lib/unpause/schema.json +++ b/packages/docker/lib/unpause/schema.json @@ -1,6 +1,7 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker", "properties": { "container": { "type": "string", diff --git a/packages/docker/lib/volume_create/schema.json b/packages/docker/lib/volume_create/schema.json index 4145ae862..34141c674 100644 --- a/packages/docker/lib/volume_create/schema.json +++ b/packages/docker/lib/volume_create/schema.json @@ -1,6 +1,7 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker", "properties": { "docker": { "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker" diff --git a/packages/docker/lib/volume_rm/schema.json b/packages/docker/lib/volume_rm/schema.json index d8a07af22..ee5f2b537 100644 --- a/packages/docker/lib/volume_rm/schema.json +++ b/packages/docker/lib/volume_rm/schema.json @@ -1,6 +1,7 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker", "properties": { "docker": { "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker" diff --git a/packages/docker/lib/wait/schema.json b/packages/docker/lib/wait/schema.json index 34e26641e..4f94295b2 100644 --- a/packages/docker/lib/wait/schema.json +++ b/packages/docker/lib/wait/schema.json @@ -1,6 +1,7 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/docker/tools/execute#/definitions/docker", "properties": { "container": { "type": "string", diff --git a/packages/docker/test/build.coffee b/packages/docker/test/build.coffee index 5a2f9982a..67ed152cb 100644 --- a/packages/docker/test/build.coffee +++ b/packages/docker/test/build.coffee @@ -16,7 +16,7 @@ describe 'docker.build', -> $ssh: ssh docker: test.docker .docker.build - false_source: 'Dockerfile' + file: "/a_dir/Dockerfile" .should.be.rejectedWith code: 'NIKITA_SCHEMA_VALIDATION_CONFIG' message: [ diff --git a/packages/docker/test/compose/index.coffee b/packages/docker/test/compose/index.coffee index be5d77966..c7cf044ca 100644 --- a/packages/docker/test/compose/index.coffee +++ b/packages/docker/test/compose/index.coffee @@ -125,7 +125,7 @@ describe 'docker.compose', -> ports: ['12303:80'] target: "#{tmpdir}/docker_compose_up_file/docker-compose.yml" await @docker.compose - service: 'compose' + services: 'up_with_service_name' target: "#{tmpdir}/docker_compose_up_file/docker-compose.yml" {$status} = await @execute command: 'ping dind -c 1' diff --git a/packages/docker/test/exec.coffee b/packages/docker/test/exec.coffee index 0e84c176d..7f11a3328 100644 --- a/packages/docker/test/exec.coffee +++ b/packages/docker/test/exec.coffee @@ -48,7 +48,7 @@ describe 'docker.exec', -> catch err err.message.should.match /Container [a-z0-9]+ is not running/ finally - @docker.rm + await @docker.rm container: 'nikita_test_exec' force: true diff --git a/packages/docker/test/ps.coffee b/packages/docker/test/ps.coffee index 2f3a2c52d..9e170fc6b 100644 --- a/packages/docker/test/ps.coffee +++ b/packages/docker/test/ps.coffee @@ -11,7 +11,10 @@ describe 'docker.ps', -> nikita $ssh: ssh docker: test.docker - , -> + , ({registry}) -> + registry.register 'clean', () -> + await @docker.rm ['nikita_test_ps_1', 'nikita_test_ps_2'], force: true + await @clean() try await @docker.run command: "sh -c 'while true; do sleep 1000; done'" @@ -27,14 +30,16 @@ describe 'docker.ps', -> count.should.eql 2 names.sort().should.eql ['nikita_test_ps_1', 'nikita_test_ps_2'] finally - await @docker.rm 'nikita_test_ps_1', force: true - await @docker.rm 'nikita_test_ps_2', force: true + await @clean() they 'option `all`', ({ssh}) -> nikita $ssh: ssh docker: test.docker - , -> + , ({registry}) -> + registry.register 'clean', () -> + await @docker.rm ['nikita_test_ps_3', 'nikita_test_ps_4'] + await @clean() try await @docker.run command: "/bin/echo 'test'" @@ -47,9 +52,8 @@ describe 'docker.ps', -> await @docker.ps all: true .then ({containers}) -> - containers.map((c) => c.Names).sort() + containers.map((c) => c.Names).filter((c) => c.startsWith 'nikita_test_ps').sort() .should.be.resolvedWith ['nikita_test_ps_3', 'nikita_test_ps_4'] finally - await @docker.rm 'nikita_test_ps_3' - await @docker.rm 'nikita_test_ps_4' + await @clean() \ No newline at end of file diff --git a/packages/docker/test/run.coffee b/packages/docker/test/run.coffee index 6b19f8d48..523bd61f0 100644 --- a/packages/docker/test/run.coffee +++ b/packages/docker/test/run.coffee @@ -7,34 +7,52 @@ they = mochaThey(test.config) describe 'docker.run', -> return unless test.tags.docker - they 'simple command', ({ssh}) -> + they 'option `rm` is `false` by default', ({ssh}) -> nikita $ssh: ssh docker: test.docker , -> - {$status, stdout} = await @docker.run - command: "/bin/echo 'test'" - image: 'alpine' - $status.should.be.true() - stdout.should.match /^test.*/ + try + await @docker.rm 'nikita_test_run_rm_false', force: true + await @docker.run + command: "/bin/echo 'test'" + image: 'alpine' + container: 'nikita_test_run_rm_false' + {names} = await @docker.ps all: true + names.should.containEql 'nikita_test_run_rm_false' + finally + await @docker.rm 'nikita_test_run_rm_false', force: true - they '--rm (flag option)', ({ssh}) -> + they 'option `rm` is `false`', ({ssh}) -> nikita $ssh: ssh docker: test.docker , -> - await @docker.rm - force: true - container: 'nikita_test_rm' - {stdout} = await @docker.run - command: "/bin/echo 'test'" - image: 'alpine' - container: 'nikita_test_rm' - rm: false - stdout.should.match /^test.*/ - await @docker.rm - force: true - container: 'nikita_test_rm' + try + await @docker.rm 'nikita_test_run_rm_true', force: true + {$status, stdout} = await @docker.run + command: "/bin/echo 'test'" + image: 'alpine' + rm: true + {names} = await @docker.ps all: true + names.should.not.containEql 'nikita_test_run_rm_true' + finally + await @docker.rm 'nikita_test_run_rm_true', force: true + + they 'output `stdout`', ({ssh}) -> + nikita + $ssh: ssh + docker: test.docker + , -> + try + {stdout} = await @docker.run + command: "/bin/echo 'test'" + image: 'alpine' + container: 'nikita_test_rm' + rm: true + stdout.should.match /^test.*/ + finally + await @docker.rm 'nikita_test_rm', force: true they 'unique option from array option', ({ssh}) -> @timeout 0 diff --git a/packages/docker/test/wait.coffee b/packages/docker/test/wait.coffee index e94055b3d..bd1cb639a 100644 --- a/packages/docker/test/wait.coffee +++ b/packages/docker/test/wait.coffee @@ -11,20 +11,23 @@ describe 'docker.wait', -> nikita $ssh: ssh docker: test.docker - , -> - await @docker.rm - container: 'nikita_test_wait' - force: true - await @docker.tools.service - image: 'httpd' - container: 'nikita_test_wait' - setTimeout => - @docker.stop + , ({registry}) -> + registry.register 'clean', () -> + await @docker.rm 'nikita_test_wait', force: true + await @clean() + try + await @docker.tools.service + image: 'httpd' container: 'nikita_test_wait' - , 50 - {$status} = await nikita - $ssh: ssh - docker: test.docker - .docker.wait - container: 'nikita_test_wait' - $status.should.be.true() + setTimeout => + @docker.stop + container: 'nikita_test_wait' + , 50 + {$status} = await nikita + $ssh: ssh + docker: test.docker + .docker.wait + container: 'nikita_test_wait' + $status.should.be.true() + finally + await @clean() diff --git a/packages/file/lib/ini/schema.json b/packages/file/lib/ini/schema.json index e8f1c1404..7a4573da3 100644 --- a/packages/file/lib/ini/schema.json +++ b/packages/file/lib/ini/schema.json @@ -39,6 +39,10 @@ "gid": { "$ref": "module://@nikitajs/file#/definitions/config/properties/gid" }, + "indent": { + "type": "string", + "description": "Characters used for indentation used by the stringifier functions." + }, "local": { "type": "boolean", "description": "Read the source file locally if it exists." @@ -53,6 +57,10 @@ "parse": { "$ref": "module://@nikitajs/file/ini/read#/definitions/config/properties/parse" }, + "separator": { + "type": "string", + "description": "Characters separating keys and values used by the stringifier functions." + }, "stringify": { "typeof": "function", "description": "User-defined function to stringify the content to ini format, default\nto `@nikitajs/file/utils/ini#stringify`." diff --git a/packages/file/lib/render/index.js b/packages/file/lib/render/index.js index 5786d4f65..e40bc2cfe 100644 --- a/packages/file/lib/render/index.js +++ b/packages/file/lib/render/index.js @@ -32,9 +32,6 @@ export default { hooks: { on_action: function ({ config }) { // Validate parameters - if (config.encoding == null) { - config.encoding = "utf8"; - } if (!(config.source || config.content)) { throw Error("Required option: source or content"); } diff --git a/packages/file/lib/render/schema.json b/packages/file/lib/render/schema.json index f0e3c3e90..766b587fc 100644 --- a/packages/file/lib/render/schema.json +++ b/packages/file/lib/render/schema.json @@ -8,6 +8,9 @@ "context": { "$ref": "module://@nikitajs/file#/definitions/config/properties/context" }, + "encoding": { + "$ref": "module://@nikitajs/file#/definitions/config/properties/encoding" + }, "engine": { "$ref": "module://@nikitajs/file#/definitions/config/properties/engine" }, diff --git a/packages/file/lib/schema.json b/packages/file/lib/schema.json index 75e946da7..23f111d49 100644 --- a/packages/file/lib/schema.json +++ b/packages/file/lib/schema.json @@ -148,6 +148,10 @@ ], "description": "Name of the marker until where the content will be replaced." }, + "transform": { + "typeof": "function", + "description": "A user-defined function used to transform the `content` property before it is written." + }, "uid": { "$ref": "module://@nikitajs/core/actions/fs/chown#/definitions/config/properties/uid" }, diff --git a/packages/file/lib/types/pacman_conf/index.js b/packages/file/lib/types/pacman_conf/index.js index 257cdff51..ccfef8621 100644 --- a/packages/file/lib/types/pacman_conf/index.js +++ b/packages/file/lib/types/pacman_conf/index.js @@ -9,9 +9,9 @@ export default { if (config.rootdir) { config.target = `${path.join(config.rootdir, config.target)}`; } - return (await this.file.ini({ + return await this.file.ini({ stringify: utils.ini.stringify_single_key - }, config)); + }, utils.object.filter(config, ['rootdir'])); }, metadata: { definitions: definitions diff --git a/packages/file/lib/types/systemd/resolved/schema.json b/packages/file/lib/types/systemd/resolved/schema.json index 091cda69d..54fe2ad56 100644 --- a/packages/file/lib/types/systemd/resolved/schema.json +++ b/packages/file/lib/types/systemd/resolved/schema.json @@ -2,14 +2,22 @@ "config": { "type": "object", "properties": { - "rootdir": { - "type": "string", - "description": "Path to the mount point corresponding to the root directory, optional." + "content": { + "type": "object", + "default": {}, + "description": "Information to serialize into the resolved configuration file." + }, + "merge": { + "$ref": "module://@nikitajs/file/ini#/definitions/config/properties/merge" }, "reload": { "type": "boolean", "default": null, - "description": "Defaults to true. If set to true the following command will be\nexecuted `systemctl daemon-reload && systemctl restart\nsystemd-resolved` after having wrote the configuration file." + "description": "Defaults to true. If set to true the following command will be executed `systemctl daemon-reload && systemctl restart systemd-resolved` after having wrote the configuration file." + }, + "rootdir": { + "type": "string", + "description": "Path to the mount point corresponding to the root directory, optional." }, "target": { "type": "string", diff --git a/packages/file/lib/types/wireguard_conf/index.js b/packages/file/lib/types/wireguard_conf/index.js index 93ad0a066..2fcd4461b 100644 --- a/packages/file/lib/types/wireguard_conf/index.js +++ b/packages/file/lib/types/wireguard_conf/index.js @@ -12,14 +12,12 @@ export default { if (config.rootdir) { config.target = `${path.join(config.rootdir, config.target)}`; } - await this.file.ini( - { - parse: utils.ini.parse_multi_brackets, - stringify: utils.ini.stringify_multi_brackets, - indent: "", - }, - config - ); + await this.file.ini({ + parse: utils.ini.parse_multi_brackets, + stringify: utils.ini.stringify_multi_brackets, + indent: "", + ...utils.object.filter(config, ['interface', 'rootdir']), + }); }, metadata: { definitions: definitions diff --git a/packages/file/lib/types/wireguard_conf/schema.json b/packages/file/lib/types/wireguard_conf/schema.json index f77189227..7dcfa80ca 100644 --- a/packages/file/lib/types/wireguard_conf/schema.json +++ b/packages/file/lib/types/wireguard_conf/schema.json @@ -1,6 +1,7 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/file/ini#/definitions/config", "properties": { "rootdir": { "type": "string", diff --git a/packages/file/lib/upload/index.js b/packages/file/lib/upload/index.js index 599247786..e83b7ad5c 100644 --- a/packages/file/lib/upload/index.js +++ b/packages/file/lib/upload/index.js @@ -44,7 +44,7 @@ export default { try { const { stats } = await this.fs.base.stat({ $ssh: false, - sudo: false, + $sudo: false, target: config.target, }); if (utils.stats.isFile(stats.mode)) { diff --git a/packages/file/lib/yaml/index.js b/packages/file/lib/yaml/index.js index eba9bd0b1..860eb0c2b 100644 --- a/packages/file/lib/yaml/index.js +++ b/packages/file/lib/yaml/index.js @@ -32,10 +32,11 @@ export default { level: "DEBUG", }); config.content = yaml.dump(config.content, { + indent: config.indent, noRefs: true, lineWidth: config.line_width, }); - await this.file(config); + await this.file(utils.object.filter(config, ['clean', 'indent', 'line_width', 'merge'])); }, metadata: { definitions: definitions, diff --git a/packages/file/test/render.coffee b/packages/file/test/render.coffee index 23b70418c..b7b0b85d8 100644 --- a/packages/file/test/render.coffee +++ b/packages/file/test/render.coffee @@ -57,13 +57,12 @@ describe 'file.render', -> $tmpdir: true , ({metadata: {tmpdir}}) -> await @file.render - content: 'Got {{ config.test }}' + content: 'Got {{ config.target }}' target: "#{tmpdir}/output" - context: config: test: 'from context' - test: 'from action' + context: config: target: 'overwritte target' await @fs.assert target: "#{tmpdir}/output" - content: 'Got from context' + content: 'Got overwritte target' describe 'handlebars', -> diff --git a/packages/file/test/types/hfile.coffee b/packages/file/test/types/hfile.coffee index 9295a8ca6..13a05555f 100644 --- a/packages/file/test/types/hfile.coffee +++ b/packages/file/test/types/hfile.coffee @@ -14,11 +14,9 @@ describe 'file.types.hfile', -> , ({metadata: {tmpdir}}) -> {$status} = await @file.types.hfile target: "#{tmpdir}/empty.xml" - content: {} $status.should.be.true() {$status} = await @file.types.hfile target: "#{tmpdir}/empty.xml" - content: {} $status.should.be.false() await @fs.assert target: "#{tmpdir}/empty.xml" diff --git a/packages/incus/lib/cluster/delete/index.js b/packages/incus/lib/cluster/delete/index.js index 7635ed6b3..2c31b21b4 100644 --- a/packages/incus/lib/cluster/delete/index.js +++ b/packages/incus/lib/cluster/delete/index.js @@ -4,10 +4,11 @@ import definitions from "./schema.json" assert { type: "json" }; // Action export default { handler: async function({config}) { + // Pre hook if (!!config.pre_delete) { await this.call(config, config.pre_delete); } - // Delete containers + // Containers removal for (const name in config.containers) { await this.incus.delete({ $header: `Container ${name} : delete`, @@ -15,6 +16,7 @@ export default { force: config.force }); } + // Networks removal for (const name in config.networks) { await this.incus.network.delete({ $header: `Network ${name} : delete`, diff --git a/packages/incus/lib/cluster/index.js b/packages/incus/lib/cluster/index.js index 31dea0ab5..91d040d7e 100644 --- a/packages/incus/lib/cluster/index.js +++ b/packages/incus/lib/cluster/index.js @@ -34,8 +34,9 @@ export default { }, async function() { // Set configuration await this.incus.init({ - $header: 'Init' - }, containerConfig); + $header: 'Init', + ...utils.object.filter(containerConfig, ['disk', 'nic', 'properties', 'proxy', 'user', 'ssh']) + }); // Set config if (containerConfig != null ? containerConfig.properties : void 0) { await this.incus.config.set({ diff --git a/packages/incus/lib/cluster/stop/schema.json b/packages/incus/lib/cluster/stop/schema.json index 6b4e6f217..4c48231fa 100644 --- a/packages/incus/lib/cluster/stop/schema.json +++ b/packages/incus/lib/cluster/stop/schema.json @@ -1,10 +1,8 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/incus/cluster#/definitions/config", "properties": { - "containers": { - "$ref": "module://@nikitajs/incus/cluster#/definitions/config/properties/containers" - }, "wait": { "type": "boolean", "default": false, diff --git a/packages/incus/lib/exec/index.js b/packages/incus/lib/exec/index.js index 3e1c3131f..b7b869a1b 100644 --- a/packages/incus/lib/exec/index.js +++ b/packages/incus/lib/exec/index.js @@ -1,6 +1,12 @@ // Dependencies +import utils from "@nikitajs/core/utils"; import { escapeshellarg as esa } from "@nikitajs/core/utils/string"; import definitions from "./schema.json" assert { type: "json" }; +import execute from "@nikitajs/core/actions/execute"; + +const properties = Object.keys(execute.metadata.definitions.config.properties).filter( + (prop) => !["command", "trap", "env"].includes(prop) +); // Action export default { @@ -15,8 +21,9 @@ export default { ].filter(Boolean).join(" "); return await this.execute( // `trap` and `env` apply to `incus exec` and not to `execute` - { ...config, env: undefined, trap: undefined }, + // { ...config, env: undefined, trap: undefined }, { + ...utils.object.copy(config, properties), command: [ `cat <<'NIKITAINCUSEXEC' | incus exec ${opt} ${esa(config.container)} -- ${esa(config.shell)}`, config.trap && "set -e", diff --git a/packages/ipa/lib/group/index.js b/packages/ipa/lib/group/index.js index cd8d8c8ca..7f70fd996 100644 --- a/packages/ipa/lib/group/index.js +++ b/packages/ipa/lib/group/index.js @@ -35,7 +35,8 @@ export default { } // Get info even when no modification was performed if (!result) { - ({result} = await this.ipa.group.show(config, { + ({result} = await this.ipa.group.show({ + connection: config.connection, cn: config.cn })); } diff --git a/packages/ipa/lib/user/index.js b/packages/ipa/lib/user/index.js index 6ea459964..3e4e1e47a 100644 --- a/packages/ipa/lib/user/index.js +++ b/packages/ipa/lib/user/index.js @@ -35,8 +35,9 @@ export default { } // Get info even when no modification was performed if (!result) { - ({result} = await this.ipa.user.show(config, { - cn: config.cn + ({result} = await this.ipa.user.show({ + connection: config.connection, + uid: config.uid, })); } return { diff --git a/packages/java/lib/keystore/add/schema.json b/packages/java/lib/keystore/add/schema.json index e27fa4e58..7acdc0f90 100644 --- a/packages/java/lib/keystore/add/schema.json +++ b/packages/java/lib/keystore/add/schema.json @@ -35,45 +35,31 @@ "storepass": { "type": "string", "description": "Password to manage the keystore." + }, + "caname": { + "type": "string", + "description": "Name of the certificate authority (CA)." + }, + "key": { + "type": "string", + "description": "Location of the private key." + }, + "keypass": { + "type": "string", + "description": "Password used to protect the certificate and its key access\ninside the keystore." + }, + "name": { + "type": "string", + "description": "Name (aka alias) to reference the certificate inside the keystore." } }, "required": [ "keystore", "storepass" ], - "dependencies": { - "cacert": { - "properties": { - "caname": { - "type": "string", - "description": "Name of the certificate authority (CA)." - } - }, - "required": [ - "caname" - ] - }, - "cert": { - "properties": { - "key": { - "type": "string", - "description": "Location of the private key." - }, - "keypass": { - "type": "string", - "description": "Password used to protect the certificate and its key access\ninside the keystore." - }, - "name": { - "type": "string", - "description": "Name (aka alias) to reference the certificate inside the keystore." - } - }, - "required": [ - "key", - "keypass", - "name" - ] - } + "dependentRequired": { + "cacert": ["caname"], + "cert": ["key", "keypass", "name"] } } } diff --git a/packages/java/test/keystore/add.coffee b/packages/java/test/keystore/add.coffee index 9ddb3ff4a..8a0be3251 100644 --- a/packages/java/test/keystore/add.coffee +++ b/packages/java/test/keystore/add.coffee @@ -24,7 +24,8 @@ describe 'java.keystore.add', -> .should.be.rejectedWith [ 'NIKITA_SCHEMA_VALIDATION_CONFIG:' 'one error was found in the configuration of action `java.keystore.add`:' - '#/dependencies/cacert/required config must have required property \'caname\'.' + '#/dependentRequired config must have property caname when property cacert is present,' + 'property is "cacert", depsCount is 1, deps is "caname".' ].join ' ' it 'cert implies key, keypass and name', -> @@ -43,9 +44,12 @@ describe 'java.keystore.add', -> .should.be.rejectedWith [ 'NIKITA_SCHEMA_VALIDATION_CONFIG:' 'multiple errors were found in the configuration of action `java.keystore.add`:' - '#/dependencies/cert/required config must have required property \'key\';' - '#/dependencies/cert/required config must have required property \'keypass\';' - '#/dependencies/cert/required config must have required property \'name\'.' + '#/dependentRequired config must have properties key, keypass, name when property cert is present,' + 'property is "cert", depsCount is 3, deps is "key, keypass, name";' + '#/dependentRequired config must have properties key, keypass, name when property cert is present,' + 'property is "cert", depsCount is 3, deps is "key, keypass, name";' + '#/dependentRequired config must have properties key, keypass, name when property cert is present,' + 'property is "cert", depsCount is 3, deps is "key, keypass, name".' ].join ' ' describe 'config', -> diff --git a/packages/java/test/keystore/remove.coffee b/packages/java/test/keystore/remove.coffee index a58803ff5..a332376de 100644 --- a/packages/java/test/keystore/remove.coffee +++ b/packages/java/test/keystore/remove.coffee @@ -104,13 +104,11 @@ describe 'java.keystore.remove', -> keystore: "#{tmpdir}/cacerts" storepass: 'changeit' name: 'node_1' - keypass: 'mypassword' $status.should.be.true() {$status} = await @java.keystore.remove keystore: "#{tmpdir}/cacerts" storepass: 'changeit' name: 'node_1' - keypass: 'mypassword' $status.should.be.false() await @java.keystore.exists keystore: "#{tmpdir}/cacerts" diff --git a/packages/krb5/lib/addprinc/index.js b/packages/krb5/lib/addprinc/index.js index 585e1566a..d8761dcca 100644 --- a/packages/krb5/lib/addprinc/index.js +++ b/packages/krb5/lib/addprinc/index.js @@ -26,7 +26,9 @@ export default { await this.krb5.execute({ $retry: 3, admin: config.admin, - command: config.password ? `addprinc -pw ${config.password} ${config.principal}` : `addprinc -randkey ${config.principal}` + command: config.password + ? `addprinc -pw ${config.password} ${config.principal}` + : `addprinc -randkey ${config.principal}`, }); } if (config.password && config.password_sync) { @@ -43,7 +45,9 @@ export default { if (!config.keytab) { return; } - await this.krb5.ktadd(config); + await this.krb5.ktadd({ + ...utils.object.filter(config, [], ["admin", "gid", "keytab", "mode", "principal", "realm", "uid"]), + }); }, metadata: { global: 'krb5', diff --git a/packages/krb5/lib/addprinc/schema.json b/packages/krb5/lib/addprinc/schema.json index 9b8d0d163..7c23dffe4 100644 --- a/packages/krb5/lib/addprinc/schema.json +++ b/packages/krb5/lib/addprinc/schema.json @@ -3,7 +3,7 @@ "type": "object", "properties": { "admin": { - "$ref": "module://@nikitajs/krb5/execute#/definitions/config/properties/admin" + "$ref": "module://@nikitajs/krb5/execute#/definitions/admin" }, "keytab": { "type": "string", diff --git a/packages/krb5/lib/delprinc/schema.json b/packages/krb5/lib/delprinc/schema.json index 3e389a284..e96671291 100644 --- a/packages/krb5/lib/delprinc/schema.json +++ b/packages/krb5/lib/delprinc/schema.json @@ -3,7 +3,7 @@ "type": "object", "properties": { "admin": { - "$ref": "module://@nikitajs/krb5/execute#/definitions/config/properties/admin" + "$ref": "module://@nikitajs/krb5/execute#/definitions/admin" }, "keytab": { "type": "string", diff --git a/packages/krb5/lib/execute/schema.json b/packages/krb5/lib/execute/schema.json index 0a4d26f37..34bffcdf0 100644 --- a/packages/krb5/lib/execute/schema.json +++ b/packages/krb5/lib/execute/schema.json @@ -3,25 +3,7 @@ "type": "object", "properties": { "admin": { - "type": "object", - "properties": { - "realm": { - "type": "string", - "description": "The realm the principal belongs to." - }, - "principal": { - "type": "string", - "description": "KAdmin principal name unless `kadmin.local` is used." - }, - "server": { - "type": "string", - "description": "Address of the kadmin server; optional, use \"kadmin.local\" if\nmissing." - }, - "password": { - "type": "string", - "description": "Password associated to the KAdmin principal." - } - } + "$ref": "module://@nikitajs/krb5/execute#/definitions/admin" }, "command": { "type": "string", @@ -43,5 +25,26 @@ "admin", "command" ] + }, + "admin": { + "type": "object", + "properties": { + "realm": { + "type": "string", + "description": "The realm the principal belongs to." + }, + "principal": { + "type": "string", + "description": "KAdmin principal name unless `kadmin.local` is used." + }, + "server": { + "type": "string", + "description": "Address of the kadmin server; optional, use \"kadmin.local\" if\nmissing." + }, + "password": { + "type": "string", + "description": "Password associated to the KAdmin principal." + } + } } } diff --git a/packages/krb5/lib/ktadd/index.js b/packages/krb5/lib/ktadd/index.js index 24f975624..a19bf182a 100644 --- a/packages/krb5/lib/ktadd/index.js +++ b/packages/krb5/lib/ktadd/index.js @@ -29,10 +29,7 @@ export default { code: [0, 1] }); if (entriesExist) { - log({ - message: "Keytab exists, check kvno validity", - level: 'DEBUG' - }); + log('DEBUG', "Keytab exists, check kvno validity"); const lines = utils.string.lines(entriesStdout); for (const line of lines) { const match = /^\s*(\d+)\s+([\d\/:]+\s+[\d\/:]+)\s+(.*)\s*$/.exec(line) @@ -70,14 +67,8 @@ export default { mdate: parseInt(values[2], 10) * 1000, kvno: parseInt(values[8], 10) }; - log({ - message: `Keytab kvno '${keytab[config.principal].kvno}', principal kvno '${princ.kvno}'`, - level: 'INFO' - }); - log({ - message: `Keytab mdate '${new Date(keytab[config.principal].mdate)}', principal mdate '${new Date(princ.mdate)}'`, - level: 'INFO' - }); + log('INFO', `Keytab kvno '${keytab[config.principal].kvno}', principal kvno '${princ.kvno}'`); + log('INFO', `Keytab mdate '${new Date(keytab[config.principal].mdate)}', principal mdate '${new Date(princ.mdate)}'`); } } // Remove principal from keytab diff --git a/packages/krb5/lib/ktadd/schema.json b/packages/krb5/lib/ktadd/schema.json index 6236f1dcf..cca92dff9 100644 --- a/packages/krb5/lib/ktadd/schema.json +++ b/packages/krb5/lib/ktadd/schema.json @@ -3,7 +3,7 @@ "type": "object", "properties": { "admin": { - "$ref": "module://@nikitajs/krb5/execute#/definitions/config/properties/admin" + "$ref": "module://@nikitajs/krb5/execute#/definitions/admin" }, "gid": { "$ref": "module://@nikitajs/file#/definitions/config/properties/gid" diff --git a/packages/krb5/lib/ktutil/add/schema.json b/packages/krb5/lib/ktutil/add/schema.json index 2b5e6cc67..a7e2de182 100644 --- a/packages/krb5/lib/ktutil/add/schema.json +++ b/packages/krb5/lib/ktutil/add/schema.json @@ -3,7 +3,7 @@ "type": "object", "properties": { "admin": { - "$ref": "module://@nikitajs/krb5/execute#/definitions/config/properties/admin" + "$ref": "module://@nikitajs/krb5/execute#/definitions/admin" }, "enctypes": { "type": "array", diff --git a/packages/krb5/lib/ticket/index.js b/packages/krb5/lib/ticket/index.js index 73ea7dcb2..d96f4aff0 100644 --- a/packages/krb5/lib/ticket/index.js +++ b/packages/krb5/lib/ticket/index.js @@ -24,7 +24,7 @@ export default { }); }, metadata: { - global: 'krb5', + // global: 'krb5', definitions: definitions } }; diff --git a/packages/krb5/lib/ticket/schema.json b/packages/krb5/lib/ticket/schema.json index 6042b4672..a17fc0783 100644 --- a/packages/krb5/lib/ticket/schema.json +++ b/packages/krb5/lib/ticket/schema.json @@ -23,14 +23,10 @@ }, "oneOf": [ { - "required": [ - "keytab" - ] + "required": ["keytab"] }, { - "required": [ - "password" - ] + "required": ["password"] } ] } diff --git a/packages/krb5/package.json b/packages/krb5/package.json index 778552399..007298965 100644 --- a/packages/krb5/package.json +++ b/packages/krb5/package.json @@ -75,7 +75,7 @@ "should" ], "throw-deprecation": true, - "timeout": 40000 + "timeout": 50000 }, "publishConfig": { "access": "public" diff --git a/packages/krb5/test/execute.coffee b/packages/krb5/test/execute.coffee index a08e6db16..efe1138b9 100644 --- a/packages/krb5/test/execute.coffee +++ b/packages/krb5/test/execute.coffee @@ -17,8 +17,8 @@ describe 'krb5.execute', -> message: [ 'NIKITA_SCHEMA_VALIDATION_CONFIG:' 'multiple errors were found in the configuration of action `krb5.execute`:' - '#/definitions/config/required config must have required property \'admin\';' - '#/definitions/config/required config must have required property \'command\'.' + '#/required config must have required property \'admin\';' + '#/required config must have required property \'command\'.' ].join ' ' describe 'action', -> diff --git a/packages/ldap/lib/acl/index.js b/packages/ldap/lib/acl/index.js index 310ad7a0a..e8966f755 100644 --- a/packages/ldap/lib/acl/index.js +++ b/packages/ldap/lib/acl/index.js @@ -10,16 +10,19 @@ export default { // Get DN if (!config.dn) { log("DEBUG", "Get DN of the database to modify"); - const { dn } = await this.ldap.tools.database(config, { - suffix: config.suffix, - }); - config.dn = dn; - log("INFO", `Database DN is ${dn}`); + config.dn = await this.ldap.tools + .database({ + ...utils.ldap.config_connection(config), + suffix: config.suffix, + }) + .then(({ dn }) => dn); + log("INFO", `Database DN is ${config.dn}`); } for (const acl of config.acls) { // Get ACLs log("DEBUG", "List all ACL of the directory"); - const { stdout } = await this.ldap.search(config, { + const { stdout } = await this.ldap.search({ + ...utils.ldap.config_connection(config), attributes: ["olcAccess"], base: `${config.dn}`, filter: "(olcAccess=*)", @@ -144,7 +147,8 @@ export default { value: olcAccess, }); } - await this.ldap.modify(config, { + await this.ldap.modify({ + ...utils.ldap.config_connection(config), operations: operations, }); $status = true; diff --git a/packages/ldap/lib/acl/schema.json b/packages/ldap/lib/acl/schema.json index 91e19191b..6d9288a85 100644 --- a/packages/ldap/lib/acl/schema.json +++ b/packages/ldap/lib/acl/schema.json @@ -1,6 +1,7 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/ldap/search#/definitions/connection", "properties": { "acls": { "type": "array", diff --git a/packages/ldap/lib/add/index.js b/packages/ldap/lib/add/index.js index de4c6e2bb..5c1de8dc2 100644 --- a/packages/ldap/lib/add/index.js +++ b/packages/ldap/lib/add/index.js @@ -1,6 +1,7 @@ // Dependencies import dedent from "dedent"; import { escapeshellarg as esa } from "@nikitajs/core/utils/string"; +import utils from "@nikitajs/ldap/utils" import definitions from "./schema.json" assert { type: "json" }; // Action @@ -18,7 +19,8 @@ export default { for (const entry of config.entry) { // Check if record already exists // exit code 32 is for "no such object" - const { $status } = await this.ldap.search(config, { + const { $status } = await this.ldap.search({ + ...utils.ldap.config_connection(config), base: entry.dn, code: [0, 32], scope: "base", diff --git a/packages/ldap/lib/delete/index.js b/packages/ldap/lib/delete/index.js index acd16eb3e..118202350 100644 --- a/packages/ldap/lib/delete/index.js +++ b/packages/ldap/lib/delete/index.js @@ -8,6 +8,9 @@ export default { const binddn = config.binddn ? `-D ${config.binddn}` : ''; const passwd = config.passwd ? `-w ${config.passwd}` : ''; if (config.uri === true) { + if (config.mesh == null) { + config.mesh = 'EXTERNAL'; + } config.uri = 'ldapi:///'; } const uri = config.uri ? `-H ${config.uri}` : ''; // URI is obtained from local openldap conf unless provided diff --git a/packages/ldap/lib/delete/schema.json b/packages/ldap/lib/delete/schema.json index 73d9cb8e9..cb6655da3 100644 --- a/packages/ldap/lib/delete/schema.json +++ b/packages/ldap/lib/delete/schema.json @@ -1,6 +1,7 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/ldap/search#/definitions/connection", "properties": { "dn": { "type": "array", @@ -13,18 +14,6 @@ "name": { "type": "string", "description": "Distinguish name storing the \"olcAccess\" property, using the database\naddress (eg: \"olcDatabase={2}bdb,cn=config\")." - }, - "binddn": { - "type": "string", - "description": "Distinguished Name to bind to the LDAP directory." - }, - "passwd": { - "type": "string", - "description": "Password for simple authentication." - }, - "uri": { - "type": "string", - "description": "LDAP Uniform Resource Identifier(s), \"ldapi:///\" if true, default to\nfalse in which case it will use your openldap client environment\nconfiguration." } }, "required": [ diff --git a/packages/ldap/lib/index/index.js b/packages/ldap/lib/index/index.js index c66a05836..500cd8a39 100644 --- a/packages/ldap/lib/index/index.js +++ b/packages/ldap/lib/index/index.js @@ -10,14 +10,16 @@ export default { const modify = {}; if (!config.dn) { log("DEBUG", "Get DN of the database to modify"); - ({ dn: config.dn } = await this.ldap.tools.database(config, { + ({ dn: config.dn } = await this.ldap.tools.database({ + ...utils.ldap.config_connection(config), suffix: config.suffix, })); log("INFO", `Discovered database DN is ${config.dn}`); } // List all indexes of the directory log("DEBUG", "List all indexes of the directory"); - const { stdout } = await this.ldap.search(config, { + const { stdout } = await this.ldap.search({ + ...utils.ldap.config_connection(config), attributes: ["olcDbIndex"], base: `${config.dn}`, filter: "(olcDbIndex=*)", @@ -66,7 +68,8 @@ export default { value: `${k} ${v[0]}`, }); } - await this.ldap.modify(config, { + await this.ldap.modify({ + ...utils.ldap.config_connection(config), operations: operations, }); } diff --git a/packages/ldap/lib/index/schema.json b/packages/ldap/lib/index/schema.json index 379e05d81..b57e2100e 100644 --- a/packages/ldap/lib/index/schema.json +++ b/packages/ldap/lib/index/schema.json @@ -1,6 +1,7 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/ldap/search#/definitions/connection", "properties": { "indexes": { "type": "object", @@ -13,18 +14,6 @@ "suffix": { "type": "string", "description": "The suffix associated with the database (eg: \"dc=example,dc=org\"),\nused as an alternative to the `dn` configuration." - }, - "binddn": { - "type": "string", - "description": "Distinguished Name to bind to the LDAP directory." - }, - "passwd": { - "type": "string", - "description": "Password for simple authentication." - }, - "uri": { - "type": "string", - "description": "LDAP Uniform Resource Identifier(s), \"ldapi:///\" if true, default to\nfalse in which case it will use your openldap client environment\nconfiguration." } } } diff --git a/packages/ldap/lib/modify/index.js b/packages/ldap/lib/modify/index.js index 970cccc99..22a915e00 100644 --- a/packages/ldap/lib/modify/index.js +++ b/packages/ldap/lib/modify/index.js @@ -1,6 +1,7 @@ // Dependencies import dedent from "dedent"; import { escapeshellarg as esa } from "@nikitajs/core/utils/string"; +import utils from '@nikitajs/ldap/utils' import definitions from "./schema.json" assert { type: "json" }; // Action @@ -19,7 +20,8 @@ export default { const originals = []; for (const operation of config.operations) { if (!config.shortcut) { - const {stdout} = await this.ldap.search(config, { + const {stdout} = await this.ldap.search({ + ...utils.ldap.config_connection(config), base: operation.dn }); originals.push(stdout); @@ -63,7 +65,8 @@ export default { for (const i in config.operations) { const operation = config.operations[i]; if (!config.shortcut) { - const {stdout} = await this.ldap.search(config, { + const {stdout} = await this.ldap.search({ + ...utils.ldap.config_connection(config), base: operation.dn }); if (stdout !== originals[i]) { diff --git a/packages/ldap/lib/schema/schema.json b/packages/ldap/lib/schema/schema.json index d867cdc36..b6278c695 100644 --- a/packages/ldap/lib/schema/schema.json +++ b/packages/ldap/lib/schema/schema.json @@ -1,8 +1,8 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/ldap/search#/definitions/connection", "properties": { - "$ref": "module://@nikitajs/ldap/search#/definitions/config", "name": { "type": "string", "description": "Common name of the schema." diff --git a/packages/ldap/lib/search/index.js b/packages/ldap/lib/search/index.js index c67769f73..c01a9598f 100644 --- a/packages/ldap/lib/search/index.js +++ b/packages/ldap/lib/search/index.js @@ -14,21 +14,24 @@ export default { config.uri = 'ldapi:///'; } // Add related config - return await this.execute(config, [ - 'ldapsearch', - '-o ldif-wrap=no', - '-LLL', // Remove comments - config.continuous ? '-c' : undefined, - config.mesh ? `-Y ${esa(config.mesh)}` : undefined, - config.binddn ? `-D ${esa(config.binddn)}` : undefined, - config.passwd ? `-w ${esa(config.passwd)}` : undefined, - config.uri ? `-H ${esa(config.uri)}` : undefined, - `-b ${esa(config.base)}`, - config.scope ? `-s ${esa(config.scope)}` : undefined, - config.filter ? `${esa(config.filter)}` : undefined, - ...(config.attributes.map(esa)), - '2>/dev/null' - ].filter(Boolean).join(' ')); + return await this.execute({ + code: config.code, + command: [ + 'ldapsearch', + '-o ldif-wrap=no', + '-LLL', // Remove comments + config.continuous ? '-c' : undefined, + config.mesh ? `-Y ${esa(config.mesh)}` : undefined, + config.binddn ? `-D ${esa(config.binddn)}` : undefined, + config.passwd ? `-w ${esa(config.passwd)}` : undefined, + config.uri ? `-H ${esa(config.uri)}` : undefined, + `-b ${esa(config.base)}`, + config.scope ? `-s ${esa(config.scope)}` : undefined, + config.filter ? `${esa(config.filter)}` : undefined, + ...(config.attributes.map(esa)), + '2>/dev/null' + ].filter(Boolean).join(' ') + }); }, metadata: { global: 'ldap', diff --git a/packages/ldap/lib/search/schema.json b/packages/ldap/lib/search/schema.json index b9e18adc9..04db28e16 100644 --- a/packages/ldap/lib/search/schema.json +++ b/packages/ldap/lib/search/schema.json @@ -1,6 +1,7 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/ldap/search#/definitions/connection", "properties": { "attributes": { "type": "array", @@ -17,7 +18,10 @@ }, "base": { "type": "string", - "description": "One or multiple DN to remove." + "description": "One or multiple DN to search." + }, + "code": { + "$ref": "module://@nikitajs/core/actions/execute#/definitions/config/properties/code" }, "filter": { "type": "string", @@ -32,7 +36,15 @@ "children" ], "description": "Distinguish name storing the \"olcAccess\" property, using the database\naddress (eg: \"olcDatabase={2}bdb,cn=config\")." - }, + } + }, + "required": [ + "base" + ] + }, + "connection": { + "type": "object", + "properties": { "binddn": { "type": "string", "description": "Distinguished Name to bind to the LDAP directory." @@ -49,9 +61,6 @@ "type": "string", "description": "LDAP Uniform Resource Identifier(s), \"ldapi:///\" if true, default to\nfalse in which case it will use your openldap client environment\nconfiguration." } - }, - "required": [ - "base" - ] + } } } diff --git a/packages/ldap/lib/tools/database/index.js b/packages/ldap/lib/tools/database/index.js index 8b3c69ed6..ee644e558 100644 --- a/packages/ldap/lib/tools/database/index.js +++ b/packages/ldap/lib/tools/database/index.js @@ -1,10 +1,12 @@ // Dependencies +import utils from '@nikitajs/ldap/utils' import definitions from "./schema.json" assert { type: "json" }; // Action export default { handler: async function({config}) { - const {stdout} = await this.ldap.search(config, { + const {stdout} = await this.ldap.search({ + ...utils.ldap.config_connection(config), base: config.base, filter: `(olcSuffix= ${config.suffix})`, attributes: ['dn'] diff --git a/packages/ldap/lib/tools/database/schema.json b/packages/ldap/lib/tools/database/schema.json index 96409571d..cd57a4968 100644 --- a/packages/ldap/lib/tools/database/schema.json +++ b/packages/ldap/lib/tools/database/schema.json @@ -1,25 +1,20 @@ { "config": { "type": "object", - "allOf": [ - { - "properties": { - "base": { - "const": "cn=config", - "default": "cn=config" - }, - "suffix": { - "type": "string", - "description": "The suffix associated with the database." - } - }, - "required": [ - "suffix" - ] + "$ref": "module://@nikitajs/ldap/search#/definitions/connection", + "properties": { + "base": { + "const": "cn=config", + "default": "cn=config" }, - { - "$ref": "module://@nikitajs/ldap/search" + "suffix": { + "type": "string", + "description": "The suffix associated with the database." } + }, + "required": [ + "suffix", + "base" ] } } diff --git a/packages/ldap/lib/tools/databases/index.js b/packages/ldap/lib/tools/databases/index.js index 1c5bbd4c7..396238b4a 100644 --- a/packages/ldap/lib/tools/databases/index.js +++ b/packages/ldap/lib/tools/databases/index.js @@ -1,23 +1,27 @@ // Dependencies -import utils from "@nikitajs/tools/utils"; +import utils from "@nikitajs/ldap/utils"; import definitions from "./schema.json" assert { type: "json" }; // Action export default { handler: async function ({ config }) { - const { stdout } = await this.ldap.search(config, { - base: config.base, - filter: "(objectClass=olcDatabaseConfig)", - attributes: ["olcDatabase"], - }); - const databases = utils.string - .lines(stdout) - .filter(function (line) { - return /^olcDatabase: /.test(line); + const databases = await this.ldap + .search({ + ...utils.ldap.config_connection(config), + base: config.base, + filter: "(objectClass=olcDatabaseConfig)", + attributes: ["olcDatabase"], }) - .map(function (line) { - return line.split(" ")[1]; - }); + .then(({ stdout }) => + utils.string + .lines(stdout) + .filter(function (line) { + return /^olcDatabase: /.test(line); + }) + .map(function (line) { + return line.split(" ")[1]; + }) + ); return { databases: databases, }; diff --git a/packages/ldap/lib/tools/databases/schema.json b/packages/ldap/lib/tools/databases/schema.json index be6bdfe0d..e6b4c6eb4 100644 --- a/packages/ldap/lib/tools/databases/schema.json +++ b/packages/ldap/lib/tools/databases/schema.json @@ -1,18 +1,15 @@ { "config": { "type": "object", - "allOf": [ - { - "properties": { - "base": { - "const": "cn=config", - "default": "cn=config" - } - } - }, - { - "$ref": "module://@nikitajs/ldap/search" + "$ref": "module://@nikitajs/ldap/search#/definitions/connection", + "properties": { + "base": { + "const": "cn=config", + "default": "cn=config" } + }, + "required": [ + "base" ] } } diff --git a/packages/ldap/lib/user/schema.json b/packages/ldap/lib/user/schema.json index 23adafc1a..82989096c 100644 --- a/packages/ldap/lib/user/schema.json +++ b/packages/ldap/lib/user/schema.json @@ -1,6 +1,7 @@ { "config": { "type": "object", + "$ref": "module://@nikitajs/ldap/search#/definitions/connection", "properties": { "name": { "type": "string", @@ -13,26 +14,6 @@ "items": { "type": "object" } - }, - "binddn": { - "type": "string", - "description": "Distinguished Name to bind to the LDAP directory." - }, - "passwd": { - "type": "string", - "description": "Password for simple authentication." - }, - "uri": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "boolean", - "default": "ldapi:///" - } - ], - "description": "LDAP Uniform Resource Identifier(s), \"ldapi:///\" if true, default to\nfalse in which case it will use your openldap client environment\nconfiguration." } } } diff --git a/packages/ldap/lib/utils/ldap.js b/packages/ldap/lib/utils/ldap.js index 07a599314..a44787caf 100644 --- a/packages/ldap/lib/utils/ldap.js +++ b/packages/ldap/lib/utils/ldap.js @@ -1,3 +1,4 @@ +import utils from "@nikitajs/core/utils" export default { acl: { @@ -68,6 +69,9 @@ export default { } } }, + config_connection(config){ + return utils.object.filter(config, [], ['binddn', 'mesh', 'passwd', 'uri']) + }, index: { /* ## Parse Index diff --git a/packages/ldap/test/schema.coffee b/packages/ldap/test/schema.coffee index 069d552e8..ec30f263a 100644 --- a/packages/ldap/test/schema.coffee +++ b/packages/ldap/test/schema.coffee @@ -7,41 +7,6 @@ they = mochaThey(test.config) describe 'ldap.schema', -> return unless test.tags.ldap - they 'entry with password', ({ssh}) -> - nikita - ldap: - binddn: test.ldap.binddn - passwd: test.ldap.passwd - uri: test.ldap.uri - $ssh: ssh - , -> - entry = - dn: "cn=nikita,#{test.ldap.suffix_dn}" - userPassword: 'secret' - uid: 'nikita' - objectClass: [ 'top', 'account', 'posixAccount' ] - uidNumber: '9610' - gidNumber: '9610' - homeDirectory: '/home/nikita' - operations = [ - dn: entry.dn - changetype: 'modify' - attributes: [ - type: 'replace' - name: 'userPassword' - value: 'newsecret' - ] - ] - await @ldap.delete - dn: entry.dn - {$status} = await @ldap.add - entry: entry - {$status} = await @ldap.modify - operations: operations - $status.should.be.true() - {$status} = await @ldap.modify - operations: operations - $status.should.be.false() - await @ldap.delete - dn: entry.dn + they.skip 'todo', ({ssh}) -> + # No test at the moment \ No newline at end of file diff --git a/packages/ldap/test/tools/database.coffee b/packages/ldap/test/tools/database.coffee index 31fc51eb2..aab55f28f 100644 --- a/packages/ldap/test/tools/database.coffee +++ b/packages/ldap/test/tools/database.coffee @@ -35,7 +35,7 @@ describe 'ldap.database', -> describe 'usage', -> - they 'create a new index', ({ssh}) -> + they 'output database information', ({ssh}) -> nikita $ssh: ssh , -> diff --git a/packages/ldap/test/tools/databases.coffee b/packages/ldap/test/tools/databases.coffee index ecd344f86..079581d71 100644 --- a/packages/ldap/test/tools/databases.coffee +++ b/packages/ldap/test/tools/databases.coffee @@ -7,12 +7,11 @@ they = mochaThey(test.config) describe 'ldap.databases', -> return unless test.tags.ldap - they 'create a new index', ({ssh}) -> + they 'list databases', ({ssh}) -> nikita $ssh: ssh , -> {databases} = await @ldap.tools.databases - suffix: test.ldap.suffix_dn uri: test.ldap.uri binddn: test.ldap.config.binddn passwd: test.ldap.config.passwd diff --git a/packages/log/lib/cli/index.js b/packages/log/lib/cli/index.js index 5152b18da..839c6c87d 100644 --- a/packages/log/lib/cli/index.js +++ b/packages/log/lib/cli/index.js @@ -138,8 +138,11 @@ export default { return line + "\n"; }, }; - config.serializer = merge(serializer, config.serializer); - return this.log.stream(config); + return this.log.stream({ + end: config.end, + serializer: merge(serializer, config.serializer), + stream: config.stream, + }); }, metadata: { argument_to_config: "enabled", diff --git a/packages/log/lib/cli/schema.json b/packages/log/lib/cli/schema.json index 876706953..5e0c74039 100644 --- a/packages/log/lib/cli/schema.json +++ b/packages/log/lib/cli/schema.json @@ -2,7 +2,7 @@ "config": { "type": "object", "properties": { - "color": { + "colors": { "oneOf": [ { "type": "boolean" diff --git a/packages/log/lib/csv/index.js b/packages/log/lib/csv/index.js index d180d0a63..8ad9edcb4 100644 --- a/packages/log/lib/csv/index.js +++ b/packages/log/lib/csv/index.js @@ -29,8 +29,12 @@ export default { return `${log.type},${log.level},${JSON.stringify(log.message)}\n`; } }; - config.serializer = merge(serializer, config.serializer); - return this.log.fs(config); + return this.log.fs({ + archive: config.archive, + basedir: config.basedir, + filename: config.filename, + serializer: merge(serializer, config.serializer), + }); }, metadata: { definitions: definitions diff --git a/packages/log/lib/fs/index.js b/packages/log/lib/fs/index.js index 4622b687d..4ec180650 100644 --- a/packages/log/lib/fs/index.js +++ b/packages/log/lib/fs/index.js @@ -23,7 +23,7 @@ export default { } try { await this.fs.base.mkdir(logdir, { - ssh: false + $ssh: false }); } catch (error) { if (error.code !== 'NIKITA_FS_MKDIR_TARGET_EEXIST') { @@ -31,13 +31,17 @@ export default { } } // Events - if (config.stream == null) { - config.stream = fs.createWriteStream(path.resolve(logdir, path.basename(config.filename))); - } - await this.log.stream(config); + // if (config.stream == null) { + // config.stream = fs.createWriteStream(path.resolve(logdir, path.basename(config.filename))); + // } + await this.log.stream({ + serializer: config.serializer, + stream: config.stream ?? fs.createWriteStream(path.resolve(logdir, path.basename(config.filename))), + }); // Handle link to latest directory await this.fs.base.symlink({ $if: latestdir, + $ssh: false, source: logdir, target: latestdir }); @@ -50,7 +54,7 @@ export default { // With ssh, filename contain the host or ip address config.filename ??= `${ssh?.config?.host || 'local'}.log`; // Log is always local - config.ssh = false; + // config.ssh = false; } } }, diff --git a/packages/log/lib/md/index.js b/packages/log/lib/md/index.js index 4bb9b52be..4603b0bd1 100644 --- a/packages/log/lib/md/index.js +++ b/packages/log/lib/md/index.js @@ -100,8 +100,12 @@ export default { return out.join(''); } }; - config.serializer = merge(serializer, config.serializer); - await this.log.fs(config); + await this.log.fs({ + archive: config.archive, + basedir: config.basedir, + filename: config.filename, + serializer: merge(serializer, config.serializer) + }); }, metadata: { definitions: definitions diff --git a/packages/service/lib/outdated/index.js b/packages/service/lib/outdated/index.js index 619c19763..f4e6d86f5 100644 --- a/packages/service/lib/outdated/index.js +++ b/packages/service/lib/outdated/index.js @@ -6,62 +6,55 @@ import definitions from "./schema.json" assert { type: "json" }; // Action export default { handler: async function ({ metadata, config, parent: { state }, tools: { log } }) { - let packages = config.cache ? state["nikita:service:packages:outdated"] : undefined; - if (packages !== undefined) { - return { - packages: packages, - } - } const cacheonly = config.cacheonly ? "-C" : ""; - try { - // Error inside the pipeline are not catched (eg no sudo permission). - // A possible solution includes breaking the pipeline into multiple calls. - // Another solution bash-only alternative is to use `set -o pipeline`. - // See https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html - // The apt-get is pretty weak, `apt-get -s -u upgrade` can be executed - // by non-root users but its output will add fake packages to the list - // due to the presence of indented comments. - ({ data: packages } = await this.execute({ - $shy: true, - command: dedent` - if command -v yum >/dev/null 2>&1; then - yum ${cacheonly} check-update -q | sed 's/\\([^\\.]*\\).*/\\1/' - elif command -v pacman >/dev/null 2>&1; then - pacman -Qu | sed 's/\\([^ ]*\\).*/\\1/' - elif command -v apt-get >/dev/null 2>&1; then - apt-get -s -u upgrade | grep '^\s' | sed 's/\\s/\\n/g' - else - echo "Unsupported Package Manager" >&2 - exit 2 - fi - `, - format: ({ stdout }) => - utils.array.flatten( - utils.string - .lines(stdout) - .map((line) => line.split(" ").map(pck => pck.trim()).filter(pck => pck !== '')) - ), - stdout_log: false, - })); - log("INFO", "Outdated packages retrieved"); - } catch (error) { - if (error.exit_code === 43) { - throw utils.error( - "NIKITA_SERVICE_OUTDATED_UNSUPPORTED_PACKAGE_MANAGER", - "at the moment, rpm (yum, dnf, ...), pacman and dpkg (apt, apt-get, ...) are supported." - ); - } else if (error.exit_code === 100) { - throw utils.error( - "NIKITA_SERVICE_OUTDATED_SUDO", - "permission denied, maybe run this command as sudoer or with the `$debug` configuration." - ); - } - throw error; - } - if (config.cache) { - log("INFO", 'Caching outdated packages.'); - state["nikita:service:packages:outdated"] = packages; - } + // Error inside the pipeline are not catched (eg no sudo permission). + // A possible solution includes breaking the pipeline into multiple calls. + // Another solution bash-only alternative is to use `set -o pipeline`. + // See https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html + // The apt-get is pretty weak, `apt-get -s -u upgrade` can be executed + // by non-root users but its output will add fake packages to the list + // due to the presence of indented comments. + const packages = await this.execute({ + $shy: true, + command: dedent` + if command -v yum >/dev/null 2>&1; then + yum ${cacheonly} check-update -q | sed 's/\\([^\\.]*\\).*/\\1/' + elif command -v pacman >/dev/null 2>&1; then + pacman -Qu | sed 's/\\([^ ]*\\).*/\\1/' + elif command -v apt-get >/dev/null 2>&1; then + apt-get -s -u upgrade | grep '^\s' | sed 's/\\s/\\n/g' + else + echo "Unsupported Package Manager" >&2 + exit 2 + fi + `, + format: ({ stdout }) => + utils.array.flatten( + utils.string.lines(stdout).map((line) => + line + .split(" ") + .map((pck) => pck.trim()) + .filter((pck) => pck !== "") + ) + ), + stdout_log: false, + }) + .then(({ data }) => data) + .catch((error) => { + if (error.exit_code === 43) { + throw utils.error( + "NIKITA_SERVICE_OUTDATED_UNSUPPORTED_PACKAGE_MANAGER", + "at the moment, rpm (yum, dnf, ...), pacman and dpkg (apt, apt-get, ...) are supported." + ); + } else if (error.exit_code === 100) { + throw utils.error( + "NIKITA_SERVICE_OUTDATED_SUDO", + "permission denied, maybe run this command as sudoer or with the `$debug` configuration." + ); + } + throw error; + }); + log("Outdated packages retrieved"); if(config.name) { return { outdated: packages.includes(config.name) diff --git a/packages/service/test/outdated.coffee b/packages/service/test/outdated.coffee index a37497184..19789d94e 100644 --- a/packages/service/test/outdated.coffee +++ b/packages/service/test/outdated.coffee @@ -8,11 +8,9 @@ describe 'service.outdated', -> return unless test.tags.service_outdated they 'list all packages', ({ssh, sudo}) -> - {packages, outdated} = await nikita + {packages, outdated} = await nikita.service.outdated $ssh: ssh $sudo: sudo - .service.outdated - cache: true should(outdated).be.undefined() packages.should.matchEach (it) -> it.should.be.a.String() @@ -33,18 +31,3 @@ describe 'service.outdated', -> name: 'XXXX' should(packages).be.undefined() outdated.should.be.false() - - they 'cache package list', ({ssh, sudo}) -> - nikita - $ssh: ssh - $sudo: sudo - , -> - await @call ({parent: {state}}) -> - should(state['nikita:service:packages:outdated']).be.undefined() - {$status} = await @service.outdated - name: test.service.name - cache: true - $status.should.be.false() - await @call ({parent: {state}}) -> - state['nikita:service:packages:outdated'].should.be.an.Array() - \ No newline at end of file diff --git a/packages/service/test/status.coffee b/packages/service/test/status.coffee index dcb19a5ea..ebd16321e 100644 --- a/packages/service/test/status.coffee +++ b/packages/service/test/status.coffee @@ -26,6 +26,5 @@ describe 'service.status', -> await @service.stop name: test.service.srv_name {started} = await @service.status - name: test.service.name - srv_name: test.service.srv_name + name: test.service.srv_name started.should.be.false() diff --git a/packages/system/lib/cgroups/index.js b/packages/system/lib/cgroups/index.js index 09b405439..a32dd754e 100644 --- a/packages/system/lib/cgroups/index.js +++ b/packages/system/lib/cgroups/index.js @@ -24,7 +24,7 @@ export default { config.ignore = [config.ignore]; } // Detect Os and version - const {os} = (await this.system.info.os()); + const {os} = await this.system.info.os(); // configure parameters based on previous OS dection const store = {}; // Enable cgroup for all distribution, it was restricted to rhel systems @@ -41,9 +41,9 @@ export default { const cpus = cgconfig.mounts.filter(function(mount) { return mount.type === 'cpu'; }); - const cpuaccts = cgconfig.mounts.filter(function(mount) { - return mount.type === 'cpuacct'; - }); + // const cpuaccts = cgconfig.mounts.filter(function(mount) { + // return mount.type === 'cpuacct'; + // }); // We choose a path which is mounted by default // if not @store['nikita:cgroups:cpu_path']? if (cpus.length > 0) { @@ -89,7 +89,8 @@ export default { config.target = '/etc/cgconfig.conf'; } } - this.file(config, { + await this.file({ + target: config.target, content: utils.cgconfig.stringify(config.cgconfig) }); return { diff --git a/packages/system/lib/limits/index.js b/packages/system/lib/limits/index.js index de971ca86..945d4367d 100644 --- a/packages/system/lib/limits/index.js +++ b/packages/system/lib/limits/index.js @@ -50,8 +50,8 @@ export default { // Calculate nproc from kernel limit if (config.nproc != null) { const { stdout: kern_limit } = await this.execute({ + $shy: true, command: "cat /proc/sys/kernel/pid_max", - shy: true, trim: true, }); if (config.nproc === true) { diff --git a/packages/system/lib/tmpfs/schema.json b/packages/system/lib/tmpfs/schema.json index 42cb93198..dc2628839 100644 --- a/packages/system/lib/tmpfs/schema.json +++ b/packages/system/lib/tmpfs/schema.json @@ -6,7 +6,7 @@ "type": "string", "description": "Used to decide what files to delete when cleaning." }, - "argument": { + "argu": { "type": "string", "description": "The destination path of the symlink if type is `L`." }, diff --git a/packages/system/package.json b/packages/system/package.json index 3877282e2..b307a6282 100644 --- a/packages/system/package.json +++ b/packages/system/package.json @@ -77,7 +77,7 @@ "should" ], "throw-deprecation": true, - "timeout": 40000 + "timeout": 50000 }, "publishConfig": { "access": "public" diff --git a/packages/system/test/cgroups.coffee b/packages/system/test/cgroups.coffee index 4e64d0cf8..36fc7c53e 100644 --- a/packages/system/test/cgroups.coffee +++ b/packages/system/test/cgroups.coffee @@ -43,10 +43,11 @@ describe 'system.cgroups', -> nikita $ssh: ssh $tmpdir: true + # $audit: true , ({metadata: {tmpdir}})-> {$status} = await @system.cgroups target: "#{tmpdir}/a_file_mount_only.cgconfig.conf" - mode: 0o0754 + # mode: 0o0754 mounts: mounts merge:false $status.should.be.true() @@ -69,7 +70,7 @@ describe 'system.cgroups', -> , ({metadata: {tmpdir}})-> {$status} = await @system.cgroups target: "#{tmpdir}/a_file_cgroup_only.cgconfig.conf" - mode: 0o0754 + # mode: 0o0754 groups: groups merge: false $status.should.be.true() @@ -102,7 +103,7 @@ describe 'system.cgroups', -> , ({metadata: {tmpdir}})-> {$status} = await @system.cgroups target: "#{tmpdir}/a_file_default_only.cgconfig.conf" - mode: 0o0754 + # mode: 0o0754 default: def merge: false $status.should.be.true() @@ -135,7 +136,7 @@ describe 'system.cgroups', -> , ({metadata: {tmpdir}})-> {$status} = await @system.cgroups target: "#{tmpdir}/a_file_complete.cgconfig.conf" - mode: 0o0754 + # mode: 0o0754 default: def groups: groups mounts: mounts @@ -194,14 +195,14 @@ describe 'system.cgroups', -> , ({metadata: {tmpdir}})-> await @system.cgroups target: "#{tmpdir}/a_file_complete.cgconfig.conf" - mode: 0o0754 + # mode: 0o0754 default: def groups: groups mounts: mounts merge: false {$status} = await @system.cgroups target: "#{tmpdir}/a_file_complete.cgconfig.conf" - mode: 0o0754 + # mode: 0o0754 default: def groups: groups mounts: mounts @@ -247,7 +248,7 @@ describe 'system.cgroups', -> , ({metadata: {tmpdir}})-> {$status} = await @system.cgroups target: "#{tmpdir}/a_file_merge_mount_groups.cgconfig.conf" - mode: 0o0754 + # mode: 0o0754 groups: groups merge: true $status.should.be.true() @@ -266,12 +267,12 @@ describe 'system.cgroups', -> , ({metadata: {tmpdir}})-> await @system.cgroups target: "#{tmpdir}/a_file_merge_mount_groups.cgconfig.conf" - mode: 0o0754 + # mode: 0o0754 groups: groups merge: true {$status} = await @system.cgroups target: "#{tmpdir}/a_file_merge_mount_groups.cgconfig.conf" - mode: 0o0754 + # mode: 0o0754 groups: groups merge: true $status.should.be.false() @@ -285,7 +286,7 @@ describe 'system.cgroups', -> , ({metadata: {tmpdir}})-> {cgroups} = await @system.cgroups target: "#{tmpdir}/a_file_merge_mount_groups.cgconfig.conf" - mode: 0o0754 + # mode: 0o0754 groups: toto: perm: admin: uid: 'toto', gid: 'toto' diff --git a/packages/system/test/limits.coffee b/packages/system/test/limits.coffee index 845db59f8..154a9aad1 100644 --- a/packages/system/test/limits.coffee +++ b/packages/system/test/limits.coffee @@ -120,7 +120,6 @@ describe 'system.limits', -> user: 'me' nofile: 2048 nproc: 2048 - shy: true {$status} = await @system.limits target: "#{tmpdir}/limits.conf" user: 'me' @@ -138,7 +137,6 @@ describe 'system.limits', -> user: 'me' nofile: 2048 nproc: 2048 - shy: true {$status} = await @system.limits target: "#{tmpdir}/limits.conf" user: 'me' diff --git a/packages/tools/lib/cron/add/index.js b/packages/tools/lib/cron/add/index.js index af109c55e..5ee812c13 100644 --- a/packages/tools/lib/cron/add/index.js +++ b/packages/tools/lib/cron/add/index.js @@ -6,25 +6,10 @@ import definitions from "./schema.json" assert { type: "json" }; // Action export default { handler: async function ({ config, tools: { log } }) { - const crontab = (() => { - if (config.user != null) { - log({ - message: `Using user ${config.user}`, - level: "DEBUG", - }); - return `crontab -u ${config.user}`; - } else { - log({ - message: "Using default user", - level: "DEBUG", - }); - return "crontab"; - } - })(); - const { stdout } = await this.execute({ - command: `${crontab} -l`, - code: [0, 1], - }); + const command = config.user ? `crontab -u ${config.user}` : "crontab"; + // console.log(await this.tools.cron.list().then(({ entries }) => entries)) + const entries = await this.tools.cron.list() + .then(({ entries }) => entries.map(({ raw }) => raw)); const new_job = `${config.when} ${config.command}`; // remove useless last element const regex = (function () { @@ -40,34 +25,27 @@ export default { })(); let added = true; let modified = false; - let jobs = utils.string - .lines(stdout.trim()) + let diff; + let jobs = entries .map((job) => { if (regex.test(job)) { added = false; if (job === new_job) { return null; // Found job, stop here } - log({ - message: "Entry has changed", - level: "WARN", - }); - utils.diff(job, new_job, config); + log("WARN", "Entry has changed"); + // console.log('>', job, new_job, config) + // console.log('<', utils.diff(job, new_job, config)); + ({ raw: diff } = utils.diff(job, new_job, config)); job = new_job; modified = true; } - if (!job) { - return null; - } return job; }) .filter((line) => line !== null); if (added) { jobs.push(new_job); - log({ - message: "Job not found in crontab, adding", - level: "WARN", - }); + log("WARN", "Job not found in crontab, adding"); } if (!(added || modified)) { jobs = null; @@ -77,6 +55,13 @@ export default { $status: false, }; } + await this.execute({ + command: [ + `${command} - < { - if (config.user != null) { - log({ - message: `Using user ${config.user}`, - level: 'INFO' - }); - return `crontab -u ${config.user}`; - } else { - log({ - message: "Using default user", - level: 'INFO' - }); - return "crontab"; - } - })(); + const command = config.user ? `crontab -u ${config.user}` : "crontab"; let status = false; const {stdout, stderr} = (await this.execute({ $shy: true, - command: `${crontab} -l` + command: `${command} -l` })); if (/^no crontab for/.test(stderr)) { throw Error('User crontab not found'); @@ -57,7 +43,7 @@ export default { } await this.execute({ command: dedent` - ${crontab} - < describe 'match', -> - they 'regexp', ({ssh}) -> + they 'value is a regexp', ({ssh}) -> nikita $ssh: ssh , -> @@ -100,15 +100,14 @@ describe 'tools.cron.add', -> when: '0 * * * *' match: '.*bin.*' $status.should.be.true() - {$status} = await @tools.cron.add + {$status, diff} = await @tools.cron.add command: "/bin/false #{rand}" when: '0 * * * *' match: /.*bin.*/ - diff: (diff) -> - diff.should.eql [ - { count: 1, added: undefined, removed: true, value: "0 * * * * /bin/false #{rand}" } - { count: 1, added: true, removed: undefined, value: "0 * * * * /bin/true #{rand}" } - ] + diff.should.eql [ + { count: 1, added: undefined, removed: true, value: "0 * * * * /bin/true #{rand}" } + { count: 1, added: true, removed: undefined, value: "0 * * * * /bin/false #{rand}" } + ] $status.should.be.true() {$status} = await @tools.cron.add command: "/bin/false #{rand}" @@ -119,7 +118,7 @@ describe 'tools.cron.add', -> command: "/bin/false #{rand}" when: '0 * * * *' - they 'string', ({ssh}) -> + they 'value is a string', ({ssh}) -> nikita $ssh: ssh , -> @@ -129,15 +128,14 @@ describe 'tools.cron.add', -> when: '0 * * * *' match: '.*bin.*' $status.should.be.true() - {$status} = await @tools.cron.add + {$status, diff} = await @tools.cron.add command: "/bin/false #{rand}" when: '0 * * * *' match: '.*bin.*' - diff: (diff) -> - diff.should.eql [ - { count: 1, added: undefined, removed: true, value: "0 * * * * /bin/false #{rand}" } - { count: 1, added: true, removed: undefined, value: "0 * * * * /bin/true #{rand}" } - ] + diff.should.eql [ + { count: 1, added: undefined, removed: true, value: "0 * * * * /bin/true #{rand}" } + { count: 1, added: true, removed: undefined, value: "0 * * * * /bin/false #{rand}" } + ] $status.should.be.true() {$status} = await @tools.cron.add command: "/bin/false #{rand}" @@ -155,6 +153,7 @@ describe 'tools.cron.add', -> $ssh: ssh , -> await @service 'cronie' + await @tools.cron.reset() await @tools.cron.add command: 'azertyytreza' when: '1 2 3 4 5'