Make local mode only work in development OS, and make it remove app containers and allow unauthenticated API requests

Local mode makes the API accept unauthenticated requests.
Local mode now also removes app containers when stopping them.

Local mode only works on a host OS that has `VARIANT_ID = "dev"` in /etc/os-release.

Also add more explicit logging when stopping an app and it was already stopped
or the container was already removed.

Change-Type: patch
Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
This commit is contained in:
Pablo Carranza Velez 2017-03-08 16:02:56 -03:00 committed by Pablo Carranza Vélez
parent 72f6b2cea5
commit 208b799c4b
4 changed files with 43 additions and 19 deletions

View File

@ -27,6 +27,8 @@ module.exports = (application) ->
next() next()
else if headerKey? && bufferEq(new Buffer(headerKey), new Buffer(secret)) else if headerKey? && bufferEq(new Buffer(headerKey), new Buffer(secret))
next() next()
else if application.localMode
next()
else else
res.sendStatus(401) res.sendStatus(401)
.catch (err) -> .catch (err) ->

View File

@ -31,6 +31,12 @@ logTypes =
stopAppSuccess: stopAppSuccess:
eventName: 'Application stop' eventName: 'Application stop'
humanName: 'Killed application' humanName: 'Killed application'
stopAppNoop:
eventName: 'Application already stopped'
humanName: 'Application is already stopped, removing container'
stopRemoveAppNoop:
eventName: 'Application already stopped and container removed'
humanName: 'Application is already stopped and the container removed'
stopAppError: stopAppError:
eventName: 'Application stop error' eventName: 'Application stop error'
humanName: 'Failed to kill application' humanName: 'Failed to kill application'
@ -160,10 +166,12 @@ application.kill = kill = (app, { updateDB = true, removeContainer = true } = {}
statusCode = '' + err.statusCode statusCode = '' + err.statusCode
# 304 means the container was already stopped - so we can just remove it # 304 means the container was already stopped - so we can just remove it
if statusCode is '304' if statusCode is '304'
logSystemEvent(logTypes.stopAppNoop, app)
container.removeAsync(v: true) if removeContainer container.removeAsync(v: true) if removeContainer
return return
# 404 means the container doesn't exist, precisely what we want! :D # 404 means the container doesn't exist, precisely what we want! :D
if statusCode is '404' if statusCode is '404'
logSystemEvent(logTypes.stopRemoveAppNoop, app)
return return
throw err throw err
.tap -> .tap ->
@ -412,22 +420,27 @@ apiPollInterval = (val) ->
setLocalMode = (val) -> setLocalMode = (val) ->
mode = checkTruthy(val) ? false mode = checkTruthy(val) ? false
Promise.try -> device.getOSVariant()
if mode and !application.localMode .then (variant) ->
logSystemMessage('Entering local mode, app will be forcefully stopped', {}, 'Enter local mode') if variant is not 'dev'
Promise.map utils.getKnexApps(), (theApp) -> logSystemMessage('Not a development OS, ignoring local mode', {}, 'Ignore local mode')
Promise.using application.lockUpdates(theApp.appId, true), -> return
# There's a slight chance the app changed after the previous select Promise.try ->
# So we fetch it again now the lock is acquired if mode and !application.localMode
utils.getKnexApp(theApp.appId) logSystemMessage('Entering local mode, app will be forcefully stopped', {}, 'Enter local mode')
.then (app) -> Promise.map utils.getKnexApps(), (theApp) ->
application.kill(app, removeContainer: false) if app? Promise.using application.lockUpdates(theApp.appId, true), ->
else if !mode and application.localMode # There's a slight chance the app changed after the previous select
logSystemMessage('Exiting local mode, app will be resumed', {}, 'Exit local mode') # So we fetch it again now the lock is acquired
Promise.map utils.getKnexApps(), (app) -> utils.getKnexApp(theApp.appId)
unlockAndStart(app) .then (app) ->
.then -> application.kill(app) if app?
application.localMode = mode else if !mode and application.localMode
logSystemMessage('Exiting local mode, app will be resumed', {}, 'Exit local mode')
Promise.map utils.getKnexApps(), (app) ->
unlockAndStart(app)
.then ->
application.localMode = mode
specialActionConfigVars = [ specialActionConfigVars = [
[ 'RESIN_SUPERVISOR_LOCAL_MODE', setLocalMode ] [ 'RESIN_SUPERVISOR_LOCAL_MODE', setLocalMode ]

View File

@ -229,4 +229,10 @@ exports.getOSVersion = memoizePromise ->
exports.isResinOSv1 = memoizePromise -> exports.isResinOSv1 = memoizePromise ->
exports.getOSVersion().then (osVersion) -> exports.getOSVersion().then (osVersion) ->
return true if /^Resin OS 1./.test(osVersion) return true if /^Resin OS 1./.test(osVersion)
return false return false
exports.getOSVariant = memoizePromise ->
utils.getOSReleaseField(config.hostOsVersionPath, 'VARIANT_ID')
.catch (err) ->
console.error('Failed to get OS variant', err, err.stack)
return undefined

View File

@ -230,7 +230,7 @@ exports.getKnexApp = (appId, columns) ->
exports.getKnexApps = (columns) -> exports.getKnexApps = (columns) ->
knex('app').select(columns) knex('app').select(columns)
exports.getOSVersion = (path) -> exports.getOSReleaseField = (path, field) ->
fs.readFileAsync(path) fs.readFileAsync(path)
.then (releaseData) -> .then (releaseData) ->
lines = releaseData.toString().split('\n') lines = releaseData.toString().split('\n')
@ -239,7 +239,10 @@ exports.getOSVersion = (path) ->
[ key, val ] = line.split('=') [ key, val ] = line.split('=')
releaseItems[_.trim(key)] = _.trim(val) releaseItems[_.trim(key)] = _.trim(val)
# Remove enclosing quotes: http://stackoverflow.com/a/19156197/2549019 # Remove enclosing quotes: http://stackoverflow.com/a/19156197/2549019
return releaseItems['PRETTY_NAME'].replace(/^"(.+(?="$))"$/, '$1') return releaseItems[field].replace(/^"(.+(?="$))"$/, '$1')
exports.getOSVersion = (path) ->
exports.getOSReleaseField(path, 'PRETTY_NAME')
.catch (err) -> .catch (err) ->
console.log('Could not get OS Version: ', err, err.stack) console.log('Could not get OS Version: ', err, err.stack)
return undefined return undefined