Update docker related dependencies

This bumps dockerode, removes resin-docker-build in favor of
@balena/compose, and updates docker-delta and docker-progress packages.

Change-type: patch
This commit is contained in:
Felipe Lalanne 2024-04-03 17:56:12 -03:00
parent 15bccbf6b3
commit ae823fea18
10 changed files with 844 additions and 1403 deletions

2089
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -39,6 +39,7 @@
"npm": ">=10"
},
"devDependencies": {
"@balena/compose": "^3.2.1",
"@balena/contrato": "^0.6.0",
"@balena/es-version": "^1.0.3",
"@balena/lint": "^8.0.2",
@ -48,7 +49,7 @@
"@types/chai-like": "^1.1.1",
"@types/chai-things": "0.0.38",
"@types/common-tags": "^1.8.1",
"@types/dockerode": "^2.5.34",
"@types/dockerode": "^3.3.28",
"@types/event-stream": "^3.3.34",
"@types/express": "^4.17.14",
"@types/json-mask": "^2.0.3",
@ -66,6 +67,7 @@
"@types/sinon": "^17.0.3",
"@types/sinon-chai": "^3.2.12",
"@types/supertest": "^6.0.2",
"@types/tar-stream": "^3.1.3",
"@types/webpack": "^5.28.0",
"@types/yargs": "^17.0.32",
"balena-auth": "^6.0.1",
@ -81,9 +83,9 @@
"common-tags": "^1.8.0",
"copy-webpack-plugin": "^12.0.0",
"deep-object-diff": "^1.1.0",
"docker-delta": "^2.2.11",
"docker-progress": "^4.0.3",
"dockerode": "^2.5.8",
"docker-delta": "^4.0.1",
"docker-progress": "^5.2.3",
"dockerode": "^4.0.2",
"duration-js": "^4.0.0",
"event-stream": "3.3.5",
"express": "^4.17.1",
@ -106,11 +108,11 @@
"morgan": "^1.10.0",
"network-checker": "^0.1.1",
"nock": "^13.1.2",
"node-loader": "^2.0.0",
"nodemon": "^3.1.0",
"pinejs-client-request": "^7.3.5",
"pretty-ms": "^7.0.1",
"request": "^2.88.2",
"resin-docker-build": "^1.1.6",
"resumable-request": "^2.0.1",
"rewire": "^7.0.0",
"rimraf": "^5.0.0",

View File

@ -506,7 +506,14 @@ export class Service {
const ulimits: ServiceConfig['ulimits'] = {};
_.each(container.HostConfig.Ulimits, ({ Name, Soft, Hard }) => {
ulimits[Name] = { soft: Soft, hard: Hard };
// The Ulimit type in @types/dockerode allows any element to be
// null which is probably wrong
if (Name != null && Soft != null && Hard != null) {
ulimits[Name] = {
soft: Soft,
hard: Hard,
};
}
});
const portMaps = PortMap.fromDockerOpts(container.HostConfig.PortBindings);

View File

@ -97,7 +97,7 @@ export class Volume {
Name: Volume.generateDockerName(this.appId, this.name),
Labels: this.config.labels,
Driver: this.config.driver,
DriverOpts: this.config.driverOpts,
DriverOpts: this.config.driverOpts!,
});
}

View File

