Create replicating test

This adds a test to check the case where a service image changes along
with networks being removed.
This commit is contained in:
Felipe Lalanne 2024-06-24 14:49:06 -04:00
parent 1c3e24ec29
commit 5d93f358aa
No known key found for this signature in database
GPG Key ID: 03E696BFD472B26A
3 changed files with 183 additions and 1 deletions

View File

@ -10,5 +10,5 @@ module.exports = {
'test/lib/chai.ts',
'test/lib/mocha-hooks.ts',
],
timeout: '30000',
timeout: '60000',
};

View File

@ -380,4 +380,115 @@ describe('state engine', () => {
Options: {},
});
});
it('updates an app with two services with a network removal', async () => {
await setTargetState({
config: {},
apps: {
'123': {
name: 'test-app',
commit: 'deadbeef',
releaseId: 1,
services: {
'1': {
image: 'alpine:3.18',
imageId: 11,
serviceName: 'one',
restart: 'unless-stopped',
running: true,
command: 'sleep infinity',
stop_signal: 'SIGKILL',
labels: {},
environment: {},
networks: ['balena'],
},
'2': {
image: 'ubuntu:focal',
imageId: 12,
serviceName: 'two',
restart: 'unless-stopped',
running: true,
command: 'sleep infinity',
labels: {},
environment: {},
network_mode: 'host',
},
},
networks: {
balena: {},
},
volumes: {},
},
},
});
const state = await getCurrentState();
expect(
state.apps['123'].services.map((s: any) => s.serviceName),
).to.deep.equal(['one', 'two']);
const containers = await docker.listContainers();
expect(
containers.map(({ Names, State }) => ({ Name: Names[0], State })),
).to.have.deep.members([
{ Name: '/one_11_1_deadbeef', State: 'running' },
{ Name: '/two_12_1_deadbeef', State: 'running' },
]);
const containerIds = containers.map(({ Id }) => Id);
await expect(docker.getNetwork('123_balena').inspect()).to.not.be.rejected;
await setTargetState({
config: {},
apps: {
'123': {
name: 'test-app',
commit: 'deadca1f',
releaseId: 2,
services: {
'1': {
image: 'alpine:latest',
imageId: 21,
serviceName: 'one',
restart: 'unless-stopped',
running: true,
command: 'sleep infinity',
stop_signal: 'SIGKILL',
networks: ['default'],
labels: {},
environment: {},
},
'2': {
image: 'ubuntu:latest',
imageId: 22,
serviceName: 'two',
restart: 'unless-stopped',
running: true,
command: 'sh -c "echo two && sleep infinity"',
stop_signal: 'SIGKILL',
network_mode: 'host',
labels: {},
environment: {},
},
},
networks: {},
volumes: {},
},
},
});
const updatedContainers = await docker.listContainers();
expect(
updatedContainers.map(({ Names, State }) => ({ Name: Names[0], State })),
).to.have.deep.members([
{ Name: '/one_21_2_deadca1f', State: 'running' },
{ Name: '/two_22_2_deadca1f', State: 'running' },
]);
// Container ids must have changed
expect(updatedContainers.map(({ Id }) => Id)).to.not.have.members(
containerIds,
);
await expect(docker.getNetwork('123_balena').inspect()).to.be.rejected;
});
});

View File

@ -692,6 +692,77 @@ describe('compose/app', () => {
expectNoStep('removeNetwork', steps2);
});
it('should perform fetch steps first even for services that reference deleted networks', async () => {
const current = createApp({
appUuid: 'deadbeef',
services: [
await createService({
appId: 1,
appUuid: 'deadbeef',
image: 'test-image',
serviceName: 'test',
composition: { networks: ['test-network'] },
}),
await createService({
appId: 1,
appUuid: 'deadbeef',
image: 'other-image',
serviceName: 'other',
composition: { network_mode: 'host' },
}),
],
networks: [
Network.fromComposeObject('test-network', 1, 'deadbeef', {}),
DEFAULT_NETWORK,
],
});
const target = createApp({
appUuid: 'deadbeef',
services: [
await createService({
appId: 1,
appUuid: 'deadbeef',
serviceName: 'test',
image: 'new-test-image',
commit: 'new-commit',
}),
await createService({
appId: 1,
appUuid: 'deadbeef',
serviceName: 'other',
image: 'new-other-image',
commit: 'new-commit',
composition: { network_mode: 'host' },
}),
],
networks: [],
isTarget: true,
});
const availableImages = [
createImage({
appUuid: 'deadbeef',
name: 'test-image',
serviceName: 'test',
}),
createImage({
appUuid: 'deadbeef',
name: 'other-image',
serviceName: 'other',
}),
];
// Take lock first
const steps = current.nextStepsForAppUpdate(
{ ...defaultContext, availableImages },
target,
);
const fetchSteps = expectSteps('fetch', steps, 2);
expect(fetchSteps.map((s) => (s as any).image.name)).to.have.deep.members(
['new-test-image', 'new-other-image'],
);
});
it('should kill dependencies of networks before changing config', async () => {
const current = createApp({
services: [