mirror of
https://github.com/openstf/stf
synced 2025-10-03 09:49:17 +02:00
408 lines
12 KiB
JavaScript
408 lines
12 KiB
JavaScript
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)
|
|
}
|