@ -5,7 +5,6 @@ import _ from 'lodash';
import memoizee from 'memoizee';
import { applyDelta, OutOfSyncError } from 'docker-delta';
import DockerToolbelt = require('docker-toolbelt');
import type { SchemaReturn } from '../config/schema-type';
import { envArrayToObject } from './conversions';
@ -43,9 +42,8 @@ type ImageNameParts = {
const DELTA_TOKEN_TIMEOUT = 10 * 60 * 1000;
export const docker = new Dockerode();
export const dockerToolbelt = new DockerToolbelt(undefined);
export const dockerProgress = new DockerProgress({
dockerToolbelt,
docker,
});
// Separate string containing registry and image name into its parts.
@ -153,10 +151,10 @@ export async function fetchDeltaWithProgress(
logFn(`Starting delta to ${imgDest}`);
const [dstInfo, srcInfo] = await Promise.all([
dockerToolbelt.getRegistryAndName(imgDest),
dockerToolbelt.getRegistryAndName(deltaOpts.deltaSource),
]);
const [dstInfo, srcInfo] = [
getRegistryAndName(imgDest),
getRegistryAndName(deltaOpts.deltaSource),
];
const token = await getAuthToken(srcInfo, dstInfo, deltaOpts);
@ -252,7 +250,7 @@ export async function fetchImageWithProgress(
{ uuid, currentApiKey }: FetchOptions,
onProgress: ProgressCallback,
): Promise<string> {
const { registry } = await dockerToolbelt.getRegistryAndName(image);
const { registry } = getRegistryAndName(image);
const dockerOpts =
// If no registry is specified, we assume the image is a public

View File

@ -1,6 +1,6 @@
import Docker from 'dockerode';
import type { Dockerfile } from 'livepush';
import { Builder } from 'resin-docker-build';
import { build } from '@balena/compose';
import { promises as fs } from 'fs';
import * as Path from 'path';
@ -10,6 +10,8 @@ import * as readline from 'readline';
import { exec } from '../src/lib/fs-utils';
const { Builder } = build;
export function getDocker(deviceAddress: string): Docker {
return new Docker({
host: deviceAddress,

View File

@ -88,7 +88,10 @@ describe('LocalModeManager', () => {
}),
);
dockerStub.listNetworks.returns(
Promise.resolve([{ Id: 'network-1' }, { Id: 'network-2' }]),
Promise.resolve([
{ Id: 'network-1' },
{ Id: 'network-2' },
] as Docker.NetworkInspectInfo[]),
);
});

View File

@ -1,8 +1,8 @@
process.env.DOCKER_HOST = 'unix:///your/dockerode/mocks/are/not/working';
import * as dockerode from 'dockerode';
import Dockerode from 'dockerode';
import { Stream } from 'stream';
import _ = require('lodash');
import _ from 'lodash';
import { NotFoundError } from '~/lib/errors';
const overrides: Dictionary<(...args: any[]) => Resolvable<any>> = {};
@ -30,13 +30,13 @@ function addAction(name: string, parameters: Dictionary<any> = {}) {
});
}
type DockerodeFunction = keyof dockerode;
for (const fn of Object.getOwnPropertyNames(dockerode.prototype)) {
type DockerodeFunction = keyof Omit<Dockerode, 'modem'>;
for (const fn of Object.getOwnPropertyNames(Dockerode.prototype)) {
if (
fn !== 'constructor' &&
typeof (dockerode.prototype as any)[fn] === 'function'
typeof (Dockerode.prototype as any)[fn] === 'function'
) {
(dockerode.prototype as any)[fn] = async function (...args: any[]) {
(Dockerode.prototype as any)[fn] = async function (...args: any[]) {
console.log(`🐳 Calling ${fn}...`);
if (overrides[fn] != null) {
return overrides[fn](args);
@ -66,8 +66,8 @@ registerOverride(
*/
export function registerOverride<
T extends DockerodeFunction,
P extends Parameters<dockerode[T]>,
R extends ReturnType<dockerode[T]>,
P extends Parameters<Dockerode[T]>,
R extends ReturnType<Dockerode[T]>,
>(name: T, fn: (...args: P) => R) {
console.log(`Overriding ${name}...`);
overrides[name] = fn;
@ -87,14 +87,14 @@ export interface TestData {
}
function createMockedDockerode(data: TestData) {
const mockedDockerode = dockerode.prototype;
const mockedDockerode = Dockerode.prototype;
mockedDockerode.listImages = async () => [];
mockedDockerode.listVolumes = async () => {
addAction('listVolumes');
return {
Volumes: data.volumes as dockerode.VolumeInspectInfo[],
Volumes: data.volumes as Dockerode.VolumeInspectInfo[],
Warnings: [],
};
};
@ -120,18 +120,18 @@ function createMockedDockerode(data: TestData) {
},
name: volume.name,
modem: {},
} as dockerode.Volume;
} as Dockerode.Volume;
};
mockedDockerode.createContainer = async (
options: dockerode.ContainerCreateOptions,
options: Dockerode.ContainerCreateOptions,
) => {
addAction('createContainer', { options });
return {
start: async () => {
addAction('start');
},
} as dockerode.Container;
} as Dockerode.Container;
};
mockedDockerode.getContainer = (id: string) => {
@ -167,7 +167,7 @@ function createMockedDockerode(data: TestData) {
return c;
});
},
} as dockerode.Container;
} as Dockerode.Container;
};
mockedDockerode.getNetwork = (id: string) => {
@ -177,7 +177,7 @@ function createMockedDockerode(data: TestData) {
addAction('inspect');
return data.networks[id];
},
} as dockerode.Network;
} as Dockerode.Network;
};
mockedDockerode.getImage = (name: string) => {
@ -193,7 +193,7 @@ function createMockedDockerode(data: TestData) {
name,
});
},
} as dockerode.Image;
} as Dockerode.Image;
};
return mockedDockerode;
@ -232,16 +232,14 @@ export async function testWithData(
};
// grab the original prototype...
const basePrototype = clonePrototype(dockerode.prototype);
// @ts-expect-error setting a RO property
dockerode.prototype = createMockedDockerode(mockedData);
const basePrototype = clonePrototype(Dockerode.prototype);
Dockerode.prototype = createMockedDockerode(mockedData);
try {
// run the test...
await test();
} finally {
// reset the original prototype...
assignPrototype(dockerode.prototype, basePrototype);
assignPrototype(Dockerode.prototype, basePrototype);
}
}

