mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-01-31 08:25:36 +00:00
Rename networks to <appUuid>_<networkName>
This is required as we are phasing out app ids and we need to be able to get app uuid from the current state of the network. The app-id now exists as a container in new networks This commit will restart containers as it needs to recreate the network.
This commit is contained in:
parent
0835b29874
commit
5c5483dd3d
@ -29,6 +29,7 @@ import { pathExistsOnHost } from '../lib/fs-utils';
|
||||
|
||||
export interface AppConstructOpts {
|
||||
appId: number;
|
||||
appUuid?: string;
|
||||
appName?: string;
|
||||
commit?: string;
|
||||
source?: string;
|
||||
@ -52,6 +53,7 @@ interface ChangingPair<T> {
|
||||
|
||||
export class App {
|
||||
public appId: number;
|
||||
public appUuid?: string;
|
||||
// When setting up an application from current state, these values are not available
|
||||
public appName?: string;
|
||||
public commit?: string;
|
||||
@ -65,6 +67,7 @@ export class App {
|
||||
|
||||
public constructor(opts: AppConstructOpts, public isTargetState: boolean) {
|
||||
this.appId = opts.appId;
|
||||
this.appUuid = opts.appUuid;
|
||||
this.appName = opts.appName;
|
||||
this.commit = opts.commit;
|
||||
this.source = opts.source;
|
||||
@ -77,6 +80,7 @@ export class App {
|
||||
this.networks.default = Network.fromComposeObject(
|
||||
'default',
|
||||
opts.appId,
|
||||
opts.appUuid!, // app uuid always exists on the target state
|
||||
{},
|
||||
);
|
||||
}
|
||||
@ -160,7 +164,6 @@ export class App {
|
||||
target.commit != null &&
|
||||
this.commit !== target.commit
|
||||
) {
|
||||
// TODO: The next PR should change this to support multiapp commit values
|
||||
steps.push(
|
||||
generateStep('updateCommit', {
|
||||
target: target.commit,
|
||||
@ -732,7 +735,7 @@ export class App {
|
||||
const networks = _.mapValues(
|
||||
JSON.parse(app.networks) ?? {},
|
||||
(conf, name) => {
|
||||
return Network.fromComposeObject(name, app.appId, conf ?? {});
|
||||
return Network.fromComposeObject(name, app.appId, app.uuid, conf ?? {});
|
||||
},
|
||||
);
|
||||
|
||||
@ -799,6 +802,7 @@ export class App {
|
||||
return new App(
|
||||
{
|
||||
appId: app.appId,
|
||||
appUuid: app.uuid,
|
||||
commit: app.commit,
|
||||
appName: app.name,
|
||||
source: app.source,
|
||||
|
@ -23,16 +23,12 @@ export function getAll(): Bluebird<Network[]> {
|
||||
});
|
||||
}
|
||||
|
||||
export function getAllByAppId(appId: number): Bluebird<Network[]> {
|
||||
return getAll().filter((network: Network) => network.appId === appId);
|
||||
}
|
||||
|
||||
export async function get(network: {
|
||||
async function get(network: {
|
||||
name: string;
|
||||
appId: number;
|
||||
appUuid: string;
|
||||
}): Promise<Network> {
|
||||
const dockerNet = await docker
|
||||
.getNetwork(Network.generateDockerName(network.appId, network.name))
|
||||
.getNetwork(Network.generateDockerName(network.appUuid, network.name))
|
||||
.inspect();
|
||||
return Network.fromDockerNetwork(dockerNet);
|
||||
}
|
||||
@ -41,7 +37,7 @@ export async function create(network: Network) {
|
||||
try {
|
||||
const existing = await get({
|
||||
name: network.name,
|
||||
appId: network.appId,
|
||||
appUuid: network.appUuid!, // new networks will always have uuid
|
||||
});
|
||||
if (!network.isEqualConfig(existing)) {
|
||||
throw new ResourceRecreationAttemptError('network', network.name);
|
||||
@ -52,7 +48,7 @@ export async function create(network: Network) {
|
||||
} catch (e) {
|
||||
if (!NotFoundError(e)) {
|
||||
logger.logSystemEvent(logTypes.createNetworkError, {
|
||||
network: { name: network.name, appId: network.appId },
|
||||
network: { name: network.name, appUuid: network.appUuid },
|
||||
error: e,
|
||||
});
|
||||
throw e;
|
||||
|
@ -1,4 +1,3 @@
|
||||
import * as Bluebird from 'bluebird';
|
||||
import * as _ from 'lodash';
|
||||
import * as dockerode from 'dockerode';
|
||||
|
||||
@ -11,29 +10,64 @@ import * as ComposeUtils from './utils';
|
||||
import { ComposeNetworkConfig, NetworkConfig } from './types/network';
|
||||
|
||||
import { InvalidNetworkNameError } from './errors';
|
||||
import { InternalInconsistencyError } from '../lib/errors';
|
||||
|
||||
export class Network {
|
||||
public appId: number;
|
||||
public appUuid?: string;
|
||||
public name: string;
|
||||
public config: NetworkConfig;
|
||||
|
||||
private constructor() {}
|
||||
|
||||
private static deconstructDockerName(
|
||||
name: string,
|
||||
): { name: string; appId?: number; appUuid?: string } {
|
||||
const matchWithAppId = name.match(/^(\d+)_(\S+)/);
|
||||
if (matchWithAppId == null) {
|
||||
const matchWithAppUuid = name.match(/^([0-9a-f-A-F]{32,})_(\S+)/);
|
||||
|
||||
if (!matchWithAppUuid) {
|
||||
throw new InvalidNetworkNameError(name);
|
||||
}
|
||||
|
||||
const appUuid = matchWithAppUuid[1];
|
||||
return { name: matchWithAppUuid[2], appUuid };
|
||||
}
|
||||
|
||||
const appId = parseInt(matchWithAppId[1], 10);
|
||||
if (isNaN(appId)) {
|
||||
throw new InvalidNetworkNameError(name);
|
||||
}
|
||||
|
||||
return {
|
||||
appId,
|
||||
name: matchWithAppId[2],
|
||||
};
|
||||
}
|
||||
|
||||
public static fromDockerNetwork(
|
||||
network: dockerode.NetworkInspectInfo,
|
||||
): Network {
|
||||
const ret = new Network();
|
||||
|
||||
const match = network.Name.match(/^([0-9]+)_(.+)$/);
|
||||
if (match == null) {
|
||||
throw new InvalidNetworkNameError(network.Name);
|
||||
// Detect the name and appId from the inspect data
|
||||
const { name, appId, appUuid } = Network.deconstructDockerName(
|
||||
network.Name,
|
||||
);
|
||||
|
||||
const labels = network.Labels ?? {};
|
||||
if (!appId && isNaN(parseInt(labels['io.balena.app-id'], 10))) {
|
||||
// This should never happen as supervised networks will always have either
|
||||
// the id or the label
|
||||
throw new InternalInconsistencyError(
|
||||
`Could not read app id from network: ${network.Name}`,
|
||||
);
|
||||
}
|
||||
|
||||
// If the regex match succeeds `match[1]` should be a number
|
||||
const appId = parseInt(match[1], 10);
|
||||
|
||||
ret.appId = appId;
|
||||
ret.name = match[2];
|
||||
ret.appId = appId ?? parseInt(labels['io.balena.app-id'], 10);
|
||||
ret.name = name;
|
||||
ret.appUuid = appUuid;
|
||||
|
||||
const config = network.IPAM?.Config || [];
|
||||
|
||||
@ -51,7 +85,7 @@ export class Network {
|
||||
},
|
||||
enableIPv6: network.EnableIPv6,
|
||||
internal: network.Internal,
|
||||
labels: _.omit(ComposeUtils.normalizeLabels(network.Labels ?? {}), [
|
||||
labels: _.omit(ComposeUtils.normalizeLabels(labels), [
|
||||
'io.balena.supervised',
|
||||
]),
|
||||
options: network.Options ?? {},
|
||||
@ -63,6 +97,7 @@ export class Network {
|
||||
public static fromComposeObject(
|
||||
name: string,
|
||||
appId: number,
|
||||
appUuid: string,
|
||||
network: Partial<Omit<ComposeNetworkConfig, 'ipam'>> & {
|
||||
ipam?: Partial<ComposeNetworkConfig['ipam']>;
|
||||
},
|
||||
@ -70,6 +105,7 @@ export class Network {
|
||||
const net = new Network();
|
||||
net.name = name;
|
||||
net.appId = appId;
|
||||
net.appUuid = appUuid;
|
||||
|
||||
Network.validateComposeConfig(network);
|
||||
|
||||
@ -95,12 +131,13 @@ export class Network {
|
||||
},
|
||||
enableIPv6: network.enable_ipv6 || false,
|
||||
internal: network.internal || false,
|
||||
labels: network.labels || {},
|
||||
labels: {
|
||||
'io.balena.app-id': String(appId),
|
||||
...ComposeUtils.normalizeLabels(network.labels || {}),
|
||||
},
|
||||
options: network.driver_opts || {},
|
||||
};
|
||||
|
||||
net.config.labels = ComposeUtils.normalizeLabels(net.config.labels);
|
||||
|
||||
return net;
|
||||
}
|
||||
|
||||
@ -117,7 +154,7 @@ export class Network {
|
||||
|
||||
public async create(): Promise<void> {
|
||||
logger.logSystemEvent(logTypes.createNetwork, {
|
||||
network: { name: this.name },
|
||||
network: { name: this.name, appUuid: this.appUuid },
|
||||
});
|
||||
|
||||
await docker.createNetwork(this.toDockerConfig());
|
||||
@ -125,7 +162,7 @@ export class Network {
|
||||
|
||||
public toDockerConfig(): dockerode.NetworkCreateOptions {
|
||||
return {
|
||||
Name: Network.generateDockerName(this.appId, this.name),
|
||||
Name: Network.generateDockerName(this.appUuid!, this.name),
|
||||
Driver: this.config.driver,
|
||||
CheckDuplicate: true,
|
||||
Options: this.config.options,
|
||||
@ -153,28 +190,41 @@ export class Network {
|
||||
};
|
||||
}
|
||||
|
||||
public remove(): Bluebird<void> {
|
||||
public async remove() {
|
||||
logger.logSystemEvent(logTypes.removeNetwork, {
|
||||
network: { name: this.name, appId: this.appId },
|
||||
network: { name: this.name, appUuid: this.appUuid },
|
||||
});
|
||||
|
||||
const networkName = Network.generateDockerName(this.appId, this.name);
|
||||
|
||||
return Bluebird.resolve(docker.listNetworks())
|
||||
.then((networks) => networks.filter((n) => n.Name === networkName))
|
||||
.then(([network]) => {
|
||||
if (!network) {
|
||||
return Bluebird.resolve();
|
||||
// Find the network
|
||||
const [networkName] = (await docker.listNetworks())
|
||||
.filter((network) => {
|
||||
try {
|
||||
const { appId, appUuid, name } = Network.deconstructDockerName(
|
||||
network.Name,
|
||||
);
|
||||
return (
|
||||
name === this.name &&
|
||||
(appId === this.appId || appUuid === this.appUuid)
|
||||
);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
return Bluebird.resolve(
|
||||
docker.getNetwork(networkName).remove(),
|
||||
).tapCatch((error) => {
|
||||
logger.logSystemEvent(logTypes.removeNetworkError, {
|
||||
network: { name: this.name, appId: this.appId },
|
||||
error,
|
||||
});
|
||||
});
|
||||
})
|
||||
.map((network) => network.Name);
|
||||
|
||||
if (!networkName) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await docker.getNetwork(networkName).remove();
|
||||
} catch (error) {
|
||||
logger.logSystemEvent(logTypes.removeNetworkError, {
|
||||
network: { name: this.name, appUuid: this.appUuid },
|
||||
error,
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
public isEqualConfig(network: Network): boolean {
|
||||
@ -210,8 +260,8 @@ export class Network {
|
||||
}
|
||||
}
|
||||
|
||||
public static generateDockerName(appId: number, name: string) {
|
||||
return `${appId}_${name}`;
|
||||
public static generateDockerName(appIdOrUuid: number | string, name: string) {
|
||||
return `${appIdOrUuid}_${name}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,7 +171,7 @@ export class Service {
|
||||
networks = config.networks || {};
|
||||
}
|
||||
// Prefix the network entries with the app id
|
||||
networks = _.mapKeys(networks, (_v, k) => `${service.appId}_${k}`);
|
||||
networks = _.mapKeys(networks, (_v, k) => `${service.appUuid}_${k}`);
|
||||
// Ensure that we add an alias of the service name
|
||||
networks = _.mapValues(networks, (v) => {
|
||||
if (v.aliases == null) {
|
||||
@ -257,7 +257,7 @@ export class Service {
|
||||
) {
|
||||
if (networks[config.networkMode!] == null && !serviceNetworkMode) {
|
||||
// The network mode has not been set explicitly
|
||||
config.networkMode = `${service.appId}_${config.networkMode}`;
|
||||
config.networkMode = `${service.appUuid}_${config.networkMode}`;
|
||||
// If we don't have any networks, we need to
|
||||
// create the default with some default options
|
||||
networks[config.networkMode] = {
|
||||
@ -1008,11 +1008,23 @@ export class Service {
|
||||
public hasNetwork(networkName: string) {
|
||||
// TODO; we could probably export network naming methods to another
|
||||
// module to avoid duplicate code
|
||||
return `${this.appId}_${networkName}` in this.config.networks;
|
||||
// We don't know if this service is current or target state so we need
|
||||
// to check both appId and appUuid since the current service may still
|
||||
// have appId
|
||||
return (
|
||||
`${this.appUuid}_${networkName}` in this.config.networks ||
|
||||
`${this.appId}_${networkName}` in this.config.networks
|
||||
);
|
||||
}
|
||||
|
||||
public hasNetworkMode(networkName: string) {
|
||||
return `${this.appId}_${networkName}` === this.config.networkMode;
|
||||
// We don't know if this service is current or target state so we need
|
||||
// to check both appId and appUuid since the current service may still
|
||||
// have appId
|
||||
return (
|
||||
`${this.appUuid}_${networkName}` === this.config.networkMode ||
|
||||
`${this.appId}_${networkName}` === this.config.networkMode
|
||||
);
|
||||
}
|
||||
|
||||
public hasVolume(volumeName: string) {
|
||||
|
@ -12,7 +12,7 @@ import { withMockerode } from './lib/mockerode';
|
||||
|
||||
function getDefaultNetwork(appId: number) {
|
||||
return {
|
||||
default: Network.fromComposeObject('default', appId, {}),
|
||||
default: Network.fromComposeObject('default', appId, 'deadbeef', {}),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,7 @@
|
||||
"Type": "journald",
|
||||
"Config": {}
|
||||
},
|
||||
"NetworkMode": "1011165_default",
|
||||
"NetworkMode": "aaaaaaaa_default",
|
||||
"PortBindings": {},
|
||||
"RestartPolicy": {
|
||||
"Name": "always",
|
||||
@ -209,7 +209,7 @@
|
||||
"IPv6Gateway": "",
|
||||
"MacAddress": "",
|
||||
"Networks": {
|
||||
"1011165_default": {
|
||||
"aaaaaaaa_default": {
|
||||
"IPAMConfig": {},
|
||||
"Links": null,
|
||||
"Aliases": [
|
||||
|
@ -209,7 +209,7 @@
|
||||
"IPv6Gateway": "",
|
||||
"MacAddress": "",
|
||||
"Networks": {
|
||||
"1011165_default": {
|
||||
"aaaaaaaa_default": {
|
||||
"IPAMConfig": null,
|
||||
"Links": null,
|
||||
"Aliases": [
|
||||
|
@ -43,7 +43,7 @@
|
||||
"Type": "journald",
|
||||
"Config": {}
|
||||
},
|
||||
"NetworkMode": "1011165_default",
|
||||
"NetworkMode": "aaaaaaaa_default",
|
||||
"PortBindings": {},
|
||||
"RestartPolicy": {
|
||||
"Name": "always",
|
||||
@ -209,7 +209,7 @@
|
||||
"IPv6Gateway": "",
|
||||
"MacAddress": "",
|
||||
"Networks": {
|
||||
"1011165_default": {
|
||||
"aaaaaaaa_default": {
|
||||
"IPAMConfig": null,
|
||||
"Links": null,
|
||||
"Aliases": [
|
||||
|
@ -4,7 +4,6 @@ import rewire = require('rewire');
|
||||
|
||||
import { unlinkAll } from '../../src/lib/fs-utils';
|
||||
import * as applicationManager from '../../src/compose/application-manager';
|
||||
import * as networkManager from '../../src/compose/network-manager';
|
||||
import * as serviceManager from '../../src/compose/service-manager';
|
||||
import * as volumeManager from '../../src/compose/volume-manager';
|
||||
import * as commitStore from '../../src/compose/commit';
|
||||
@ -185,7 +184,6 @@ function buildRoutes(): Router {
|
||||
}
|
||||
|
||||
// TO-DO: Create a cleaner way to restore previous values.
|
||||
const originalNetGetAll = networkManager.getAllByAppId;
|
||||
const originalVolGetAll = volumeManager.getAllByAppId;
|
||||
const originalSvcGetAppId = serviceManager.getAllByAppId;
|
||||
const originalSvcGetStatus = serviceManager.getState;
|
||||
@ -194,8 +192,6 @@ const originalReadyForUpdates = apiBinder.__get__('readyForUpdates');
|
||||
function setupStubs() {
|
||||
apiBinder.__set__('readyForUpdates', true);
|
||||
// @ts-expect-error Assigning to a RO property
|
||||
networkManager.getAllByAppId = async () => STUBBED_VALUES.networks;
|
||||
// @ts-expect-error Assigning to a RO property
|
||||
volumeManager.getAllByAppId = async () => STUBBED_VALUES.volumes;
|
||||
// @ts-expect-error Assigning to a RO property
|
||||
serviceManager.getState = async () => STUBBED_VALUES.services;
|
||||
@ -207,8 +203,6 @@ function setupStubs() {
|
||||
function restoreStubs() {
|
||||
apiBinder.__set__('readyForUpdates', originalReadyForUpdates);
|
||||
// @ts-expect-error Assigning to a RO property
|
||||
networkManager.getAllByAppId = originalNetGetAll;
|
||||
// @ts-expect-error Assigning to a RO property
|
||||
volumeManager.getAllByAppId = originalVolGetAll;
|
||||
// @ts-expect-error Assigning to a RO property
|
||||
serviceManager.getState = originalSvcGetStatus;
|
||||
|
@ -26,10 +26,12 @@ function createApp({
|
||||
volumes = [] as Volume[],
|
||||
isTarget = false,
|
||||
appId = 1,
|
||||
appUuid = 'appuuid',
|
||||
} = {}) {
|
||||
return new App(
|
||||
{
|
||||
appId,
|
||||
appUuid,
|
||||
services,
|
||||
networks: networks.reduce(
|
||||
(res, net) => ({ ...res, [net.name]: net }),
|
||||
@ -44,6 +46,7 @@ function createApp({
|
||||
async function createService(
|
||||
{
|
||||
appId = 1,
|
||||
appUuid = 'appuuid',
|
||||
serviceName = 'test',
|
||||
commit = 'test-commit',
|
||||
...conf
|
||||
@ -53,6 +56,7 @@ async function createService(
|
||||
const svc = await Service.fromComposeObject(
|
||||
{
|
||||
appId,
|
||||
appUuid,
|
||||
serviceName,
|
||||
commit,
|
||||
running: true,
|
||||
@ -71,14 +75,18 @@ async function createService(
|
||||
function createImage(
|
||||
{
|
||||
appId = 1,
|
||||
appUuid = 'appuuid',
|
||||
dependent = 0,
|
||||
name = 'test-image',
|
||||
serviceName = 'test',
|
||||
commit = 'test-commit',
|
||||
...extra
|
||||
} = {} as Partial<Image>,
|
||||
) {
|
||||
return {
|
||||
appId,
|
||||
appUuid,
|
||||
commit,
|
||||
dependent,
|
||||
name,
|
||||
serviceName,
|
||||
@ -107,7 +115,7 @@ function expectNoStep(action: CompositionStepAction, steps: CompositionStep[]) {
|
||||
expectSteps(action, steps, 0, 0);
|
||||
}
|
||||
|
||||
const defaultNetwork = Network.fromComposeObject('default', 1, {});
|
||||
const defaultNetwork = Network.fromComposeObject('default', 1, 'appuuid', {});
|
||||
|
||||
describe('compose/app', () => {
|
||||
before(() => {
|
||||
@ -323,14 +331,12 @@ describe('compose/app', () => {
|
||||
|
||||
it('should generate the correct step sequence for a volume purge request', async () => {
|
||||
const service = await createService({
|
||||
appId: 1,
|
||||
appUuid: 'deadbeef',
|
||||
image: 'test-image',
|
||||
composition: { volumes: ['db-volume:/data'] },
|
||||
});
|
||||
const volume = Volume.fromComposeObject(
|
||||
'db-volume',
|
||||
service.appId,
|
||||
'deadbeef',
|
||||
);
|
||||
const volume = Volume.fromComposeObject('db-volume', 1, 'deadbeef');
|
||||
const contextWithImages = {
|
||||
...defaultContext,
|
||||
...{
|
||||
@ -428,7 +434,7 @@ describe('compose/app', () => {
|
||||
it('should correctly infer a network create step', () => {
|
||||
const current = createApp({ networks: [] });
|
||||
const target = createApp({
|
||||
networks: [Network.fromComposeObject('default', 1, {})],
|
||||
networks: [Network.fromComposeObject('default', 1, 'deadbeef', {})],
|
||||
isTarget: true,
|
||||
});
|
||||
|
||||
@ -442,7 +448,9 @@ describe('compose/app', () => {
|
||||
|
||||
it('should correctly infer a network remove step', () => {
|
||||
const current = createApp({
|
||||
networks: [Network.fromComposeObject('test-network', 1, {})],
|
||||
networks: [
|
||||
Network.fromComposeObject('test-network', 1, 'deadbeef', {}),
|
||||
],
|
||||
isTarget: true,
|
||||
});
|
||||
const target = createApp({ networks: [], isTarget: true });
|
||||
@ -459,8 +467,8 @@ describe('compose/app', () => {
|
||||
it('should correctly infer more than one network removal step', () => {
|
||||
const current = createApp({
|
||||
networks: [
|
||||
Network.fromComposeObject('test-network', 1, {}),
|
||||
Network.fromComposeObject('test-network-2', 1, {}),
|
||||
Network.fromComposeObject('test-network', 1, 'deadbeef', {}),
|
||||
Network.fromComposeObject('test-network-2', 1, 'deadbeef', {}),
|
||||
],
|
||||
isTarget: true,
|
||||
});
|
||||
@ -480,11 +488,13 @@ describe('compose/app', () => {
|
||||
|
||||
it('should correctly infer a network recreation step', () => {
|
||||
const current = createApp({
|
||||
networks: [Network.fromComposeObject('test-network', 1, {})],
|
||||
networks: [
|
||||
Network.fromComposeObject('test-network', 1, 'deadbeef', {}),
|
||||
],
|
||||
});
|
||||
const target = createApp({
|
||||
networks: [
|
||||
Network.fromComposeObject('test-network', 1, {
|
||||
Network.fromComposeObject('test-network', 1, 'deadbeef', {
|
||||
labels: { TEST: 'TEST' },
|
||||
}),
|
||||
],
|
||||
@ -524,20 +534,28 @@ describe('compose/app', () => {
|
||||
expect(createNetworkStep)
|
||||
.to.have.property('target')
|
||||
.that.has.property('config')
|
||||
.that.deep.includes({ labels: { TEST: 'TEST' } });
|
||||
.that.deep.includes({
|
||||
labels: { TEST: 'TEST', 'io.balena.app-id': '1' },
|
||||
});
|
||||
});
|
||||
|
||||
it('should kill dependencies of networks before removing', async () => {
|
||||
const current = createApp({
|
||||
appUuid: 'deadbeef',
|
||||
services: [
|
||||
await createService({
|
||||
composition: { networks: { 'test-network': {} } },
|
||||
appId: 1,
|
||||
appUuid: 'deadbeef',
|
||||
composition: { networks: ['test-network'] },
|
||||
}),
|
||||
],
|
||||
networks: [Network.fromComposeObject('test-network', 1, {})],
|
||||
networks: [
|
||||
Network.fromComposeObject('test-network', 1, 'deadbeef', {}),
|
||||
],
|
||||
});
|
||||
const target = createApp({
|
||||
services: [await createService()],
|
||||
appUuid: 'deadbeef',
|
||||
services: [await createService({ appUuid: 'deadbeef' })],
|
||||
networks: [],
|
||||
isTarget: true,
|
||||
});
|
||||
@ -554,10 +572,10 @@ describe('compose/app', () => {
|
||||
const current = createApp({
|
||||
services: [
|
||||
await createService({
|
||||
composition: { networks: { 'test-network': {} } },
|
||||
composition: { networks: ['test-network'] },
|
||||
}),
|
||||
],
|
||||
networks: [Network.fromComposeObject('test-network', 1, {})],
|
||||
networks: [Network.fromComposeObject('test-network', 1, 'appuuid', {})],
|
||||
});
|
||||
const target = createApp({
|
||||
services: [
|
||||
@ -566,7 +584,7 @@ describe('compose/app', () => {
|
||||
}),
|
||||
],
|
||||
networks: [
|
||||
Network.fromComposeObject('test-network', 1, {
|
||||
Network.fromComposeObject('test-network', 1, 'appuuid', {
|
||||
labels: { test: 'test' },
|
||||
}),
|
||||
],
|
||||
@ -599,7 +617,7 @@ describe('compose/app', () => {
|
||||
|
||||
it('should not create the default network if it already exists', () => {
|
||||
const current = createApp({
|
||||
networks: [Network.fromComposeObject('default', 1, {})],
|
||||
networks: [Network.fromComposeObject('default', 1, 'deadbeef', {})],
|
||||
});
|
||||
const target = createApp({ networks: [], isTarget: true });
|
||||
|
||||
@ -611,17 +629,17 @@ describe('compose/app', () => {
|
||||
});
|
||||
|
||||
describe('service state behavior', () => {
|
||||
it('should create a kill step for service which is no longer referenced', async () => {
|
||||
it('should create a kill step for a service which is no longer referenced', async () => {
|
||||
const current = createApp({
|
||||
services: [
|
||||
await createService({ appId: 1, serviceName: 'main' }),
|
||||
await createService({ appId: 1, serviceName: 'aux' }),
|
||||
],
|
||||
networks: [Network.fromComposeObject('test-network', 1, {})],
|
||||
networks: [defaultNetwork],
|
||||
});
|
||||
const target = createApp({
|
||||
services: [await createService({ appId: 1, serviceName: 'main' })],
|
||||
networks: [Network.fromComposeObject('test-network', 1, {})],
|
||||
networks: [defaultNetwork],
|
||||
isTarget: true,
|
||||
});
|
||||
|
||||
@ -827,7 +845,7 @@ describe('compose/app', () => {
|
||||
const intermediate = createApp({
|
||||
services: [],
|
||||
// Default network was already created
|
||||
networks: [Network.fromComposeObject('default', 1, {})],
|
||||
networks: [defaultNetwork],
|
||||
});
|
||||
|
||||
// now should see a 'start'
|
||||
@ -1189,7 +1207,10 @@ describe('compose/app', () => {
|
||||
appId: 1,
|
||||
}),
|
||||
],
|
||||
networks: [defaultNetwork, Network.fromComposeObject('test', 1, {})],
|
||||
networks: [
|
||||
defaultNetwork,
|
||||
Network.fromComposeObject('test', 1, 'appuuid', {}),
|
||||
],
|
||||
isTarget: true,
|
||||
});
|
||||
|
||||
|
@ -15,12 +15,12 @@ import { InstancedAppState } from '../../../src/types/state';
|
||||
|
||||
import * as dbHelper from '../../lib/db-helper';
|
||||
|
||||
const DEFAULT_NETWORK = Network.fromComposeObject('default', 1, {});
|
||||
const DEFAULT_NETWORK = Network.fromComposeObject('default', 1, 'appuuid', {});
|
||||
|
||||
async function createService(
|
||||
{
|
||||
appId = 1,
|
||||
appUuid = 'app-uuid',
|
||||
appUuid = 'appuuid',
|
||||
serviceName = 'main',
|
||||
commit = 'main-commit',
|
||||
...conf
|
||||
@ -54,7 +54,7 @@ async function createService(
|
||||
function createImage(
|
||||
{
|
||||
appId = 1,
|
||||
appUuid = 'app-uuid',
|
||||
appUuid = 'appuuid',
|
||||
name = 'test-image',
|
||||
serviceName = 'main',
|
||||
commit = 'main-commit',
|
||||
@ -582,7 +582,7 @@ describe('compose/application-manager', () => {
|
||||
await createService({
|
||||
image: 'main-image',
|
||||
appId: 1,
|
||||
appUuid: 'app-uuid',
|
||||
appUuid: 'appuuid',
|
||||
commit: 'new-release',
|
||||
serviceName: 'main',
|
||||
composition: {
|
||||
@ -592,7 +592,7 @@ describe('compose/application-manager', () => {
|
||||
await createService({
|
||||
image: 'dep-image',
|
||||
appId: 1,
|
||||
appUuid: 'app-uuid',
|
||||
appUuid: 'appuuid',
|
||||
commit: 'new-release',
|
||||
serviceName: 'dep',
|
||||
}),
|
||||
@ -611,7 +611,7 @@ describe('compose/application-manager', () => {
|
||||
services: [
|
||||
await createService({
|
||||
appId: 1,
|
||||
appUuid: 'app-uuid',
|
||||
appUuid: 'appuuid',
|
||||
commit: 'old-release',
|
||||
serviceName: 'main',
|
||||
composition: {
|
||||
@ -620,7 +620,7 @@ describe('compose/application-manager', () => {
|
||||
}),
|
||||
await createService({
|
||||
appId: 1,
|
||||
appUuid: 'app-uuid',
|
||||
appUuid: 'appuuid',
|
||||
commit: 'old-release',
|
||||
serviceName: 'dep',
|
||||
}),
|
||||
@ -630,14 +630,14 @@ describe('compose/application-manager', () => {
|
||||
// Both images have been downloaded
|
||||
createImage({
|
||||
appId: 1,
|
||||
appUuid: 'app-uuid',
|
||||
appUuid: 'appuuid',
|
||||
name: 'main-image',
|
||||
serviceName: 'main',
|
||||
commit: 'new-release',
|
||||
}),
|
||||
createImage({
|
||||
appId: 1,
|
||||
appUuid: 'app-uuid',
|
||||
appUuid: 'appuuid',
|
||||
name: 'dep-image',
|
||||
serviceName: 'dep',
|
||||
commit: 'new-release',
|
||||
@ -1202,8 +1202,8 @@ describe('compose/application-manager', () => {
|
||||
],
|
||||
networks: [
|
||||
// Default networks for two apps
|
||||
Network.fromComposeObject('default', 1, {}),
|
||||
Network.fromComposeObject('default', 2, {}),
|
||||
Network.fromComposeObject('default', 1, 'app-one', {}),
|
||||
Network.fromComposeObject('default', 2, 'app-two', {}),
|
||||
],
|
||||
},
|
||||
true,
|
||||
@ -1217,8 +1217,8 @@ describe('compose/application-manager', () => {
|
||||
services: [],
|
||||
networks: [
|
||||
// Default networks for two apps
|
||||
Network.fromComposeObject('default', 1, {}),
|
||||
Network.fromComposeObject('default', 2, {}),
|
||||
Network.fromComposeObject('default', 1, 'app-one', {}),
|
||||
Network.fromComposeObject('default', 2, 'app-two', {}),
|
||||
],
|
||||
images: [
|
||||
createImage({
|
||||
|
@ -10,10 +10,16 @@ import { log } from '../../../src/lib/supervisor-console';
|
||||
describe('compose/network', () => {
|
||||
describe('creating a network from a compose object', () => {
|
||||
it('creates a default network configuration if no config is given', () => {
|
||||
const network = Network.fromComposeObject('default', 12345, {});
|
||||
const network = Network.fromComposeObject(
|
||||
'default',
|
||||
12345,
|
||||
'deadbeef',
|
||||
{},
|
||||
);
|
||||
|
||||
expect(network.name).to.equal('default');
|
||||
expect(network.appId).to.equal(12345);
|
||||
expect(network.appUuid).to.equal('deadbeef');
|
||||
|
||||
// Default configuration options
|
||||
expect(network.config.driver).to.equal('bridge');
|
||||
@ -23,12 +29,14 @@ describe('compose/network', () => {
|
||||
options: {},
|
||||
});
|
||||
expect(network.config.enableIPv6).to.equal(false);
|
||||
expect(network.config.labels).to.deep.equal({});
|
||||
expect(network.config.labels).to.deep.equal({
|
||||
'io.balena.app-id': '12345',
|
||||
});
|
||||
expect(network.config.options).to.deep.equal({});
|
||||
});
|
||||
|
||||
it('normalizes legacy labels', () => {
|
||||
const network = Network.fromComposeObject('default', 12345, {
|
||||
const network = Network.fromComposeObject('default', 12345, 'deadbeef', {
|
||||
labels: {
|
||||
'io.resin.features.something': '1234',
|
||||
},
|
||||
@ -36,11 +44,12 @@ describe('compose/network', () => {
|
||||
|
||||
expect(network.config.labels).to.deep.equal({
|
||||
'io.balena.features.something': '1234',
|
||||
'io.balena.app-id': '12345',
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts valid IPAM configurations', () => {
|
||||
const network0 = Network.fromComposeObject('default', 12345, {
|
||||
const network0 = Network.fromComposeObject('default', 12345, 'deadbeef', {
|
||||
ipam: { driver: 'dummy', config: [], options: {} },
|
||||
});
|
||||
|
||||
@ -51,7 +60,7 @@ describe('compose/network', () => {
|
||||
options: {},
|
||||
});
|
||||
|
||||
const network1 = Network.fromComposeObject('default', 12345, {
|
||||
const network1 = Network.fromComposeObject('default', 12345, 'deadbeef', {
|
||||
ipam: {
|
||||
driver: 'default',
|
||||
config: [
|
||||
@ -84,7 +93,7 @@ describe('compose/network', () => {
|
||||
it('warns about IPAM configuration without both gateway and subnet', () => {
|
||||
const logSpy = sinon.spy(log, 'warn');
|
||||
|
||||
Network.fromComposeObject('default', 12345, {
|
||||
Network.fromComposeObject('default', 12345, 'deadbeef', {
|
||||
ipam: {
|
||||
driver: 'default',
|
||||
config: [
|
||||
@ -103,7 +112,7 @@ describe('compose/network', () => {
|
||||
|
||||
logSpy.resetHistory();
|
||||
|
||||
Network.fromComposeObject('default', 12345, {
|
||||
Network.fromComposeObject('default', 12345, 'deadbeef', {
|
||||
ipam: {
|
||||
driver: 'default',
|
||||
config: [
|
||||
@ -124,7 +133,7 @@ describe('compose/network', () => {
|
||||
});
|
||||
|
||||
it('parses values from a compose object', () => {
|
||||
const network1 = Network.fromComposeObject('default', 12345, {
|
||||
const network1 = Network.fromComposeObject('default', 12345, 'deadbeef', {
|
||||
driver: 'bridge',
|
||||
enable_ipv6: true,
|
||||
internal: false,
|
||||
@ -171,6 +180,7 @@ describe('compose/network', () => {
|
||||
|
||||
expect(dockerConfig.Labels).to.deep.equal({
|
||||
'io.balena.supervised': 'true',
|
||||
'io.balena.app-id': '12345',
|
||||
'com.docker.some-label': 'yes',
|
||||
});
|
||||
|
||||
@ -209,9 +219,17 @@ describe('compose/network', () => {
|
||||
Name: '1234',
|
||||
} as NetworkInspectInfo),
|
||||
).to.throw();
|
||||
|
||||
expect(() =>
|
||||
Network.fromDockerNetwork({
|
||||
Id: 'deadbeef',
|
||||
Name: 'a173bdb734884b778f5cc3dffd18733e_default',
|
||||
Labels: {}, // no app-id
|
||||
} as NetworkInspectInfo),
|
||||
).to.throw();
|
||||
});
|
||||
|
||||
it('creates a network object from a docker network configuration', () => {
|
||||
it('creates a network object from a legacy docker network configuration', () => {
|
||||
const network = Network.fromDockerNetwork({
|
||||
Id: 'deadbeef',
|
||||
Name: '1234_default',
|
||||
@ -233,6 +251,7 @@ describe('compose/network', () => {
|
||||
'com.docker.some-option': 'abcd',
|
||||
} as NetworkInspectInfo['Options'],
|
||||
Labels: {
|
||||
'io.balena.supervised': 'true',
|
||||
'io.balena.features.something': '123',
|
||||
} as NetworkInspectInfo['Labels'],
|
||||
} as NetworkInspectInfo);
|
||||
@ -257,6 +276,56 @@ describe('compose/network', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('creates a network object from a docker network configuration', () => {
|
||||
const network = Network.fromDockerNetwork({
|
||||
Id: 'deadbeef',
|
||||
Name: 'a173bdb734884b778f5cc3dffd18733e_default',
|
||||
Driver: 'bridge',
|
||||
EnableIPv6: true,
|
||||
IPAM: {
|
||||
Driver: 'default',
|
||||
Options: {},
|
||||
Config: [
|
||||
{
|
||||
Subnet: '172.18.0.0/16',
|
||||
Gateway: '172.18.0.1',
|
||||
},
|
||||
],
|
||||
} as NetworkInspectInfo['IPAM'],
|
||||
Internal: true,
|
||||
Containers: {},
|
||||
Options: {
|
||||
'com.docker.some-option': 'abcd',
|
||||
} as NetworkInspectInfo['Options'],
|
||||
Labels: {
|
||||
'io.balena.supervised': 'true',
|
||||
'io.balena.features.something': '123',
|
||||
'io.balena.app-id': '1234',
|
||||
} as NetworkInspectInfo['Labels'],
|
||||
} as NetworkInspectInfo);
|
||||
|
||||
expect(network.appId).to.equal(1234);
|
||||
expect(network.appUuid).to.equal('a173bdb734884b778f5cc3dffd18733e');
|
||||
expect(network.name).to.equal('default');
|
||||
expect(network.config.enableIPv6).to.equal(true);
|
||||
expect(network.config.ipam.driver).to.equal('default');
|
||||
expect(network.config.ipam.options).to.deep.equal({});
|
||||
expect(network.config.ipam.config).to.deep.equal([
|
||||
{
|
||||
subnet: '172.18.0.0/16',
|
||||
gateway: '172.18.0.1',
|
||||
},
|
||||
]);
|
||||
expect(network.config.internal).to.equal(true);
|
||||
expect(network.config.options).to.deep.equal({
|
||||
'com.docker.some-option': 'abcd',
|
||||
});
|
||||
expect(network.config.labels).to.deep.equal({
|
||||
'io.balena.features.something': '123',
|
||||
'io.balena.app-id': '1234',
|
||||
});
|
||||
});
|
||||
|
||||
it('normalizes legacy label names and excludes supervised label', () => {
|
||||
const network = Network.fromDockerNetwork({
|
||||
Id: 'deadbeef',
|
||||
@ -284,7 +353,7 @@ describe('compose/network', () => {
|
||||
it('creates a docker compose network object from the internal network config', () => {
|
||||
const network = Network.fromDockerNetwork({
|
||||
Id: 'deadbeef',
|
||||
Name: '1234_default',
|
||||
Name: 'a173bdb734884b778f5cc3dffd18733e_default',
|
||||
Driver: 'bridge',
|
||||
EnableIPv6: true,
|
||||
IPAM: {
|
||||
@ -304,9 +373,13 @@ describe('compose/network', () => {
|
||||
} as NetworkInspectInfo['Options'],
|
||||
Labels: {
|
||||
'io.balena.features.something': '123',
|
||||
'io.balena.app-id': '12345',
|
||||
} as NetworkInspectInfo['Labels'],
|
||||
} as NetworkInspectInfo);
|
||||
|
||||
expect(network.appId).to.equal(12345);
|
||||
expect(network.appUuid).to.equal('a173bdb734884b778f5cc3dffd18733e');
|
||||
|
||||
// Convert to compose object
|
||||
const compose = network.toComposeObject();
|
||||
expect(compose.driver).to.equal('bridge');
|
||||
@ -327,23 +400,26 @@ describe('compose/network', () => {
|
||||
});
|
||||
expect(compose.labels).to.deep.equal({
|
||||
'io.balena.features.something': '123',
|
||||
'io.balena.app-id': '12345',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('generateDockerName', () => {
|
||||
it('creates a proper network name from the user given name and the app id', () => {
|
||||
expect(Network.generateDockerName(12345, 'default')).to.equal(
|
||||
'12345_default',
|
||||
it('creates a proper network name from the user given name and the app uuid', () => {
|
||||
expect(Network.generateDockerName('deadbeef', 'default')).to.equal(
|
||||
'deadbeef_default',
|
||||
);
|
||||
expect(Network.generateDockerName('deadbeef', 'bleh')).to.equal(
|
||||
'deadbeef_bleh',
|
||||
);
|
||||
expect(Network.generateDockerName(12345, 'bleh')).to.equal('12345_bleh');
|
||||
expect(Network.generateDockerName(1, 'default')).to.equal('1_default');
|
||||
});
|
||||
});
|
||||
|
||||
describe('comparing network configurations', () => {
|
||||
it('ignores IPAM configuration', () => {
|
||||
const network = Network.fromComposeObject('default', 12345, {
|
||||
const network = Network.fromComposeObject('default', 12345, 'deadbeef', {
|
||||
ipam: {
|
||||
driver: 'default',
|
||||
config: [
|
||||
@ -357,13 +433,15 @@ describe('compose/network', () => {
|
||||
},
|
||||
});
|
||||
expect(
|
||||
network.isEqualConfig(Network.fromComposeObject('default', 12345, {})),
|
||||
network.isEqualConfig(
|
||||
Network.fromComposeObject('default', 12345, 'deadbeef', {}),
|
||||
),
|
||||
).to.be.true;
|
||||
|
||||
// Only ignores ipam.config, not other ipam elements
|
||||
expect(
|
||||
network.isEqualConfig(
|
||||
Network.fromComposeObject('default', 12345, {
|
||||
Network.fromComposeObject('default', 12345, 'deadbeef', {
|
||||
ipam: { driver: 'aaa' },
|
||||
}),
|
||||
),
|
||||
@ -372,26 +450,61 @@ describe('compose/network', () => {
|
||||
|
||||
it('compares configurations recursively', () => {
|
||||
expect(
|
||||
Network.fromComposeObject('default', 12345, {}).isEqualConfig(
|
||||
Network.fromComposeObject('default', 12345, {}),
|
||||
Network.fromComposeObject(
|
||||
'default',
|
||||
12345,
|
||||
'deadbeef',
|
||||
{},
|
||||
).isEqualConfig(
|
||||
Network.fromComposeObject('default', 12345, 'deadbeef', {}),
|
||||
),
|
||||
).to.be.true;
|
||||
expect(
|
||||
Network.fromComposeObject('default', 12345, {
|
||||
Network.fromComposeObject('default', 12345, 'deadbeef', {
|
||||
driver: 'default',
|
||||
}).isEqualConfig(Network.fromComposeObject('default', 12345, {})),
|
||||
}).isEqualConfig(
|
||||
Network.fromComposeObject('default', 12345, 'deadbeef', {}),
|
||||
),
|
||||
).to.be.false;
|
||||
expect(
|
||||
Network.fromComposeObject('default', 12345, {
|
||||
Network.fromComposeObject('default', 12345, 'deadbeef', {
|
||||
enable_ipv6: true,
|
||||
}).isEqualConfig(Network.fromComposeObject('default', 12345, {})),
|
||||
}).isEqualConfig(
|
||||
Network.fromComposeObject('default', 12345, 'deadbeef', {}),
|
||||
),
|
||||
).to.be.false;
|
||||
expect(
|
||||
Network.fromComposeObject('default', 12345, {
|
||||
Network.fromComposeObject('default', 12345, 'deadbeef', {
|
||||
enable_ipv6: false,
|
||||
internal: false,
|
||||
}).isEqualConfig(
|
||||
Network.fromComposeObject('default', 12345, { internal: true }),
|
||||
Network.fromComposeObject('default', 12345, 'deadbeef', {
|
||||
internal: true,
|
||||
}),
|
||||
),
|
||||
).to.be.false;
|
||||
|
||||
// Comparison of a network without the app-uuid and a network
|
||||
// with uuid has to return false
|
||||
expect(
|
||||
Network.fromComposeObject(
|
||||
'default',
|
||||
12345,
|
||||
'deadbeef',
|
||||
{},
|
||||
).isEqualConfig(
|
||||
Network.fromDockerNetwork({
|
||||
Id: 'deadbeef',
|
||||
Name: '12345_default',
|
||||
IPAM: {
|
||||
Driver: 'default',
|
||||
Options: {},
|
||||
Config: [],
|
||||
} as NetworkInspectInfo['IPAM'],
|
||||
Labels: {
|
||||
'io.balena.supervised': 'true',
|
||||
} as NetworkInspectInfo['Labels'],
|
||||
} as NetworkInspectInfo),
|
||||
),
|
||||
).to.be.false;
|
||||
});
|
||||
@ -400,26 +513,31 @@ describe('compose/network', () => {
|
||||
describe('creating networks', () => {
|
||||
it('creates a new network on the engine with the given data', async () => {
|
||||
await withMockerode(async (mockerode) => {
|
||||
const network = Network.fromComposeObject('default', 12345, {
|
||||
ipam: {
|
||||
driver: 'default',
|
||||
config: [
|
||||
{
|
||||
subnet: '172.20.0.0/16',
|
||||
ip_range: '172.20.10.0/24',
|
||||
gateway: '172.20.0.1',
|
||||
},
|
||||
],
|
||||
options: {},
|
||||
const network = Network.fromComposeObject(
|
||||
'default',
|
||||
12345,
|
||||
'deadbeef',
|
||||
{
|
||||
ipam: {
|
||||
driver: 'default',
|
||||
config: [
|
||||
{
|
||||
subnet: '172.20.0.0/16',
|
||||
ip_range: '172.20.10.0/24',
|
||||
gateway: '172.20.0.1',
|
||||
},
|
||||
],
|
||||
options: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
);
|
||||
|
||||
// Create the network
|
||||
await network.create();
|
||||
|
||||
// Check that the create function was called with proper arguments
|
||||
expect(mockerode.createNetwork).to.have.been.calledOnceWith({
|
||||
Name: '12345_default',
|
||||
Name: 'deadbeef_default',
|
||||
Driver: 'bridge',
|
||||
CheckDuplicate: true,
|
||||
IPAM: {
|
||||
@ -437,6 +555,7 @@ describe('compose/network', () => {
|
||||
Internal: false,
|
||||
Labels: {
|
||||
'io.balena.supervised': 'true',
|
||||
'io.balena.app-id': '12345',
|
||||
},
|
||||
Options: {},
|
||||
});
|
||||
@ -445,19 +564,24 @@ describe('compose/network', () => {
|
||||
|
||||
it('throws the error if there is a problem while creating the network', async () => {
|
||||
await withMockerode(async (mockerode) => {
|
||||
const network = Network.fromComposeObject('default', 12345, {
|
||||
ipam: {
|
||||
driver: 'default',
|
||||
config: [
|
||||
{
|
||||
subnet: '172.20.0.0/16',
|
||||
ip_range: '172.20.10.0/24',
|
||||
gateway: '172.20.0.1',
|
||||
},
|
||||
],
|
||||
options: {},
|
||||
const network = Network.fromComposeObject(
|
||||
'default',
|
||||
12345,
|
||||
'deadbeef',
|
||||
{
|
||||
ipam: {
|
||||
driver: 'default',
|
||||
config: [
|
||||
{
|
||||
subnet: '172.20.0.0/16',
|
||||
ip_range: '172.20.10.0/24',
|
||||
gateway: '172.20.0.1',
|
||||
},
|
||||
],
|
||||
options: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
);
|
||||
|
||||
// Re-define the dockerode.createNetwork to throw
|
||||
mockerode.createNetwork.rejects('Unknown engine error');
|
||||
@ -471,10 +595,10 @@ describe('compose/network', () => {
|
||||
});
|
||||
|
||||
describe('removing a network', () => {
|
||||
it('removes the network from the engine if it exists', async () => {
|
||||
it('removes the legacy network from the engine if it exists', async () => {
|
||||
// Create a mock network to add to the mock engine
|
||||
const dockerNetwork = createNetwork({
|
||||
Id: 'deadbeef',
|
||||
Id: 'aaaaaaa',
|
||||
Name: '12345_default',
|
||||
});
|
||||
|
||||
@ -484,7 +608,48 @@ describe('compose/network', () => {
|
||||
expect(await mockerode.listNetworks()).to.have.lengthOf(1);
|
||||
|
||||
// Create a dummy network object
|
||||
const network = Network.fromComposeObject('default', 12345, {});
|
||||
const network = Network.fromComposeObject(
|
||||
'default',
|
||||
12345,
|
||||
'deadbeef',
|
||||
{},
|
||||
);
|
||||
|
||||
// Perform the operation
|
||||
await network.remove();
|
||||
|
||||
// The removal step should delete the object from the engine data
|
||||
expect(mockerode.removeNetwork).to.have.been.calledOnceWith(
|
||||
'aaaaaaa',
|
||||
);
|
||||
},
|
||||
{ networks: [dockerNetwork] },
|
||||
);
|
||||
});
|
||||
|
||||
it('removes the network from the engine if it exists', async () => {
|
||||
// Create a mock network to add to the mock engine
|
||||
const dockerNetwork = createNetwork({
|
||||
Id: 'deadbeef',
|
||||
Name: 'a173bdb734884b778f5cc3dffd18733e_default',
|
||||
Labels: {
|
||||
'io.balena.supervised': 'true',
|
||||
'io.balena.app-id': '12345',
|
||||
},
|
||||
});
|
||||
|
||||
await withMockerode(
|
||||
async (mockerode) => {
|
||||
// Check that the engine has the network
|
||||
expect(await mockerode.listNetworks()).to.have.lengthOf(1);
|
||||
|
||||
// Create a dummy network object
|
||||
const network = Network.fromComposeObject(
|
||||
'default',
|
||||
12345,
|
||||
'a173bdb734884b778f5cc3dffd18733e',
|
||||
{},
|
||||
);
|
||||
|
||||
// Perform the operation
|
||||
await network.remove();
|
||||
@ -501,7 +666,7 @@ describe('compose/network', () => {
|
||||
it('ignores the request if the given network does not exist on the engine', async () => {
|
||||
// Create a mock network to add to the mock engine
|
||||
const mockNetwork = createNetwork({
|
||||
Id: 'deadbeef',
|
||||
Id: 'aaaaaaaa',
|
||||
Name: 'some_network',
|
||||
});
|
||||
|
||||
@ -511,7 +676,12 @@ describe('compose/network', () => {
|
||||
expect(await mockerode.listNetworks()).to.have.lengthOf(1);
|
||||
|
||||
// Create a dummy network object
|
||||
const network = Network.fromComposeObject('default', 12345, {});
|
||||
const network = Network.fromComposeObject(
|
||||
'default',
|
||||
12345,
|
||||
'deadbeef',
|
||||
{},
|
||||
);
|
||||
|
||||
// This should not fail
|
||||
await expect(network.remove()).to.not.be.rejected;
|
||||
@ -526,18 +696,29 @@ describe('compose/network', () => {
|
||||
it('throws the error if there is a problem while removing the network', async () => {
|
||||
// Create a mock network to add to the mock engine
|
||||
const mockNetwork = createNetwork({
|
||||
Id: 'deadbeef',
|
||||
Name: '12345_default',
|
||||
Id: 'aaaaaaaa',
|
||||
Name: 'a173bdb734884b778f5cc3dffd18733e_default',
|
||||
Labels: {
|
||||
'io.balena.app-id': '12345',
|
||||
},
|
||||
});
|
||||
|
||||
await withMockerode(
|
||||
async (mockerode) => {
|
||||
// We can change the return value of the mockerode removeNetwork
|
||||
// to have the remove operation fail
|
||||
mockerode.removeNetwork.throws('Failed to remove the network');
|
||||
mockerode.removeNetwork.throws({
|
||||
statusCode: 500,
|
||||
message: 'Failed to remove the network',
|
||||
});
|
||||
|
||||
// Create a dummy network object
|
||||
const network = Network.fromComposeObject('default', 12345, {});
|
||||
const network = Network.fromComposeObject(
|
||||
'default',
|
||||
12345,
|
||||
'a173bdb734884b778f5cc3dffd18733e',
|
||||
{},
|
||||
);
|
||||
|
||||
await expect(network.remove()).to.be.rejected;
|
||||
},
|
||||
|
@ -429,6 +429,7 @@ describe('compose/service', () => {
|
||||
await Service.fromComposeObject(
|
||||
{
|
||||
appId: 123456,
|
||||
appUuid: 'deadbeef',
|
||||
serviceId: 123456,
|
||||
serviceName: 'test',
|
||||
composition: {
|
||||
@ -448,7 +449,7 @@ describe('compose/service', () => {
|
||||
).toDockerContainer({ deviceName: 'foo' } as any).NetworkingConfig,
|
||||
).to.deep.equal({
|
||||
EndpointsConfig: {
|
||||
'123456_balena': {
|
||||
deadbeef_balena: {
|
||||
IPAMConfig: {
|
||||
IPv4Address: '1.2.3.4',
|
||||
},
|
||||
@ -470,7 +471,7 @@ describe('compose/service', () => {
|
||||
).toDockerContainer({ deviceName: 'foo' } as any).NetworkingConfig,
|
||||
).to.deep.equal({
|
||||
EndpointsConfig: {
|
||||
'123456_balena': {
|
||||
deadbeef_balena: {
|
||||
IPAMConfig: {
|
||||
IPv4Address: '1.2.3.4',
|
||||
IPv6Address: '5.6.7.8',
|
||||
|
Loading…
x
Reference in New Issue
Block a user