fix(ci): ignore terminating CloudNativePG rollout remnants

This commit is contained in:
whyour
2026-09-04 04:21:17 +08:00
parent 03afd7e899
commit 2005cb6ba9
2 changed files with 59 additions and 4 deletions
+15 -4
View File
@@ -485,11 +485,21 @@ function verifyImageIds(pods, expectedDigests, description) {
return [...new Set(imageIds)]; return [...new Set(imageIds)];
} }
function activeOperatorPods(pods) {
// A successful Deployment rollout can leave the old Pod terminating with
// an empty imageID. It is not evidence for the new serving generation.
return pods.filter(
(pod) =>
!pod.metadata?.deletionTimestamp &&
(pod.spec?.containers || []).some((container) =>
container.image.includes('cloudnative-pg'),
),
);
}
function operatorPods() { function operatorPods() {
return kubectlJson(['-n', 'cnpg-system', 'get', 'pods']).items.filter((pod) => return activeOperatorPods(
(pod.spec?.containers || []).some((container) => kubectlJson(['-n', 'cnpg-system', 'get', 'pods']).items,
container.image.includes('cloudnative-pg'),
),
); );
} }
@@ -1034,6 +1044,7 @@ if (require.main === module) {
} }
module.exports = { module.exports = {
activeOperatorPods,
imageDigest, imageDigest,
imageTag, imageTag,
localApplicationManifest, localApplicationManifest,
@@ -5,6 +5,7 @@ const path = require('node:path');
const { test } = require('node:test'); const { test } = require('node:test');
const { const {
activeOperatorPods,
imageDigest, imageDigest,
imageTag, imageTag,
localApplicationManifest, localApplicationManifest,
@@ -20,6 +21,49 @@ const INDEX = `sha256:${'a'.repeat(64)}`;
const PLATFORM = `sha256:${'b'.repeat(64)}`; const PLATFORM = `sha256:${'b'.repeat(64)}`;
const VERSION = readReleaseIdentity(path.resolve(__dirname, '../..')).version; const VERSION = readReleaseIdentity(path.resolve(__dirname, '../..')).version;
test('operator image evidence excludes terminating rollout remnants but never missing or unreviewed live pods', () => {
const operator = (imageID, deleting = false) => ({
metadata: deleting ? { deletionTimestamp: '2026-09-03T20:00:17Z' } : {},
spec: {
containers: [
{ image: `ghcr.io/cloudnative-pg/cloudnative-pg:1.30.0@${INDEX}` },
],
},
status: { containerStatuses: [{ imageID }] },
});
const live = operator(`ghcr.io/cloudnative-pg/cloudnative-pg@${PLATFORM}`);
const old = operator('', true);
assert.throws(() =>
verifyImageIds([live, old], [INDEX, PLATFORM], 'operator'),
);
assert.deepEqual(activeOperatorPods([live, old]), [live]);
assert.deepEqual(
verifyImageIds(
activeOperatorPods([live, old]),
[INDEX, PLATFORM],
'operator',
),
[`ghcr.io/cloudnative-pg/cloudnative-pg@${PLATFORM}`],
);
assert.throws(() =>
verifyImageIds(activeOperatorPods([old]), [INDEX], 'operator'),
);
assert.throws(() =>
verifyImageIds(
activeOperatorPods([live, operator('')]),
[INDEX, PLATFORM],
'operator',
),
);
assert.throws(() =>
verifyImageIds(
activeOperatorPods([live, operator(`sha256:${'c'.repeat(64)}`)]),
[INDEX, PLATFORM],
'operator',
),
);
});
function pods(...imageIds) { function pods(...imageIds) {
return imageIds.map((imageID) => ({ return imageIds.map((imageID) => ({
status: { containerStatuses: [{ imageID }] }, status: { containerStatuses: [{ imageID }] },