mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-02-12 05:55:38 +00:00
Improvements in proxyvisor:
* Better parameter handling in PUT /v1/devices/:uuid * An update hook response of 200 will cause the proxyvisor to stop pinging the hook * Allow deleting dependent apps and devices * Implement delete dependent device hook * Omit some fields when responding with a device object
This commit is contained in:
parent
7988933ac9
commit
feb97539ef
@ -1,3 +1,8 @@
|
|||||||
|
* Better parameter handling in PUT /v1/devices/:uuid [Pablo]
|
||||||
|
* An update hook response of 200 will cause the proxyvisor to stop pinging the hook [Pablo]
|
||||||
|
* Allow deleting dependent apps and devices [Pablo]
|
||||||
|
* Implement delete dependent device hook [Pablo]
|
||||||
|
* Omit some fields when responding with a device object [Pablo]
|
||||||
* Add validation to dependent device provisioning [Pablo]
|
* Add validation to dependent device provisioning [Pablo]
|
||||||
|
|
||||||
# v2.5.2
|
# v2.5.2
|
||||||
|
@ -96,6 +96,9 @@ knex.init = Promise.all([
|
|||||||
t.json('targetEnvironment')
|
t.json('targetEnvironment')
|
||||||
t.json('config')
|
t.json('config')
|
||||||
t.json('targetConfig')
|
t.json('targetConfig')
|
||||||
|
t.boolean('markedForDeletion')
|
||||||
|
else
|
||||||
|
addColumn('dependentDevice', 'markedForDeletion', 'boolean')
|
||||||
])
|
])
|
||||||
|
|
||||||
module.exports = knex
|
module.exports = knex
|
||||||
|
@ -36,7 +36,7 @@ parseDeviceFields = (device) ->
|
|||||||
device.environment = JSON.parse(device.environment ? '{}')
|
device.environment = JSON.parse(device.environment ? '{}')
|
||||||
device.targetConfig = JSON.parse(device.targetConfig ? '{}')
|
device.targetConfig = JSON.parse(device.targetConfig ? '{}')
|
||||||
device.targetEnvironment = JSON.parse(device.targetEnvironment ? '{}')
|
device.targetEnvironment = JSON.parse(device.targetEnvironment ? '{}')
|
||||||
return device
|
return _.omit(device, 'markedForDeletion', 'logs_channel')
|
||||||
|
|
||||||
|
|
||||||
router.get '/v1/devices', (req, res) ->
|
router.get '/v1/devices', (req, res) ->
|
||||||
@ -101,6 +101,7 @@ router.get '/v1/devices/:uuid', (req, res) ->
|
|||||||
knex('dependentDevice').select().where({ uuid })
|
knex('dependentDevice').select().where({ uuid })
|
||||||
.then ([ device ]) ->
|
.then ([ device ]) ->
|
||||||
return res.status(404).send('Device not found') if !device?
|
return res.status(404).send('Device not found') if !device?
|
||||||
|
return res.status(410).send('Device deleted') if device.markedForDeletion
|
||||||
res.json(parseDeviceFields(device))
|
res.json(parseDeviceFields(device))
|
||||||
.catch (err) ->
|
.catch (err) ->
|
||||||
console.error("Error on #{req.method} #{url.parse(req.url).pathname}", err, err.stack)
|
console.error("Error on #{req.method} #{url.parse(req.url).pathname}", err, err.stack)
|
||||||
@ -117,6 +118,7 @@ router.post '/v1/devices/:uuid/logs', (req, res) ->
|
|||||||
knex('dependentDevice').select().where({ uuid })
|
knex('dependentDevice').select().where({ uuid })
|
||||||
.then ([ device ]) ->
|
.then ([ device ]) ->
|
||||||
return res.status(404).send('Device not found') if !device?
|
return res.status(404).send('Device not found') if !device?
|
||||||
|
return res.status(410).send('Device deleted') if device.markedForDeletion
|
||||||
pubnub.publish({ channel: "device-#{device.logs_channel}-logs", message: m })
|
pubnub.publish({ channel: "device-#{device.logs_channel}-logs", message: m })
|
||||||
res.status(202).send('OK')
|
res.status(202).send('OK')
|
||||||
.catch (err) ->
|
.catch (err) ->
|
||||||
@ -149,21 +151,31 @@ router.put '/v1/devices/:uuid', (req, res) ->
|
|||||||
environment = JSON.stringify(environment) if isDefined(environment)
|
environment = JSON.stringify(environment) if isDefined(environment)
|
||||||
config = JSON.stringify(config) if isDefined(config)
|
config = JSON.stringify(config) if isDefined(config)
|
||||||
|
|
||||||
|
fieldsToUpdateOnDB = _.pickBy({ status, is_online, commit, config, environment }, isDefined)
|
||||||
|
fieldsToUpdateOnAPI = _.pick(fieldsToUpdateOnDB, 'status', 'is_online', 'commit')
|
||||||
|
|
||||||
|
if _.isEmpty(fieldsToUpdateOnDB)
|
||||||
|
res.status(400).send('At least one device attribute must be updated')
|
||||||
|
return
|
||||||
|
|
||||||
Promise.join(
|
Promise.join(
|
||||||
utils.getConfig('apiKey')
|
utils.getConfig('apiKey')
|
||||||
knex('dependentDevice').select().where({ uuid })
|
knex('dependentDevice').select().where({ uuid })
|
||||||
(apiKey, [ device ]) ->
|
(apiKey, [ device ]) ->
|
||||||
throw new Error('apikey not found') if !apiKey?
|
throw new Error('apikey not found') if !apiKey?
|
||||||
return res.status(404).send('Device not found') if !device?
|
return res.status(404).send('Device not found') if !device?
|
||||||
|
return res.status(410).send('Device deleted') if device.markedForDeletion
|
||||||
|
throw new Error('Device is invalid') if !device.deviceId?
|
||||||
|
Promise.try ->
|
||||||
|
if !_.isEmpty(fieldsToUpdateOnAPI)
|
||||||
resinApi.patch
|
resinApi.patch
|
||||||
resource: 'device'
|
resource: 'device'
|
||||||
id: device.deviceId
|
id: device.deviceId
|
||||||
body: _.pickBy({ status, is_online, commit }, isDefined)
|
body: fieldsToUpdateOnAPI
|
||||||
customOptions:
|
customOptions:
|
||||||
apikey: apiKey
|
apikey: apiKey
|
||||||
.then ->
|
.then ->
|
||||||
fieldsToUpdate = _.pickBy({ status, is_online, commit, config, environment }, isDefined)
|
knex('dependentDevice').update(fieldsToUpdateOnDB).where({ uuid })
|
||||||
knex('dependentDevice').update(fieldsToUpdate).where({ uuid })
|
|
||||||
.then ->
|
.then ->
|
||||||
res.json(parseDeviceFields(device))
|
res.json(parseDeviceFields(device))
|
||||||
)
|
)
|
||||||
@ -230,6 +242,8 @@ exports.fetchAndSetTargetsForDependentApps = (state, fetchFn, apiKey) ->
|
|||||||
return app.commit? and app.imageId? and !_.some(localApps, imageId: app.imageId)
|
return app.commit? and app.imageId? and !_.some(localApps, imageId: app.imageId)
|
||||||
toBeRemoved = _.filter localApps, (app, appId) ->
|
toBeRemoved = _.filter localApps, (app, appId) ->
|
||||||
return app.commit? and !_.some(remoteApps, imageId: app.imageId)
|
return app.commit? and !_.some(remoteApps, imageId: app.imageId)
|
||||||
|
toBeDeletedFromDB = _.filter localApps, (app, appId) ->
|
||||||
|
return !remoteApps[appId]?
|
||||||
Promise.map toBeDownloaded, (app) ->
|
Promise.map toBeDownloaded, (app) ->
|
||||||
fetchFn(app, false)
|
fetchFn(app, false)
|
||||||
.then ->
|
.then ->
|
||||||
@ -246,6 +260,10 @@ exports.fetchAndSetTargetsForDependentApps = (state, fetchFn, apiKey) ->
|
|||||||
.then (n) ->
|
.then (n) ->
|
||||||
knex('dependentApp').insert(app) if n == 0
|
knex('dependentApp').insert(app) if n == 0
|
||||||
)
|
)
|
||||||
|
.then ->
|
||||||
|
knex('dependentApp').del().whereIn('appId', _.keys(toBeDeletedFromDB))
|
||||||
|
.then ->
|
||||||
|
knex('dependentDevice').update({ markedForDeletion: true }).whereNotIn('uuid', _.keys(state.devices))
|
||||||
.then ->
|
.then ->
|
||||||
Promise.all _.map state.devices, (device, uuid) ->
|
Promise.all _.map state.devices, (device, uuid) ->
|
||||||
# Only consider one app per dependent device for now
|
# Only consider one app per dependent device for now
|
||||||
@ -283,21 +301,6 @@ exports.fetchAndSetTargetsForDependentApps = (state, fetchFn, apiKey) ->
|
|||||||
.catch (err) ->
|
.catch (err) ->
|
||||||
console.error('Error fetching dependent apps', err, err.stack)
|
console.error('Error fetching dependent apps', err, err.stack)
|
||||||
|
|
||||||
sendUpdate = (device, endpoint) ->
|
|
||||||
request.putAsync "#{endpoint}#{device.uuid}", {
|
|
||||||
json: true
|
|
||||||
body:
|
|
||||||
appId: parseInt(device.appId)
|
|
||||||
commit: device.targetCommit
|
|
||||||
environment: JSON.parse(device.targetEnvironment)
|
|
||||||
config: JSON.parse(device.targetConfig)
|
|
||||||
}
|
|
||||||
.spread (response, body) ->
|
|
||||||
if response.statusCode != 200
|
|
||||||
throw new Error("Hook returned #{response.statusCode}: #{body}")
|
|
||||||
.catch (err) ->
|
|
||||||
return console.error("Error updating device #{device.uuid}", err, err.stack)
|
|
||||||
|
|
||||||
getHookEndpoint = (appId) ->
|
getHookEndpoint = (appId) ->
|
||||||
knex('dependentApp').select('parentAppId').where({ appId })
|
knex('dependentApp').select('parentAppId').where({ appId })
|
||||||
.then ([ { parentAppId } ]) ->
|
.then ([ { parentAppId } ]) ->
|
||||||
@ -310,6 +313,39 @@ getHookEndpoint = (appId) ->
|
|||||||
conf.RESIN_DEPENDENT_DEVICES_HOOK_ADDRESS ?
|
conf.RESIN_DEPENDENT_DEVICES_HOOK_ADDRESS ?
|
||||||
"#{appConfig.proxyvisorHookReceiver}/v1/devices/"
|
"#{appConfig.proxyvisorHookReceiver}/v1/devices/"
|
||||||
|
|
||||||
|
do ->
|
||||||
|
acknowledgedState = {}
|
||||||
|
sendUpdate = (device, endpoint) ->
|
||||||
|
stateToSend = {
|
||||||
|
appId: parseInt(device.appId)
|
||||||
|
commit: device.targetCommit
|
||||||
|
environment: JSON.parse(device.targetEnvironment)
|
||||||
|
config: JSON.parse(device.targetConfig)
|
||||||
|
}
|
||||||
|
request.putAsync "#{endpoint}#{device.uuid}", {
|
||||||
|
json: true
|
||||||
|
body: stateToSend
|
||||||
|
}
|
||||||
|
.spread (response, body) ->
|
||||||
|
if response.statusCode == 200
|
||||||
|
acknowledgedState[device.uuid] = _.omit(stateToSend, 'appId')
|
||||||
|
else
|
||||||
|
acknowledgedState[device.uuid] = null
|
||||||
|
throw new Error("Hook returned #{response.statusCode}: #{body}") if response.statusCode != 202
|
||||||
|
.catch (err) ->
|
||||||
|
return console.error("Error updating device #{device.uuid}", err, err.stack)
|
||||||
|
|
||||||
|
sendDeleteHook = (device, endpoint) ->
|
||||||
|
uuid = device.uuid
|
||||||
|
request.delAsync("#{endpoint}#{uuid}")
|
||||||
|
.spread (response, body) ->
|
||||||
|
if response.statusCode == 200
|
||||||
|
knex('dependentDevice').del().where({ uuid })
|
||||||
|
else
|
||||||
|
throw new Error("Hook returned #{response.statusCode}: #{body}")
|
||||||
|
.catch (err) ->
|
||||||
|
return console.error("Error deleting device #{device.uuid}", err, err.stack)
|
||||||
|
|
||||||
exports.sendUpdates = ->
|
exports.sendUpdates = ->
|
||||||
endpoints = {}
|
endpoints = {}
|
||||||
knex('dependentDevice').select()
|
knex('dependentDevice').select()
|
||||||
@ -320,8 +356,10 @@ exports.sendUpdates = ->
|
|||||||
environment: device.targetEnvironment
|
environment: device.targetEnvironment
|
||||||
config: device.targetConfig
|
config: device.targetConfig
|
||||||
}
|
}
|
||||||
if device.targetCommit? and !_.isEqual(targetState, currentState)
|
|
||||||
endpoints[device.appId] ?= getHookEndpoint(device.appId)
|
endpoints[device.appId] ?= getHookEndpoint(device.appId)
|
||||||
endpoints[device.appId]
|
endpoints[device.appId]
|
||||||
.then (endpoint) ->
|
.then (endpoint) ->
|
||||||
|
if device.markedForDeletion
|
||||||
|
sendDeleteHook(device, endpoint)
|
||||||
|
else if device.targetCommit? and !_.isEqual(targetState, currentState) and !_.isEqual(targetState, acknowledgedState[device.uuid])
|
||||||
sendUpdate(device, endpoint)
|
sendUpdate(device, endpoint)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user