Ignore the supervisor in the target state

Starting with v3 state endpoint, the supervisor may receive the configuration
for the supervisor service on the target state. This commit allows the
supervisor to filter out the supervisor container from the current and target
state to let the update-balena-supervisor script handle the creation and update
of the supervisor container.

Updating and creating the supervisor container will be handled by a
future commit
This commit is contained in:
Felipe Lalanne 2021-09-21 23:48:17 +00:00
parent 8e40f1c2f5
commit b2b1b111b3
2 changed files with 80 additions and 5 deletions

View File

@ -26,6 +26,7 @@ import { checkTruthy, checkString } from '../lib/validation';
import { ServiceComposeConfig, DeviceMetadata } from './types/service';
import { ImageInspectInfo } from 'dockerode';
import { pathExistsOnHost } from '../lib/fs-utils';
import { getSupervisorMetadata } from '../lib/supervisor-metadata';
export interface AppConstructOpts {
appId: number;
@ -773,20 +774,26 @@ export class App {
...opts,
};
const supervisorMeta = await getSupervisorMetadata();
const isService = (svc: ServiceComposeConfig) =>
!svc.labels ||
!svc.labels['io.balena.image.class'] ||
svc.labels?.['io.balena.image.class'] == null ||
svc.labels['io.balena.image.class'] === 'service';
const isDataStore = (svc: ServiceComposeConfig) =>
!svc.labels ||
!svc.labels['io.balena.image.store'] ||
svc.labels?.['io.balena.image.store'] == null ||
svc.labels['io.balena.image.store'] === 'data';
const isSupervisor = (svc: ServiceComposeConfig) =>
app.uuid === supervisorMeta.uuid &&
(svc.serviceName === supervisorMeta.serviceName ||
// keep compatibility with older supervisor releases
svc.serviceName === 'main');
// In the db, the services are an array, but here we switch them to an
// object so that they are consistent
const services: Service[] = await Promise.all(
(JSON.parse(app.services) ?? [])
JSON.parse(app.services ?? [])
.filter(
// For the host app, `io.balena.image.*` labels indicate special way
// to install the service image, so we ignore those we don't know how to
@ -795,6 +802,9 @@ export class App {
(svc: ServiceComposeConfig) =>
!app.isHost || (isService(svc) && isDataStore(svc)),
)
// Ignore the supervisor service itself from the target state for now
// until the supervisor can update itself
.filter((svc: ServiceComposeConfig) => !isSupervisor(svc))
.map(async (svc: ServiceComposeConfig) => {
// Try to fill the image id if the image is downloaded
let imageInfo: ImageInspectInfo | undefined;
@ -819,6 +829,7 @@ export class App {
);
}),
);
return new App(
{
appId: app.appId,

View File

@ -0,0 +1,64 @@
import * as memoizee from 'memoizee';
import * as config from '../config';
import { InternalInconsistencyError } from './errors';
export type SupervisorMetadata = {
uuid: string;
serviceName: string;
};
/**
* Although it might feel unsettling to hardcode these ids here.
* the main purpose of app uuids is to have environment independent
* apps. These ids will be the same in balena-cloud.com and balena-staging.com
* and they should be the same in open-balena instances for target state
* v3 to work with those instances.
*
* This will only be necessary until the supervisor becomes an actual app
* on balena
*/
const SUPERVISOR_APPS: { [arch: string]: SupervisorMetadata } = {
amd64: {
uuid: '52e35121417640b1b28a680504e4039b',
serviceName: 'balena-supervisor',
},
aarch64: {
uuid: '900de4f3cbac4b9bbd232885a35e407b',
serviceName: 'balena-supervisor',
},
armv7hf: {
uuid: '2e66a95795c149959c69472a8c2f92b8',
serviceName: 'balena-supervisor',
},
i386: {
uuid: '531b357e155c480cbec0fdd33041a1f5',
serviceName: 'balena-supervisor',
},
rpi: {
uuid: '6822565f766e413e96d9bebe2227cdcc',
serviceName: 'balena-supervisor',
},
};
/**
* Get the metadata from the supervisor container
*
* This is needed for the supervisor to identify itself on the target
* state and on getStatus() in device-state.ts
*
* TODO: remove this once the supervisor knows how to update itself
*/
export const getSupervisorMetadata = memoizee(
async () => {
const deviceArch = await config.get('deviceArch');
const meta: SupervisorMetadata = SUPERVISOR_APPS[deviceArch];
if (meta == null) {
throw new InternalInconsistencyError(
`Unknown device architecture ${deviceArch}. Could not find matching supervisor metadata.`,
);
}
return meta;
},
{ promise: true },
);