mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-01-31 16:35:23 +00:00
Merge pull request #814 from balena-io/fix-migration-from-legacy-apps
fix: When updating from a legacy supervisor, use updated resource ids…
This commit is contained in:
commit
e281b4d5c2
@ -13,7 +13,7 @@ validation = require './lib/validation'
|
||||
systemd = require './lib/systemd'
|
||||
updateLock = require './lib/update-lock'
|
||||
{ singleToMulticontainerApp } = require './lib/migration'
|
||||
{ ENOENT, EISDIR } = require './lib/errors'
|
||||
{ ENOENT, EISDIR, NotFoundError } = require './lib/errors'
|
||||
|
||||
DeviceConfig = require './device-config'
|
||||
ApplicationManager = require './application-manager'
|
||||
@ -144,11 +144,94 @@ module.exports = class DeviceState extends EventEmitter
|
||||
applyTargetHealthy = conf.offlineMode or !@applyInProgress or @applications.fetchesInProgress > 0 or cycleTimeWithinInterval
|
||||
return applyTargetHealthy
|
||||
|
||||
normaliseLegacy: =>
|
||||
migrateLegacyApps: (balenaApi) =>
|
||||
console.log('Migrating ids for legacy app...')
|
||||
@db.models('app').select()
|
||||
.then (apps) =>
|
||||
if apps.length == 0
|
||||
console.log('No app to migrate')
|
||||
return
|
||||
app = apps[0]
|
||||
services = JSON.parse(app.services)
|
||||
# Check there's a main service, with legacy-container set
|
||||
if services.length != 1
|
||||
console.log("App doesn't have a single service, ignoring")
|
||||
return
|
||||
service = services[0]
|
||||
if !service.labels['io.resin.legacy-container'] and !service.labels['io.balena.legacy-container']
|
||||
console.log('Service is not marked as legacy, ignoring')
|
||||
return
|
||||
console.log("Getting release #{app.commit} for app #{app.appId} from API")
|
||||
balenaApi.get(
|
||||
resource: 'release'
|
||||
options:
|
||||
filter:
|
||||
belongs_to__application: app.appId
|
||||
commit: app.commit
|
||||
status: 'success'
|
||||
expand:
|
||||
contains__image: [ 'image' ]
|
||||
)
|
||||
.then (releasesFromAPI) =>
|
||||
if releasesFromAPI.length == 0
|
||||
throw new Error('No compatible releases found in API')
|
||||
release = releasesFromAPI[0]
|
||||
releaseId = release.id
|
||||
image = release.contains__image[0].image[0]
|
||||
imageId = image.id
|
||||
serviceId = image.is_a_build_of__service.__id
|
||||
imageUrl = image.is_stored_at__image_location
|
||||
if image.content_hash
|
||||
imageUrl += "@#{image.content_hash}"
|
||||
console.log("Found a release with releaseId #{releaseId}, imageId #{imageId}, serviceId #{serviceId}")
|
||||
console.log("Image location is #{imageUrl}")
|
||||
Promise.join(
|
||||
@applications.docker.getImage(service.image).inspect().catchReturn(NotFoundError, null)
|
||||
@db.models('image').where(name: service.image).select()
|
||||
(imageFromDocker, imagesFromDB) =>
|
||||
@db.transaction (trx) ->
|
||||
Promise.try ->
|
||||
if imagesFromDB.length > 0
|
||||
console.log('Deleting existing image entry in db')
|
||||
trx('image').where(name: service.image).del()
|
||||
else
|
||||
console.log('No image in db to delete')
|
||||
.then ->
|
||||
if imageFromDocker?
|
||||
console.log('Inserting fixed image entry in db')
|
||||
newImage = {
|
||||
name: imageUrl,
|
||||
appId: app.appId,
|
||||
serviceId: serviceId,
|
||||
serviceName: service.serviceName,
|
||||
imageId: imageId,
|
||||
releaseId: releaseId,
|
||||
dependent: 0
|
||||
dockerImageId: imageFromDocker.Id
|
||||
}
|
||||
trx('image').insert(newImage)
|
||||
else
|
||||
console.log('Image is not downloaded, so not saving it to db')
|
||||
.then ->
|
||||
service.image = imageUrl
|
||||
service.serviceID = serviceId
|
||||
service.imageId = imageId
|
||||
service.releaseId = releaseId
|
||||
delete service.labels['io.resin.legacy-container']
|
||||
delete service.labels['io.balena.legacy-container']
|
||||
app.services = JSON.stringify([ service ])
|
||||
app.releaseId = releaseId
|
||||
console.log('Updating app entry in db')
|
||||
trx('app').update(app).where({ appId: app.appId })
|
||||
)
|
||||
|
||||
normaliseLegacy: (balenaApi) =>
|
||||
# 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()
|
||||
# We also need to get the releaseId, serviceId, imageId and updated image URL
|
||||
@migrateLegacyApps(balenaApi)
|
||||
.then =>
|
||||
console.log('Killing legacy containers')
|
||||
@applications.services.killAllLegacy()
|
||||
.then =>
|
||||
console.log('Migrating legacy app volumes')
|
||||
@applications.getTargetApps()
|
||||
@ -171,11 +254,7 @@ module.exports = class DeviceState extends EventEmitter
|
||||
'targetStateSet', 'offlineMode'
|
||||
])
|
||||
.then (conf) =>
|
||||
Promise.try =>
|
||||
if validation.checkTruthy(conf.legacyAppsPresent)
|
||||
@normaliseLegacy()
|
||||
.then =>
|
||||
@applications.init()
|
||||
@applications.init()
|
||||
.then =>
|
||||
if !validation.checkTruthy(conf.initialConfigSaved)
|
||||
@saveInitialConfig()
|
||||
|
@ -23,6 +23,7 @@ startupConfigFields = [
|
||||
'mixpanelHost'
|
||||
'loggingEnabled'
|
||||
'localMode'
|
||||
'legacyAppsPresent'
|
||||
]
|
||||
|
||||
module.exports = class Supervisor extends EventEmitter
|
||||
@ -60,6 +61,10 @@ module.exports = class Supervisor extends EventEmitter
|
||||
enableLogs: checkTruthy(conf.loggingEnabled),
|
||||
localMode: checkTruthy(conf.localMode)
|
||||
})
|
||||
.then =>
|
||||
if checkTruthy(conf.legacyAppsPresent)
|
||||
console.log('Legacy app detected, running migration')
|
||||
@deviceState.normaliseLegacy(@apiBinder.balenaApi)
|
||||
.then =>
|
||||
@deviceState.init()
|
||||
.then =>
|
||||
|
Loading…
x
Reference in New Issue
Block a user