balena-supervisor/src/device-state.coffee

563 lines
17 KiB
CoffeeScript
Raw Normal View History

DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
Promise = require 'bluebird'
_ = require 'lodash'
Lock = require 'rwlock'
EventEmitter = require 'events'
fs = Promise.promisifyAll(require('fs'))
express = require 'express'
bodyParser = require 'body-parser'
hostConfig = require './host-config'
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
network = require './network'
constants = require './lib/constants'
validation = require './lib/validation'
device = require './lib/device'
updateLock = require './lib/update-lock'
DeviceConfig = require './device-config'
Logger = require './logger'
ApplicationManager = require './application-manager'
validateLocalState = (state) ->
if !state.name? or !validation.isValidShortText(state.name)
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
throw new Error('Invalid device name')
if !state.apps? or !validation.isValidAppsObject(state.apps)
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
throw new Error('Invalid apps')
if !state.config? or !validation.isValidEnv(state.config)
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
throw new Error('Invalid device configuration')
validateDependentState = (state) ->
if state.apps? and !validation.isValidDependentAppsObject(state.apps)
throw new Error('Invalid dependent apps')
if state.devices? and !validation.isValidDependentDevicesObject(state.devices)
throw new Error('Invalid dependent devices')
validateState = Promise.method (state) ->
if !_.isObject(state)
throw new Error('State must be an object')
if !_.isObject(state.local)
throw new Error('Local state must be an object')
validateLocalState(state.local)
if state.dependent?
validateDependentState(state.dependent)
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
singleToMulticontainerApp = (app) ->
environment = {}
for key of app.env
if !/^RESIN_/.test(key)
environment[key] = app.env[key]
appId = app.appId
conf = app.config ? {}
newApp = {
appId: appId
commit: app.commit
name: app.name
releaseId: 1
networks: {}
volumes: {}
}
defaultVolume = "resin-app-#{appId}"
newApp.volumes[defaultVolume] = {}
updateStrategy = conf['RESIN_SUPERVISOR_UPDATE_STRATEGY']
if updateStrategy == null
updateStrategy = 'download-then-kill'
handoverTimeout = conf['RESIN_SUPERVISOR_HANDOVER_TIMEOUT']
if handoverTimeout == null
handoverTimeout = ''
restartPolicy = conf['RESIN_APP_RESTART_POLICY']
if restartPolicy == null
restartPolicy = 'always'
newApp.services = [
{
serviceId: 1
appId: appId
serviceName: app.name.toLowerCase()
imageId: 1
commit: app.commit
releaseId: 1
image: app.imageId
privileged: true
networkMode: 'host'
volumes: [
"#{defaultVolume}:/data"
],
labels: {
'io.resin.features.kernel-modules': '1'
'io.resin.features.firmware': '1'
'io.resin.features.dbus': '1',
'io.resin.features.supervisor-api': '1'
'io.resin.features.resin-api': '1'
'io.resin.update.strategy': updateStrategy
'io.resin.update.handover-timeout': handoverTimeout
'io.resin.legacy-container': '1'
},
environment: environment
restart: restartPolicy
running: true
}
]
return newApp
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
class DeviceStateRouter
constructor: (@deviceState) ->
{ @applications, @config } = @deviceState
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
@router = express.Router()
@router.use(bodyParser.urlencoded(extended: true))
@router.use(bodyParser.json())
@router.post '/v1/reboot', (req, res) =>
force = validation.checkTruthy(req.body.force)
@deviceState.executeStepAction({ action: 'reboot' }, { force })
.then (response) ->
res.status(202).json(response)
.catch (err) ->
if err instanceof updateLock.UpdatesLockedError
status = 423
else
status = 500
res.status(status).json({ Data: '', Error: err?.message or err or 'Unknown error' })
@router.post '/v1/shutdown', (req, res) =>
force = validation.checkTruthy(req.body.force)
@deviceState.executeStepAction({ action: 'shutdown' }, { force })
.then (response) ->
res.status(202).json(response)
.catch (err) ->
if err instanceof updateLock.UpdatesLockedError
status = 423
else
status = 500
res.status(status).json({ Data: '', Error: err?.message or err or 'Unknown error' })
@router.get '/v1/device/host-config', (req, res) ->
hostConfig.get()
.then (conf) ->
res.json(conf)
.catch (err) ->
res.status(503).send(err?.message or err or 'Unknown error')
@router.patch '/v1/device/host-config', (req, res) =>
hostConfig.patch(req.body, @config)
.then ->
res.status(200).send('OK')
.catch (err) ->
res.status(503).send(err?.message or err or 'Unknown error')
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
@router.get '/v1/device', (req, res) =>
@deviceState.getStatus()
.then (state) ->
stateToSend = _.pick(state.local, [
'api_port'
'commit'
'ip_address'
'status'
'download_progress'
'os_version'
'supervisor_version'
'update_pending'
'update_failed'
'update_downloaded'
])
res.json(stateToSend)
.catch (err) ->
res.status(500).json({ Data: '', Error: err?.message or err or 'Unknown error' })
@router.use(@applications.router)
module.exports = class DeviceState extends EventEmitter
constructor: ({ @db, @config, @eventTracker }) ->
@logger = new Logger({ @eventTracker })
@deviceConfig = new DeviceConfig({ @db, @config, @logger })
@applications = new ApplicationManager({ @config, @logger, @db, @eventTracker, deviceState: this })
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
@on 'error', (err) ->
console.error('Error in deviceState: ', err, err.stack)
@_currentVolatile = {}
_lock = new Lock()
@_writeLock = Promise.promisify(_lock.async.writeLock)
@_readLock = Promise.promisify(_lock.async.readLock)
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
@lastSuccessfulUpdate = null
@failedUpdates = 0
@applyInProgress = false
@lastApplyStart = process.hrtime()
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
@scheduledApply = null
@shuttingDown = false
@_router = new DeviceStateRouter(this)
@router = @_router.router
@on 'apply-target-state-end', (err) ->
if err?
console.log("Apply error #{err}")
else
console.log('Apply success!')
@on 'step-completed', (err) ->
if err?
console.log("Step completed with error #{err}")
else
console.log('Step success!')
@on 'step-error', (err) ->
console.log("Step error #{err}")
@applications.on('change', @reportCurrentState)
healthcheck: =>
@config.getMany([ 'appUpdatePollInterval', 'offlineMode' ])
.then (conf) =>
cycleTimeWithinInterval = process.hrtime(@lastApplyStart)[0] - @applications.timeSpentFetching < 2 * conf.appUpdatePollInterval
applyTargetHealthy = conf.offlineMode or !@applyInProgress or @applications.fetchesInProgress > 0 or cycleTimeWithinInterval
return applyTargetHealthy and @deviceConfig.gosuperHealthy
normaliseLegacy: =>
# When legacy apps are present, we kill their containers and migrate their /data to a named volume
# (everything else is handled by the knex migration)
console.log('Killing legacy containers')
@applications.services.killAllLegacy()
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
.then =>
console.log('Migrating legacy app volumes')
@applications.getTargetApps()
.map (app) =>
@applications.volumes.createFromLegacy(app.appId)
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
.then =>
@config.set({ legacyAppsPresent: 'false' })
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
init: ->
@config.getMany([
'logsChannelSecret', 'pubnub', 'offlineMode', 'loggingEnabled', 'initialConfigSaved',
'listenPort', 'apiSecret', 'osVersion', 'osVariant', 'version', 'provisioned',
'resinApiEndpoint', 'connectivityCheckEnabled', 'legacyAppsPresent'
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
])
.then (conf) =>
@logger.init({
pubnub: conf.pubnub
channel: "device-#{conf.logsChannelSecret}-logs"
offlineMode: conf.offlineMode
enable: conf.loggingEnabled
})
.then =>
@config.on 'change', (changedConfig) =>
if changedConfig.loggingEnabled?
@logger.enable(changedConfig.loggingEnabled)
if changedConfig.apiSecret?
@reportCurrentState(api_secret: changedConfig.apiSecret)
.then =>
if validation.checkTruthy(conf.legacyAppsPresent)
@normaliseLegacy()
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
.then =>
@applications.init()
.then =>
if !validation.checkTruthy(conf.initialConfigSaved)
@saveInitialConfig()
.then =>
@initNetworkChecks(conf)
console.log('Reporting initial state, supervisor version and API info')
@reportCurrentState(
api_port: conf.listenPort
api_secret: conf.apiSecret
os_version: conf.osVersion
os_variant: conf.osVariant
supervisor_version: conf.version
provisioning_progress: null
provisioning_state: ''
logs_channel: conf.logsChannelSecret
update_failed: false
update_pending: false
update_downloaded: false
)
.then =>
if !conf.provisioned
@loadTargetFromFile()
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
.then =>
@triggerApplyTarget({ initial: true })
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
initNetworkChecks: ({ resinApiEndpoint, connectivityCheckEnabled }) =>
network.startConnectivityCheck resinApiEndpoint, connectivityCheckEnabled, (connected) =>
@connected = connected
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
@config.on 'change', (changedConfig) ->
if changedConfig.connectivityCheckEnabled?
network.enableConnectivityCheck(changedConfig.connectivityCheckEnabled)
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
console.log('Starting periodic check for IP addresses')
network.startIPAddressUpdate (addresses) =>
@reportCurrentState(
ip_address: addresses.join(' ')
)
, @config.constants.ipAddressUpdateInterval
saveInitialConfig: =>
@deviceConfig.getCurrent()
.then (devConf) =>
@deviceConfig.setTarget(devConf)
.then =>
@config.set({ initialConfigSaved: 'true' })
emitAsync: (ev, args...) =>
setImmediate => @emit(ev, args...)
_readLockTarget: =>
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
@_readLock('target').disposer (release) ->
release()
_writeLockTarget: =>
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
@_writeLock('target').disposer (release) ->
release()
_inferStepsLock: =>
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
@_writeLock('inferSteps').disposer (release) ->
release()
usingReadLockTarget: (fn) =>
Promise.using @_readLockTarget, -> fn()
usingWriteLockTarget: (fn) =>
Promise.using @_writeLockTarget, -> fn()
usingInferStepsLock: (fn) =>
Promise.using @_inferStepsLock, -> fn()
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
setTarget: (target) ->
validateState(target)
.then =>
@usingWriteLockTarget =>
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
# Apps, deviceConfig, dependent
@db.transaction (trx) =>
Promise.try =>
@config.set({ name: target.local.name }, trx)
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
.then =>
@deviceConfig.setTarget(target.local.config, trx)
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
.then =>
@applications.setTarget(target.local.apps, target.dependent, trx)
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
getTarget: ({ initial = false, intermediate = false } = {}) =>
@usingReadLockTarget =>
if intermediate
return @intermediateTarget
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
Promise.props({
local: Promise.props({
name: @config.get('name')
config: @deviceConfig.getTarget({ initial })
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
apps: @applications.getTargetApps()
})
dependent: @applications.getDependentTargets()
})
getStatus: ->
@applications.getStatus()
.then (appsStatus) =>
theState = { local: {}, dependent: {} }
_.merge(theState.local, @_currentVolatile)
theState.local.apps = appsStatus.local
theState.dependent.apps = appsStatus.dependent
if appsStatus.commit
theState.local.is_on__commit = appsStatus.commit
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
return theState
getCurrentForComparison: ->
Promise.join(
@config.get('name')
@deviceConfig.getCurrent()
@applications.getCurrentForComparison()
@applications.getDependentState()
(name, devConfig, apps, dependent) ->
return {
local: {
name
config: devConfig
apps
}
dependent
}
)
reportCurrentState: (newState = {}) =>
_.assign(@_currentVolatile, newState)
@emitAsync('change')
_convertLegacyAppsJson: (appsArray) ->
config = _.reduce(appsArray, (conf, app) ->
return _.merge({}, conf, @deviceConfig.filterConfigKeys(app.config))
, {})
apps = _.map(appsArray, singleToMulticontainerApp)
return { apps, config }
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
loadTargetFromFile: (appsPath) ->
appsPath ?= constants.appsJsonPath
fs.readFileAsync(appsPath, 'utf8')
.then(JSON.parse)
.then (stateFromFile) =>
if !_.isEmpty(stateFromFile)
if _.isArray(stateFromFile)
# This is a legacy apps.json
stateFromFile = @_convertLegacyAppsJson(stateFromFile)
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
images = _.flatten(_.map(_.values(stateFromFile.apps), (app, appId) =>
_.map app.services, (service, serviceId) =>
svc = {
image: service.image
serviceName: service.serviceName
imageId: service.imageId
serviceId
appId
}
return @applications.imageForService(svc)
))
Promise.map images, (img) =>
@applications.images.normalise(img.name)
.then (name) =>
img.name = name
@applications.images.save(img)
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
.then =>
@deviceConfig.getCurrent()
.then (deviceConf) ->
_.defaults(stateFromFile.config, deviceConf)
@setTarget({
local: stateFromFile
})
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
.catch (err) =>
@eventTracker.track('Loading preloaded apps failed', { error: err })
reboot: (force, skipLock) =>
@applications.stopAll({ force, skipLock })
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
.then =>
@logger.logSystemMessage('Rebooting', {}, 'Reboot')
device.reboot()
.tap =>
@emit('shutdown')
shutdown: (force, skipLock) =>
@applications.stopAll({ force, skipLock })
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
.then =>
@logger.logSystemMessage('Shutting down', {}, 'Shutdown')
device.shutdown()
.tap =>
@shuttingDown = true
@emitAsync('shutdown')
executeStepAction: (step, { force, initial, skipLock }) =>
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
Promise.try =>
if _.includes(@deviceConfig.validActions, step.action)
@deviceConfig.executeStepAction(step, { initial })
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
else if _.includes(@applications.validActions, step.action)
@applications.executeStepAction(step, { force, skipLock })
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
else
switch step.action
when 'reboot'
@reboot(force, skipLock)
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
when 'shutdown'
@shutdown(force, skipLock)
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
when 'noop'
Promise.resolve()
else
throw new Error("Invalid action #{step.action}")
applyStep: (step, { force, initial, intermediate, skipLock }, updateContext) =>
if @shuttingDown
return
updateContext.stepsInProgress.push(step)
@executeStepAction(step, { force, initial, skipLock })
.finally =>
@usingInferStepsLock ->
_.pullAllWith(updateContext.stepsInProgress, [ step ], _.isEqual)
.catch (err) =>
@emitAsync('step-error', err, step)
throw err
.then (stepResult) =>
@emitAsync('step-completed', null, step, stepResult)
@continueApplyTarget({ force, initial, intermediate }, updateContext)
applyError: (err, force, { initial, intermediate }) =>
if !intermediate
@applyInProgress = false
@failedUpdates += 1
@reportCurrentState(update_failed: true)
if @scheduledApply?
console.log('Updating failed, but there is already another update scheduled immediately: ', err)
else
delay = Math.min((2 ** @failedUpdates) * 500, 30000)
# If there was an error then schedule another attempt briefly in the future.
console.log('Scheduling another update attempt due to failure: ', delay, err)
@triggerApplyTarget({ force, delay, initial })
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
@emitAsync('apply-target-state-error', err)
@emitAsync('apply-target-state-end', err)
throw err
applyTarget: ({ force = false, initial = false, intermediate = false, skipLock = false } = {}, updateContext) =>
if !updateContext?
updateContext = {
stepsInProgress: []
applyContinueScheduled: false
}
Promise.try =>
if !intermediate
@applyBlocker
.then =>
console.log('Applying target state')
@usingInferStepsLock =>
Promise.join(
@getCurrentForComparison()
@getTarget({ initial, intermediate })
(currentState, targetState) =>
@deviceConfig.getRequiredSteps(currentState, targetState, updateContext.stepsInProgress)
.then (deviceConfigSteps) =>
if !_.isEmpty(deviceConfigSteps)
return deviceConfigSteps
else
@applications.getRequiredSteps(currentState, targetState, updateContext.stepsInProgress, intermediate)
)
.then (steps) =>
if _.isEmpty(steps) and _.isEmpty(updateContext.stepsInProgress)
if !intermediate
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
console.log('Finished applying target state')
@applyInProgress = false
@applications.timeSpentFetching = 0
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
@failedUpdates = 0
@lastSuccessfulUpdate = Date.now()
@reportCurrentState(update_failed: false, update_pending: false, update_downloaded: false)
@emitAsync('apply-target-state-end', null)
return
if !intermediate
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
@reportCurrentState(update_pending: true)
Promise.map steps, (step) =>
@applyStep(step, { force, initial, intermediate, skipLock }, updateContext)
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
.catch (err) =>
@applyError(err, force, { initial, intermediate })
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
continueApplyTarget: ({ force = false, initial = false, intermediate = false } = {}, updateContext) =>
Promise.try =>
if !intermediate
@applyBlocker
.then =>
if updateContext.applyContinueScheduled
return
updateContext.applyContinueScheduled = true
Promise.delay(1000)
.then =>
updateContext.applyContinueScheduled = false
@applyTarget({ force, initial, intermediate }, updateContext)
pauseNextApply: =>
@applyBlocker = new Promise (resolve) =>
@applyUnblocker = resolve
resumeNextApply: =>
@applyUnblocker?()
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
triggerApplyTarget: ({ force = false, delay = 0, initial = false } = {}) =>
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
if @applyInProgress
if !@scheduledApply?
@scheduledApply = { force, delay }
else
# If a delay has been set it's because we need to hold off before applying again,
# so we need to respect the maximum delay that has been passed
@scheduledApply.delay = Math.max(delay, @scheduledApply.delay)
@scheduledApply.force or= force
return
@applyInProgress = true
Promise.delay(delay)
.then =>
@lastApplyStart = process.hrtime()
@applyTarget({ force, initial })
.finally =>
if @scheduledApply?
@triggerApplyTarget(@scheduledApply)
@scheduledApply = null
DeviceState: implement a module to manage the device's target and current state This module will take care of applying the target state for the device and reporting its current state. The state itself is handled by two other modules, ApplicationManager and DeviceConfig. The former will take care of running applications (including the dependent ones via its Proxyvisor), and the latter will take care of device configuration like config.txt and supervisor configuration variables. The way state is applied differs radically from the previous approach: the old application.coffee had a big `update` function that took all of the steps from fetching the target state to running the containers. DeviceState, instead, does an iterative process through `triggerApplyTarget` of inferring the next steps to perform towards the target state, by looking at the current state and asking the ApplicationManager and DeviceConfig for the next steps. It then applies the next steps and every time a step is completed, it schedules another round of inferring and applying the next steps. Special care is taken to ensure `applyTarget` is not called simultaneously more than once. This commit also adds a "device" module to handle reboot and shutdown, and moves gosuper calls to a separate module. The module also uses a "network" module to manage network-related parts of the device's current state: IP addresses and the connectivity check. The module implements a "normaliseLegacy" function that allows a migration from the models from older versions of the supervisor to the multicontainer models, so that in case of a supervisor update we can have minimal downtime and bandwidth consumption when updating to the multicontainer supervisor - this migration allows us to avoid cleaning up images, and also allows migrating the contents of the old /data for the app. Changelog-Entry: Infer the current state of the device when applying the target state Change-Type: patch Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2017-11-01 00:24:54 -07:00
return
applyIntermediateTarget: (intermediateTarget, { force = false, skipLock = false } = {}) =>
@intermediateTarget = _.cloneDeep(intermediateTarget)
@applyTarget({ intermediate: true, force, skipLock })
.then =>
@intermediateTarget = null