balena-supervisor/test/integration/compose/volume.spec.ts

181 lines
4.6 KiB
TypeScript
Raw Normal View History

import { expect } from 'chai';
import type { SinonStub } from 'sinon';
import { stub } from 'sinon';
import Volume from '~/src/compose/volume';
import * as logTypes from '~/lib/log-types';
import * as logger from '~/src/logger';
import * as Docker from 'dockerode';
import { createVolume, withMockerode } from '~/test-lib/mockerode';
describe('compose/volume: integration tests', () => {
const docker = new Docker();
describe('creating and removing docker volumes', () => {
before(() => {
// TODO: can we spy the actual log stream instead of stubbing and using
// implementation details?
stub(logger, 'logSystemEvent');
});
afterEach(() => {
(logger.logSystemEvent as SinonStub).reset();
});
after(async () => {
const { Volumes: allVolumes } = await docker.listVolumes();
await Promise.all(
allVolumes.map(({ Name }) => docker.getVolume(Name).remove()),
);
(logger.logSystemEvent as SinonStub).restore();
});
it('should use defaults to create the volume when no options are given', async () => {
const volume = Volume.fromComposeObject(
'one_volume',
1032480,
'deadbeef',
);
// Create the volume
await volume.create();
const dockerVolumeName = Volume.generateDockerName(
volume.appId,
volume.name,
);
// This should not throw
const dockerVolume = await docker.getVolume(dockerVolumeName).inspect();
expect(dockerVolume).to.deep.include({
Name: dockerVolumeName,
Driver: 'local',
Labels: {
'io.balena.supervised': 'true',
'io.balena.app-uuid': 'deadbeef',
},
});
expect(logger.logSystemEvent).to.have.been.calledOnceWith(
logTypes.createVolume,
);
// Test volume removal
await volume.remove();
// The volume should no longer exist
await expect(docker.getVolume(dockerVolumeName).inspect()).to.be.rejected;
// Check that log entry was generated
expect(logger.logSystemEvent).to.have.been.calledWith(
logTypes.removeVolume,
);
});
it('should pass configuration options to the engine', async () => {
const volume = Volume.fromComposeObject(
'one_volume',
1032480,
'deadbeef',
{
driver: 'local',
driver_opts: {
type: 'tmpfs',
device: 'tmpfs',
},
labels: {
'my-label': 'test-label',
},
},
);
await volume.create();
const dockerVolumeName = Volume.generateDockerName(
volume.appId,
volume.name,
);
// This should not throw
const dockerVolume = await docker.getVolume(dockerVolumeName).inspect();
expect(dockerVolume).to.deep.include({
Name: dockerVolumeName,
Driver: 'local',
Labels: {
'my-label': 'test-label',
'io.balena.supervised': 'true',
'io.balena.app-uuid': 'deadbeef',
},
Options: {
device: 'tmpfs',
type: 'tmpfs',
},
});
expect(logger.logSystemEvent).to.have.been.calledOnceWith(
logTypes.createVolume,
);
// Test volume removal
await volume.remove();
// The volume should no longer exist
await expect(docker.getVolume(dockerVolumeName).inspect()).to.be.rejected;
// Check that log entry was generated
expect(logger.logSystemEvent).to.have.been.calledWith(
logTypes.removeVolume,
);
});
it('should report an error if the volume does not exist', async () => {
const volume = Volume.fromComposeObject('aaa', 1234, 'deadbeef');
const dockerVolumeName = Volume.generateDockerName(
volume.appId,
volume.name,
);
// The volume should not exist before
await expect(docker.getVolume(dockerVolumeName).inspect()).to.be.rejected;
// Remove the volume, this should not throw
await expect(volume.remove()).to.not.be.rejected;
// Check that log entry was generated
expect(logger.logSystemEvent).to.have.been.calledWith(
logTypes.removeVolumeError,
);
});
it('should report an error if a problem happens while removing the volume', async () => {
const dockerVolume = createVolume({
Name: '1234_aaa',
});
// We only use mockerode to simulate errors
await withMockerode(
async (mockerode) => {
const volume = Volume.fromComposeObject('aaa', 1234, 'deadbeef');
// Stub the mockerode method to fail
mockerode.removeVolume.rejects('Something bad happened');
// Check engine state before
expect((await mockerode.listVolumes()).Volumes).to.have.lengthOf(1);
// Remove the volume, this should not throw
await expect(volume.remove()).to.not.be.rejected;
// Check that log entry was generated
expect(logger.logSystemEvent).to.have.been.calledWith(
logTypes.removeVolumeError,
);
},
{ volumes: [dockerVolume] },
);
});
});
});