Improve net alias comparison to prevent unwanted restarts

Network aliases are now compared checking that the target state is a
subset of the current state. This will prevent service restarts due to
additional aliases created by docker in the container.

Closes: #2134
Change-type: patch
This commit is contained in:
Felipe Lalanne 2023-04-07 16:05:13 -04:00
parent cb98133717
commit 27f0d2e655
2 changed files with 81 additions and 10 deletions

View File

@ -1074,20 +1074,17 @@ export class Service {
if (current.aliases == null) {
sameNetwork = false;
} else {
// Take out the container id from both aliases, as it *will* be present
// in a currently running container, and can also be present in the target
// for example when doing a start-service
// Also sort the aliases, so we can do a simple comparison
const [currentAliases, targetAliases] = [
current.aliases,
target.aliases,
].map((aliases) =>
_.sortBy(
aliases.filter((a) => !_.startsWith(this.containerId || '', a)),
),
);
];
sameNetwork = _.isEqual(currentAliases, targetAliases);
// Docker may add keep old container ids as aliases for a specific service after
// restarts, this means that the target aliases really needs to be a subset of the
// current aliases to prevent service restarts when re-applying the same target state
sameNetwork =
_.intersection(currentAliases, targetAliases).length ===
targetAliases.length;
}
}
if (target.ipv4Address != null) {

View File

@ -574,6 +574,80 @@ describe('compose/service: unit tests', () => {
expect(svc1.isEqualConfig(svc2, {})).to.be.true;
});
});
it('should accept that target network aliases are a subset of current network aliases', async () => {
const svc1 = await Service.fromComposeObject(
{
appId: 1,
serviceId: 1,
serviceName: 'test',
composition: {
networks: {
test: {
aliases: ['hello', 'world'],
},
},
},
},
{ appName: 'test' } as any,
);
const svc2 = await Service.fromComposeObject(
{
appId: 1,
serviceId: 1,
serviceName: 'test',
composition: {
networks: {
test: {
aliases: ['hello', 'sweet', 'world'],
},
},
},
},
{ appName: 'test' } as any,
);
// All aliases in target service (svc1) are contained in service 2
expect(svc2.isEqualConfig(svc1, {})).to.be.true;
// But the opposite is not true
expect(svc1.isEqualConfig(svc2, {})).to.be.false;
});
it('should accept equal lists of network aliases', async () => {
const svc1 = await Service.fromComposeObject(
{
appId: 1,
serviceId: 1,
serviceName: 'test',
composition: {
networks: {
test: {
aliases: ['hello', 'world'],
},
},
},
},
{ appName: 'test' } as any,
);
const svc2 = await Service.fromComposeObject(
{
appId: 1,
serviceId: 1,
serviceName: 'test',
composition: {
networks: {
test: {
aliases: ['hello', 'world'],
},
},
},
},
{ appName: 'test' } as any,
);
expect(svc1.isEqualConfig(svc2, {})).to.be.true;
expect(svc2.isEqualConfig(svc1, {})).to.be.true;
});
});
describe('Feature labels', () => {