import { promisify } from 'util' import to from 'await-to-js' import { readFile } from 'fs' import { Client } from 'ssh2' import logger from '@uci/logger' let log = {} // declare module wide log to be set during construction const read = promisify(readFile) // ssh client is already an event emitter class Ssh extends Client { constructor(opts={}) { super() this.opts = opts log = logger({ name: 'remote-code', file:'src/ssh.js', class:'Ssh', id: opts.id }) this.ready = false // additional options available this.rpath = opts.rpath || '/opt' // will cd to this path upon connect } async connect(copts = {}) { const opts = Object.assign({},this.opts,copts) // merge any changes pasted via connect // log.info('connection options ', opts) opts.readyTimeout = opts.readyTimeout | 5000 // default was 20 seconds...too long opts.privateKey = opts.privateKey || opts.privateKeyPath ? await read(opts.privateKeyPath) : null // log.info(opts.privateKeyPath, opts.privateKey) super.connect(opts) return new Promise( (resolve,reject) => { this.on('ready', async () => { this.ready=true log.info(`connected to ${this.opts.host}`) resolve(`connected to ${this.opts.host}`) }) this.on('error', (err) => { log.info('connection error', err) reject(err) }) }) } close() { this.ready=false this.end() } async exec(command = 'ls -la', opts={}) { if (!this.ready) { log.info('not connected, attempting connect first') let [err] = await to(this.connect()) if (err) return err } // log.info('executing command ', command) const remote = this let superexec = super.exec.bind(this) return new Promise(function(resolve, reject) { let reply=[] let error=[] superexec(command, function(err, stream) { if (err) reject(err) stream.on('finish', () => { if (remote.opts.close || opts.close) { log.info('closing connection') remote.close } if (error.length!==0) remote.emit('client:error',error) resolve({command:command,reply:reply,error:error}) }) .on('data', function(data) { reply.push(data) log.info(`${remote.opts.host}$ ${data}`) }).stderr.on('data', function(data) { error.push(data) log.info(`${remote.opts.host}:ERR$ ${data}`) }) }) // end super }) // end Promise } } export default Ssh