Fix database migration for legacyApps

Migration `M00008` had a bug with the check for legacy apps, which
resulted in devices that had at some point been updated from a single
container supervisor to get the error

```
Undefined binding(s) detected when compiling UPDATE. Undefined column(s): [appUuid] query
```

This adds a new migration with the fix to ensure broken fix the
inconsistent database state.

Change-type: patch
Closes: #1913
This commit is contained in:
Felipe Lalanne 2022-04-01 17:13:50 -03:00
parent ffc61ee0b4
commit eee2460445
2 changed files with 45 additions and 39 deletions

View File

@ -10,45 +10,6 @@ export async function up(knex) {
table.string('appUuid');
table.string('commit');
});
const legacyAppsPresent = await knex('config')
.where({ key: 'legacyAppsPresent' })
.select('value');
// If there are legacy apps we let the database normalization function
// populate the correct values
if (legacyAppsPresent && legacyAppsPresent.length > 0) {
return;
}
// Otherwise delete cloud target apps and images in the database so they can get repopulated
// with the uuid from the target state. Removing the `targetStateSet` configuration ensures that
// the supervisor will maintain the current state and will only apply the new target once it gets
// a new cloud copy, which should include the proper metadata
await knex('image').del();
await knex('app').whereNot({ source: 'local' }).del();
await knex('config').where({ key: 'targetStateSet' }).del();
const apps = await knex('app').select();
// For remaining local apps, if any, the appUuid is not that relevant, so just
// use appId to prevent the app from getting uninstalled. Adding the appUuid will restart
// the app though
await Promise.all(
apps.map((app) => {
const services = JSON.parse(app.services).map((svc) => ({
...svc,
appUuid: app.appId.toString(),
}));
return knex('app')
.where({ id: app.id })
.update({
uuid: app.appId.toString(),
services: JSON.stringify(services),
});
}),
);
}
export function down() {

45
src/migrations/M00009.js Normal file
View File

@ -0,0 +1,45 @@
export async function up(knex) {
const legacyAppsPresent = await knex('config')
.where({ key: 'legacyAppsPresent' })
.select('value')
.first();
// If there are legacy apps we let the database normalization function
// populate the correct values
if (legacyAppsPresent && legacyAppsPresent.value === 'true') {
return;
}
// Otherwise delete cloud target apps and images in the database so they can get repopulated
// with the uuid from the target state. Removing the `targetStateSet` configuration ensures that
// the supervisor will maintain the current state and will only apply the new target once it gets
// a new cloud copy, which should include the proper metadata
await knex('image').del();
await knex('app').whereNot({ source: 'local' }).del();
await knex('config').where({ key: 'targetStateSet' }).del();
const apps = await knex('app').select();
// For remaining local apps, if any, the appUuid is not that relevant, so just
// use appId to prevent the app from getting uninstalled. Adding the appUuid will restart
// the app though
await Promise.all(
apps.map((app) => {
const services = JSON.parse(app.services).map((svc) => ({
...svc,
appUuid: app.appId.toString(),
}));
return knex('app')
.where({ id: app.id })
.update({
uuid: app.appId.toString(),
services: JSON.stringify(services),
});
}),
);
}
export function down() {
throw new Error('Not Implemented');
}