Skip to content

Commit b651759

Browse files
committed
fix: ensure multiple images sharing a tag are correctly tracked
Rolling tags like "latest" may resolve to different images on scale-up or scale-down event or when using Rollouts, causing out of order scans.
1 parent 7af1ab4 commit b651759

File tree

4 files changed

+22
-11
lines changed

4 files changed

+22
-11
lines changed

src/scanner/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ export async function scanImagesAndSendResults(
138138
const imageState = await getWorkloadImageAlreadyScanned(
139139
workload,
140140
workload.imageName,
141+
workload.imageId,
141142
);
142143
if (workloadState === undefined && imageState === undefined) {
143144
logger.info(

src/state.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import LruCache from 'lru-cache';
44
import { config } from './common/config';
55
import { extractNamespaceName } from './supervisor/watchers/internal-namespaces';
66

7-
const imagesLruCacheOptions: LruCache.Options<string, string> = {
7+
const imagesLruCacheOptions: LruCache.Options<string, Set<string>> = {
88
// limit cache size so we don't exceed memory limit
99
max: config.IMAGES_SCANNED_CACHE.MAX_SIZE,
1010
// limit cache life so if our backend loses track of an image's data,
@@ -72,10 +72,12 @@ export async function deleteWorkloadAlreadyScanned(
7272

7373
export async function getWorkloadImageAlreadyScanned(
7474
workload: WorkloadAlreadyScanned,
75+
imageName: string,
7576
imageId: string,
7677
): Promise<string | undefined> {
77-
const key = getWorkloadImageAlreadyScannedKey(workload, imageId);
78-
return state.imagesAlreadyScanned.get(key);
78+
const key = getWorkloadImageAlreadyScannedKey(workload, imageName);
79+
const hasImageId = state.imagesAlreadyScanned.get(key)?.has(imageId);
80+
return hasImageId ? imageId : undefined;
7981
}
8082

8183
export async function setWorkloadImageAlreadyScanned(
@@ -84,7 +86,16 @@ export async function setWorkloadImageAlreadyScanned(
8486
value: string,
8587
): Promise<boolean> {
8688
const key = getWorkloadImageAlreadyScannedKey(workload, imageId);
87-
return state.imagesAlreadyScanned.set(key, value);
89+
const images = state.imagesAlreadyScanned.get(key);
90+
if (images !== undefined) {
91+
images.add(value);
92+
} else {
93+
const set = new Set<string>();
94+
set.add(value);
95+
state.imagesAlreadyScanned.set(key, set);
96+
}
97+
98+
return true;
8899
}
89100

90101
export async function deleteWorkloadImagesAlreadyScanned(
@@ -126,7 +137,9 @@ export function deleteNamespace(namespace: V1Namespace): void {
126137

127138
export const state = {
128139
shutdownInProgress: false,
129-
imagesAlreadyScanned: new LruCache<string, string>(imagesLruCacheOptions),
140+
imagesAlreadyScanned: new LruCache<string, Set<string>>(
141+
imagesLruCacheOptions,
142+
),
130143
workloadsAlreadyScanned: new LruCache<string, string>(
131144
workloadsLruCacheOptions,
132145
),

src/supervisor/watchers/handlers/pod.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export async function handleReadyPod(
3333
const scanned = await getWorkloadImageAlreadyScanned(
3434
workload,
3535
workload.imageName,
36+
workload.imageId,
3637
);
3738
// ImageID contains the resolved image digest.
3839
// ImageName may contain a tag. The image behind this tag can be mutated and can change over time.

test/unit/scanner/scan-results-caching.spec.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -206,12 +206,8 @@ describe('scan results caching', () => {
206206
const sendScanResultsMock = jest.spyOn(transmitter, 'sendScanResults');
207207

208208
// Act
209-
await state.setWorkloadAlreadyScanned(workload, undefined as any);
210-
await state.setWorkloadImageAlreadyScanned(
211-
workload,
212-
workload.imageName,
213-
undefined as any,
214-
);
209+
state.state.workloadsAlreadyScanned.reset();
210+
state.state.imagesAlreadyScanned.reset();
215211

216212
const workloadName = 'mock';
217213
const pulledImages = [];

0 commit comments

Comments
 (0)