This is an automated email from the ASF dual-hosted git repository. alexkli pushed a commit to branch dockerode in repository https://gitbox.apache.org/repos/asf/openwhisk-wskdebug.git
commit ff27527282946e9cce565c82c7bc39ce3c3d81dd Author: Alexander Klimetschek <[email protected]> AuthorDate: Mon Apr 20 20:09:05 2020 -0700 use docker api client dockerode instead of `docker` child process - improves performance slightly as no child process execution is required - easier to use API than building cli arguments & parsing stdout --- package-lock.json | 212 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- package.json | 1 + src/debugger.js | 10 ++- src/invoker.js | 127 ++++++++++++++++++++++++-------- src/log.js | 6 ++ 5 files changed, 319 insertions(+), 37 deletions(-) diff --git a/package-lock.json b/package-lock.json index ae04f84..9912708 100644 --- a/package-lock.json +++ b/package-lock.json @@ -462,6 +462,14 @@ "integrity": "sha1-7L0W+JSbFXGDcRsb2jNPN4QBhas=", "dev": true }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, "astral-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", @@ -479,11 +487,34 @@ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, "binary-extensions": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==" }, + "bl": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.2.tgz", + "integrity": "sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ==", + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -508,6 +539,20 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, + "buffer": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", + "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, "caching-transform": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", @@ -567,6 +612,11 @@ "readdirp": "~3.3.0" } }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, "clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -645,6 +695,17 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, + "concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, "convert-source-map": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", @@ -733,6 +794,27 @@ "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "dev": true }, + "docker-modem": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-2.1.2.tgz", + "integrity": "sha512-fwlfnsK9WV+m+qc/NZCiGt+oYAMjmCUeir0a/l3oHb0yc8FhRAucdwT4htKD3aLtV+1w2syQePH9pQFxsq1GFA==", + "requires": { + "debug": "^4.1.1", + "readable-stream": "^3.5.0", + "split-ca": "^1.0.1", + "ssh2": "^0.8.7" + } + }, + "dockerode": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-3.2.0.tgz", + "integrity": "sha512-C+y/W4Kks7YLBsfUOTMkk1IVilb4cdj+rE+UZ5hnE+rpcn2frSs7kX+6H8GteTqHcv8sln+GyxuP1qdno3IzIw==", + "requires": { + "concat-stream": "~2.0.0", + "docker-modem": "^2.1.0", + "tar-fs": "~2.0.1" + } + }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -755,6 +837,14 @@ "iconv-lite": "~0.4.13" } }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, "es-abstract": { "version": "1.17.5", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", @@ -1202,6 +1292,11 @@ "integrity": "sha512-33X7H/wdfO99GdRLLgkjUrD4geAFdq/Uv0kl3HD4da6HDixd2GUg8Mw7dahLCV9r/EARkmtYBB6Tch4EEokFTQ==", "dev": true }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, "fs-extra": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", @@ -1402,6 +1497,11 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", @@ -1452,8 +1552,7 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "inquirer": { "version": "7.1.0", @@ -1971,6 +2070,11 @@ "minimist": "^1.2.5" } }, + "mkdirp-classic": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.2.tgz", + "integrity": "sha512-ejdnDQcR75gwknmMw/tx02AuRs8jCtqFoFqDZMjiNxsu85sRIJVXDKHuLYvUUPRBUtV2FpSZa9bL1BUa3BdR2g==" + }, "mocha": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.1.1.tgz", @@ -2472,7 +2576,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -2690,6 +2793,15 @@ "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", "dev": true }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -2702,6 +2814,16 @@ "integrity": "sha512-pVzZdDpWwWqEVVLshWUHjNwuVP7SfcmPraYuqocJp1yo2U1R7P+5QAfDhdItkuoGqIBnBYrtPp7rEPqDn9HlZA==", "dev": true }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, "readdirp": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz", @@ -2922,12 +3044,35 @@ } } }, + "split-ca": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz", + "integrity": "sha1-bIOv82kvphJW4M0ZfgXp3hV2kaY=" + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "ssh2": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-0.8.9.tgz", + "integrity": "sha512-GmoNPxWDMkVpMFa9LVVzQZHF6EW3WKmBwL+4/GeILf2hFmix5Isxm7Amamo8o7bHiU0tC+wXsGcUXOxp8ChPaw==", + "requires": { + "ssh2-streams": "~0.4.10" + } + }, + "ssh2-streams": { + "version": "0.4.10", + "resolved": "https://registry.npmjs.org/ssh2-streams/-/ssh2-streams-0.4.10.tgz", + "integrity": "sha512-8pnlMjvnIZJvmTzUIIA5nT4jr2ZWNNVHwyXfMGdRJbug9TpI3kd99ffglgfSWqujVv/0gxwMsDn9j9RVst8yhQ==", + "requires": { + "asn1": "~0.2.0", + "bcrypt-pbkdf": "^1.0.2", + "streamsearch": "~0.1.2" + } + }, "stream-events": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", @@ -2937,6 +3082,11 @@ "stubs": "^3.0.0" } }, + "streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" + }, "string-width": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", @@ -2989,6 +3139,21 @@ "es-abstract": "^1.17.5" } }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" + } + } + }, "strip-ansi": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", @@ -3082,6 +3247,29 @@ } } }, + "tar-fs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz", + "integrity": "sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==", + "requires": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.0.0" + } + }, + "tar-stream": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.2.tgz", + "integrity": "sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q==", + "requires": { + "bl": "^4.0.1", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + } + }, "teeny-request": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-6.0.1.tgz", @@ -3155,6 +3343,11 @@ "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==", "dev": true }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", @@ -3170,6 +3363,11 @@ "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, "typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", @@ -3199,6 +3397,11 @@ "integrity": "sha1-iS/pWWCAXoVRnxzUOJ8stMu3ZS8=", "dev": true }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, "uuid": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", @@ -3299,8 +3502,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write": { "version": "1.0.3", diff --git a/package.json b/package.json index 3c90c97..f98e612 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "chalk": "^4.0.0", "clone": "^2.1.2", "debug": "^4.1.1", + "dockerode": "^3.2.0", "fetch-retry": "^3.1.0", "fs-extra": "^8.1.0", "isomorphic-fetch": "^2.2.1", diff --git a/src/debugger.js b/src/debugger.js index e7d250b..58462ef 100644 --- a/src/debugger.js +++ b/src/debugger.js @@ -287,11 +287,19 @@ class Debugger { if (this.agentMgr) { await this.tryCatch(this.agentMgr.shutdown()); } + + // ------------< critical removal must happen above this line >--------------- + + // in VS Code, we will not run beyond this line upon debug stop. + // this is because invoker.stop() will kill the container & thus close the + // debug port, upon which VS Code kills the debug process (us) if (this.invoker) { await this.tryCatch(this.invoker.stop()); - log.debug(`stopped container: ${this.invoker.name()}`); } + if (this.watcher) { + // this is not critical on a process exit, only if Debugger is used programmatically + // and might be reused for a new run() await this.tryCatch(this.watcher.stop()); log.debug("stopped source file watching"); } diff --git a/src/invoker.js b/src/invoker.js index 32b9d65..7d85134 100644 --- a/src/invoker.js +++ b/src/invoker.js @@ -22,6 +22,7 @@ const fetch = require('fetch-retry')(require('isomorphic-fetch')); const kinds = require('./kinds/kinds'); const path = require('path'); const log = require("./log"); +const Docker = require('dockerode'); const RUNTIME_PORT = 8080; const INIT_RETRY_DELAY_MS = 100; @@ -32,11 +33,13 @@ const OPENWHISK_DEFAULTS = { memory: 256 }; -function execute(cmd, options, debug2) { +function execute(cmd, options) { cmd = cmd.replace(/\s+/g, ' '); + + log.verboseStep(`${cmd}`) + const result = execSync(cmd, options); - (debug2 || log.debug)(`executed: ${cmd}`); if (result) { return result.toString().trim(); } else { @@ -54,6 +57,39 @@ function resolveValue(value, ...args) { } } +function asContainerName(name) { + // docker container names are restricted to [a-zA-Z0-9][a-zA-Z0-9_.-]* + + // 1. replace special characters with dash + name = name.replace(/[^a-zA-Z0-9_.-]+/g, '-'); + // 2. leading character is more limited + name = name.replace(/^[^a-zA-Z0-9]+/g, ''); + // 3. (nice to have) remove trailing special chars + name = name.replace(/[^a-zA-Z0-9]+$/g, ''); + + return name; +} + +function addressForContainerPort(containerInfo, port) { + if (containerInfo && containerInfo.NetworkSettings && containerInfo.NetworkSettings.Ports) { + const ports = containerInfo.NetworkSettings.Ports; + // example: + // Ports { + // '8080/tcp': [ { HostIp: '0.0.0.0', HostPort: '32812' } ], + // '9229/tcp': [ { HostIp: '0.0.0.0', HostPort: '9229' } ] + // } + const portEntry = ports[`${port}/tcp`]; + if (portEntry && Array.isArray(portEntry) && portEntry.length >= 1) { + const address = portEntry[0]; + return `${address.HostIp}:${address.HostPort}`; + } else { + return null; + } + } else { + return null; + } +} + class OpenWhiskInvoker { constructor(actionName, action, options, wskProps, wsk) { this.actionName = actionName; @@ -79,12 +115,14 @@ class OpenWhiskInvoker { this.wskProps = wskProps; this.wsk = wsk; - this.containerName = this.asContainerName(`wskdebug-${this.action.name}-${Date.now()}`); + this.containerName = asContainerName(`wskdebug-${this.action.name}-${Date.now()}`); + this.docker = new Docker(); } async checkIfDockerAvailable() { try { - execute("docker info", {stdio: 'ignore'}); + await this.docker.info(); + log.debug("docker - availability check") } catch (e) { throw new Error("Docker not running on local system. A local docker environment is required for the debugger.") } @@ -190,8 +228,10 @@ class OpenWhiskInvoker { await this.checkIfDockerAvailable(); try { - execute(`docker inspect --type=image ${this.image} 2> /dev/null`); + await this.docker.getImage(this.image).inspect(); + debug2(`docker - image inspected, is present: ${this.image}`) } catch (e) { + debug2(`docker - image inspected, not found: ${this.image}`) // make sure the user can see the image download process as part of docker run showDockerRunOutput = true; log.warn(` @@ -208,10 +248,42 @@ class OpenWhiskInvoker { `); } + // console.log(this.debug.command); + // console.log(this.debug.command.split(" ")); + + // TODO: switch docker run to dockerode.run() + // - find the minimal HostConfig that works for the below run options + // https://docs.docker.com/engine/api/v1.37/#operation/ContainerCreate + // https://github.com/apocas/dockerode/issues/257 + // https://github.com/apocas/dockerode/blob/master/lib/docker.js#L1442 + // https://medium.com/@johnnyeric/how-to-reproduce-command-docker-run-via-docker-remote-api-with-node-js-5918d7b221ea + // - kinds/nodejs.js has to switch from docker args to HostConfig map for -e and -v + // - --docker-args (this.dockerArgsFromUser) must be parsed and turned into HostConfig + // - replaces docker logs call as well, using streams to pass and write into sdtout/err + // - allows to intercept logging using our log.log() & log.error() calls (?) + // - also must use global.mochaLogFile + // - no stdin needed + // - returns dockerode container object (store as this.container) + // - call this.container.kill() on it to get rid of it (already done in stop()) + + // await this.docker.run( + // this.image, + // [ 'sh', '-c', ...this.debug.command.split(" ") ], + // showDockerRunOutput ? [process.stdout] : [], + // { + // HostConfig: { + // AutoRemove: true, + // PortBindings: { + // [`${RUNTIME_PORT}/tcp`]: [{ HostPort: RUNTIME_PORT }] + // } + // } + // } + // ); + // log.debug("docker - run"); execute( `docker run -d - --name ${this.name()} + --name ${this.containerName} --rm -m ${this.memory} -p ${RUNTIME_PORT} @@ -222,11 +294,16 @@ class OpenWhiskInvoker { ${this.debug.command} `, // live stream view for docker image download output - { stdio: showDockerRunOutput ? "inherit" : null }, - debug2 + { stdio: showDockerRunOutput ? "inherit" : null } ); + debug2(`docker - started container ${this.containerName}`); - this.containerRunning = true; + this.container = this.docker.getContainer(this.containerName); + + // ask docker for the exposed IP and port of the RUNTIME_PORT on the container + const containerInfo = await this.container.inspect(); + debug2(`docker - retrieved container metadata`); + this.containerURL = `http://${addressForContainerPort(containerInfo, RUNTIME_PORT)}`; log.stopSpinner(); spawn("docker", ["logs", "-t", "-f", this.name()], { @@ -236,6 +313,7 @@ class OpenWhiskInvoker { global.mochaLogFile || "inherit" // stderr ] }); + log.debug(`docker - trailing logs`); } getSourcePath() { @@ -308,40 +386,27 @@ class OpenWhiskInvoker { } async stop() { - if (this.containerRunning) { - execute(`docker kill ${this.name()}`); + if (this.container) { + // log this here for VS Code, will be the last visible log message since + // we will be killed by VS code after the container is gone after the kill() + log.log(`Stopping container ${this.name()}.`); + await this.container.kill(); + delete this.container; + log.debug(`docker - stopped container ${this.name()}`); } } name() { - return this.containerName; + return this.container ? this.container.id : ""; } url() { - if (!this.containerURL) { - // ask docker for the exposed IP and port of the RUNTIME_PORT on the container - const host = execute(`docker port ${this.name()} ${RUNTIME_PORT}`); - this.containerURL = `http://${host}`; - } - return this.containerURL; + return this.containerURL || ""; } timeout() { return this.action.limits.timeout || OPENWHISK_DEFAULTS.timeout; } - - asContainerName(name) { - // docker container names are restricted to [a-zA-Z0-9][a-zA-Z0-9_.-]* - - // 1. replace special characters with dash - name = name.replace(/[^a-zA-Z0-9_.-]+/g, '-'); - // 2. leading character is more limited - name = name.replace(/^[^a-zA-Z0-9]+/g, ''); - // 3. (nice to have) remove trailing special chars - name = name.replace(/[^a-zA-Z0-9]+$/g, ''); - - return name; - } } module.exports = OpenWhiskInvoker; diff --git a/src/log.js b/src/log.js index fc39e25..8c13932 100644 --- a/src/log.js +++ b/src/log.js @@ -117,6 +117,12 @@ module.exports = { } }, + verboseStep: function(...args) { + if (this.isVerbose) { + this.step(...args); + } + }, + verboseWrite: function(text) { if (this.isVerbose) { process.stdout.write(text);