View File

@ -1,4 +1,4 @@
import dockerode from 'dockerode';
import Dockerode from 'dockerode';
import sinon from 'sinon';
import { randomUUID as uuidv4 } from 'crypto';
@ -14,31 +14,31 @@ type DeepPartial<T> = {
// Partial container inspect info for receiving as testing data
export type PartialContainerInspectInfo =
DeepPartial<dockerode.ContainerInspectInfo> & {
DeepPartial<Dockerode.ContainerInspectInfo> & {
Id: string;
};
export type PartialNetworkInspectInfo =
DeepPartial<dockerode.NetworkInspectInfo> & {
DeepPartial<Dockerode.NetworkInspectInfo> & {
Id: string;
};
export type PartialVolumeInspectInfo =
DeepPartial<dockerode.VolumeInspectInfo> & {
DeepPartial<Dockerode.VolumeInspectInfo> & {
Name: string;
};
export type PartialImageInspectInfo =
DeepPartial<dockerode.ImageInspectInfo> & {
DeepPartial<Dockerode.ImageInspectInfo> & {
Id: string;
};
type Methods<T> = {
[K in keyof T]: T[K] extends (...args: any) => any ? T[K] : never;
type Fake<T> = {
[K in keyof T]: T[K] extends (...args: any[]) => any ? T[K] : never;
};
function createFake<Prototype extends object>(prototype: Prototype) {
return (Object.getOwnPropertyNames(prototype) as Array<keyof Prototype>)
function createFake<T extends object>(prototype: T): Fake<T> {
return (Object.getOwnPropertyNames(prototype) as Array<keyof T>)
.filter((fn) => fn === 'constructor' || typeof prototype[fn] === 'function')
.reduce(
(res, fn) => ({
@ -51,7 +51,7 @@ function createFake<Prototype extends object>(prototype: Prototype) {
);
},
}),
{} as Methods<Prototype>,
{} as Fake<T>,
);
}
@ -86,7 +86,7 @@ export function createNetwork(network: PartialNetworkInspectInfo) {
...networkInspect,
};
const fakeNetwork = createFake(dockerode.Network.prototype);
const fakeNetwork = createFake(Dockerode.Network.prototype);
return {
...fakeNetwork, // by default all methods fail unless overriden
@ -103,7 +103,7 @@ export type MockNetwork = ReturnType<typeof createNetwork>;
export function createContainer(container: PartialContainerInspectInfo) {
const createContainerInspectInfo = (
partial: PartialContainerInspectInfo,
): dockerode.ContainerInspectInfo => {
): Dockerode.ContainerInspectInfo => {
const {
Id,
State,
@ -200,12 +200,12 @@ export function createContainer(container: PartialContainerInspectInfo) {
],
...ContainerInfo,
} as dockerode.ContainerInspectInfo;
} as Dockerode.ContainerInspectInfo;
};
const createContainerInfo = (
containerInspectInfo: dockerode.ContainerInspectInfo,
): dockerode.ContainerInfo => {
containerInspectInfo: Dockerode.ContainerInspectInfo,
): Dockerode.ContainerInfo => {
const {
Id,
Name,
@ -239,7 +239,7 @@ export function createContainer(container: PartialContainerInspectInfo) {
NetworkSettings: {
Networks: NetworkSettings.Networks,
},
Mounts: Mounts as dockerode.ContainerInfo['Mounts'],
Mounts: Mounts as Dockerode.ContainerInfo['Mounts'],
};
};
@ -248,7 +248,7 @@ export function createContainer(container: PartialContainerInspectInfo) {
const { Id: id } = inspectInfo;
const fakeContainer = createFake(dockerode.Container.prototype);
const fakeContainer = createFake(Dockerode.Container.prototype);
return {
...fakeContainer, // by default all methods fail unless overriden
@ -318,7 +318,7 @@ export function createImage(
) {
const createImageInspectInfo = (
partialImage: PartialImageInspectInfo,
): dockerode.ImageInspectInfo => {
): Dockerode.ImageInspectInfo => {
const { Id, ContainerConfig, Config, GraphDriver, RootFS, ...Info } =
partialImage;
@ -357,7 +357,7 @@ export function createImage(
Labels: {},
...ContainerConfig,
} as dockerode.ImageInspectInfo['ContainerConfig'],
} as Dockerode.ImageInspectInfo['ContainerConfig'],
DockerVersion: '17.05.0-ce',
Author: '',
Config: {
@ -385,7 +385,7 @@ export function createImage(
...(Config?.Labels ?? {}),
},
...Config,
} as dockerode.ImageInspectInfo['Config'],
} as Dockerode.ImageInspectInfo['Config'],
Architecture: 'arm64',
Os: 'linux',
@ -400,7 +400,7 @@ export function createImage(
Name: 'aufs',
...GraphDriver,
} as dockerode.ImageInspectInfo['GraphDriver'],
} as Dockerode.ImageInspectInfo['GraphDriver'],
RootFS: {
Type: 'layers',
Layers: [
@ -410,13 +410,13 @@ export function createImage(
],
...RootFS,
} as dockerode.ImageInspectInfo['RootFS'],
} as Dockerode.ImageInspectInfo['RootFS'],
...Info,
};
};
const createImageInfo = (imageInspectInfo: dockerode.ImageInspectInfo) => {
const createImageInfo = (imageInspectInfo: Dockerode.ImageInspectInfo) => {
const {
Id,
Parent: ParentId,
@ -470,7 +470,7 @@ export function createImage(
const info = createImageInfo(inspectInfo);
const { Id: id } = inspectInfo;
const fakeImage = createFake(dockerode.Image.prototype);
const fakeImage = createFake(Dockerode.Image.prototype);
return {
...fakeImage, // by default all methods fail unless overriden
@ -492,20 +492,20 @@ export type MockImage = ReturnType<typeof createImage>;
export function createVolume(volume: PartialVolumeInspectInfo) {
const { Name, Labels, ...partialVolumeInfo } = volume;
const inspectInfo: dockerode.VolumeInspectInfo = {
const inspectInfo: Dockerode.VolumeInspectInfo = {
Name,
Driver: 'local',
Mountpoint: '/var/lib/docker/volumes/resin-data',
Labels: {
...Labels,
} as dockerode.VolumeInspectInfo['Labels'],
} as Dockerode.VolumeInspectInfo['Labels'],
Scope: 'local',
Options: {},
...partialVolumeInfo,
};
const fakeVolume = createFake(dockerode.Volume.prototype);
const fakeVolume = createFake(Dockerode.Volume.prototype);
return {
...fakeVolume, // by default all methods fail unless overriden
name: Name,
@ -630,7 +630,7 @@ export class MockEngine {
return Promise.resolve(delete this.networks[network.id]);
}
createNetwork(options: dockerode.NetworkCreateOptions) {
createNetwork(options: Dockerode.NetworkCreateOptions) {
const Id = uuidv4();
const network = createNetwork({ Id, ...options });
@ -645,7 +645,7 @@ export class MockEngine {
);
}
createContainer(options: dockerode.ContainerCreateOptions) {
createContainer(options: Dockerode.ContainerCreateOptions) {
const Id = uuidv4();
const { name: Name, HostConfig, NetworkingConfig, ...Config } = options;
@ -890,12 +890,12 @@ export class MockEngine {
}
export function createMockerode(engine: MockEngine) {
const dockerodeStubs: Stubs<dockerode> = (
Object.getOwnPropertyNames(dockerode.prototype) as Array<keyof dockerode>
const dockerodeStubs: Stubs<Dockerode> = (
Object.getOwnPropertyNames(Dockerode.prototype) as Array<keyof Dockerode>
)
.filter((fn) => typeof dockerode.prototype[fn] === 'function')
.filter((fn) => typeof Dockerode.prototype[fn] === 'function')
.reduce((stubMap, fn) => {
const stub = sinon.stub(dockerode.prototype, fn);
const stub = sinon.stub(Dockerode.prototype, fn);
const proto: any = MockEngine.prototype;
if (fn in proto) {
@ -907,7 +907,7 @@ export function createMockerode(engine: MockEngine) {
}
return { ...stubMap, [fn]: stub };
}, {} as Stubs<dockerode>);
}, {} as Stubs<Dockerode>);
const { removeImage, removeNetwork, removeVolume, removeContainer } = engine;

View File

@ -131,6 +131,10 @@ module.exports = function (env) {
},
],
},
{
test: /\.node$/,
loader: 'node-loader',
},
],
},
externals: (_context, request, callback) => {