mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-02-20 17:52:51 +00:00
Add app uuid as metadata to new volumes
We cannot modify older volumes but newly created volumes will contain app uuid as metadata so they can be migrated at some point in the future.
This commit is contained in:
parent
0b19dee511
commit
0835b29874
@ -726,7 +726,7 @@ export class App {
|
||||
if (conf.labels == null) {
|
||||
conf.labels = {};
|
||||
}
|
||||
return Volume.fromComposeObject(name, app.appId, conf);
|
||||
return Volume.fromComposeObject(name, app.appId, app.uuid, conf);
|
||||
});
|
||||
|
||||
const networks = _.mapValues(
|
||||
|
@ -78,11 +78,19 @@ export async function remove(volume: Volume) {
|
||||
}
|
||||
|
||||
export async function createFromPath(
|
||||
{ name, appId }: VolumeNameOpts,
|
||||
{ name, appId, appUuid }: VolumeNameOpts & { appUuid?: string },
|
||||
config: Partial<VolumeConfig>,
|
||||
oldPath: string,
|
||||
): Promise<Volume> {
|
||||
const volume = Volume.fromComposeObject(name, appId, config);
|
||||
const volume = Volume.fromComposeObject(
|
||||
name,
|
||||
appId,
|
||||
// We may not have a uuid here, but we need one to create a volume
|
||||
// from a compose object. We pass uuid as undefined here so that we will
|
||||
// fallback to id comparison for apps
|
||||
appUuid as any,
|
||||
config,
|
||||
);
|
||||
|
||||
await create(volume);
|
||||
const inspect = await docker
|
||||
|
@ -1,5 +1,4 @@
|
||||
import * as Docker from 'dockerode';
|
||||
import assign = require('lodash/assign');
|
||||
import isEqual = require('lodash/isEqual');
|
||||
import omitBy = require('lodash/omitBy');
|
||||
|
||||
@ -27,6 +26,7 @@ export class Volume {
|
||||
private constructor(
|
||||
public name: string,
|
||||
public appId: number,
|
||||
public appUuid: string,
|
||||
public config: VolumeConfig,
|
||||
) {}
|
||||
|
||||
@ -40,26 +40,34 @@ export class Volume {
|
||||
|
||||
// Detect the name and appId from the inspect data
|
||||
const { name, appId } = this.deconstructDockerName(inspect.Name);
|
||||
const appUuid = config.labels['io.balena.app-uuid'];
|
||||
|
||||
return new Volume(name, appId, config);
|
||||
return new Volume(name, appId, appUuid, config);
|
||||
}
|
||||
|
||||
public static fromComposeObject(
|
||||
name: string,
|
||||
appId: number,
|
||||
config: Partial<ComposeVolumeConfig>,
|
||||
appUuid: string,
|
||||
config = {} as Partial<ComposeVolumeConfig>,
|
||||
) {
|
||||
const filledConfig: VolumeConfig = {
|
||||
driverOpts: config.driver_opts || {},
|
||||
driver: config.driver || 'local',
|
||||
labels: ComposeUtils.normalizeLabels(config.labels || {}),
|
||||
labels: {
|
||||
// We only need to assign the labels here, as when we
|
||||
// get it from the daemon, they should already be there
|
||||
...ComposeUtils.normalizeLabels(config.labels || {}),
|
||||
...constants.defaultVolumeLabels,
|
||||
|
||||
// the app uuid will always be in the target state, the
|
||||
// only reason this is done this way is to be compatible
|
||||
// with loading a volume from backup (see lib/migration)
|
||||
...(appUuid && { 'io.balena.app-uuid': appUuid }),
|
||||
},
|
||||
};
|
||||
|
||||
// We only need to assign the labels here, as when we
|
||||
// get it from the daemon, they should already be there
|
||||
assign(filledConfig.labels, constants.defaultVolumeLabels);
|
||||
|
||||
return new Volume(name, appId, filledConfig);
|
||||
return new Volume(name, appId, appUuid, filledConfig);
|
||||
}
|
||||
|
||||
public toComposeObject(): ComposeVolumeConfig {
|
||||
@ -141,7 +149,14 @@ export class Volume {
|
||||
// TODO: Export these to a constant
|
||||
return omitBy(
|
||||
labels,
|
||||
(_v, k) => k === 'io.resin.supervised' || k === 'io.balena.supervised',
|
||||
(_v, k) =>
|
||||
k === 'io.resin.supervised' ||
|
||||
k === 'io.balena.supervised' ||
|
||||
// TODO: we need to omit the app-uuid label
|
||||
// in the comparison or else the supervisor will try to recreate
|
||||
// the volume, which won't fail but won't have any effect on the volume
|
||||
// either, leading to a supervisor target state apply loop
|
||||
k === 'io.balena.app-uuid',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -31,8 +31,9 @@ const defaultLegacyVolume = () => 'resin-data';
|
||||
/**
|
||||
* Creates a docker volume from the legacy data directory
|
||||
*/
|
||||
export async function createVolumeFromLegacyData(
|
||||
async function createVolumeFromLegacyData(
|
||||
appId: number,
|
||||
appUuid: string,
|
||||
): Promise<Volume | void> {
|
||||
const name = defaultLegacyVolume();
|
||||
const legacyPath = path.join(
|
||||
@ -42,7 +43,11 @@ export async function createVolumeFromLegacyData(
|
||||
);
|
||||
|
||||
try {
|
||||
return await volumeManager.createFromPath({ name, appId }, {}, legacyPath);
|
||||
return await volumeManager.createFromPath(
|
||||
{ name, appId, appUuid },
|
||||
{},
|
||||
legacyPath,
|
||||
);
|
||||
} catch (e) {
|
||||
logger.logSystemMessage(
|
||||
`Warning: could not migrate legacy /data volume: ${e.message}`,
|
||||
@ -128,7 +133,7 @@ export async function normaliseLegacyDatabase() {
|
||||
|
||||
// We need to get the app.uuid, release.id, serviceId, image.id and updated imageUrl
|
||||
const release = releases[0];
|
||||
const uuid = release.belongs_to__application[0].uuid;
|
||||
const appUuid = release.belongs_to__application[0].uuid;
|
||||
const image = release.contains__image[0].image[0];
|
||||
const serviceId = image.is_a_build_of__service.__id;
|
||||
const imageUrl = !image.content_hash
|
||||
@ -168,7 +173,7 @@ export async function normaliseLegacyDatabase() {
|
||||
await trx('image').insert({
|
||||
name: imageUrl,
|
||||
appId: app.appId,
|
||||
appUuid: uuid,
|
||||
appUuid,
|
||||
serviceId,
|
||||
serviceName: service.serviceName,
|
||||
imageId: image.id,
|
||||
@ -188,7 +193,7 @@ export async function normaliseLegacyDatabase() {
|
||||
services: JSON.stringify([
|
||||
Object.assign(service, {
|
||||
appId: app.appId,
|
||||
appUuid: uuid,
|
||||
appUuid,
|
||||
image: imageUrl,
|
||||
serviceId,
|
||||
imageId: image.id,
|
||||
@ -196,7 +201,7 @@ export async function normaliseLegacyDatabase() {
|
||||
commit: app.commit,
|
||||
}),
|
||||
]),
|
||||
uuid,
|
||||
uuid: appUuid,
|
||||
releaseId: release.id,
|
||||
class: 'fleet',
|
||||
});
|
||||
@ -215,8 +220,9 @@ export async function normaliseLegacyDatabase() {
|
||||
await applicationManager.initialized;
|
||||
const targetApps = await applicationManager.getTargetApps();
|
||||
|
||||
for (const app of Object.values(targetApps)) {
|
||||
await createVolumeFromLegacyData(app.id);
|
||||
for (const appUuid of Object.keys(targetApps)) {
|
||||
const app = targetApps[appUuid];
|
||||
await createVolumeFromLegacyData(app.id, appUuid);
|
||||
}
|
||||
|
||||
await config.set({
|
||||
|
@ -138,7 +138,7 @@ describe('compose/app', () => {
|
||||
// Setup current and target apps
|
||||
const current = createApp();
|
||||
const target = createApp({
|
||||
volumes: [Volume.fromComposeObject('test-volume', 1, {})],
|
||||
volumes: [Volume.fromComposeObject('test-volume', 1, 'deadbeef')],
|
||||
isTarget: true,
|
||||
});
|
||||
|
||||
@ -156,8 +156,8 @@ describe('compose/app', () => {
|
||||
const current = createApp();
|
||||
const target = createApp({
|
||||
volumes: [
|
||||
Volume.fromComposeObject('test-volume', 1, {}),
|
||||
Volume.fromComposeObject('test-volume-2', 1, {}),
|
||||
Volume.fromComposeObject('test-volume', 1, 'deadbeef'),
|
||||
Volume.fromComposeObject('test-volume-2', 1, 'deadbeef'),
|
||||
],
|
||||
isTarget: true,
|
||||
});
|
||||
@ -186,12 +186,12 @@ describe('compose/app', () => {
|
||||
it('should not infer a volume remove step when the app is still referenced', () => {
|
||||
const current = createApp({
|
||||
volumes: [
|
||||
Volume.fromComposeObject('test-volume', 1, {}),
|
||||
Volume.fromComposeObject('test-volume-2', 1, {}),
|
||||
Volume.fromComposeObject('test-volume', 1, 'deadbeef'),
|
||||
Volume.fromComposeObject('test-volume-2', 1, 'deadbeef'),
|
||||
],
|
||||
});
|
||||
const target = createApp({
|
||||
volumes: [Volume.fromComposeObject('test-volume-2', 1, {})],
|
||||
volumes: [Volume.fromComposeObject('test-volume-2', 1, 'deadbeef')],
|
||||
isTarget: true,
|
||||
});
|
||||
|
||||
@ -201,11 +201,11 @@ describe('compose/app', () => {
|
||||
|
||||
it('should correctly infer volume recreation steps', () => {
|
||||
const current = createApp({
|
||||
volumes: [Volume.fromComposeObject('test-volume', 1, {})],
|
||||
volumes: [Volume.fromComposeObject('test-volume', 1, 'deadbeef')],
|
||||
});
|
||||
const target = createApp({
|
||||
volumes: [
|
||||
Volume.fromComposeObject('test-volume', 1, {
|
||||
Volume.fromComposeObject('test-volume', 1, 'deadbeef', {
|
||||
labels: { test: 'test' },
|
||||
}),
|
||||
],
|
||||
@ -221,8 +221,12 @@ describe('compose/app', () => {
|
||||
const [removalStep] = expectSteps('removeVolume', stepsForRemoval);
|
||||
expect(removalStep)
|
||||
.to.have.property('current')
|
||||
.that.has.property('config')
|
||||
.that.deep.includes({ labels: { 'io.balena.supervised': 'true' } });
|
||||
.that.has.property('name')
|
||||
.that.equals('test-volume');
|
||||
expect(removalStep)
|
||||
.to.have.property('current')
|
||||
.that.has.property('appId')
|
||||
.that.equals(1);
|
||||
|
||||
// we are assuming that after the execution steps the current state of the
|
||||
// app will look like this
|
||||
@ -241,7 +245,11 @@ describe('compose/app', () => {
|
||||
.to.have.property('target')
|
||||
.that.has.property('config')
|
||||
.that.deep.includes({
|
||||
labels: { 'io.balena.supervised': 'true', test: 'test' },
|
||||
labels: {
|
||||
'io.balena.supervised': 'true',
|
||||
'io.balena.app-uuid': 'deadbeef',
|
||||
test: 'test',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@ -252,7 +260,7 @@ describe('compose/app', () => {
|
||||
composition: { volumes: ['test-volume:/data'] },
|
||||
}),
|
||||
],
|
||||
volumes: [Volume.fromComposeObject('test-volume', 1, {})],
|
||||
volumes: [Volume.fromComposeObject('test-volume', 1, 'deadbeef')],
|
||||
});
|
||||
const target = createApp({
|
||||
services: [
|
||||
@ -261,7 +269,7 @@ describe('compose/app', () => {
|
||||
}),
|
||||
],
|
||||
volumes: [
|
||||
Volume.fromComposeObject('test-volume', 1, {
|
||||
Volume.fromComposeObject('test-volume', 1, 'deadbeef', {
|
||||
labels: { test: 'test' },
|
||||
}),
|
||||
],
|
||||
@ -279,7 +287,7 @@ describe('compose/app', () => {
|
||||
|
||||
it('should correctly infer to remove an app volumes when the app is being removed', async () => {
|
||||
const current = createApp({
|
||||
volumes: [Volume.fromComposeObject('test-volume', 1, {})],
|
||||
volumes: [Volume.fromComposeObject('test-volume', 1, 'deadbeef')],
|
||||
});
|
||||
|
||||
const steps = await current.stepsToRemoveApp(defaultContext);
|
||||
@ -297,12 +305,12 @@ describe('compose/app', () => {
|
||||
service.status = 'Stopping';
|
||||
const current = createApp({
|
||||
services: [service],
|
||||
volumes: [Volume.fromComposeObject('test-volume', 1, {})],
|
||||
volumes: [Volume.fromComposeObject('test-volume', 1, 'deadbeef')],
|
||||
});
|
||||
const target = createApp({
|
||||
services: [service],
|
||||
volumes: [
|
||||
Volume.fromComposeObject('test-volume', 1, {
|
||||
Volume.fromComposeObject('test-volume', 1, 'deadbeef', {
|
||||
labels: { test: 'test' },
|
||||
}),
|
||||
],
|
||||
@ -318,7 +326,11 @@ describe('compose/app', () => {
|
||||
image: 'test-image',
|
||||
composition: { volumes: ['db-volume:/data'] },
|
||||
});
|
||||
const volume = Volume.fromComposeObject('db-volume', service.appId, {});
|
||||
const volume = Volume.fromComposeObject(
|
||||
'db-volume',
|
||||
service.appId,
|
||||
'deadbeef',
|
||||
);
|
||||
const contextWithImages = {
|
||||
...defaultContext,
|
||||
...{
|
||||
|
@ -866,7 +866,7 @@ describe('compose/application-manager', () => {
|
||||
} = createCurrentState({
|
||||
services: [],
|
||||
networks: [DEFAULT_NETWORK],
|
||||
volumes: [Volume.fromComposeObject('test-volume', 1, {})],
|
||||
volumes: [Volume.fromComposeObject('test-volume', 1, 'deadbeef')],
|
||||
});
|
||||
|
||||
const steps = await applicationManager.inferNextSteps(
|
||||
@ -893,7 +893,7 @@ describe('compose/application-manager', () => {
|
||||
services: [],
|
||||
networks: [],
|
||||
// Volume with different id
|
||||
volumes: [Volume.fromComposeObject('test-volume', 2, {})],
|
||||
volumes: [Volume.fromComposeObject('test-volume', 2, 'deadbeef')],
|
||||
});
|
||||
|
||||
const steps = await applicationManager.inferNextSteps(
|
||||
|
@ -34,8 +34,11 @@ describe('compose/volume-manager', () => {
|
||||
}),
|
||||
createVolume({
|
||||
Name: Volume.generateDockerName(1, 'mysql'),
|
||||
// Recently created volumes contain io.balena.supervised label
|
||||
Labels: { 'io.balena.supervised': '1' },
|
||||
// Recently created volumes contain io.balena.supervised label and app-uuid
|
||||
Labels: {
|
||||
'io.balena.supervised': '1',
|
||||
'io.balena.app-uuid': 'deadbeef',
|
||||
},
|
||||
}),
|
||||
createVolume({
|
||||
Name: Volume.generateDockerName(1, 'backend'),
|
||||
@ -56,6 +59,7 @@ describe('compose/volume-manager', () => {
|
||||
await expect(volumeManager.getAll()).to.eventually.deep.equal([
|
||||
{
|
||||
appId: 1,
|
||||
appUuid: undefined,
|
||||
config: {
|
||||
driver: 'local',
|
||||
driverOpts: {},
|
||||
@ -67,17 +71,20 @@ describe('compose/volume-manager', () => {
|
||||
},
|
||||
{
|
||||
appId: 1,
|
||||
appUuid: 'deadbeef',
|
||||
config: {
|
||||
driver: 'local',
|
||||
driverOpts: {},
|
||||
labels: {
|
||||
'io.balena.supervised': '1',
|
||||
'io.balena.app-uuid': 'deadbeef',
|
||||
},
|
||||
},
|
||||
name: 'mysql',
|
||||
},
|
||||
{
|
||||
appId: 1,
|
||||
appUuid: undefined,
|
||||
config: {
|
||||
driver: 'local',
|
||||
driverOpts: {},
|
||||
@ -126,6 +133,7 @@ describe('compose/volume-manager', () => {
|
||||
).to.eventually.deep.equal([
|
||||
{
|
||||
appId: 111,
|
||||
appUuid: undefined,
|
||||
config: {
|
||||
driver: 'local',
|
||||
driverOpts: {},
|
||||
@ -152,7 +160,7 @@ describe('compose/volume-manager', () => {
|
||||
).to.be.rejected;
|
||||
|
||||
// Volume to create
|
||||
const volume = Volume.fromComposeObject('main', 111, {});
|
||||
const volume = Volume.fromComposeObject('main', 111, 'deadbeef', {});
|
||||
sinon.spy(volume, 'create');
|
||||
|
||||
// Create volume
|
||||
@ -177,7 +185,7 @@ describe('compose/volume-manager', () => {
|
||||
await withMockerode(
|
||||
async () => {
|
||||
// Create compose object for volume already set up in mock engine
|
||||
const volume = Volume.fromComposeObject('main', 111, {});
|
||||
const volume = Volume.fromComposeObject('main', 111, 'deadbeef', {});
|
||||
sinon.spy(volume, 'create');
|
||||
|
||||
// Create volume
|
||||
@ -206,7 +214,7 @@ describe('compose/volume-manager', () => {
|
||||
await withMockerode(
|
||||
async (mockerode) => {
|
||||
// Volume to remove
|
||||
const volume = Volume.fromComposeObject('main', 111, {});
|
||||
const volume = Volume.fromComposeObject('main', 111, 'deadbeef', {});
|
||||
sinon.spy(volume, 'remove');
|
||||
|
||||
// Remove volume
|
||||
@ -234,7 +242,7 @@ describe('compose/volume-manager', () => {
|
||||
await withMockerode(
|
||||
async (mockerode) => {
|
||||
// Volume to remove
|
||||
const volume = Volume.fromComposeObject('main', 111, {});
|
||||
const volume = Volume.fromComposeObject('main', 111, 'deadbeef', {});
|
||||
sinon.spy(volume, 'remove');
|
||||
|
||||
// Remove volume
|
||||
|
@ -9,28 +9,40 @@ import { createVolume, withMockerode } from '../../lib/mockerode';
|
||||
describe('compose/volume', () => {
|
||||
describe('creating a volume from a compose object', () => {
|
||||
it('should use proper defaults when no compose configuration is provided', () => {
|
||||
const volume = Volume.fromComposeObject('my_volume', 1234, {});
|
||||
const volume = Volume.fromComposeObject(
|
||||
'my_volume',
|
||||
1234,
|
||||
'deadbeef',
|
||||
{},
|
||||
);
|
||||
|
||||
expect(volume.name).to.equal('my_volume');
|
||||
expect(volume.appId).to.equal(1234);
|
||||
expect(volume.appUuid).to.equal('deadbeef');
|
||||
expect(volume.config).to.deep.equal({
|
||||
driver: 'local',
|
||||
driverOpts: {},
|
||||
labels: {
|
||||
'io.balena.supervised': 'true',
|
||||
'io.balena.app-uuid': 'deadbeef',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly parse compose volumes without an explicit driver', () => {
|
||||
const volume = Volume.fromComposeObject('one_volume', 1032480, {
|
||||
driver_opts: {
|
||||
opt1: 'test',
|
||||
const volume = Volume.fromComposeObject(
|
||||
'one_volume',
|
||||
1032480,
|
||||
'deadbeef',
|
||||
{
|
||||
driver_opts: {
|
||||
opt1: 'test',
|
||||
},
|
||||
labels: {
|
||||
'my-label': 'test-label',
|
||||
},
|
||||
},
|
||||
labels: {
|
||||
'my-label': 'test-label',
|
||||
},
|
||||
});
|
||||
);
|
||||
|
||||
expect(volume).to.have.property('appId').that.equals(1032480);
|
||||
expect(volume).to.have.property('name').that.equals('one_volume');
|
||||
@ -39,6 +51,7 @@ describe('compose/volume', () => {
|
||||
.that.has.property('labels')
|
||||
.that.deep.equals({
|
||||
'io.balena.supervised': 'true',
|
||||
'io.balena.app-uuid': 'deadbeef',
|
||||
'my-label': 'test-label',
|
||||
});
|
||||
expect(volume)
|
||||
@ -54,15 +67,20 @@ describe('compose/volume', () => {
|
||||
});
|
||||
|
||||
it('should correctly parse compose volumes with an explicit driver', () => {
|
||||
const volume = Volume.fromComposeObject('one_volume', 1032480, {
|
||||
driver: 'other',
|
||||
driver_opts: {
|
||||
opt1: 'test',
|
||||
const volume = Volume.fromComposeObject(
|
||||
'one_volume',
|
||||
1032480,
|
||||
'deadbeef',
|
||||
{
|
||||
driver: 'other',
|
||||
driver_opts: {
|
||||
opt1: 'test',
|
||||
},
|
||||
labels: {
|
||||
'my-label': 'test-label',
|
||||
},
|
||||
},
|
||||
labels: {
|
||||
'my-label': 'test-label',
|
||||
},
|
||||
});
|
||||
);
|
||||
|
||||
expect(volume).to.have.property('appId').that.equals(1032480);
|
||||
expect(volume).to.have.property('name').that.equals('one_volume');
|
||||
@ -71,6 +89,7 @@ describe('compose/volume', () => {
|
||||
.that.has.property('labels')
|
||||
.that.deep.equals({
|
||||
'io.balena.supervised': 'true',
|
||||
'io.balena.app-uuid': 'deadbeef',
|
||||
'my-label': 'test-label',
|
||||
});
|
||||
expect(volume)
|
||||
@ -119,6 +138,7 @@ describe('compose/volume', () => {
|
||||
Driver: 'local',
|
||||
Labels: {
|
||||
'io.balena.supervised': 'true',
|
||||
'io.balena.app-uuid': 'deadbeef',
|
||||
},
|
||||
Mountpoint: '/var/lib/docker/volumes/1032480_one_volume/_data',
|
||||
Name: '1032480_one_volume',
|
||||
@ -128,11 +148,13 @@ describe('compose/volume', () => {
|
||||
|
||||
expect(volume).to.have.property('appId').that.equals(1032480);
|
||||
expect(volume).to.have.property('name').that.equals('one_volume');
|
||||
expect(volume).to.have.property('appUuid').that.equals('deadbeef');
|
||||
expect(volume)
|
||||
.to.have.property('config')
|
||||
.that.has.property('labels')
|
||||
.that.deep.equals({
|
||||
'io.balena.supervised': 'true',
|
||||
'io.balena.app-uuid': 'deadbeef',
|
||||
});
|
||||
expect(volume)
|
||||
.to.have.property('config')
|
||||
@ -160,7 +182,11 @@ describe('compose/volume', () => {
|
||||
|
||||
it('should use defaults to create the volume when no options are given', async () => {
|
||||
await withMockerode(async (mockerode) => {
|
||||
const volume = Volume.fromComposeObject('one_volume', 1032480, {});
|
||||
const volume = Volume.fromComposeObject(
|
||||
'one_volume',
|
||||
1032480,
|
||||
'deadbeef',
|
||||
);
|
||||
|
||||
await volume.create();
|
||||
|
||||
@ -169,6 +195,7 @@ describe('compose/volume', () => {
|
||||
Driver: 'local',
|
||||
Labels: {
|
||||
'io.balena.supervised': 'true',
|
||||
'io.balena.app-uuid': 'deadbeef',
|
||||
},
|
||||
DriverOpts: {},
|
||||
});
|
||||
@ -177,14 +204,19 @@ describe('compose/volume', () => {
|
||||
|
||||
it('should pass configuration options to the engine', async () => {
|
||||
await withMockerode(async (mockerode) => {
|
||||
const volume = Volume.fromComposeObject('one_volume', 1032480, {
|
||||
driver_opts: {
|
||||
opt1: 'test',
|
||||
const volume = Volume.fromComposeObject(
|
||||
'one_volume',
|
||||
1032480,
|
||||
'deadbeef',
|
||||
{
|
||||
driver_opts: {
|
||||
opt1: 'test',
|
||||
},
|
||||
labels: {
|
||||
'my-label': 'test-label',
|
||||
},
|
||||
},
|
||||
labels: {
|
||||
'my-label': 'test-label',
|
||||
},
|
||||
});
|
||||
);
|
||||
|
||||
await volume.create();
|
||||
|
||||
@ -194,6 +226,7 @@ describe('compose/volume', () => {
|
||||
Labels: {
|
||||
'my-label': 'test-label',
|
||||
'io.balena.supervised': 'true',
|
||||
'io.balena.app-uuid': 'deadbeef',
|
||||
},
|
||||
DriverOpts: {
|
||||
opt1: 'test',
|
||||
@ -208,7 +241,11 @@ describe('compose/volume', () => {
|
||||
|
||||
it('should log successful volume creation to the cloud', async () => {
|
||||
await withMockerode(async (mockerode) => {
|
||||
const volume = Volume.fromComposeObject('one_volume', 1032480, {});
|
||||
const volume = Volume.fromComposeObject(
|
||||
'one_volume',
|
||||
1032480,
|
||||
'deadbeef',
|
||||
);
|
||||
|
||||
await volume.create();
|
||||
|
||||
@ -223,8 +260,8 @@ describe('compose/volume', () => {
|
||||
describe('comparing volume configuration', () => {
|
||||
it('should ignore name and supervisor labels in the comparison', () => {
|
||||
expect(
|
||||
Volume.fromComposeObject('aaa', 1234, {}).isEqualConfig(
|
||||
Volume.fromComposeObject('bbb', 4567, {
|
||||
Volume.fromComposeObject('aaa', 1234, 'deadbeef').isEqualConfig(
|
||||
Volume.fromComposeObject('bbb', 4567, 'deadbeef', {
|
||||
driver: 'local',
|
||||
driver_opts: {},
|
||||
}),
|
||||
@ -232,13 +269,30 @@ describe('compose/volume', () => {
|
||||
).to.be.true;
|
||||
|
||||
expect(
|
||||
Volume.fromComposeObject('aaa', 1234, {}).isEqualConfig(
|
||||
Volume.fromComposeObject('bbb', 4567, {}),
|
||||
Volume.fromComposeObject('aaa', 1234, 'deadbeef').isEqualConfig(
|
||||
Volume.fromComposeObject('bbb', 4567, 'deadc0de'),
|
||||
),
|
||||
).to.be.true;
|
||||
|
||||
expect(
|
||||
Volume.fromComposeObject('aaa', 1234, {}).isEqualConfig(
|
||||
Volume.fromComposeObject('aaa', 1234, 'deadbeef').isEqualConfig(
|
||||
Volume.fromDockerVolume({
|
||||
Name: '1234_aaa',
|
||||
Driver: 'local',
|
||||
Labels: {
|
||||
'io.balena.supervised': 'true',
|
||||
'io.balena.app-uuid': 'deadbeef',
|
||||
},
|
||||
Options: {},
|
||||
Mountpoint: '/var/lib/docker/volumes/1032480_one_volume/_data',
|
||||
Scope: 'local',
|
||||
}),
|
||||
),
|
||||
).to.be.true;
|
||||
|
||||
// the app-uuid should be omitted from the comparison
|
||||
expect(
|
||||
Volume.fromComposeObject('aaa', 1234, 'deadbeef').isEqualConfig(
|
||||
Volume.fromDockerVolume({
|
||||
Name: '1234_aaa',
|
||||
Driver: 'local',
|
||||
@ -253,7 +307,7 @@ describe('compose/volume', () => {
|
||||
).to.be.true;
|
||||
|
||||
expect(
|
||||
Volume.fromComposeObject('aaa', 1234, {}).isEqualConfig(
|
||||
Volume.fromComposeObject('aaa', 1234, null as any).isEqualConfig(
|
||||
Volume.fromDockerVolume({
|
||||
Name: '4567_bbb',
|
||||
Driver: 'local',
|
||||
@ -268,13 +322,14 @@ describe('compose/volume', () => {
|
||||
).to.be.true;
|
||||
|
||||
expect(
|
||||
Volume.fromComposeObject('aaa', 1234, {}).isEqualConfig(
|
||||
Volume.fromComposeObject('aaa', 1234, 'deadbeef').isEqualConfig(
|
||||
Volume.fromDockerVolume({
|
||||
Name: '1234_aaa',
|
||||
Driver: 'local',
|
||||
Labels: {
|
||||
'some.other.label': '123',
|
||||
'io.balena.supervised': 'true',
|
||||
'io.balena.app-uuid': 'deadbeef',
|
||||
},
|
||||
Options: {},
|
||||
Mountpoint: '/var/lib/docker/volumes/1032480_one_volume/_data',
|
||||
@ -286,8 +341,8 @@ describe('compose/volume', () => {
|
||||
|
||||
it('should compare based on driver configuration and options', () => {
|
||||
expect(
|
||||
Volume.fromComposeObject('aaa', 1234, {}).isEqualConfig(
|
||||
Volume.fromComposeObject('aaa', 1234, {
|
||||
Volume.fromComposeObject('aaa', 1234, 'deadbeef').isEqualConfig(
|
||||
Volume.fromComposeObject('aaa', 1234, 'deadbeef', {
|
||||
driver: 'other',
|
||||
driver_opts: {},
|
||||
}),
|
||||
@ -295,10 +350,10 @@ describe('compose/volume', () => {
|
||||
).to.be.false;
|
||||
|
||||
expect(
|
||||
Volume.fromComposeObject('aaa', 1234, {
|
||||
Volume.fromComposeObject('aaa', 1234, 'deadbeef', {
|
||||
driver: 'other',
|
||||
}).isEqualConfig(
|
||||
Volume.fromComposeObject('aaa', 1234, {
|
||||
Volume.fromComposeObject('aaa', 1234, 'deadbeef', {
|
||||
driver: 'other',
|
||||
driver_opts: {},
|
||||
}),
|
||||
@ -306,15 +361,15 @@ describe('compose/volume', () => {
|
||||
).to.be.true;
|
||||
|
||||
expect(
|
||||
Volume.fromComposeObject('aaa', 1234, {}).isEqualConfig(
|
||||
Volume.fromComposeObject('aaa', 1234, {
|
||||
Volume.fromComposeObject('aaa', 1234, 'deadbeef', {}).isEqualConfig(
|
||||
Volume.fromComposeObject('aaa', 1234, 'deadbeef', {
|
||||
driver_opts: { opt: '123' },
|
||||
}),
|
||||
),
|
||||
).to.be.false;
|
||||
|
||||
expect(
|
||||
Volume.fromComposeObject('aaa', 1234, {
|
||||
Volume.fromComposeObject('aaa', 1234, 'deadbeef', {
|
||||
driver: 'other',
|
||||
labels: { 'some.other.label': '123' },
|
||||
driver_opts: { 'some-opt': '123' },
|
||||
@ -334,7 +389,7 @@ describe('compose/volume', () => {
|
||||
).to.be.false;
|
||||
|
||||
expect(
|
||||
Volume.fromComposeObject('aaa', 1234, {
|
||||
Volume.fromComposeObject('aaa', 1234, 'deadbeef', {
|
||||
driver: 'other',
|
||||
labels: { 'some.other.label': '123' },
|
||||
driver_opts: { 'some-opt': '123' },
|
||||
@ -375,7 +430,7 @@ describe('compose/volume', () => {
|
||||
|
||||
await withMockerode(
|
||||
async (mockerode) => {
|
||||
const volume = Volume.fromComposeObject('aaa', 1234, {});
|
||||
const volume = Volume.fromComposeObject('aaa', 1234, 'deadbeef');
|
||||
|
||||
// Check engine state before (this is really to test that mockerode is doing its job)
|
||||
expect((await mockerode.listVolumes()).Volumes).to.have.lengthOf(1);
|
||||
@ -402,7 +457,7 @@ describe('compose/volume', () => {
|
||||
|
||||
await withMockerode(
|
||||
async (mockerode) => {
|
||||
const volume = Volume.fromComposeObject('aaa', 1234, {});
|
||||
const volume = Volume.fromComposeObject('aaa', 1234, 'deadbeef');
|
||||
|
||||
// Check engine state before
|
||||
expect((await mockerode.listVolumes()).Volumes).to.have.lengthOf(1);
|
||||
@ -430,7 +485,7 @@ describe('compose/volume', () => {
|
||||
});
|
||||
await withMockerode(
|
||||
async (mockerode) => {
|
||||
const volume = Volume.fromComposeObject('aaa', 1234, {});
|
||||
const volume = Volume.fromComposeObject('aaa', 1234, 'deadbeef');
|
||||
|
||||
// Check engine state before
|
||||
expect((await mockerode.listVolumes()).Volumes).to.have.lengthOf(1);
|
||||
@ -456,7 +511,7 @@ describe('compose/volume', () => {
|
||||
});
|
||||
await withMockerode(
|
||||
async (mockerode) => {
|
||||
const volume = Volume.fromComposeObject('aaa', 1234, {});
|
||||
const volume = Volume.fromComposeObject('aaa', 1234, 'deadbeef');
|
||||
|
||||
// Stub the mockerode method to fail
|
||||
mockerode.removeVolume.rejects('Something bad happened');
|
||||
|
Loading…
x
Reference in New Issue
Block a user