mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-05-05 10:32:56 +00:00
Merge pull request #1351 from balena-io/singleton-docker
Make docker module a singleton
This commit is contained in:
commit
96e55585ea
2
src/application-manager.d.ts
vendored
2
src/application-manager.d.ts
vendored
@ -25,7 +25,6 @@ import {
|
||||
import Network from './compose/network';
|
||||
import Service from './compose/service';
|
||||
import Volume from './compose/volume';
|
||||
import DockerUtils from './lib/docker-utils';
|
||||
|
||||
declare interface Options {
|
||||
force?: boolean;
|
||||
@ -52,7 +51,6 @@ class ApplicationManager extends EventEmitter {
|
||||
public deviceState: DeviceState;
|
||||
public eventTracker: EventTracker;
|
||||
public apiBinder: APIBinder;
|
||||
public docker: DockerUtils;
|
||||
|
||||
public services: ServiceManager;
|
||||
public volumes: VolumeManager;
|
||||
|
@ -11,7 +11,8 @@ import { log } from './lib/supervisor-console';
|
||||
import * as config from './config';
|
||||
|
||||
import { validateTargetContracts } from './lib/contracts';
|
||||
import { DockerUtils as Docker } from './lib/docker-utils';
|
||||
import { docker } from './lib/docker-utils';
|
||||
import * as dockerUtils from './lib/docker-utils';
|
||||
import { LocalModeManager } from './local-mode';
|
||||
import * as updateLock from './lib/update-lock';
|
||||
import { checkTruthy, checkInt, checkString } from './lib/validation';
|
||||
@ -172,30 +173,24 @@ export class ApplicationManager extends EventEmitter {
|
||||
this.eventTracker = eventTracker;
|
||||
this.deviceState = deviceState;
|
||||
this.apiBinder = apiBinder;
|
||||
this.docker = new Docker();
|
||||
this.images = new Images({
|
||||
docker: this.docker,
|
||||
logger: this.logger,
|
||||
});
|
||||
this.services = new ServiceManager({
|
||||
docker: this.docker,
|
||||
logger: this.logger,
|
||||
});
|
||||
this.networks = new NetworkManager({
|
||||
docker: this.docker,
|
||||
logger: this.logger,
|
||||
});
|
||||
this.volumes = new VolumeManager({
|
||||
docker: this.docker,
|
||||
logger: this.logger,
|
||||
});
|
||||
this.proxyvisor = new Proxyvisor({
|
||||
logger: this.logger,
|
||||
docker: this.docker,
|
||||
images: this.images,
|
||||
applications: this,
|
||||
});
|
||||
this.localModeManager = new LocalModeManager(this.docker, this.logger);
|
||||
this.localModeManager = new LocalModeManager(this.logger);
|
||||
this.timeSpentFetching = 0;
|
||||
this.fetchesInProgress = 0;
|
||||
this._targetVolatilePerImageId = {};
|
||||
@ -257,9 +252,7 @@ export class ApplicationManager extends EventEmitter {
|
||||
})
|
||||
.then(() => {
|
||||
const cleanup = () => {
|
||||
return this.docker
|
||||
.listContainers({ all: true })
|
||||
.then((containers) => {
|
||||
return docker.listContainers({ all: true }).then((containers) => {
|
||||
return this.logger.clearOutOfDateDBLogs(_.map(containers, 'Id'));
|
||||
});
|
||||
};
|
||||
@ -1061,14 +1054,12 @@ export class ApplicationManager extends EventEmitter {
|
||||
|
||||
createTargetVolume(name, appId, volume) {
|
||||
return Volume.fromComposeObject(name, appId, volume, {
|
||||
docker: this.docker,
|
||||
logger: this.logger,
|
||||
});
|
||||
}
|
||||
|
||||
createTargetNetwork(name, appId, network) {
|
||||
return Network.fromComposeObject(name, appId, network, {
|
||||
docker: this.docker,
|
||||
logger: this.logger,
|
||||
});
|
||||
}
|
||||
@ -1076,7 +1067,7 @@ export class ApplicationManager extends EventEmitter {
|
||||
normaliseAndExtendAppFromDB(app) {
|
||||
return Promise.join(
|
||||
config.get('extendedEnvOptions'),
|
||||
this.docker
|
||||
dockerUtils
|
||||
.getNetworkGateway(constants.supervisorNetworkInterface)
|
||||
.catch(() => '127.0.0.1'),
|
||||
Promise.props({
|
||||
|
@ -8,9 +8,11 @@ import * as db from '../db';
|
||||
import * as constants from '../lib/constants';
|
||||
import {
|
||||
DeltaFetchOptions,
|
||||
DockerUtils,
|
||||
FetchOptions,
|
||||
docker,
|
||||
dockerToolbelt,
|
||||
} from '../lib/docker-utils';
|
||||
import * as dockerUtils from '../lib/docker-utils';
|
||||
import { DeltaStillProcessingError, NotFoundError } from '../lib/errors';
|
||||
import * as LogTypes from '../lib/log-types';
|
||||
import * as validation from '../lib/validation';
|
||||
@ -26,7 +28,6 @@ interface ImageEvents {
|
||||
type ImageEventEmitter = StrictEventEmitter<EventEmitter, ImageEvents>;
|
||||
|
||||
interface ImageConstructOpts {
|
||||
docker: DockerUtils;
|
||||
logger: Logger;
|
||||
}
|
||||
|
||||
@ -56,7 +57,6 @@ type NormalisedDockerImage = Docker.ImageInfo & {
|
||||
};
|
||||
|
||||
export class Images extends (EventEmitter as new () => ImageEventEmitter) {
|
||||
private docker: DockerUtils;
|
||||
private logger: Logger;
|
||||
|
||||
public appUpdatePollInterval: number;
|
||||
@ -72,7 +72,6 @@ export class Images extends (EventEmitter as new () => ImageEventEmitter) {
|
||||
public constructor(opts: ImageConstructOpts) {
|
||||
super();
|
||||
|
||||
this.docker = opts.docker;
|
||||
this.logger = opts.logger;
|
||||
}
|
||||
|
||||
@ -202,7 +201,7 @@ export class Images extends (EventEmitter as new () => ImageEventEmitter) {
|
||||
cb: (dockerImages: NormalisedDockerImage[], composeImages: Image[]) => T,
|
||||
) {
|
||||
const [normalisedImages, dbImages] = await Promise.all([
|
||||
Bluebird.map(this.docker.listImages({ digests: true }), async (image) => {
|
||||
Bluebird.map(docker.listImages({ digests: true }), async (image) => {
|
||||
const newImage = _.clone(image) as NormalisedDockerImage;
|
||||
newImage.NormalisedRepoTags = await this.getNormalisedTags(image);
|
||||
return newImage;
|
||||
@ -337,8 +336,8 @@ export class Images extends (EventEmitter as new () => ImageEventEmitter) {
|
||||
supervisorImage,
|
||||
usedImageIds,
|
||||
] = await Promise.all([
|
||||
this.docker.getRegistryAndName(constants.supervisorImage),
|
||||
this.docker.getImage(constants.supervisorImage).inspect(),
|
||||
dockerToolbelt.getRegistryAndName(constants.supervisorImage),
|
||||
docker.getImage(constants.supervisorImage).inspect(),
|
||||
db
|
||||
.models('image')
|
||||
.select('dockerImageId')
|
||||
@ -366,7 +365,7 @@ export class Images extends (EventEmitter as new () => ImageEventEmitter) {
|
||||
);
|
||||
};
|
||||
|
||||
const dockerImages = await this.docker.listImages({ digests: true });
|
||||
const dockerImages = await docker.listImages({ digests: true });
|
||||
for (const image of dockerImages) {
|
||||
// Cleanup should remove truly dangling images (i.e dangling and with no digests)
|
||||
if (Images.isDangling(image) && !_.includes(usedImageIds, image.Id)) {
|
||||
@ -377,7 +376,9 @@ export class Images extends (EventEmitter as new () => ImageEventEmitter) {
|
||||
) {
|
||||
// We also remove images from the supervisor repository with a different tag
|
||||
for (const tag of image.RepoTags) {
|
||||
const imageNameComponents = await this.docker.getRegistryAndName(tag);
|
||||
const imageNameComponents = await dockerToolbelt.getRegistryAndName(
|
||||
tag,
|
||||
);
|
||||
if (isSupervisorRepoTag(imageNameComponents)) {
|
||||
images.push(image.Id);
|
||||
}
|
||||
@ -400,7 +401,7 @@ export class Images extends (EventEmitter as new () => ImageEventEmitter) {
|
||||
imageName: string,
|
||||
): Promise<Docker.ImageInspectInfo> {
|
||||
try {
|
||||
return await this.docker.getImage(imageName).inspect();
|
||||
return await docker.getImage(imageName).inspect();
|
||||
} catch (e) {
|
||||
if (NotFoundError(e)) {
|
||||
const digest = imageName.split('@')[1];
|
||||
@ -418,7 +419,7 @@ export class Images extends (EventEmitter as new () => ImageEventEmitter) {
|
||||
|
||||
for (const image of imagesFromDb) {
|
||||
if (image.dockerImageId != null) {
|
||||
return await this.docker.getImage(image.dockerImageId).inspect();
|
||||
return await docker.getImage(image.dockerImageId).inspect();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -435,7 +436,7 @@ export class Images extends (EventEmitter as new () => ImageEventEmitter) {
|
||||
for (const image of images) {
|
||||
log.debug(`Cleaning up ${image}`);
|
||||
try {
|
||||
await this.docker.getImage(image).remove({ force: true });
|
||||
await docker.getImage(image).remove({ force: true });
|
||||
delete this.imageCleanupFailures[image];
|
||||
} catch (e) {
|
||||
this.logger.logSystemMessage(
|
||||
@ -459,7 +460,7 @@ export class Images extends (EventEmitter as new () => ImageEventEmitter) {
|
||||
}
|
||||
|
||||
public normalise(imageName: string): Bluebird<string> {
|
||||
return this.docker.normaliseImageName(imageName);
|
||||
return dockerToolbelt.normaliseImageName(imageName);
|
||||
}
|
||||
|
||||
private static isDangling(image: Docker.ImageInfo): boolean {
|
||||
@ -496,7 +497,7 @@ export class Images extends (EventEmitter as new () => ImageEventEmitter) {
|
||||
if (img.dockerImageId == null) {
|
||||
// Legacy image from before we started using dockerImageId, so we try to remove it
|
||||
// by name
|
||||
await this.docker.getImage(img.name).remove({ force: true });
|
||||
await docker.getImage(img.name).remove({ force: true });
|
||||
removed = true;
|
||||
} else {
|
||||
const imagesFromDb = await db
|
||||
@ -512,11 +513,11 @@ export class Images extends (EventEmitter as new () => ImageEventEmitter) {
|
||||
_.merge(_.clone(image), { status: 'Deleting' }),
|
||||
);
|
||||
this.logger.logSystemEvent(LogTypes.deleteImage, { image });
|
||||
this.docker.getImage(img.dockerImageId).remove({ force: true });
|
||||
docker.getImage(img.dockerImageId).remove({ force: true });
|
||||
removed = true;
|
||||
} else if (!Images.hasDigest(img.name)) {
|
||||
// Image has a regular tag, so we might have to remove unnecessary tags
|
||||
const dockerImage = await this.docker
|
||||
const dockerImage = await docker
|
||||
.getImage(img.dockerImageId)
|
||||
.inspect();
|
||||
const differentTags = _.reject(imagesFromDb, { name: img.name });
|
||||
@ -528,7 +529,7 @@ export class Images extends (EventEmitter as new () => ImageEventEmitter) {
|
||||
_.some(differentTags, { name: t }),
|
||||
)
|
||||
) {
|
||||
await this.docker.getImage(img.name).remove({ noprune: true });
|
||||
await docker.getImage(img.name).remove({ noprune: true });
|
||||
}
|
||||
removed = false;
|
||||
} else {
|
||||
@ -589,7 +590,7 @@ export class Images extends (EventEmitter as new () => ImageEventEmitter) {
|
||||
const srcImage = await this.inspectByName(deltaOpts.deltaSource);
|
||||
|
||||
deltaOpts.deltaSourceId = srcImage.Id;
|
||||
const id = await this.docker.fetchDeltaWithProgress(
|
||||
const id = await dockerUtils.fetchDeltaWithProgress(
|
||||
image.name,
|
||||
deltaOpts,
|
||||
onProgress,
|
||||
@ -597,8 +598,8 @@ export class Images extends (EventEmitter as new () => ImageEventEmitter) {
|
||||
);
|
||||
|
||||
if (!Images.hasDigest(image.name)) {
|
||||
const { repo, tag } = await this.docker.getRepoAndTag(image.name);
|
||||
await this.docker.getImage(id).tag({ repo, tag });
|
||||
const { repo, tag } = await dockerUtils.getRepoAndTag(image.name);
|
||||
await docker.getImage(id).tag({ repo, tag });
|
||||
}
|
||||
|
||||
return id;
|
||||
@ -610,7 +611,7 @@ export class Images extends (EventEmitter as new () => ImageEventEmitter) {
|
||||
onProgress: (evt: FetchProgressEvent) => void,
|
||||
): Promise<string> {
|
||||
this.logger.logSystemEvent(LogTypes.downloadImage, { image });
|
||||
return this.docker.fetchImageWithProgress(image.name, opts, onProgress);
|
||||
return dockerUtils.fetchImageWithProgress(image.name, opts, onProgress);
|
||||
}
|
||||
|
||||
// TODO: find out if imageId can actually be null
|
||||
|
@ -3,7 +3,7 @@ import * as _ from 'lodash';
|
||||
import { fs } from 'mz';
|
||||
|
||||
import * as constants from '../lib/constants';
|
||||
import Docker from '../lib/docker-utils';
|
||||
import { docker } from '../lib/docker-utils';
|
||||
import { ENOENT, NotFoundError } from '../lib/errors';
|
||||
import logTypes = require('../lib/log-types');
|
||||
import { Logger } from '../logger';
|
||||
@ -13,23 +13,20 @@ import log from '../lib/supervisor-console';
|
||||
import { ResourceRecreationAttemptError } from './errors';
|
||||
|
||||
export class NetworkManager {
|
||||
private docker: Docker;
|
||||
private logger: Logger;
|
||||
|
||||
constructor(opts: NetworkOptions) {
|
||||
this.docker = opts.docker;
|
||||
this.logger = opts.logger;
|
||||
}
|
||||
|
||||
public getAll(): Bluebird<Network[]> {
|
||||
return this.getWithBothLabels().map((network: { Name: string }) => {
|
||||
return this.docker
|
||||
return docker
|
||||
.getNetwork(network.Name)
|
||||
.inspect()
|
||||
.then((net) => {
|
||||
return Network.fromDockerNetwork(
|
||||
{
|
||||
docker: this.docker,
|
||||
logger: this.logger,
|
||||
},
|
||||
net,
|
||||
@ -43,13 +40,10 @@ export class NetworkManager {
|
||||
}
|
||||
|
||||
public async get(network: { name: string; appId: number }): Promise<Network> {
|
||||
const dockerNet = await this.docker
|
||||
const dockerNet = await docker
|
||||
.getNetwork(Network.generateDockerName(network.appId, network.name))
|
||||
.inspect();
|
||||
return Network.fromDockerNetwork(
|
||||
{ docker: this.docker, logger: this.logger },
|
||||
dockerNet,
|
||||
);
|
||||
return Network.fromDockerNetwork({ logger: this.logger }, dockerNet);
|
||||
}
|
||||
|
||||
public async create(network: Network) {
|
||||
@ -89,7 +83,7 @@ export class NetworkManager {
|
||||
fs.stat(`/sys/class/net/${constants.supervisorNetworkInterface}`),
|
||||
)
|
||||
.then(() => {
|
||||
return this.docker
|
||||
return docker
|
||||
.getNetwork(constants.supervisorNetworkInterface)
|
||||
.inspect();
|
||||
})
|
||||
@ -108,16 +102,16 @@ export class NetworkManager {
|
||||
public ensureSupervisorNetwork(): Bluebird<void> {
|
||||
const removeIt = () => {
|
||||
return Bluebird.resolve(
|
||||
this.docker.getNetwork(constants.supervisorNetworkInterface).remove(),
|
||||
docker.getNetwork(constants.supervisorNetworkInterface).remove(),
|
||||
).then(() => {
|
||||
return this.docker
|
||||
return docker
|
||||
.getNetwork(constants.supervisorNetworkInterface)
|
||||
.inspect();
|
||||
});
|
||||
};
|
||||
|
||||
return Bluebird.resolve(
|
||||
this.docker.getNetwork(constants.supervisorNetworkInterface).inspect(),
|
||||
docker.getNetwork(constants.supervisorNetworkInterface).inspect(),
|
||||
)
|
||||
.then((net) => {
|
||||
if (
|
||||
@ -138,7 +132,7 @@ export class NetworkManager {
|
||||
.catch(NotFoundError, () => {
|
||||
log.debug(`Creating ${constants.supervisorNetworkInterface} network`);
|
||||
return Bluebird.resolve(
|
||||
this.docker.createNetwork({
|
||||
docker.createNetwork({
|
||||
Name: constants.supervisorNetworkInterface,
|
||||
Options: {
|
||||
'com.docker.network.bridge.name':
|
||||
@ -160,12 +154,12 @@ export class NetworkManager {
|
||||
|
||||
private getWithBothLabels() {
|
||||
return Bluebird.join(
|
||||
this.docker.listNetworks({
|
||||
docker.listNetworks({
|
||||
filters: {
|
||||
label: ['io.resin.supervised'],
|
||||
},
|
||||
}),
|
||||
this.docker.listNetworks({
|
||||
docker.listNetworks({
|
||||
filters: {
|
||||
label: ['io.balena.supervised'],
|
||||
},
|
||||
|
@ -1,7 +1,7 @@
|
||||
import * as Bluebird from 'bluebird';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import Docker from '../lib/docker-utils';
|
||||
import { docker } from '../lib/docker-utils';
|
||||
import { InvalidAppIdError } from '../lib/errors';
|
||||
import logTypes = require('../lib/log-types');
|
||||
import { checkInt } from '../lib/validation';
|
||||
@ -22,7 +22,6 @@ import {
|
||||
} from './errors';
|
||||
|
||||
export interface NetworkOptions {
|
||||
docker: Docker;
|
||||
logger: Logger;
|
||||
}
|
||||
|
||||
@ -31,11 +30,9 @@ export class Network {
|
||||
public name: string;
|
||||
public config: NetworkConfig;
|
||||
|
||||
private docker: Docker;
|
||||
private logger: Logger;
|
||||
|
||||
private constructor(opts: NetworkOptions) {
|
||||
this.docker = opts.docker;
|
||||
this.logger = opts.logger;
|
||||
}
|
||||
|
||||
@ -145,7 +142,7 @@ export class Network {
|
||||
network: { name: this.name },
|
||||
});
|
||||
|
||||
return await this.docker.createNetwork(this.toDockerConfig());
|
||||
return await docker.createNetwork(this.toDockerConfig());
|
||||
}
|
||||
|
||||
public toDockerConfig(): DockerNetworkConfig {
|
||||
@ -191,7 +188,7 @@ export class Network {
|
||||
});
|
||||
|
||||
return Bluebird.resolve(
|
||||
this.docker
|
||||
docker
|
||||
.getNetwork(Network.generateDockerName(this.appId, this.name))
|
||||
.remove(),
|
||||
).tapCatch((error) => {
|
||||
|
@ -8,7 +8,7 @@ import { fs } from 'mz';
|
||||
import StrictEventEmitter from 'strict-event-emitter-types';
|
||||
|
||||
import * as config from '../config';
|
||||
import Docker from '../lib/docker-utils';
|
||||
import { docker } from '../lib/docker-utils';
|
||||
import Logger from '../logger';
|
||||
|
||||
import { PermissiveNumber } from '../config/types';
|
||||
@ -26,7 +26,6 @@ import { serviceNetworksToDockerNetworks } from './utils';
|
||||
import log from '../lib/supervisor-console';
|
||||
|
||||
interface ServiceConstructOpts {
|
||||
docker: Docker;
|
||||
logger: Logger;
|
||||
}
|
||||
|
||||
@ -44,7 +43,6 @@ interface KillOpts {
|
||||
}
|
||||
|
||||
export class ServiceManager extends (EventEmitter as new () => ServiceManagerEventEmitter) {
|
||||
private docker: Docker;
|
||||
private logger: Logger;
|
||||
|
||||
// Whether a container has died, indexed by ID
|
||||
@ -56,7 +54,6 @@ export class ServiceManager extends (EventEmitter as new () => ServiceManagerEve
|
||||
|
||||
public constructor(opts: ServiceConstructOpts) {
|
||||
super();
|
||||
this.docker = opts.docker;
|
||||
this.logger = opts.logger;
|
||||
}
|
||||
|
||||
@ -68,7 +65,7 @@ export class ServiceManager extends (EventEmitter as new () => ServiceManagerEve
|
||||
|
||||
const services = await Bluebird.map(containers, async (container) => {
|
||||
try {
|
||||
const serviceInspect = await this.docker
|
||||
const serviceInspect = await docker
|
||||
.getContainer(container.Id)
|
||||
.inspect();
|
||||
const service = Service.fromDockerContainer(serviceInspect);
|
||||
@ -138,7 +135,7 @@ export class ServiceManager extends (EventEmitter as new () => ServiceManagerEve
|
||||
public async getByDockerContainerId(
|
||||
containerId: string,
|
||||
): Promise<Service | null> {
|
||||
const container = await this.docker.getContainer(containerId).inspect();
|
||||
const container = await docker.getContainer(containerId).inspect();
|
||||
if (
|
||||
container.Config.Labels['io.balena.supervised'] == null &&
|
||||
container.Config.Labels['io.resin.supervised'] == null
|
||||
@ -159,7 +156,7 @@ export class ServiceManager extends (EventEmitter as new () => ServiceManagerEve
|
||||
);
|
||||
}
|
||||
|
||||
await this.docker.getContainer(svc.containerId).rename({
|
||||
await docker.getContainer(svc.containerId).rename({
|
||||
name: `${service.serviceName}_${metadata.imageId}_${metadata.releaseId}`,
|
||||
});
|
||||
}
|
||||
@ -180,10 +177,10 @@ export class ServiceManager extends (EventEmitter as new () => ServiceManagerEve
|
||||
// Containers haven't been normalized (this is an updated supervisor)
|
||||
// so we need to stop and remove them
|
||||
const supervisorImageId = (
|
||||
await this.docker.getImage(constants.supervisorImage).inspect()
|
||||
await docker.getImage(constants.supervisorImage).inspect()
|
||||
).Id;
|
||||
|
||||
for (const container of await this.docker.listContainers({ all: true })) {
|
||||
for (const container of await docker.listContainers({ all: true })) {
|
||||
if (container.ImageID !== supervisorImageId) {
|
||||
await this.killContainer(container.Id, {
|
||||
serviceName: 'legacy',
|
||||
@ -212,7 +209,7 @@ export class ServiceManager extends (EventEmitter as new () => ServiceManagerEve
|
||||
}
|
||||
|
||||
try {
|
||||
await this.docker
|
||||
await docker
|
||||
.getContainer(existingService.containerId)
|
||||
.remove({ v: true });
|
||||
} catch (e) {
|
||||
@ -244,7 +241,7 @@ export class ServiceManager extends (EventEmitter as new () => ServiceManagerEve
|
||||
`No containerId provided for service ${service.serviceName} in ServiceManager.updateMetadata. Service: ${service}`,
|
||||
);
|
||||
}
|
||||
return this.docker.getContainer(existing.containerId);
|
||||
return docker.getContainer(existing.containerId);
|
||||
} catch (e) {
|
||||
if (!NotFoundError(e)) {
|
||||
this.logger.logSystemEvent(LogTypes.installServiceError, {
|
||||
@ -280,12 +277,12 @@ export class ServiceManager extends (EventEmitter as new () => ServiceManagerEve
|
||||
this.logger.logSystemEvent(LogTypes.installService, { service });
|
||||
this.reportNewStatus(mockContainerId, service, 'Installing');
|
||||
|
||||
const container = await this.docker.createContainer(conf);
|
||||
const container = await docker.createContainer(conf);
|
||||
service.containerId = container.id;
|
||||
|
||||
await Promise.all(
|
||||
_.map((nets || {}).EndpointsConfig, (endpointConfig, name) =>
|
||||
this.docker.getNetwork(name).connect({
|
||||
docker.getNetwork(name).connect({
|
||||
Container: container.id,
|
||||
EndpointConfig: endpointConfig,
|
||||
}),
|
||||
@ -366,7 +363,7 @@ export class ServiceManager extends (EventEmitter as new () => ServiceManagerEve
|
||||
);
|
||||
}
|
||||
|
||||
this.logger.attach(this.docker, container.id, { serviceId, imageId });
|
||||
this.logger.attach(container.id, { serviceId, imageId });
|
||||
|
||||
if (!alreadyStarted) {
|
||||
this.logger.logSystemEvent(LogTypes.startServiceSuccess, { service });
|
||||
@ -389,10 +386,7 @@ export class ServiceManager extends (EventEmitter as new () => ServiceManagerEve
|
||||
this.listening = true;
|
||||
|
||||
const listen = async () => {
|
||||
const stream = await this.docker.getEvents({
|
||||
// Remove the as any once
|
||||
// https://github.com/DefinitelyTyped/DefinitelyTyped/pull/43100
|
||||
// is merged and released
|
||||
const stream = await docker.getEvents({
|
||||
filters: { type: ['container'] } as any,
|
||||
});
|
||||
|
||||
@ -434,7 +428,7 @@ export class ServiceManager extends (EventEmitter as new () => ServiceManagerEve
|
||||
`serviceId and imageId not defined for service: ${service.serviceName} in ServiceManager.listenToEvents`,
|
||||
);
|
||||
}
|
||||
this.logger.attach(this.docker, data.id, {
|
||||
this.logger.attach(data.id, {
|
||||
serviceId,
|
||||
imageId,
|
||||
});
|
||||
@ -487,7 +481,7 @@ export class ServiceManager extends (EventEmitter as new () => ServiceManagerEve
|
||||
`containerId not defined for service: ${service.serviceName} in ServiceManager.attachToRunning`,
|
||||
);
|
||||
}
|
||||
this.logger.attach(this.docker, service.containerId, {
|
||||
this.logger.attach(service.containerId, {
|
||||
serviceId,
|
||||
imageId,
|
||||
});
|
||||
@ -544,7 +538,7 @@ export class ServiceManager extends (EventEmitter as new () => ServiceManagerEve
|
||||
this.reportNewStatus(containerId, service, 'Stopping');
|
||||
}
|
||||
|
||||
const containerObj = this.docker.getContainer(containerId);
|
||||
const containerObj = docker.getContainer(containerId);
|
||||
const killPromise = Bluebird.resolve(containerObj.stop())
|
||||
.then(() => {
|
||||
if (removeContainer) {
|
||||
@ -605,7 +599,7 @@ export class ServiceManager extends (EventEmitter as new () => ServiceManagerEve
|
||||
labelList: string[],
|
||||
): Promise<Dockerode.ContainerInfo[]> {
|
||||
const listWithPrefix = (prefix: string) =>
|
||||
this.docker.listContainers({
|
||||
docker.listContainers({
|
||||
all: true,
|
||||
filters: {
|
||||
label: _.map(labelList, (v) => `${prefix}${v}`),
|
||||
@ -627,7 +621,7 @@ export class ServiceManager extends (EventEmitter as new () => ServiceManagerEve
|
||||
`No containerId provided for service ${service.serviceName} in ServiceManager.prepareForHandover. Service: ${service}`,
|
||||
);
|
||||
}
|
||||
const container = this.docker.getContainer(svc.containerId);
|
||||
const container = docker.getContainer(svc.containerId);
|
||||
await container.update({ RestartPolicy: {} });
|
||||
return await container.rename({
|
||||
name: `old_${service.serviceName}_${service.imageId}_${service.imageId}_${service.releaseId}`,
|
||||
|
@ -1,10 +1,11 @@
|
||||
import * as Docker from 'dockerode';
|
||||
import * as _ from 'lodash';
|
||||
import * as Path from 'path';
|
||||
import { VolumeInspectInfo } from 'dockerode';
|
||||
|
||||
import constants = require('../lib/constants');
|
||||
import { NotFoundError } from '../lib/errors';
|
||||
import { safeRename } from '../lib/fs-utils';
|
||||
import { docker } from '../lib/docker-utils';
|
||||
import * as LogTypes from '../lib/log-types';
|
||||
import { defaultLegacyVolume } from '../lib/migration';
|
||||
import Logger from '../logger';
|
||||
@ -12,7 +13,6 @@ import { ResourceRecreationAttemptError } from './errors';
|
||||
import Volume, { VolumeConfig } from './volume';
|
||||
|
||||
export interface VolumeMangerConstructOpts {
|
||||
docker: Docker;
|
||||
logger: Logger;
|
||||
}
|
||||
|
||||
@ -22,30 +22,23 @@ export interface VolumeNameOpts {
|
||||
}
|
||||
|
||||
export class VolumeManager {
|
||||
private docker: Docker;
|
||||
private logger: Logger;
|
||||
|
||||
public constructor(opts: VolumeMangerConstructOpts) {
|
||||
this.docker = opts.docker;
|
||||
this.logger = opts.logger;
|
||||
}
|
||||
|
||||
public async get({ name, appId }: VolumeNameOpts): Promise<Volume> {
|
||||
return Volume.fromDockerVolume(
|
||||
{ docker: this.docker, logger: this.logger },
|
||||
await this.docker
|
||||
.getVolume(Volume.generateDockerName(appId, name))
|
||||
.inspect(),
|
||||
{ logger: this.logger },
|
||||
await docker.getVolume(Volume.generateDockerName(appId, name)).inspect(),
|
||||
);
|
||||
}
|
||||
|
||||
public async getAll(): Promise<Volume[]> {
|
||||
const volumeInspect = await this.listWithBothLabels();
|
||||
return volumeInspect.map((inspect) =>
|
||||
Volume.fromDockerVolume(
|
||||
{ logger: this.logger, docker: this.docker },
|
||||
inspect,
|
||||
),
|
||||
Volume.fromDockerVolume({ logger: this.logger }, inspect),
|
||||
);
|
||||
}
|
||||
|
||||
@ -111,11 +104,10 @@ export class VolumeManager {
|
||||
): Promise<Volume> {
|
||||
const volume = Volume.fromComposeObject(name, appId, config, {
|
||||
logger: this.logger,
|
||||
docker: this.docker,
|
||||
});
|
||||
|
||||
await this.create(volume);
|
||||
const inspect = await this.docker
|
||||
const inspect = await docker
|
||||
.getVolume(Volume.generateDockerName(volume.appId, volume.name))
|
||||
.inspect();
|
||||
|
||||
@ -139,8 +131,8 @@ export class VolumeManager {
|
||||
// *all* containers. This means we don't remove
|
||||
// something that's part of a sideloaded container
|
||||
const [dockerContainers, dockerVolumes] = await Promise.all([
|
||||
this.docker.listContainers(),
|
||||
this.docker.listVolumes(),
|
||||
docker.listContainers(),
|
||||
docker.listVolumes(),
|
||||
]);
|
||||
|
||||
const containerVolumes = _(dockerContainers)
|
||||
@ -160,17 +152,15 @@ export class VolumeManager {
|
||||
// in the target state
|
||||
referencedVolumes,
|
||||
);
|
||||
await Promise.all(
|
||||
volumesToRemove.map((v) => this.docker.getVolume(v).remove()),
|
||||
);
|
||||
await Promise.all(volumesToRemove.map((v) => docker.getVolume(v).remove()));
|
||||
}
|
||||
|
||||
private async listWithBothLabels(): Promise<Docker.VolumeInspectInfo[]> {
|
||||
private async listWithBothLabels(): Promise<VolumeInspectInfo[]> {
|
||||
const [legacyResponse, currentResponse] = await Promise.all([
|
||||
this.docker.listVolumes({
|
||||
docker.listVolumes({
|
||||
filters: { label: ['io.resin.supervised'] },
|
||||
}),
|
||||
this.docker.listVolumes({
|
||||
docker.listVolumes({
|
||||
filters: { label: ['io.balena.supervised'] },
|
||||
}),
|
||||
]);
|
||||
|
@ -4,6 +4,7 @@ import isEqual = require('lodash/isEqual');
|
||||
import omitBy = require('lodash/omitBy');
|
||||
|
||||
import constants = require('../lib/constants');
|
||||
import { docker } from '../lib/docker-utils';
|
||||
import { InternalInconsistencyError } from '../lib/errors';
|
||||
import * as LogTypes from '../lib/log-types';
|
||||
import { LabelObject } from '../lib/types';
|
||||
@ -12,7 +13,6 @@ import * as ComposeUtils from './utils';
|
||||
|
||||
export interface VolumeConstructOpts {
|
||||
logger: Logger;
|
||||
docker: Docker;
|
||||
}
|
||||
|
||||
export interface VolumeConfig {
|
||||
@ -29,7 +29,6 @@ export interface ComposeVolumeConfig {
|
||||
|
||||
export class Volume {
|
||||
private logger: Logger;
|
||||
private docker: Docker;
|
||||
|
||||
private constructor(
|
||||
public name: string,
|
||||
@ -38,7 +37,6 @@ export class Volume {
|
||||
opts: VolumeConstructOpts,
|
||||
) {
|
||||
this.logger = opts.logger;
|
||||
this.docker = opts.docker;
|
||||
}
|
||||
|
||||
public static fromDockerVolume(
|
||||
@ -100,7 +98,7 @@ export class Volume {
|
||||
this.logger.logSystemEvent(LogTypes.createVolume, {
|
||||
volume: { name: this.name },
|
||||
});
|
||||
await this.docker.createVolume({
|
||||
await docker.createVolume({
|
||||
Name: Volume.generateDockerName(this.appId, this.name),
|
||||
Labels: this.config.labels,
|
||||
Driver: this.config.driver,
|
||||
@ -114,7 +112,7 @@ export class Volume {
|
||||
});
|
||||
|
||||
try {
|
||||
await this.docker
|
||||
await docker
|
||||
.getVolume(Volume.generateDockerName(this.appId, this.name))
|
||||
.remove();
|
||||
} catch (e) {
|
||||
|
@ -42,20 +42,20 @@ interface ImageNameParts {
|
||||
// (10 mins)
|
||||
const DELTA_TOKEN_TIMEOUT = 10 * 60 * 1000;
|
||||
|
||||
export class DockerUtils extends DockerToolbelt {
|
||||
public dockerProgress: DockerProgress;
|
||||
export const docker = new Dockerode();
|
||||
export const dockerToolbelt = new DockerToolbelt(undefined);
|
||||
export const dockerProgress = new DockerProgress({
|
||||
dockerToolbelt,
|
||||
});
|
||||
|
||||
public constructor(opts?: Dockerode.DockerOptions) {
|
||||
super(opts);
|
||||
this.dockerProgress = new DockerProgress({ dockerToolbelt: this });
|
||||
}
|
||||
|
||||
public async getRepoAndTag(
|
||||
export async function getRepoAndTag(
|
||||
image: string,
|
||||
): Promise<{ repo: string; tag: string }> {
|
||||
const { registry, imageName, tagName } = await this.getRegistryAndName(
|
||||
image,
|
||||
);
|
||||
const {
|
||||
registry,
|
||||
imageName,
|
||||
tagName,
|
||||
} = await dockerToolbelt.getRegistryAndName(image);
|
||||
|
||||
let repoName = imageName;
|
||||
|
||||
@ -66,7 +66,7 @@ export class DockerUtils extends DockerToolbelt {
|
||||
return { repo: repoName, tag: tagName };
|
||||
}
|
||||
|
||||
public async fetchDeltaWithProgress(
|
||||
export async function fetchDeltaWithProgress(
|
||||
imgDest: string,
|
||||
deltaOpts: DeltaFetchOptions,
|
||||
onProgress: ProgressCallback,
|
||||
@ -86,7 +86,7 @@ export class DockerUtils extends DockerToolbelt {
|
||||
logFn(
|
||||
`Unsupported delta version: ${deltaOpts.deltaVersion}. Falling back to regular pull`,
|
||||
);
|
||||
return await this.fetchImageWithProgress(imgDest, deltaOpts, onProgress);
|
||||
return await fetchImageWithProgress(imgDest, deltaOpts, onProgress);
|
||||
}
|
||||
|
||||
// We need to make sure that we're not trying to apply a
|
||||
@ -95,30 +95,27 @@ export class DockerUtils extends DockerToolbelt {
|
||||
// image pull
|
||||
if (
|
||||
deltaOpts.deltaVersion === 3 &&
|
||||
(await DockerUtils.isV2DeltaImage(this, deltaOpts.deltaSourceId))
|
||||
(await isV2DeltaImage(deltaOpts.deltaSourceId))
|
||||
) {
|
||||
logFn(
|
||||
`Cannot create a delta from V2 to V3, falling back to regular pull`,
|
||||
);
|
||||
return await this.fetchImageWithProgress(imgDest, deltaOpts, onProgress);
|
||||
logFn(`Cannot create a delta from V2 to V3, falling back to regular pull`);
|
||||
return await fetchImageWithProgress(imgDest, deltaOpts, onProgress);
|
||||
}
|
||||
|
||||
// Since the supevisor never calls this function with a source anymore,
|
||||
// this should never happen, but w ehandle it anyway
|
||||
if (deltaOpts.deltaSource == null) {
|
||||
logFn('Falling back to regular pull due to lack of a delta source');
|
||||
return this.fetchImageWithProgress(imgDest, deltaOpts, onProgress);
|
||||
return fetchImageWithProgress(imgDest, deltaOpts, onProgress);
|
||||
}
|
||||
|
||||
const docker = this;
|
||||
logFn(`Starting delta to ${imgDest}`);
|
||||
|
||||
const [dstInfo, srcInfo] = await Promise.all([
|
||||
this.getRegistryAndName(imgDest),
|
||||
this.getRegistryAndName(deltaOpts.deltaSource),
|
||||
dockerToolbelt.getRegistryAndName(imgDest),
|
||||
dockerToolbelt.getRegistryAndName(deltaOpts.deltaSource),
|
||||
]);
|
||||
|
||||
const token = await this.getAuthToken(srcInfo, dstInfo, deltaOpts);
|
||||
const token = await getAuthToken(srcInfo, dstInfo, deltaOpts);
|
||||
|
||||
const opts: request.requestLib.CoreOptions = {
|
||||
followRedirect: false,
|
||||
@ -160,7 +157,7 @@ export class DockerUtils extends DockerToolbelt {
|
||||
maxRetries: deltaOpts.deltaRetryCount,
|
||||
retryInterval: deltaOpts.deltaRetryInterval,
|
||||
};
|
||||
id = await DockerUtils.applyRsyncDelta(
|
||||
id = await applyRsyncDelta(
|
||||
deltaSrc,
|
||||
deltaUrl,
|
||||
timeout,
|
||||
@ -183,27 +180,15 @@ export class DockerUtils extends DockerToolbelt {
|
||||
`Got an error when parsing delta server response for v3 delta: ${e}`,
|
||||
);
|
||||
}
|
||||
id = await DockerUtils.applyBalenaDelta(
|
||||
docker,
|
||||
name,
|
||||
token,
|
||||
onProgress,
|
||||
logFn,
|
||||
);
|
||||
id = await applyBalenaDelta(name, token, onProgress, logFn);
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unsupported delta version: ${deltaOpts.deltaVersion}`,
|
||||
);
|
||||
throw new Error(`Unsupported delta version: ${deltaOpts.deltaVersion}`);
|
||||
}
|
||||
} catch (e) {
|
||||
if (e instanceof OutOfSyncError) {
|
||||
logFn('Falling back to regular pull due to delta out of sync error');
|
||||
return await this.fetchImageWithProgress(
|
||||
imgDest,
|
||||
deltaOpts,
|
||||
onProgress,
|
||||
);
|
||||
return await this.fetchImageWithProgress(imgDest, deltaOpts, onProgress);
|
||||
} else {
|
||||
logFn(`Delta failed with ${e}`);
|
||||
throw e;
|
||||
@ -214,12 +199,12 @@ export class DockerUtils extends DockerToolbelt {
|
||||
return id;
|
||||
}
|
||||
|
||||
public async fetchImageWithProgress(
|
||||
export async function fetchImageWithProgress(
|
||||
image: string,
|
||||
{ uuid, currentApiKey }: FetchOptions,
|
||||
onProgress: ProgressCallback,
|
||||
): Promise<string> {
|
||||
const { registry } = await this.getRegistryAndName(image);
|
||||
const { registry } = await dockerToolbelt.getRegistryAndName(image);
|
||||
|
||||
const dockerOpts = {
|
||||
authconfig: {
|
||||
@ -229,11 +214,11 @@ export class DockerUtils extends DockerToolbelt {
|
||||
},
|
||||
};
|
||||
|
||||
await this.dockerProgress.pull(image, onProgress, dockerOpts);
|
||||
return (await this.getImage(image).inspect()).Id;
|
||||
await dockerProgress.pull(image, onProgress, dockerOpts);
|
||||
return (await docker.getImage(image).inspect()).Id;
|
||||
}
|
||||
|
||||
public async getImageEnv(id: string): Promise<EnvVarObject> {
|
||||
export async function getImageEnv(id: string): Promise<EnvVarObject> {
|
||||
const inspect = await this.getImage(id).inspect();
|
||||
|
||||
try {
|
||||
@ -244,7 +229,7 @@ export class DockerUtils extends DockerToolbelt {
|
||||
}
|
||||
}
|
||||
|
||||
public async getNetworkGateway(networkName: string): Promise<string> {
|
||||
export async function getNetworkGateway(networkName: string): Promise<string> {
|
||||
if (networkName === 'host') {
|
||||
return '127.0.0.1';
|
||||
}
|
||||
@ -264,7 +249,7 @@ export class DockerUtils extends DockerToolbelt {
|
||||
);
|
||||
}
|
||||
|
||||
private static applyRsyncDelta(
|
||||
function applyRsyncDelta(
|
||||
imgSrc: string,
|
||||
deltaUrl: string,
|
||||
applyTimeout: number,
|
||||
@ -308,8 +293,7 @@ export class DockerUtils extends DockerToolbelt {
|
||||
});
|
||||
}
|
||||
|
||||
private static async applyBalenaDelta(
|
||||
docker: DockerUtils,
|
||||
async function applyBalenaDelta(
|
||||
deltaImg: string,
|
||||
token: string | null,
|
||||
onProgress: ProgressCallback,
|
||||
@ -327,14 +311,11 @@ export class DockerUtils extends DockerToolbelt {
|
||||
};
|
||||
}
|
||||
|
||||
await docker.dockerProgress.pull(deltaImg, onProgress, auth);
|
||||
await dockerProgress.pull(deltaImg, onProgress, auth);
|
||||
return (await docker.getImage(deltaImg).inspect()).Id;
|
||||
}
|
||||
|
||||
public static async isV2DeltaImage(
|
||||
docker: DockerUtils,
|
||||
imageName: string,
|
||||
): Promise<boolean> {
|
||||
export async function isV2DeltaImage(imageName: string): Promise<boolean> {
|
||||
const inspect = await docker.getImage(imageName).inspect();
|
||||
|
||||
// It's extremely unlikely that an image is valid if
|
||||
@ -344,7 +325,7 @@ export class DockerUtils extends DockerToolbelt {
|
||||
return inspect.Size < 40 && inspect.VirtualSize < 40;
|
||||
}
|
||||
|
||||
private getAuthToken = memoizee(
|
||||
const getAuthToken = memoizee(
|
||||
async (
|
||||
srcInfo: ImageNameParts,
|
||||
dstInfo: ImageNameParts,
|
||||
@ -374,6 +355,3 @@ export class DockerUtils extends DockerToolbelt {
|
||||
},
|
||||
{ maxAge: DELTA_TOKEN_TIMEOUT, promise: true },
|
||||
);
|
||||
}
|
||||
|
||||
export default DockerUtils;
|
||||
|
@ -15,6 +15,7 @@ import * as db from '../db';
|
||||
import DeviceState from '../device-state';
|
||||
import * as constants from '../lib/constants';
|
||||
import { BackupError, DatabaseParseError, NotFoundError } from '../lib/errors';
|
||||
import { docker } from '../lib/docker-utils';
|
||||
import { pathExistsOnHost } from '../lib/fs-utils';
|
||||
import { log } from '../lib/supervisor-console';
|
||||
import {
|
||||
@ -179,7 +180,7 @@ export async function normaliseLegacyDatabase(
|
||||
`Found a release with releaseId ${release.id}, imageId ${image.id}, serviceId ${serviceId}\nImage location is ${imageUrl}`,
|
||||
);
|
||||
|
||||
const imageFromDocker = await application.docker
|
||||
const imageFromDocker = await docker
|
||||
.getImage(service.image)
|
||||
.inspect()
|
||||
.catch((error) => {
|
||||
|
@ -1,10 +1,10 @@
|
||||
import * as Bluebird from 'bluebird';
|
||||
import * as Docker from 'dockerode';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import * as config from './config';
|
||||
import * as db from './db';
|
||||
import * as constants from './lib/constants';
|
||||
import { docker } from './lib/docker-utils';
|
||||
import { SupervisorContainerNotFoundError } from './lib/errors';
|
||||
import log from './lib/supervisor-console';
|
||||
import { Logger } from './logger';
|
||||
@ -71,7 +71,6 @@ const SUPERVISOR_CONTAINER_NAME_FALLBACK = 'resin_supervisor';
|
||||
*/
|
||||
export class LocalModeManager {
|
||||
public constructor(
|
||||
public docker: Docker,
|
||||
public logger: Logger,
|
||||
private containerId: string | undefined = constants.containerId,
|
||||
) {}
|
||||
@ -121,16 +120,14 @@ export class LocalModeManager {
|
||||
|
||||
// Query the engine to get currently running containers and installed images.
|
||||
public async collectEngineSnapshot(): Promise<EngineSnapshotRecord> {
|
||||
const containersPromise = this.docker
|
||||
const containersPromise = docker
|
||||
.listContainers()
|
||||
.then((resp) => _.map(resp, 'Id'));
|
||||
const imagesPromise = this.docker
|
||||
.listImages()
|
||||
.then((resp) => _.map(resp, 'Id'));
|
||||
const volumesPromise = this.docker
|
||||
const imagesPromise = docker.listImages().then((resp) => _.map(resp, 'Id'));
|
||||
const volumesPromise = docker
|
||||
.listVolumes()
|
||||
.then((resp) => _.map(resp.Volumes, 'Name'));
|
||||
const networksPromise = this.docker
|
||||
const networksPromise = docker
|
||||
.listNetworks()
|
||||
.then((resp) => _.map(resp, 'Id'));
|
||||
|
||||
@ -149,7 +146,7 @@ export class LocalModeManager {
|
||||
private async collectContainerResources(
|
||||
nameOrId: string,
|
||||
): Promise<EngineSnapshot> {
|
||||
const inspectInfo = await this.docker.getContainer(nameOrId).inspect();
|
||||
const inspectInfo = await docker.getContainer(nameOrId).inspect();
|
||||
return new EngineSnapshot(
|
||||
[inspectInfo.Id],
|
||||
[inspectInfo.Image],
|
||||
@ -236,25 +233,25 @@ export class LocalModeManager {
|
||||
|
||||
// Delete engine objects. We catch every deletion error, so that we can attempt other objects deletions.
|
||||
await Bluebird.map(objects.containers, (cId) => {
|
||||
return this.docker
|
||||
return docker
|
||||
.getContainer(cId)
|
||||
.remove({ force: true })
|
||||
.catch((e) => log.error(`Unable to delete container ${cId}`, e));
|
||||
});
|
||||
await Bluebird.map(objects.images, (iId) => {
|
||||
return this.docker
|
||||
return docker
|
||||
.getImage(iId)
|
||||
.remove({ force: true })
|
||||
.catch((e) => log.error(`Unable to delete image ${iId}`, e));
|
||||
});
|
||||
await Bluebird.map(objects.networks, (nId) => {
|
||||
return this.docker
|
||||
return docker
|
||||
.getNetwork(nId)
|
||||
.remove()
|
||||
.catch((e) => log.error(`Unable to delete network ${nId}`, e));
|
||||
});
|
||||
await Bluebird.map(objects.volumes, (vId) => {
|
||||
return this.docker
|
||||
return docker
|
||||
.getVolume(vId)
|
||||
.remove()
|
||||
.catch((e) => log.error(`Unable to delete volume ${vId}`, e));
|
||||
|
@ -4,7 +4,6 @@ import * as _ from 'lodash';
|
||||
import * as config from './config';
|
||||
import * as db from './db';
|
||||
import { EventTracker } from './event-tracker';
|
||||
import Docker from './lib/docker-utils';
|
||||
import { LogType } from './lib/log-types';
|
||||
import { writeLock } from './lib/update-lock';
|
||||
import {
|
||||
@ -159,7 +158,6 @@ export class Logger {
|
||||
}
|
||||
|
||||
public attach(
|
||||
docker: Docker,
|
||||
containerId: string,
|
||||
serviceInfo: { serviceId: number; imageId: number },
|
||||
): Bluebird<void> {
|
||||
@ -170,7 +168,7 @@ export class Logger {
|
||||
}
|
||||
|
||||
return Bluebird.using(this.lock(containerId), async () => {
|
||||
const logs = new ContainerLogs(containerId, docker);
|
||||
const logs = new ContainerLogs(containerId);
|
||||
this.containerLogs[containerId] = logs;
|
||||
logs.on('error', (err) => {
|
||||
log.error('Container log retrieval error', err);
|
||||
|
@ -4,7 +4,7 @@ import * as _ from 'lodash';
|
||||
import * as Stream from 'stream';
|
||||
import StrictEventEmitter from 'strict-event-emitter-types';
|
||||
|
||||
import Docker from '../lib/docker-utils';
|
||||
import { docker } from '../lib/docker-utils';
|
||||
|
||||
export interface ContainerLog {
|
||||
message: string;
|
||||
@ -21,7 +21,7 @@ interface LogsEvents {
|
||||
type LogsEventEmitter = StrictEventEmitter<EventEmitter, LogsEvents>;
|
||||
|
||||
export class ContainerLogs extends (EventEmitter as new () => LogsEventEmitter) {
|
||||
public constructor(public containerId: string, private docker: Docker) {
|
||||
public constructor(public containerId: string) {
|
||||
super();
|
||||
}
|
||||
|
||||
@ -34,7 +34,7 @@ export class ContainerLogs extends (EventEmitter as new () => LogsEventEmitter)
|
||||
const stdoutLogOpts = { stdout: true, stderr: false, ...logOpts };
|
||||
const stderrLogOpts = { stderr: true, stdout: false, ...logOpts };
|
||||
|
||||
const container = this.docker.getContainer(this.containerId);
|
||||
const container = docker.getContainer(this.containerId);
|
||||
const stdoutStream = await container.logs(stdoutLogOpts);
|
||||
const stderrStream = await container.logs(stderrLogOpts);
|
||||
|
||||
|
@ -17,6 +17,7 @@ import * as url from 'url';
|
||||
import { log } from './lib/supervisor-console';
|
||||
import * as db from './db';
|
||||
import * as config from './config';
|
||||
import * as dockerUtils from './lib/docker-utils';
|
||||
|
||||
const mkdirpAsync = Promise.promisify(mkdirp);
|
||||
|
||||
@ -344,7 +345,7 @@ const createProxyvisorRouter = function (proxyvisor) {
|
||||
};
|
||||
|
||||
export class Proxyvisor {
|
||||
constructor({ logger, docker, images, applications }) {
|
||||
constructor({ logger, images, applications }) {
|
||||
this.bindToAPI = this.bindToAPI.bind(this);
|
||||
this.executeStepAction = this.executeStepAction.bind(this);
|
||||
this.getCurrentStates = this.getCurrentStates.bind(this);
|
||||
@ -361,7 +362,6 @@ export class Proxyvisor {
|
||||
this.sendDeleteHook = this.sendDeleteHook.bind(this);
|
||||
this.sendUpdates = this.sendUpdates.bind(this);
|
||||
this.logger = logger;
|
||||
this.docker = docker;
|
||||
this.images = images;
|
||||
this.applications = applications;
|
||||
this.acknowledgedState = {};
|
||||
@ -904,7 +904,7 @@ export class Proxyvisor {
|
||||
})
|
||||
.then((parentApp) => {
|
||||
return Promise.map(parentApp?.services ?? [], (service) => {
|
||||
return this.docker.getImageEnv(service.image);
|
||||
return dockerUtils.getImageEnv(service.image);
|
||||
}).then(function (imageEnvs) {
|
||||
const imageHookAddresses = _.map(
|
||||
imageEnvs,
|
||||
|
@ -6,6 +6,7 @@ import { SinonSpy, SinonStub, spy, stub } from 'sinon';
|
||||
import chai = require('./lib/chai-config');
|
||||
import prepare = require('./lib/prepare');
|
||||
import Log from '../src/lib/supervisor-console';
|
||||
import * as dockerUtils from '../src/lib/docker-utils';
|
||||
import * as config from '../src/config';
|
||||
import { RPiConfigBackend } from '../src/config/backend';
|
||||
import DeviceState from '../src/device-state';
|
||||
@ -235,7 +236,7 @@ describe('deviceState', () => {
|
||||
apiBinder: null as any,
|
||||
});
|
||||
|
||||
stub(deviceState.applications.docker, 'getNetworkGateway').returns(
|
||||
stub(dockerUtils, 'getNetworkGateway').returns(
|
||||
Promise.resolve('172.17.0.1'),
|
||||
);
|
||||
|
||||
@ -250,8 +251,7 @@ describe('deviceState', () => {
|
||||
|
||||
after(() => {
|
||||
(Service as any).extendEnvVars.restore();
|
||||
(deviceState.applications.docker
|
||||
.getNetworkGateway as sinon.SinonStub).restore();
|
||||
(dockerUtils.getNetworkGateway as sinon.SinonStub).restore();
|
||||
(deviceState.applications.images
|
||||
.inspectByName as sinon.SinonStub).restore();
|
||||
});
|
||||
|
@ -8,6 +8,7 @@ import Service from '../src/compose/service';
|
||||
import Volume from '../src/compose/volume';
|
||||
import DeviceState from '../src/device-state';
|
||||
import EventTracker from '../src/event-tracker';
|
||||
import * as dockerUtils from '../src/lib/docker-utils';
|
||||
|
||||
import chai = require('./lib/chai-config');
|
||||
import prepare = require('./lib/prepare');
|
||||
@ -148,13 +149,13 @@ describe('ApplicationManager', function () {
|
||||
},
|
||||
}),
|
||||
);
|
||||
stub(this.applications.docker, 'getNetworkGateway').returns(
|
||||
stub(dockerUtils, 'getNetworkGateway').returns(
|
||||
Bluebird.Promise.resolve('172.17.0.1'),
|
||||
);
|
||||
stub(this.applications.docker, 'listContainers').returns(
|
||||
stub(dockerUtils.docker, 'listContainers').returns(
|
||||
Bluebird.Promise.resolve([]),
|
||||
);
|
||||
stub(this.applications.docker, 'listImages').returns(
|
||||
stub(dockerUtils.docker, 'listImages').returns(
|
||||
Bluebird.Promise.resolve([]),
|
||||
);
|
||||
stub(Service as any, 'extendEnvVars').callsFake(function (env) {
|
||||
@ -174,7 +175,6 @@ describe('ApplicationManager', function () {
|
||||
appCloned.networks,
|
||||
(config, name) => {
|
||||
return Network.fromComposeObject(name, app.appId, config, {
|
||||
docker: this.applications.docker,
|
||||
logger: this.logger,
|
||||
});
|
||||
},
|
||||
@ -235,8 +235,12 @@ describe('ApplicationManager', function () {
|
||||
|
||||
after(function () {
|
||||
this.applications.images.inspectByName.restore();
|
||||
this.applications.docker.getNetworkGateway.restore();
|
||||
this.applications.docker.listContainers.restore();
|
||||
// @ts-expect-error restore on non-stubbed type
|
||||
dockerUtils.getNetworkGateway.restore();
|
||||
// @ts-expect-error restore on non-stubbed type
|
||||
dockerUtils.docker.listContainers.restore();
|
||||
// @ts-expect-error restore on non-stubbed type
|
||||
dockerUtils.docker.listImages.restore();
|
||||
return (Service as any).extendEnvVars.restore();
|
||||
});
|
||||
|
||||
|
@ -4,7 +4,7 @@ import APIBinder from '../src/api-binder';
|
||||
import { ApplicationManager } from '../src/application-manager';
|
||||
import DeviceState from '../src/device-state';
|
||||
import * as constants from '../src/lib/constants';
|
||||
import { DockerUtils as Docker } from '../src/lib/docker-utils';
|
||||
import { docker } from '../src/lib/docker-utils';
|
||||
import { Supervisor } from '../src/supervisor';
|
||||
import { expect } from './lib/chai-config';
|
||||
|
||||
@ -30,9 +30,7 @@ describe('Startup', () => {
|
||||
deviceStateStub = stub(DeviceState.prototype as any, 'applyTarget').returns(
|
||||
Promise.resolve(),
|
||||
);
|
||||
dockerStub = stub(Docker.prototype, 'listContainers').returns(
|
||||
Promise.resolve([]),
|
||||
);
|
||||
dockerStub = stub(docker, 'listContainers').returns(Promise.resolve([]));
|
||||
});
|
||||
|
||||
after(() => {
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { expect } from 'chai';
|
||||
import { stub } from 'sinon';
|
||||
import { stub, SinonStub } from 'sinon';
|
||||
|
||||
import { docker } from '../src/lib/docker-utils';
|
||||
|
||||
import Volume from '../src/compose/volume';
|
||||
import logTypes = require('../src/lib/log-types');
|
||||
@ -8,13 +10,18 @@ const fakeLogger = {
|
||||
logSystemMessage: stub(),
|
||||
logSystemEvent: stub(),
|
||||
};
|
||||
const fakeDocker = {
|
||||
createVolume: stub(),
|
||||
};
|
||||
|
||||
const opts: any = { logger: fakeLogger, docker: fakeDocker };
|
||||
const opts: any = { logger: fakeLogger };
|
||||
|
||||
describe('Compose volumes', () => {
|
||||
let createVolumeStub: SinonStub;
|
||||
before(() => {
|
||||
createVolumeStub = stub(docker, 'createVolume');
|
||||
});
|
||||
after(() => {
|
||||
createVolumeStub.restore();
|
||||
});
|
||||
|
||||
describe('Parsing volumes', () => {
|
||||
it('should correctly parse docker volumes', () => {
|
||||
const volume = Volume.fromDockerVolume(opts, {
|
||||
@ -122,7 +129,7 @@ describe('Compose volumes', () => {
|
||||
|
||||
describe('Generating docker options', () => {
|
||||
afterEach(() => {
|
||||
fakeDocker.createVolume.reset();
|
||||
createVolumeStub.reset();
|
||||
fakeLogger.logSystemEvent.reset();
|
||||
fakeLogger.logSystemMessage.reset();
|
||||
});
|
||||
@ -143,7 +150,7 @@ describe('Compose volumes', () => {
|
||||
|
||||
await volume.create();
|
||||
expect(
|
||||
fakeDocker.createVolume.calledWith({
|
||||
createVolumeStub.calledWith({
|
||||
Labels: {
|
||||
'my-label': 'test-label',
|
||||
'io.balena.supervised': 'true',
|
||||
|
@ -4,6 +4,7 @@ import * as Docker from 'dockerode';
|
||||
import * as sinon from 'sinon';
|
||||
|
||||
import * as db from '../src/db';
|
||||
import { docker } from '../src/lib/docker-utils';
|
||||
import LocalModeManager, {
|
||||
EngineSnapshot,
|
||||
EngineSnapshotRecord,
|
||||
@ -13,7 +14,7 @@ import ShortStackError from './lib/errors';
|
||||
|
||||
describe('LocalModeManager', () => {
|
||||
let localMode: LocalModeManager;
|
||||
let dockerStub: sinon.SinonStubbedInstance<Docker>;
|
||||
let dockerStub: sinon.SinonStubbedInstance<typeof docker>;
|
||||
|
||||
const supervisorContainerId = 'super-container-1';
|
||||
|
||||
@ -32,14 +33,14 @@ describe('LocalModeManager', () => {
|
||||
before(async () => {
|
||||
await db.initialized;
|
||||
|
||||
dockerStub = sinon.createStubInstance(Docker);
|
||||
dockerStub = sinon.stub(docker);
|
||||
const loggerStub = (sinon.createStubInstance(Logger) as unknown) as Logger;
|
||||
|
||||
localMode = new LocalModeManager(
|
||||
(dockerStub as unknown) as Docker,
|
||||
loggerStub,
|
||||
supervisorContainerId,
|
||||
);
|
||||
localMode = new LocalModeManager(loggerStub, supervisorContainerId);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
sinon.restore();
|
||||
});
|
||||
|
||||
describe('EngineSnapshot', () => {
|
||||
@ -427,8 +428,4 @@ describe('LocalModeManager', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
sinon.restore();
|
||||
});
|
||||
});
|
||||
|
@ -1,13 +1,11 @@
|
||||
import { expect } from 'chai';
|
||||
import { stub } from 'sinon';
|
||||
|
||||
import DockerUtils from '../src/lib/docker-utils';
|
||||
|
||||
const dockerUtils = new DockerUtils({});
|
||||
import * as dockerUtils from '../src/lib/docker-utils';
|
||||
|
||||
describe('Deltas', () => {
|
||||
it('should correctly detect a V2 delta', async () => {
|
||||
const imageStub = stub(dockerUtils, 'getImage').returns({
|
||||
const imageStub = stub(dockerUtils.docker, 'getImage').returns({
|
||||
inspect: () => {
|
||||
return Promise.resolve({
|
||||
Id:
|
||||
@ -99,7 +97,7 @@ describe('Deltas', () => {
|
||||
},
|
||||
} as any);
|
||||
|
||||
expect(await DockerUtils.isV2DeltaImage(dockerUtils, 'test')).to.be.true;
|
||||
expect(await dockerUtils.isV2DeltaImage('test')).to.be.true;
|
||||
expect(imageStub.callCount).to.equal(1);
|
||||
imageStub.restore();
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user