mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-06-03 16:20:49 +00:00
Merge pull request #224 from resin-io/new-device-registering
Use the new device registration endpoint
This commit is contained in:
commit
fdcb5f3dff
@ -16,7 +16,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"JSONStream": "^1.1.2",
|
"JSONStream": "^1.1.2",
|
||||||
"blinking": "~0.0.2",
|
"blinking": "~0.0.2",
|
||||||
"bluebird": "^3.0.0",
|
"bluebird": "^3.5.0",
|
||||||
"body-parser": "^1.12.0",
|
"body-parser": "^1.12.0",
|
||||||
"buffer-equal-constant-time": "^1.0.1",
|
"buffer-equal-constant-time": "^1.0.1",
|
||||||
"docker-delta": "1.0.1",
|
"docker-delta": "1.0.1",
|
||||||
@ -37,7 +37,7 @@
|
|||||||
"pubnub": "^3.7.13",
|
"pubnub": "^3.7.13",
|
||||||
"request": "^2.51.0",
|
"request": "^2.51.0",
|
||||||
"request-progress": "^2.0.1",
|
"request-progress": "^2.0.1",
|
||||||
"resin-register-device": "^2.0.0",
|
"resin-register-device": "^3.0.0",
|
||||||
"rimraf": "^2.5.4",
|
"rimraf": "^2.5.4",
|
||||||
"rwlock": "^5.0.0",
|
"rwlock": "^5.0.0",
|
||||||
"sqlite3": "^3.1.0",
|
"sqlite3": "^3.1.0",
|
||||||
|
@ -51,17 +51,17 @@ knex.init.then ->
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
updateIpAddr = ->
|
||||||
|
utils.gosuper.getAsync('/v1/ipaddr', { json: true })
|
||||||
|
.spread (response, body) ->
|
||||||
|
if response.statusCode == 200 && body.Data.IPAddresses?
|
||||||
|
device.updateState(
|
||||||
|
ip_address: body.Data.IPAddresses.join(' ')
|
||||||
|
)
|
||||||
|
.catch(_.noop)
|
||||||
|
console.log('Starting periodic check for IP addresses..')
|
||||||
|
setInterval(updateIpAddr, 30 * 1000) # Every 30s
|
||||||
|
updateIpAddr()
|
||||||
|
|
||||||
console.log('Starting Apps..')
|
console.log('Starting Apps..')
|
||||||
application.initialize()
|
application.initialize()
|
||||||
|
|
||||||
updateIpAddr = ->
|
|
||||||
utils.gosuper.getAsync('/v1/ipaddr', { json: true })
|
|
||||||
.spread (response, body) ->
|
|
||||||
if response.statusCode == 200 && body.Data.IPAddresses?
|
|
||||||
device.updateState(
|
|
||||||
ip_address: body.Data.IPAddresses.join(' ')
|
|
||||||
)
|
|
||||||
.catch(_.noop)
|
|
||||||
console.log('Starting periodic check for IP addresses..')
|
|
||||||
setInterval(updateIpAddr, 30 * 1000) # Every 30s
|
|
||||||
updateIpAddr()
|
|
||||||
|
@ -2,17 +2,18 @@ Promise = require 'bluebird'
|
|||||||
knex = require './db'
|
knex = require './db'
|
||||||
utils = require './utils'
|
utils = require './utils'
|
||||||
deviceRegister = require 'resin-register-device'
|
deviceRegister = require 'resin-register-device'
|
||||||
{ resinApi } = require './request'
|
{ resinApi, request } = require './request'
|
||||||
fs = Promise.promisifyAll(require('fs'))
|
fs = Promise.promisifyAll(require('fs'))
|
||||||
config = require './config'
|
config = require './config'
|
||||||
configPath = '/boot/config.json'
|
configPath = '/boot/config.json'
|
||||||
appsPath = '/boot/apps.json'
|
appsPath = '/boot/apps.json'
|
||||||
_ = require 'lodash'
|
_ = require 'lodash'
|
||||||
deviceConfig = require './device-config'
|
deviceConfig = require './device-config'
|
||||||
|
TypedError = require 'typed-error'
|
||||||
userConfig = {}
|
userConfig = {}
|
||||||
|
|
||||||
DuplicateUuidError = (err) ->
|
DuplicateUuidError = message: '"uuid" must be unique.'
|
||||||
return err.message == '"uuid" must be unique.'
|
exports.ExchangeKeyError = class ExchangeKeyError extends TypedError
|
||||||
|
|
||||||
bootstrapper = {}
|
bootstrapper = {}
|
||||||
|
|
||||||
@ -34,27 +35,71 @@ loadPreloadedApps = ->
|
|||||||
.catch (err) ->
|
.catch (err) ->
|
||||||
utils.mixpanelTrack('Loading preloaded apps failed', { error: err })
|
utils.mixpanelTrack('Loading preloaded apps failed', { error: err })
|
||||||
|
|
||||||
|
exchangeKey = ->
|
||||||
|
resinApi.get
|
||||||
|
resource: 'device'
|
||||||
|
options:
|
||||||
|
filter:
|
||||||
|
uuid: userConfig.uuid
|
||||||
|
customOptions:
|
||||||
|
apikey: userConfig.apiKey
|
||||||
|
.catchReturn([])
|
||||||
|
.timeout(config.apiTimeout)
|
||||||
|
.then ([ device ]) ->
|
||||||
|
if not device?
|
||||||
|
throw new ExchangeKeyError("Couldn't fetch device with provisioning key")
|
||||||
|
# We found the device, we can try to generate a working device key for it
|
||||||
|
request.postAsync("#{config.apiEndpoint}/api-key/device/#{device.id}/device-key")
|
||||||
|
.spread (res, body) ->
|
||||||
|
if res.status != 200
|
||||||
|
throw new ExchangeKeyError("Couldn't generate device key with provisioning key")
|
||||||
|
userConfig.deviceApiKey = body
|
||||||
|
.return(device)
|
||||||
|
|
||||||
bootstrap = ->
|
bootstrap = ->
|
||||||
Promise.try ->
|
Promise.try ->
|
||||||
userConfig.deviceType ?= 'raspberry-pi'
|
userConfig.deviceType ?= 'raspberry-pi'
|
||||||
if userConfig.registered_at?
|
if userConfig.registered_at?
|
||||||
return userConfig
|
return userConfig
|
||||||
deviceRegister.register(resinApi, userConfig)
|
|
||||||
|
deviceRegister.register(
|
||||||
|
userId: userConfig.userId
|
||||||
|
applicationId: userConfig.applicationId
|
||||||
|
uuid: userConfig.uuid
|
||||||
|
deviceType: userConfig.deviceType
|
||||||
|
deviceApiKey: userConfig.deviceApiKey
|
||||||
|
provisioningApiKey: userConfig.apiKey
|
||||||
|
apiEndpoint: config.apiEndpoint
|
||||||
|
)
|
||||||
.timeout(config.apiTimeout)
|
.timeout(config.apiTimeout)
|
||||||
.catch DuplicateUuidError, ->
|
.catch DuplicateUuidError, ->
|
||||||
|
console.log('UUID already registered, checking if our device key is valid for it')
|
||||||
resinApi.get
|
resinApi.get
|
||||||
resource: 'device'
|
resource: 'device'
|
||||||
options:
|
options:
|
||||||
filter:
|
filter:
|
||||||
uuid: userConfig.uuid
|
uuid: userConfig.uuid
|
||||||
customOptions:
|
customOptions:
|
||||||
apikey: userConfig.apiKey
|
apikey: userConfig.deviceApiKey
|
||||||
|
.catchReturn([])
|
||||||
.timeout(config.apiTimeout)
|
.timeout(config.apiTimeout)
|
||||||
.then ([ device ]) ->
|
.then ([ device ]) ->
|
||||||
return device
|
if device?
|
||||||
.then (device) ->
|
console.log('Fetched device, all is good')
|
||||||
|
return device
|
||||||
|
# If we couldn't fetch with the device key then we can try to key exchange in case the provisioning key is an old 'user-api-key'
|
||||||
|
console.log("Couldn't fetch the device, trying to exchange for a valid key")
|
||||||
|
exchangeKey()
|
||||||
|
.tapCatch ExchangeKeyError, (err) ->
|
||||||
|
# If it fails we just have to reregister as a provisioning key doesn't have the ability to change existing devices
|
||||||
|
console.log('Exchanging key failed, having to reregister')
|
||||||
|
generateRegistration(true)
|
||||||
|
.then (device) ->
|
||||||
|
.then ({ id }) ->
|
||||||
userConfig.registered_at = Date.now()
|
userConfig.registered_at = Date.now()
|
||||||
userConfig.deviceId = device.id
|
userConfig.deviceId = id
|
||||||
|
# Delete the provisioning key now.
|
||||||
|
delete userConfig.apiKey
|
||||||
fs.writeFileAsync(configPath, JSON.stringify(userConfig))
|
fs.writeFileAsync(configPath, JSON.stringify(userConfig))
|
||||||
.return(userConfig)
|
.return(userConfig)
|
||||||
.then (userConfig) ->
|
.then (userConfig) ->
|
||||||
@ -63,7 +108,7 @@ bootstrap = ->
|
|||||||
.then ->
|
.then ->
|
||||||
knex('config').insert([
|
knex('config').insert([
|
||||||
{ key: 'uuid', value: userConfig.uuid }
|
{ key: 'uuid', value: userConfig.uuid }
|
||||||
{ key: 'apiKey', value: userConfig.apiKey }
|
{ key: 'apiKey', value: userConfig.deviceApiKey }
|
||||||
{ key: 'username', value: userConfig.username }
|
{ key: 'username', value: userConfig.username }
|
||||||
{ key: 'userId', value: userConfig.userId }
|
{ key: 'userId', value: userConfig.userId }
|
||||||
{ key: 'version', value: utils.supervisorVersion }
|
{ key: 'version', value: utils.supervisorVersion }
|
||||||
@ -75,19 +120,21 @@ readConfig = ->
|
|||||||
fs.readFileAsync(configPath, 'utf8')
|
fs.readFileAsync(configPath, 'utf8')
|
||||||
.then(JSON.parse)
|
.then(JSON.parse)
|
||||||
|
|
||||||
readConfigAndEnsureUUID = ->
|
generateRegistration = (forceReregister = false) ->
|
||||||
Promise.try ->
|
Promise.try ->
|
||||||
return userConfig.uuid if userConfig.uuid?
|
if forceReregister
|
||||||
deviceRegister.generateUUID()
|
userConfig.uuid = deviceRegister.generateUniqueKey()
|
||||||
.then (uuid) ->
|
userConfig.deviceApiKey = deviceRegister.generateUniqueKey()
|
||||||
userConfig.uuid = uuid
|
else
|
||||||
fs.writeFileAsync(configPath, JSON.stringify(userConfig))
|
userConfig.uuid ?= deviceRegister.generateUniqueKey()
|
||||||
.return(uuid)
|
userConfig.deviceApiKey ?= deviceRegister.generateUniqueKey()
|
||||||
|
fs.writeFileAsync(configPath, JSON.stringify(userConfig))
|
||||||
|
.return(userConfig.uuid)
|
||||||
.catch (err) ->
|
.catch (err) ->
|
||||||
console.log('Error generating and saving UUID: ', err)
|
console.log('Error generating and saving UUID: ', err)
|
||||||
Promise.delay(config.bootstrapRetryDelay)
|
Promise.delay(config.bootstrapRetryDelay)
|
||||||
.then ->
|
.then ->
|
||||||
readConfigAndEnsureUUID()
|
generateRegistration()
|
||||||
|
|
||||||
bootstrapOrRetry = ->
|
bootstrapOrRetry = ->
|
||||||
utils.mixpanelTrack('Device bootstrap')
|
utils.mixpanelTrack('Device bootstrap')
|
||||||
@ -101,6 +148,15 @@ bootstrapper.done = new Promise (resolve) ->
|
|||||||
bootstrapper.doneBootstrapping = ->
|
bootstrapper.doneBootstrapping = ->
|
||||||
bootstrapper.bootstrapped = true
|
bootstrapper.bootstrapped = true
|
||||||
resolve(userConfig)
|
resolve(userConfig)
|
||||||
|
# If we're still using an old api key we can try to exchange it for a valid device key
|
||||||
|
if userConfig.apiKey?
|
||||||
|
exchangeKey()
|
||||||
|
.then ->
|
||||||
|
delete userConfig.apiKey
|
||||||
|
knex('config').update(value: userConfig.deviceApiKey).where(key: 'apiKey')
|
||||||
|
.then ->
|
||||||
|
fs.writeFileAsync(configPath, JSON.stringify(userConfig))
|
||||||
|
|
||||||
|
|
||||||
bootstrapper.bootstrapped = false
|
bootstrapper.bootstrapped = false
|
||||||
bootstrapper.startBootstrapping = ->
|
bootstrapper.startBootstrapping = ->
|
||||||
@ -115,13 +171,16 @@ bootstrapper.startBootstrapping = ->
|
|||||||
bootstrapper.doneBootstrapping() if !bootstrapper.offlineMode
|
bootstrapper.doneBootstrapping() if !bootstrapper.offlineMode
|
||||||
return uuid.value
|
return uuid.value
|
||||||
console.log('New device detected. Bootstrapping..')
|
console.log('New device detected. Bootstrapping..')
|
||||||
readConfigAndEnsureUUID()
|
|
||||||
|
generateRegistration()
|
||||||
.tap ->
|
.tap ->
|
||||||
loadPreloadedApps()
|
loadPreloadedApps()
|
||||||
.tap (uuid) ->
|
.tap (uuid) ->
|
||||||
if bootstrapper.offlineMode
|
if bootstrapper.offlineMode
|
||||||
return knex('config').insert({ key: 'uuid', value: uuid })
|
return knex('config').insert({ key: 'uuid', value: uuid })
|
||||||
else
|
else
|
||||||
return bootstrapOrRetry()
|
bootstrapOrRetry()
|
||||||
|
# Don't wait on bootstrapping here, bootstrapper.done is for that.
|
||||||
|
return
|
||||||
|
|
||||||
module.exports = bootstrapper
|
module.exports = bootstrapper
|
||||||
|
@ -21,10 +21,10 @@ exports.getID = memoizePromise ->
|
|||||||
bootstrap.done
|
bootstrap.done
|
||||||
.then ->
|
.then ->
|
||||||
Promise.all([
|
Promise.all([
|
||||||
knex('config').select('value').where(key: 'apiKey')
|
utils.getConfig('apiKey')
|
||||||
knex('config').select('value').where(key: 'uuid')
|
utils.getConfig('uuid')
|
||||||
])
|
])
|
||||||
.spread ([{ value: apiKey }], [{ value: uuid }]) ->
|
.spread (apiKey, uuid) ->
|
||||||
resinApi.get(
|
resinApi.get(
|
||||||
resource: 'device'
|
resource: 'device'
|
||||||
options:
|
options:
|
||||||
@ -178,9 +178,9 @@ do ->
|
|||||||
if _.size(stateDiff) is 0
|
if _.size(stateDiff) is 0
|
||||||
return
|
return
|
||||||
applyPromise = Promise.join(
|
applyPromise = Promise.join(
|
||||||
knex('config').select('value').where(key: 'apiKey')
|
utils.getConfig('apiKey')
|
||||||
device.getID()
|
device.getID()
|
||||||
([{ value: apiKey }], deviceID) ->
|
(apiKey, deviceID) ->
|
||||||
stateDiff = getStateDiff()
|
stateDiff = getStateDiff()
|
||||||
if _.size(stateDiff) is 0 || !apiKey?
|
if _.size(stateDiff) is 0 || !apiKey?
|
||||||
return
|
return
|
||||||
|
@ -50,9 +50,9 @@ router.post '/v1/devices', (req, res) ->
|
|||||||
utils.getConfig('apiKey')
|
utils.getConfig('apiKey')
|
||||||
utils.getConfig('userId')
|
utils.getConfig('userId')
|
||||||
device.getID()
|
device.getID()
|
||||||
deviceRegister.generateUUID()
|
|
||||||
randomHexString.generate()
|
randomHexString.generate()
|
||||||
(apiKey, userId, deviceId, uuid, logsChannel) ->
|
(apiKey, userId, deviceId, logsChannel) ->
|
||||||
|
uuid = deviceRegister.generateUniqueKey()
|
||||||
d =
|
d =
|
||||||
user: userId
|
user: userId
|
||||||
application: req.body.appId
|
application: req.body.appId
|
||||||
|
@ -7,7 +7,7 @@ while true; do
|
|||||||
echo "UUID missing from config file, VPN cannot connect"
|
echo "UUID missing from config file, VPN cannot connect"
|
||||||
sleep 2
|
sleep 2
|
||||||
else
|
else
|
||||||
read uuid api_key <<<$(jq -r '.uuid,.apiKey' $CONFIG_PATH)
|
read uuid api_key <<<$(jq -r '.uuid,.deviceApiKey // .apiKey' $CONFIG_PATH)
|
||||||
mkdir -p /var/volatile/
|
mkdir -p /var/volatile/
|
||||||
echo $uuid > /var/volatile/vpnfile
|
echo $uuid > /var/volatile/vpnfile
|
||||||
echo $api_key >> /var/volatile/vpnfile
|
echo $api_key >> /var/volatile/vpnfile
|
||||||
|
Loading…
x
Reference in New Issue
Block a user