mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-01-19 03:06:27 +00:00
Replace io.resin labels (and their env vars) with io.balena equivalents
But we keep backwards compatibility by normalizing existing io.resin labels into io.balena ones, and adding both RESIN_ and BALENA_ env vars for these features. Change-Type: minor Signed-off-by: Pablo Carranza Velez <pablo@balena.io>
This commit is contained in:
parent
3b7bf9a4b7
commit
ed3f5522ae
@ -96,7 +96,7 @@ module.exports = class ApplicationManager extends EventEmitter
|
||||
# Only called for dead containers, so no need to take locks or anything
|
||||
@services.remove(step.current)
|
||||
updateMetadata: (step, { force = false, skipLock = false } = {}) =>
|
||||
skipLock or= checkTruthy(step.current.config.labels['io.resin.legacy-container'])
|
||||
skipLock or= checkTruthy(step.current.config.labels['io.balena.legacy-container'])
|
||||
@_lockingIfNecessary step.current.appId, { force, skipLock: skipLock or step.options?.skipLock }, =>
|
||||
@services.updateMetadata(step.current, step.target)
|
||||
restart: (step, { force = false, skipLock = false } = {}) =>
|
||||
@ -591,11 +591,11 @@ module.exports = class ApplicationManager extends EventEmitter
|
||||
# Either this is a new service, or the current one has already been killed
|
||||
return @_fetchOrStartStep(current, target, needsDownload, dependenciesMetForStart)
|
||||
else
|
||||
strategy = checkString(target.config.labels['io.resin.update.strategy'])
|
||||
strategy = checkString(target.config.labels['io.balena.update.strategy'])
|
||||
validStrategies = [ 'download-then-kill', 'kill-then-download', 'delete-then-download', 'hand-over' ]
|
||||
if !_.includes(validStrategies, strategy)
|
||||
strategy = 'download-then-kill'
|
||||
timeout = checkInt(target.config.labels['io.resin.update.handover-timeout'])
|
||||
timeout = checkInt(target.config.labels['io.balena.update.handover-timeout'])
|
||||
return @_strategySteps[strategy](current, target, needsDownload, dependenciesMetForStart, dependenciesMetForKill, needsSpecialKill, timeout)
|
||||
|
||||
_nextStepsForAppUpdate: (currentApp, targetApp, localMode, availableImages = [], downloading = []) =>
|
||||
@ -608,12 +608,12 @@ module.exports = class ApplicationManager extends EventEmitter
|
||||
currentApp ?= emptyApp
|
||||
if currentApp.services?.length == 1 and targetApp.services?.length == 1 and
|
||||
targetApp.services[0].serviceName == currentApp.services[0].serviceName and
|
||||
checkTruthy(currentApp.services[0].config.labels['io.resin.legacy-container'])
|
||||
checkTruthy(currentApp.services[0].config.labels['io.balena.legacy-container'])
|
||||
# This is a legacy preloaded app or container, so we didn't have things like serviceId.
|
||||
# We hack a few things to avoid an unnecessary restart of the preloaded app
|
||||
# (but ensuring it gets updated if it actually changed)
|
||||
targetApp.services[0].config.labels['io.resin.legacy-container'] = currentApp.services[0].labels['io.resin.legacy-container']
|
||||
targetApp.services[0].config.labels['io.resin.service-id'] = currentApp.services[0].labels['io.resin.service-id']
|
||||
targetApp.services[0].config.labels['io.balena.legacy-container'] = currentApp.services[0].labels['io.balena.legacy-container']
|
||||
targetApp.services[0].config.labels['io.balena.service-id'] = currentApp.services[0].labels['io.balena.service-id']
|
||||
targetApp.services[0].serviceId = currentApp.services[0].serviceId
|
||||
|
||||
appId = targetApp.appId ? currentApp.appId
|
||||
|
@ -313,4 +313,9 @@ module.exports = class Images extends EventEmitter
|
||||
isSameImage: @isSameImage
|
||||
|
||||
_getLocalModeImages: =>
|
||||
Promise.join(
|
||||
@docker.listImages(filters: label: [ 'io.resin.local.image=1' ])
|
||||
@docker.listImages(filters: label: [ 'io.balena.local.image=1' ])
|
||||
(legacyImages, currentImages) ->
|
||||
_.unionBy(legacyImages, currentImages, 'Id')
|
||||
)
|
||||
|
@ -1,5 +1,6 @@
|
||||
import * as Bluebird from 'bluebird';
|
||||
import { fs } from 'mz';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import * as constants from '../lib/constants';
|
||||
import Docker = require('../lib/docker-utils');
|
||||
@ -17,11 +18,7 @@ export class NetworkManager {
|
||||
}
|
||||
|
||||
public getAll(): Bluebird<Network[]> {
|
||||
return Bluebird.resolve(this.docker.listNetworks({
|
||||
filters: {
|
||||
label: [ 'io.resin.supervised' ],
|
||||
},
|
||||
}))
|
||||
return this.getWithBothLabels()
|
||||
.map((network: { Name: string }) => {
|
||||
return this.docker.getNetwork(network.Name).inspect()
|
||||
.then((net) => {
|
||||
@ -90,4 +87,22 @@ export class NetworkManager {
|
||||
});
|
||||
}
|
||||
|
||||
private getWithBothLabels() {
|
||||
return Bluebird.join(
|
||||
this.docker.listNetworks({
|
||||
filters: {
|
||||
label: [ 'io.resin.supervised' ],
|
||||
},
|
||||
}),
|
||||
this.docker.listNetworks({
|
||||
filters: {
|
||||
label: [ 'io.balena.supervised' ],
|
||||
},
|
||||
}),
|
||||
(legacyNetworks, currentNetworks) => {
|
||||
return _.unionBy(currentNetworks, legacyNetworks, 'Id');
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
import logTypes = require('../lib/log-types');
|
||||
import { checkInt } from '../lib/validation';
|
||||
import { Logger } from '../logger';
|
||||
import * as ComposeUtils from './utils';
|
||||
|
||||
import {
|
||||
DockerIPAMConfig,
|
||||
@ -83,7 +84,7 @@ export class Network {
|
||||
},
|
||||
enableIPv6: network.EnableIPv6,
|
||||
internal: network.Internal,
|
||||
labels: _.omit(network.Labels, [ 'io.resin.supervised' ]),
|
||||
labels: _.omit(ComposeUtils.normalizeLabels(network.Labels), [ 'io.balena.supervised' ]),
|
||||
options: network.Options,
|
||||
};
|
||||
|
||||
@ -125,6 +126,7 @@ export class Network {
|
||||
labels: { },
|
||||
options: { },
|
||||
});
|
||||
net.config.labels = ComposeUtils.normalizeLabels(net.config.labels);
|
||||
|
||||
return net;
|
||||
}
|
||||
@ -177,7 +179,7 @@ export class Network {
|
||||
EnableIPv6: this.config.enableIPv6,
|
||||
Internal: this.config.internal,
|
||||
Labels: _.merge({}, {
|
||||
'io.resin.supervised': 'true',
|
||||
'io.balena.supervised': 'true',
|
||||
}, this.config.labels),
|
||||
};
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ module.exports = class ServiceManager extends EventEmitter
|
||||
@logger.logSystemEvent(logTypes.removeDeadServiceError, { service, error: err })
|
||||
|
||||
getAllByAppId: (appId) =>
|
||||
@getAll("io.resin.app-id=#{appId}")
|
||||
@getAll("app-id=#{appId}")
|
||||
|
||||
stopAllByAppId: (appId) =>
|
||||
Promise.map @getAllByAppId(appId), (service) =>
|
||||
@ -174,10 +174,20 @@ module.exports = class ServiceManager extends EventEmitter
|
||||
.finally =>
|
||||
@reportChange(containerId)
|
||||
|
||||
_listWithBothLabels: (labelList) =>
|
||||
listWithPrefix = (prefix) =>
|
||||
@docker.listContainers({ all: true, filters: label: _.map(labelList, (v) -> prefix + v) })
|
||||
Promise.join(
|
||||
listWithPrefix('io.resin.')
|
||||
listWithPrefix('io.balena.')
|
||||
(legacyContainers, currentContainers) ->
|
||||
_.unionBy(legacyContainers, currentContainers, 'Id')
|
||||
)
|
||||
|
||||
# Gets all existing containers that correspond to apps
|
||||
getAll: (extraLabelFilters = []) =>
|
||||
filters = label: [ 'io.resin.supervised' ].concat(extraLabelFilters)
|
||||
@docker.listContainers({ all: true, filters })
|
||||
filterLabels = [ 'supervised' ].concat(extraLabelFilters)
|
||||
@_listWithBothLabels(filterLabels)
|
||||
.mapSeries (container) =>
|
||||
@docker.getContainer(container.Id).inspect()
|
||||
.then(Service.fromDockerContainer)
|
||||
@ -190,7 +200,7 @@ module.exports = class ServiceManager extends EventEmitter
|
||||
|
||||
# Returns the first container matching a service definition
|
||||
get: (service) =>
|
||||
@getAll("io.resin.service-id=#{service.serviceId}")
|
||||
@getAll("service-id=#{service.serviceId}")
|
||||
.filter((currentService) -> currentService.isEqualConfig(service))
|
||||
.then (services) ->
|
||||
if services.length == 0
|
||||
@ -218,7 +228,7 @@ module.exports = class ServiceManager extends EventEmitter
|
||||
getByDockerContainerId: (containerId) =>
|
||||
@docker.getContainer(containerId).inspect()
|
||||
.then (container) ->
|
||||
if !container.Config.Labels['io.resin.supervised']?
|
||||
if !(container.Config.Labels['io.balena.supervised']? or container.Config.Labels['io.resin.supervised']?)
|
||||
return null
|
||||
return Service.fromDockerContainer(container)
|
||||
|
||||
@ -267,7 +277,7 @@ module.exports = class ServiceManager extends EventEmitter
|
||||
.then =>
|
||||
@start(targetService)
|
||||
.then =>
|
||||
@waitToKill(currentService, targetService.config.labels['io.resin.update.handover-timeout'])
|
||||
@waitToKill(currentService, targetService.config.labels['io.balena.update.handover-timeout'])
|
||||
.then =>
|
||||
@kill(currentService)
|
||||
|
||||
|
@ -201,13 +201,13 @@ export class Service {
|
||||
service.appId || 0,
|
||||
service.serviceName || '',
|
||||
);
|
||||
config.labels = Service.extendLabels(
|
||||
config.labels = ComposeUtils.normalizeLabels(Service.extendLabels(
|
||||
config.labels || { },
|
||||
options,
|
||||
service.appId || 0,
|
||||
service.serviceId || 0,
|
||||
service.serviceName || '',
|
||||
);
|
||||
));
|
||||
|
||||
// Any other special case handling
|
||||
if (config.networkMode === 'host' && !config.hostname) {
|
||||
@ -427,7 +427,7 @@ export class Service {
|
||||
image: container.Config.Image,
|
||||
environment: conversions.envArrayToObject(container.Config.Env || [ ]),
|
||||
privileged: container.HostConfig.Privileged || false,
|
||||
labels: container.Config.Labels || { },
|
||||
labels: ComposeUtils.normalizeLabels(container.Config.Labels || { }),
|
||||
running: container.State.Running,
|
||||
restart,
|
||||
capAdd: container.HostConfig.CapAdd || [ ],
|
||||
@ -471,9 +471,9 @@ export class Service {
|
||||
tty: container.Config.Tty || false,
|
||||
};
|
||||
|
||||
svc.appId = checkInt(container.Config.Labels['io.resin.app-id']) || null;
|
||||
svc.serviceId = checkInt(container.Config.Labels['io.resin.service-id']) || null;
|
||||
svc.serviceName = container.Config.Labels['io.resin.service-name'];
|
||||
svc.appId = checkInt(svc.config.labels['io.balena.app-id']) || null;
|
||||
svc.serviceId = checkInt(svc.config.labels['io.balena.service-id']) || null;
|
||||
svc.serviceName = svc.config.labels['io.balena.service-name'];
|
||||
const nameMatch = container.Name.match(/.*_(\d+)_(\d+)$/);
|
||||
|
||||
svc.imageId = nameMatch != null ? checkInt(nameMatch[1]) || null : null;
|
||||
@ -788,10 +788,10 @@ export class Service {
|
||||
serviceName: string,
|
||||
): { [labelName: string]: string } {
|
||||
let newLabels = _.defaults(labels, {
|
||||
'io.resin.supervised': 'true',
|
||||
'io.resin.app-id': appId.toString(),
|
||||
'io.resin.service-id': serviceId.toString(),
|
||||
'io.resin.service-name': serviceName,
|
||||
'io.balena.supervised': 'true',
|
||||
'io.balena.app-id': appId.toString(),
|
||||
'io.balena.service-id': serviceId.toString(),
|
||||
'io.balena.service-name': serviceName,
|
||||
});
|
||||
|
||||
const imageLabels = _.get(imageInfo, 'Config.Labels', { });
|
||||
|
@ -300,45 +300,48 @@ export function addFeaturesFromLabels(
|
||||
service: Service,
|
||||
options: DeviceMetadata,
|
||||
): void {
|
||||
if (checkTruthy(service.config.labels['io.resin.features.dbus'])) {
|
||||
const setEnvVariables = function (key: string, val: string) {
|
||||
service.config.environment[`RESIN_${key}`] = val;
|
||||
service.config.environment[`BALENA_${key}`] = val;
|
||||
};
|
||||
if (checkTruthy(service.config.labels['io.balena.features.dbus'])) {
|
||||
service.config.volumes.push('/run/dbus:/host/run/dbus');
|
||||
}
|
||||
|
||||
if (
|
||||
checkTruthy(service.config.labels['io.resin.features.kernel-modules']) &&
|
||||
checkTruthy(service.config.labels['io.balena.features.kernel-modules']) &&
|
||||
options.hostPathExists.modules
|
||||
) {
|
||||
service.config.volumes.push('/lib/modules:/lib/modules');
|
||||
}
|
||||
|
||||
if (
|
||||
checkTruthy(service.config.labels['io.resin.features.firmware']) &&
|
||||
checkTruthy(service.config.labels['io.balena.features.firmware']) &&
|
||||
options.hostPathExists.firmware
|
||||
) {
|
||||
service.config.volumes.push('/lib/firmware:/lib/firmware');
|
||||
}
|
||||
|
||||
if (checkTruthy(service.config.labels['io.resin.features.balena-socket'])) {
|
||||
if (checkTruthy(service.config.labels['io.balena.features.balena-socket'])) {
|
||||
service.config.volumes.push('/var/run/balena.sock:/var/run/balena.sock');
|
||||
if (service.config.environment['DOCKER_HOST'] == null) {
|
||||
service.config.environment['DOCKER_HOST'] = 'unix:///var/run/balena.sock';
|
||||
}
|
||||
}
|
||||
|
||||
if (checkTruthy('io.resin.features.resin-api')) {
|
||||
service.config.environment['RESIN_API_KEY'] = options.deviceApiKey;
|
||||
if (checkTruthy('io.balena.features.balena-api')) {
|
||||
setEnvVariables('API_KEY', options.deviceApiKey);
|
||||
}
|
||||
|
||||
if (checkTruthy(service.config.labels['io.resin.features.supervisor-api'])) {
|
||||
service.config.environment['RESIN_SUPERVISOR_PORT'] = options.listenPort.toString();
|
||||
service.config.environment['RESIN_SUPERVISOR_API_KEY'] = options.apiSecret;
|
||||
if (checkTruthy(service.config.labels['io.balena.features.supervisor-api'])) {
|
||||
setEnvVariables('SUPERVISOR_PORT', options.listenPort.toString());
|
||||
setEnvVariables('SUPERVISOR_API_KEY', options.apiSecret);
|
||||
if (service.config.networkMode === 'host') {
|
||||
service.config.environment['RESIN_SUPERVISOR_HOST'] = '127.0.0.1';
|
||||
service.config.environment['RESIN_SUPERVISOR_ADDRESS'] = `http://127.0.0.1:${options.listenPort}`;
|
||||
setEnvVariables('SUPERVISOR_HOST', '127.0.0.1');
|
||||
setEnvVariables('SUPERVISOR_ADDRESS', `http://127.0.0.1:${options.listenPort}`);
|
||||
} else {
|
||||
service.config.environment['RESIN_SUPERVISOR_HOST'] = options.supervisorApiHost;
|
||||
service.config.environment['RESIN_SUPERVISOR_ADDRESS'] =
|
||||
`http://${options.supervisorApiHost}:${options.listenPort}`;
|
||||
setEnvVariables('SUPERVISOR_HOST', options.supervisorApiHost);
|
||||
setEnvVariables('SUPERVISOR_ADDRESS', `http://${options.supervisorApiHost}:${options.listenPort}`);
|
||||
service.config.networks[constants.supervisorNetworkInterface] = { };
|
||||
}
|
||||
} else {
|
||||
@ -442,3 +445,14 @@ export function normalizeNullValues(obj: Dictionary<any>): void {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function normalizeLabels(
|
||||
labels: { [key: string]: string },
|
||||
): { [key: string]: string } {
|
||||
const legacyLabels = _.mapKeys(_.pickBy(labels, (_v, k) => _.startsWith(k, 'io.resin.')), (_v, k) => {
|
||||
return k.replace(/resin/g, 'balena'); // e.g. io.resin.features.resin-api -> io.balena.features.balena-api
|
||||
});
|
||||
const balenaLabels = _.pickBy(labels, (_v, k) => _.startsWith(k, 'io.balena.'));
|
||||
const otherLabels = _.pickBy(labels, (_v, k) => !(_.startsWith(k, 'io.balena.') || _.startsWith(k, 'io.resin.')));
|
||||
return _.assign({}, otherLabels, legacyLabels, balenaLabels);
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ constants = require '../lib/constants'
|
||||
{ NotFoundError } = require '../lib/errors'
|
||||
{ defaultLegacyVolume } = require '../lib/migration'
|
||||
{ safeRename } = require '../lib/fs-utils'
|
||||
ComposeUtils = require './utils'
|
||||
|
||||
module.exports = class Volumes
|
||||
constructor: ({ @docker, @logger }) ->
|
||||
@ -20,17 +21,25 @@ module.exports = class Volumes
|
||||
name: name
|
||||
appId: appId
|
||||
config: {
|
||||
labels: _.omit(volume.Labels, _.keys(constants.defaultVolumeLabels))
|
||||
labels: _.omit(ComposeUtils.normalizeLabels(volume.Labels), _.keys(constants.defaultVolumeLabels))
|
||||
driverOpts: volume.Options
|
||||
}
|
||||
handle: volume
|
||||
}
|
||||
|
||||
getAll: =>
|
||||
_listWithBothLabels: =>
|
||||
Promise.join(
|
||||
@docker.listVolumes(filters: label: [ 'io.resin.supervised' ])
|
||||
.then (response) =>
|
||||
volumes = response.Volumes ? []
|
||||
Promise.map volumes, (volume) =>
|
||||
@docker.listVolumes(filters: label: [ 'io.balena.supervised' ])
|
||||
(legacyVolumesResponse, currentVolumesResponse) ->
|
||||
legacyVolumes = legacyVolumesResponse.Volumes ? []
|
||||
currentVolumes = currentVolumesResponse.Volumes ? []
|
||||
return _.unionBy(legacyVolumes, currentVolumes, 'Name')
|
||||
)
|
||||
|
||||
getAll: =>
|
||||
@_listWithBothLabels()
|
||||
.map (volume) =>
|
||||
@docker.getVolume(volume.Name).inspect()
|
||||
.then(@format)
|
||||
|
||||
|
@ -34,7 +34,7 @@ const constants = {
|
||||
imageCleanupErrorIgnoreTimeout: 3600 * 1000,
|
||||
maxDeltaDownloads: 3,
|
||||
defaultVolumeLabels: {
|
||||
'io.resin.supervised': 'true',
|
||||
'io.balena.supervised': 'true',
|
||||
},
|
||||
bootBlockDevice: '/dev/mmcblk0p1',
|
||||
hostConfigVarPrefix: 'RESIN_HOST_',
|
||||
|
@ -3,7 +3,7 @@ m = require 'mochainon'
|
||||
|
||||
{ Network } = require '../src/compose/network'
|
||||
|
||||
describe 'compose/network.coffee', ->
|
||||
describe 'compose/network', ->
|
||||
|
||||
describe 'compose config -> internal config', ->
|
||||
|
||||
@ -70,6 +70,6 @@ describe 'compose/network.coffee', ->
|
||||
EnableIPv6: false,
|
||||
Internal: false,
|
||||
Labels: {
|
||||
'io.resin.supervised': 'true'
|
||||
'io.balena.supervised': 'true'
|
||||
}
|
||||
})
|
Loading…
Reference in New Issue
Block a user