From 73dd625edec61b24d1484c6ca7264f6a54dc2973 Mon Sep 17 00:00:00 2001 From: Kostas Lekkas Date: Fri, 10 Mar 2017 15:11:27 +0000 Subject: [PATCH] Require superuser for scan commands, also introduce docker timeout --- lib/actions/local/common.coffee | 123 ++++++++++++++++---------------- lib/actions/local/scan.coffee | 11 +-- 2 files changed, 68 insertions(+), 66 deletions(-) diff --git a/lib/actions/local/common.coffee b/lib/actions/local/common.coffee index 3691d58e..df2d1a85 100644 --- a/lib/actions/local/common.coffee +++ b/lib/actions/local/common.coffee @@ -4,76 +4,75 @@ Docker = require('docker-toolbelt') form = require('resin-cli-form') chalk = require('chalk') -filterOutSupervisorContainer = (container) -> +exports.dockerPort = dockerPort = 2375 +exports.dockerTimeout = dockerTimeout = 2000 + +exports.filterOutSupervisorContainer = filterOutSupervisorContainer = (container) -> for name in container.Names return false if name.includes('resin_supervisor') return true -module.exports = +exports.selectContainerFromDevice = Promise.method (deviceIp, filterSupervisor = false) -> + docker = new Docker(host: deviceIp, port: dockerPort, timeout: dockerTimeout) - filterOutSupervisorContainer: filterOutSupervisorContainer + # List all containers, including those not running + docker.listContainersAsync(all: true) + .filter (container) -> + return true if not filterSupervisor + filterOutSupervisorContainer(container) + .then (containers) -> + if _.isEmpty(containers) + throw new Error("No containers found in #{deviceIp}") - selectContainerFromDevice: Promise.method (deviceIp, filterSupervisor = false) -> - docker = new Docker(host: deviceIp, port: 2375) + return form.ask + message: 'Select a container' + type: 'list' + choices: _.map containers, (container) -> + containerName = container.Names[0] or 'Untitled' + shortContainerId = ('' + container.Id).substr(0, 11) + containerStatus = container.Status - # List all containers, including those not running - docker.listContainersAsync(all: true) - .filter (container) -> - return true if not filterSupervisor - filterOutSupervisorContainer(container) - .then (containers) -> - if _.isEmpty(containers) - throw new Error("No containers found in #{deviceIp}") + return { + name: "#{containerName} (#{shortContainerId}) - #{containerStatus}" + value: container.Id + } - return form.ask - message: 'Select a container' - type: 'list' - choices: _.map containers, (container) -> - containerName = container.Names[0] or 'Untitled' - shortContainerId = ('' + container.Id).substr(0, 11) - containerStatus = container.Status +exports.pipeContainerStream = Promise.method ({ deviceIp, name, outStream, follow = false }) -> + docker = new Docker(host: deviceIp, port: dockerPort) - return { - name: "#{containerName} (#{shortContainerId}) - #{containerStatus}" - value: container.Id - } + container = docker.getContainer(name) + container.inspectAsync() + .then (containerInfo) -> + return containerInfo?.State?.Running + .then (isRunning) -> + container.attachAsync + logs: not follow or not isRunning + stream: follow and isRunning + stdout: true + stderr: true + .then (containerStream) -> + containerStream.pipe(outStream) + .catch (err) -> + err = '' + err.statusCode + if err is '404' + return console.log(chalk.red.bold("Container '#{name}' not found.")) + throw err - pipeContainerStream: Promise.method ({ deviceIp, name, outStream, follow = false }) -> - docker = new Docker(host: deviceIp, port: 2375) +# A function to reliably execute a command +# in all supported operating systems, including +# different Windows environments like `cmd.exe` +# and `Cygwin` should be encapsulated in a +# re-usable package. +exports.getSubShellCommand = (command) -> + os = require('os') - container = docker.getContainer(name) - container.inspectAsync() - .then (containerInfo) -> - return containerInfo?.State?.Running - .then (isRunning) -> - container.attachAsync - logs: not follow or not isRunning - stream: follow and isRunning - stdout: true - stderr: true - .then (containerStream) -> - containerStream.pipe(outStream) - .catch (err) -> - err = '' + err.statusCode - if err is '404' - return console.log(chalk.red.bold("Container '#{name}' not found.")) - throw err - - # A function to reliably execute a command - # in all supported operating systems, including - # different Windows environments like `cmd.exe` - # and `Cygwin` should be encapsulated in a - # re-usable package. - getSubShellCommand: (command) -> - os = require('os') - - if os.platform() is 'win32' - return { - program: 'cmd.exe' - args: [ '/s', '/c', command ] - } - else - return { - program: '/bin/sh' - args: [ '-c', command ] - } + if os.platform() is 'win32' + return { + program: 'cmd.exe' + args: [ '/s', '/c', command ] + } + else + return { + program: '/bin/sh' + args: [ '-c', command ] + } diff --git a/lib/actions/local/scan.coffee b/lib/actions/local/scan.coffee index 1acea70a..601e74a9 100644 --- a/lib/actions/local/scan.coffee +++ b/lib/actions/local/scan.coffee @@ -14,7 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. ### - dockerInfoProperties = [ 'Containers' 'ContainersRunning' @@ -64,6 +63,7 @@ module.exports = Docker = require('docker-toolbelt') { discover } = require('resin-sync') { SpinnerPromise } = require('resin-cli-visuals') + { dockerPort, dockerTimeout } = require('./common') if options.timeout? options.timeout *= 1000 @@ -74,13 +74,16 @@ module.exports = startMessage: 'Scanning for local resinOS devices..' stopMessage: 'Reporting scan results' .filter ({ address }) -> - docker = new Docker(host: address, port: 2375) - docker.infoAsync().return(true).catchReturn(false) + Promise.try -> + docker = new Docker(host: address, port: dockerPort, timeout: dockerTimeout) + docker.pingAsync() + .return(true) + .catchReturn(false) .tap (devices) -> if _.isEmpty(devices) throw new Error('Could not find any resinOS devices in the local network') .map ({ host, address }) -> - docker = new Docker(host: address, port: 2375) + docker = new Docker(host: address, port: dockerPort, timeout: dockerTimeout) Promise.props dockerInfo: docker.infoAsync().catchReturn('Could not get Docker info') dockerVersion: docker.versionAsync().catchReturn('Could not get Docker version')