Completed
Push — master ( 0e0d3f...a0dea7 )
by Reetesh
38s
created

lib/platforms/core/tunnel/process.js   A

Size

Lines of Code 102

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
nc 1
dl 0
loc 102
rs 10
noi 0

4 Functions

Rating   Name   Duplication   Size   Complexity  
process.js ➔ waitForServerExit 0 15 ?
process.js ➔ apiReq 0 8 ?
A process.js ➔ ??? 0 4 1
process.js ➔ waitForExit 0 13 ?
1
'use strict';
2
3
let
4
  execFile = require('child_process').execFile,
5
  Bluebird = require('bluebird'),
6
  retry = require('p-retry'),
7
  Log = require('./../../../core/log').Log,
8
  Request = require('./../../../core/request').Request,
9
  ProcessBase = require('./../../../core/process').Process
10
11
let log = new Log('Platforms.Core.Tunnel.Process')
12
13
class Process extends ProcessBase {
14
15
  constructor(pid, tunnelId) {
16
    super(pid)
17
    this.tunnelId = tunnelId
18
  }
19
20
  create(command, args, callbacks, argsParser, stdoutFnCreator, exitFn) {
21
    argsParser(args)
22
    callbacks = callbacks || { }
23
    if(!callbacks.onstderr) {
24
      callbacks.onstderr = (err) => {
25
        log.warn('unexpected stderr', err, this)
26
      }
27
    }
28
    return new Bluebird((resolve, reject) => {
29
      this.proc = execFile(command, args)
30
      this.pid = this.proc.pid
31
      log.debug('execFile %s %s, created %d', command, args.join(' '), this.pid)
32
      this.proc.stdout.on('data', stdoutFnCreator(this, command, args, resolve, reject))
33
      this.proc.stderr.on('data', callbacks.onstderr)
34
      this.proc.on('error', reject)
35
      this.proc.on('exit', code => {
36
        exitFn(this, command, args, code, reject)
37
      })
38
    })
39
  }
40
41
  stop(isStopped, getStatus, auth) {
42
    this.stopping = true
43
    return apiReq(this.endpoint, 'DELETE', auth)
44
    .then(() => {
45
      return waitForServerExit(this, isStopped, getStatus, auth)
46
    })
47
    .then(() => {
48
      return ProcessBase.prototype.stop.call(this)
49
    })
50
    .then(() => {
51
      return waitForExit(this)
52
    })
53
    .catch(err => {
54
      if(err.message.match(/already stopped/)) {
55
        return Promise.resolve(true)
56
      }
57
      throw err
58
    })
59
  }
60
61
}
62
63
function waitForServerExit(proc, isStopped, getStatus, auth) {
64
  let max = 60, factor = 1, minTimeout = 2000
65
  const check = () => {
66
    return apiReq(proc.endpoint, 'GET', auth)
67
    .then(response => {
68
      if(isStopped(response)) {
69
        log.debug('server tunnel instance (serverId %s) corresponding to tunnel with pid %d is stopped', proc.serverId, proc.pid)
70
        return true
71
      }
72
      log.debug('serverId %s not terminated yet on server, status is %s', proc.pid, proc.serverId, getStatus(response))
73
      throw new Error('Platforms.Core.Tunnel.Process: not terminated on server side yet')
74
    })
75
  }
76
  return retry(check, { retries: max, minTimeout: minTimeout, factor: factor })
77
}
78
79
function waitForExit(proc) {
80
  let max = 60, factor = 1, minTimeout = 2000
81
  const check = () => {
82
    if('stopped' === proc.status()) {
83
      log.debug('killed')
84
      delete proc.stopping
85
      return true
86
    }
87
    log.debug('still alive...')
88
    throw new Error('waiting for process to die')
89
  }
90
  return retry(check, { retries: max, minTimeout: minTimeout, factor: factor })
91
}
92
93
function apiReq(url, method, auth) {
94
  let options = {
95
    json: true,
96
    auth: auth
97
  }
98
  let req = new Request()
99
  return req.request(url, method, options)
100
}
101
102
exports.Process = Process
103