mirror of
https://github.com/openstf/stf
synced 2025-10-03 09:49:17 +02:00
Ported CLI from commander to yargs because yargs suits our large CLI better and is easier to develop with. Split commands into separate files since the file was getting annoyingly long.
This commit is contained in:
parent
72a16ed2ff
commit
cc736ba0ac
35 changed files with 2006 additions and 1538 deletions
408
lib/cli/local/index.js
Normal file
408
lib/cli/local/index.js
Normal file
|
@ -0,0 +1,408 @@
|
|||
module.exports.command = 'local [serial..]'
|
||||
|
||||
module.exports.describe = 'Start a complete local development environment.'
|
||||
|
||||
module.exports.builder = function(yargs) {
|
||||
var os = require('os')
|
||||
|
||||
return yargs
|
||||
.env('STF_LOCAL')
|
||||
.strict()
|
||||
.option('adb-host', {
|
||||
describe: 'The ADB server host.'
|
||||
, type: 'string'
|
||||
, default: '127.0.0.1'
|
||||
})
|
||||
.option('adb-port', {
|
||||
describe: 'The ADB server port.'
|
||||
, type: 'number'
|
||||
, default: 5037
|
||||
})
|
||||
.option('allow-remote', {
|
||||
alias: 'R'
|
||||
, describe: 'Whether to allow remote devices in STF. Highly ' +
|
||||
'unrecommended due to almost unbelievable slowness on the ADB side ' +
|
||||
'and duplicate device issues when used locally while having a ' +
|
||||
'cable connected at the same time.'
|
||||
, type: 'boolean'
|
||||
})
|
||||
.option('api-port', {
|
||||
describe: 'The port the api unit should run at.'
|
||||
, type: 'number'
|
||||
, default: 7106
|
||||
})
|
||||
.option('app-port', {
|
||||
describe: 'The port the app unit should run at.'
|
||||
, type: 'number'
|
||||
, default: 7105
|
||||
})
|
||||
.option('auth-options', {
|
||||
describe: 'JSON array of options to pass to the auth unit.'
|
||||
, type: 'string'
|
||||
, default: '[]'
|
||||
})
|
||||
.option('auth-port', {
|
||||
describe: 'The port the auth unit should run at.'
|
||||
, type: 'number'
|
||||
, default: 7120
|
||||
})
|
||||
.option('auth-secret', {
|
||||
describe: 'The secret to use for auth JSON Web Tokens. Anyone who ' +
|
||||
'knows this token can freely enter the system if they want, so keep ' +
|
||||
'it safe.'
|
||||
, type: 'string'
|
||||
, default: 'kute kittykat'
|
||||
})
|
||||
.option('auth-type', {
|
||||
describe: 'The type of auth unit to start.'
|
||||
, type: 'string'
|
||||
, choices: ['mock', 'ldap', 'oauth2', 'saml2', 'openid']
|
||||
, default: 'mock'
|
||||
})
|
||||
.option('auth-url', {
|
||||
alias: 'a'
|
||||
, describe: 'URL to the auth unit.'
|
||||
, type: 'string'
|
||||
})
|
||||
.option('bind-app-dealer', {
|
||||
describe: 'The address to bind the app-side ZeroMQ DEALER endpoint to.'
|
||||
, type: 'string'
|
||||
, default: 'tcp://127.0.0.1:7112'
|
||||
})
|
||||
.option('bind-app-pub', {
|
||||
describe: 'The address to bind the app-side ZeroMQ PUB endpoint to.'
|
||||
, type: 'string'
|
||||
, default: 'tcp://127.0.0.1:7111'
|
||||
})
|
||||
.option('bind-app-pull', {
|
||||
describe: 'The address to bind the app-side ZeroMQ PULL endpoint to.'
|
||||
, type: 'string'
|
||||
, default: 'tcp://127.0.0.1:7113'
|
||||
})
|
||||
.option('bind-dev-dealer', {
|
||||
describe: 'The address to bind the device-side ZeroMQ DEALER endpoint to.'
|
||||
, type: 'string'
|
||||
, default: 'tcp://127.0.0.1:7115'
|
||||
})
|
||||
.option('bind-dev-pub', {
|
||||
describe: 'The address to bind the device-side ZeroMQ PUB endpoint to.'
|
||||
, type: 'string'
|
||||
, default: 'tcp://127.0.0.1:7114'
|
||||
})
|
||||
.option('bind-dev-pull', {
|
||||
describe: 'The address to bind the device-side ZeroMQ PULL endpoint to.'
|
||||
, type: 'string'
|
||||
, default: 'tcp://127.0.0.1:7116'
|
||||
})
|
||||
.option('cleanup', {
|
||||
describe: 'Attempt to reset the device between uses by uninstalling' +
|
||||
'apps, resetting accounts and clearing caches. Does not do a perfect ' +
|
||||
'job currently. Negate with --no-cleanup.'
|
||||
, type: 'boolean'
|
||||
, default: true
|
||||
})
|
||||
.option('group-timeout', {
|
||||
alias: 't'
|
||||
, describe: 'Timeout in seconds for automatic release of inactive devices.'
|
||||
, type: 'number'
|
||||
, default: 900
|
||||
})
|
||||
.option('lock-rotation', {
|
||||
describe: 'Whether to lock rotation when devices are being used. ' +
|
||||
'Otherwise changing device orientation may not always work due to ' +
|
||||
'sensitive sensors quickly or immediately reverting it back to the ' +
|
||||
'physical orientation.'
|
||||
, type: 'boolean'
|
||||
})
|
||||
.option('mute-master', {
|
||||
describe: 'Whether to mute master volume when devices are being used.'
|
||||
, type: 'boolean'
|
||||
})
|
||||
.option('port', {
|
||||
alias: ['p', 'poorxy-port']
|
||||
, describe: 'The port STF should run at.'
|
||||
, type: 'number'
|
||||
, default: 7100
|
||||
})
|
||||
.option('provider', {
|
||||
describe: 'An easily identifiable name for the UI and/or log output.'
|
||||
, type: 'string'
|
||||
, default: os.hostname()
|
||||
})
|
||||
.option('provider-max-port', {
|
||||
describe: 'Highest port number for device workers to use.'
|
||||
, type: 'number'
|
||||
, default: 7700
|
||||
})
|
||||
.option('provider-min-port', {
|
||||
describe: 'Lowest port number for device workers to use.'
|
||||
, type: 'number'
|
||||
, default: 7400
|
||||
})
|
||||
.option('public-ip', {
|
||||
describe: 'The IP or hostname to use in URLs.'
|
||||
, type: 'string'
|
||||
, default: 'localhost'
|
||||
})
|
||||
.option('serial', {
|
||||
describe: 'Only use devices with these serial numbers.'
|
||||
, type: 'array'
|
||||
})
|
||||
.option('storage-options', {
|
||||
describe: 'JSON array of options to pass to the storage unit.'
|
||||
, type: 'string'
|
||||
, default: '[]'
|
||||
})
|
||||
.option('storage-plugin-apk-port', {
|
||||
describe: 'The port the storage-plugin-apk unit should run at.'
|
||||
, type: 'number'
|
||||
, default: 7104
|
||||
})
|
||||
.option('storage-plugin-image-port', {
|
||||
describe: 'The port the storage-plugin-image unit should run at.'
|
||||
, type: 'number'
|
||||
, default: 7103
|
||||
})
|
||||
.option('storage-port', {
|
||||
describe: 'The port the storage unit should run at.'
|
||||
, type: 'number'
|
||||
, default: 7102
|
||||
})
|
||||
.option('storage-type', {
|
||||
describe: 'The type of storage unit to start.'
|
||||
, type: 'string'
|
||||
, choices: ['temp', 's3']
|
||||
, default: 'temp'
|
||||
})
|
||||
.option('user-profile-url', {
|
||||
describe: 'URL to external user profile page'
|
||||
, type: 'string'
|
||||
})
|
||||
.option('vnc-initial-size', {
|
||||
describe: 'The initial size to use for the experimental VNC server.'
|
||||
, type: 'string'
|
||||
, default: '600x800'
|
||||
, coerce: function(val) {
|
||||
return val.split('x').map(Number)
|
||||
}
|
||||
})
|
||||
.option('websocket-port', {
|
||||
describe: 'The port the websocket unit should run at.'
|
||||
, type: 'number'
|
||||
, default: 7110
|
||||
})
|
||||
.epilog('Each option can be be overwritten with an environment variable ' +
|
||||
'by converting the option to uppercase, replacing dashes with ' +
|
||||
'underscores and prefixing it with `STF_LOCAL_` (e.g. ' +
|
||||
'`STF_LOCAL_ALLOW_REMOTE`).')
|
||||
}
|
||||
|
||||
module.exports.handler = function(argv) {
|
||||
var util = require('util')
|
||||
var path = require('path')
|
||||
|
||||
var Promise = require('bluebird')
|
||||
|
||||
var logger = require('../../util/logger')
|
||||
var log = logger.createLogger('cli:local')
|
||||
var procutil = require('../../util/procutil')
|
||||
|
||||
// Each forked process waits for signals to stop, and so we run over the
|
||||
// default limit of 10. So, it's not a leak, but a refactor wouldn't hurt.
|
||||
process.setMaxListeners(20)
|
||||
|
||||
function run() {
|
||||
var procs = [
|
||||
// app triproxy
|
||||
procutil.fork(path.resolve(__dirname, '..'), [
|
||||
'triproxy', 'app001'
|
||||
, '--bind-pub', argv.bindAppPub
|
||||
, '--bind-dealer', argv.bindAppDealer
|
||||
, '--bind-pull', argv.bindAppPull
|
||||
])
|
||||
|
||||
// device triproxy
|
||||
, procutil.fork(path.resolve(__dirname, '..'), [
|
||||
'triproxy', 'dev001'
|
||||
, '--bind-pub', argv.bindDevPub
|
||||
, '--bind-dealer', argv.bindDevDealer
|
||||
, '--bind-pull', argv.bindDevPull
|
||||
])
|
||||
|
||||
// processor one
|
||||
, procutil.fork(path.resolve(__dirname, '..'), [
|
||||
'processor', 'proc001'
|
||||
, '--connect-app-dealer', argv.bindAppDealer
|
||||
, '--connect-dev-dealer', argv.bindDevDealer
|
||||
])
|
||||
|
||||
// processor two
|
||||
, procutil.fork(path.resolve(__dirname, '..'), [
|
||||
'processor', 'proc002'
|
||||
, '--connect-app-dealer', argv.bindAppDealer
|
||||
, '--connect-dev-dealer', argv.bindDevDealer
|
||||
])
|
||||
|
||||
// reaper one
|
||||
, procutil.fork(path.resolve(__dirname, '..'), [
|
||||
'reaper', 'reaper001'
|
||||
, '--connect-push', argv.bindDevPull
|
||||
, '--connect-sub', argv.bindAppPub
|
||||
])
|
||||
|
||||
// provider
|
||||
, procutil.fork(path.resolve(__dirname, '..'), [
|
||||
'provider'
|
||||
, '--name', argv.provider
|
||||
, '--min-port', argv.providerMinPort
|
||||
, '--max-port', argv.providerMaxPort
|
||||
, '--connect-sub', argv.bindDevPub
|
||||
, '--connect-push', argv.bindDevPull
|
||||
, '--group-timeout', argv.groupTimeout
|
||||
, '--public-ip', argv.publicIp
|
||||
, '--storage-url'
|
||||
, util.format('http://localhost:%d/', argv.port)
|
||||
, '--adb-host', argv.adbHost
|
||||
, '--adb-port', argv.adbPort
|
||||
, '--vnc-initial-size', argv.vncInitialSize.join('x')
|
||||
]
|
||||
.concat(argv.allowRemote ? ['--allow-remote'] : [])
|
||||
.concat(argv.muteMaster ? ['--mute-master'] : [])
|
||||
.concat(argv.lockRotation ? ['--lock-rotation'] : [])
|
||||
.concat(!argv.cleanup ? ['--no-cleanup'] : [])
|
||||
.concat(argv.serial))
|
||||
|
||||
// auth
|
||||
, procutil.fork(path.resolve(__dirname, '..'), [
|
||||
util.format('auth-%s', argv.authType)
|
||||
, '--port', argv.authPort
|
||||
, '--secret', argv.authSecret
|
||||
, '--app-url', util.format(
|
||||
'http://%s:%d/'
|
||||
, argv.publicIp
|
||||
, argv.port
|
||||
)
|
||||
].concat(JSON.parse(argv.authOptions)))
|
||||
|
||||
// app
|
||||
, procutil.fork(path.resolve(__dirname, '..'), [
|
||||
'app'
|
||||
, '--port', argv.appPort
|
||||
, '--secret', argv.authSecret
|
||||
, '--auth-url', argv.authUrl || util.format(
|
||||
'http://%s:%d/auth/%s/'
|
||||
, argv.publicIp
|
||||
, argv.port
|
||||
, {
|
||||
oauth2: 'oauth'
|
||||
, saml2: 'saml'
|
||||
}[argv.authType] || argv.authType
|
||||
)
|
||||
, '--websocket-url', util.format(
|
||||
'http://%s:%d/'
|
||||
, argv.publicIp
|
||||
, argv.websocketPort
|
||||
)
|
||||
].concat((function() {
|
||||
var extra = []
|
||||
if (argv.userProfileUrl) {
|
||||
extra.push('--user-profile-url', argv.userProfileUrl)
|
||||
}
|
||||
return extra
|
||||
})()))
|
||||
|
||||
// api
|
||||
, procutil.fork(path.resolve(__dirname, '..'), [
|
||||
'api'
|
||||
, '--port', argv.apiPort
|
||||
, '--secret', argv.authSecret
|
||||
, '--connect-push', argv.bindAppPull
|
||||
, '--connect-sub', argv.bindAppPub
|
||||
])
|
||||
|
||||
// websocket
|
||||
, procutil.fork(path.resolve(__dirname, '..'), [
|
||||
'websocket'
|
||||
, '--port', argv.websocketPort
|
||||
, '--secret', argv.authSecret
|
||||
, '--storage-url'
|
||||
, util.format('http://localhost:%d/', argv.port)
|
||||
, '--connect-sub', argv.bindAppPub
|
||||
, '--connect-push', argv.bindAppPull
|
||||
])
|
||||
|
||||
// storage
|
||||
, procutil.fork(path.resolve(__dirname, '..'), [
|
||||
util.format('storage-%s', argv.storageType)
|
||||
, '--port', argv.storagePort
|
||||
].concat(JSON.parse(argv.storageOptions)))
|
||||
|
||||
// image processor
|
||||
, procutil.fork(path.resolve(__dirname, '..'), [
|
||||
'storage-plugin-image'
|
||||
, '--port', argv.storagePluginImagePort
|
||||
, '--storage-url'
|
||||
, util.format('http://localhost:%d/', argv.port)
|
||||
])
|
||||
|
||||
// apk processor
|
||||
, procutil.fork(path.resolve(__dirname, '..'), [
|
||||
'storage-plugin-apk'
|
||||
, '--port', argv.storagePluginApkPort
|
||||
, '--storage-url'
|
||||
, util.format('http://localhost:%d/', argv.port)
|
||||
])
|
||||
|
||||
// poorxy
|
||||
, procutil.fork(path.resolve(__dirname, '..'), [
|
||||
'poorxy'
|
||||
, '--port', argv.port
|
||||
, '--app-url'
|
||||
, util.format('http://localhost:%d/', argv.appPort)
|
||||
, '--auth-url'
|
||||
, util.format('http://localhost:%d/', argv.authPort)
|
||||
, '--api-url'
|
||||
, util.format('http://localhost:%d/', argv.apiPort)
|
||||
, '--websocket-url'
|
||||
, util.format('http://localhost:%d/', argv.websocketPort)
|
||||
, '--storage-url'
|
||||
, util.format('http://localhost:%d/', argv.storagePort)
|
||||
, '--storage-plugin-image-url'
|
||||
, util.format('http://localhost:%d/', argv.storagePluginImagePort)
|
||||
, '--storage-plugin-apk-url'
|
||||
, util.format('http://localhost:%d/', argv.storagePluginApkPort)
|
||||
])
|
||||
]
|
||||
|
||||
function shutdown() {
|
||||
log.info('Shutting down all child processes')
|
||||
procs.forEach(function(proc) {
|
||||
proc.cancel()
|
||||
})
|
||||
return Promise.settle(procs)
|
||||
}
|
||||
|
||||
process.on('SIGINT', function() {
|
||||
log.info('Received SIGINT, waiting for processes to terminate')
|
||||
})
|
||||
|
||||
process.on('SIGTERM', function() {
|
||||
log.info('Received SIGTERM, waiting for processes to terminate')
|
||||
})
|
||||
|
||||
return Promise.all(procs)
|
||||
.then(function() {
|
||||
process.exit(0)
|
||||
})
|
||||
.catch(function(err) {
|
||||
log.fatal('Child process had an error', err.stack)
|
||||
return shutdown()
|
||||
.then(function() {
|
||||
process.exit(1)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return procutil.fork(__filename, ['migrate'])
|
||||
.done(run)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue