Skip to content

Commit 614fc72

Browse files
committed
fix: managing cache state is now synchronous
We no longer use async when managing cache state because we suspect it spins the event loop and causes race conditions where the same image is scanned multiple times even though it should have been cached the first time. Also, add debug logging to inspect the data in the cache.
1 parent 972a646 commit 614fc72

File tree

14 files changed

+126
-126
lines changed

14 files changed

+126
-126
lines changed

src/scanner/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,8 @@ export async function scanImagesAndSendResults(
134134

135135
// All workloads are identical, pick the first one
136136
const workload = workloadMetadata[0];
137-
const workloadState = await getWorkloadAlreadyScanned(workload);
138-
const imageState = await getWorkloadImageAlreadyScanned(
137+
const workloadState = getWorkloadAlreadyScanned(workload);
138+
const imageState = getWorkloadImageAlreadyScanned(
139139
workload,
140140
workload.imageName,
141141
workload.imageId,

src/state.ts

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { KubernetesObject, V1Namespace } from '@kubernetes/client-node';
22
import LruCache from 'lru-cache';
33

44
import { config } from './common/config';
5+
import { logger } from './common/logger';
56
import { extractNamespaceName } from './supervisor/watchers/internal-namespaces';
67

78
const imagesLruCacheOptions: LruCache.Options<string, Set<string>> = {
@@ -42,43 +43,62 @@ function getWorkloadImageAlreadyScannedKey(
4243
return `${workload.uid}/${imageName}`;
4344
}
4445

45-
export async function getWorkloadAlreadyScanned(
46+
export function getWorkloadAlreadyScanned(
4647
workload: WorkloadAlreadyScanned,
47-
): Promise<string | undefined> {
48+
): string | undefined {
4849
const key = workload.uid;
4950
return state.workloadsAlreadyScanned.get(key);
5051
}
5152

52-
export async function setWorkloadAlreadyScanned(
53+
export function setWorkloadAlreadyScanned(
5354
workload: WorkloadAlreadyScanned,
5455
revision: string,
55-
): Promise<boolean> {
56+
): boolean {
5657
const key = workload.uid;
5758
return state.workloadsAlreadyScanned.set(key, revision);
5859
}
5960

60-
export async function deleteWorkloadAlreadyScanned(
61+
export function deleteWorkloadAlreadyScanned(
6162
workload: WorkloadAlreadyScanned,
62-
): Promise<void> {
63+
): void {
6364
const key = workload.uid;
6465
state.workloadsAlreadyScanned.del(key);
6566
}
6667

67-
export async function getWorkloadImageAlreadyScanned(
68+
export function getWorkloadImageAlreadyScanned(
6869
workload: WorkloadAlreadyScanned,
6970
imageName: string,
7071
imageId: string,
71-
): Promise<string | undefined> {
72+
): string | undefined {
73+
const cachedImages = state.imagesAlreadyScanned.dump().map((entry) => {
74+
const values = new Array<string>();
75+
for (const v in entry.v.values()) {
76+
values.push(v);
77+
}
78+
return `entry: ${entry.e}, key: ${entry.k}; values: ${values.join(',')}`;
79+
});
80+
logger.debug(
81+
{ 'kubernetes-monitor': { cachedImages } },
82+
'images in the cache',
83+
);
84+
7285
const key = getWorkloadImageAlreadyScannedKey(workload, imageName);
7386
const hasImageId = state.imagesAlreadyScanned.get(key)?.has(imageId);
74-
return hasImageId ? imageId : undefined;
87+
const response = hasImageId ? imageId : undefined;
88+
if (response !== undefined) {
89+
logger.debug(
90+
{ 'kubernetes-monitor': { imageId } },
91+
'image already exists in cache',
92+
);
93+
}
94+
return response;
7595
}
7696

77-
export async function setWorkloadImageAlreadyScanned(
97+
export function setWorkloadImageAlreadyScanned(
7898
workload: WorkloadAlreadyScanned,
7999
imageName: string,
80100
imageId: string,
81-
): Promise<boolean> {
101+
): boolean {
82102
const key = getWorkloadImageAlreadyScannedKey(workload, imageName);
83103
const images = state.imagesAlreadyScanned.get(key);
84104
if (images !== undefined) {
@@ -92,9 +112,9 @@ export async function setWorkloadImageAlreadyScanned(
92112
return true;
93113
}
94114

95-
export async function deleteWorkloadImagesAlreadyScanned(
115+
export function deleteWorkloadImagesAlreadyScanned(
96116
workload: WorkloadImagesAlreadyScanned,
97-
): Promise<void> {
117+
): void {
98118
for (const imageId of workload.imageIds) {
99119
const key = getWorkloadImageAlreadyScannedKey(workload, imageId);
100120
state.imagesAlreadyScanned.del(key);

src/supervisor/watchers/handlers/argo-rollout.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -116,16 +116,14 @@ export async function argoRolloutWatchHandler(
116116
const workloadAlreadyScanned =
117117
kubernetesObjectToWorkloadAlreadyScanned(rollout);
118118
if (workloadAlreadyScanned !== undefined) {
119-
await Promise.all([
120-
deleteWorkloadAlreadyScanned(workloadAlreadyScanned),
121-
deleteWorkloadImagesAlreadyScanned({
122-
...workloadAlreadyScanned,
123-
imageIds: rollout.spec.template.spec.containers
124-
.filter((container) => container.image !== undefined)
125-
.map((container) => container.image!),
126-
}),
127-
deleteWorkloadFromScanQueue(workloadAlreadyScanned),
128-
]);
119+
deleteWorkloadAlreadyScanned(workloadAlreadyScanned);
120+
deleteWorkloadImagesAlreadyScanned({
121+
...workloadAlreadyScanned,
122+
imageIds: rollout.spec.template.spec.containers
123+
.filter((container) => container.image !== undefined)
124+
.map((container) => container.image!),
125+
});
126+
deleteWorkloadFromScanQueue(workloadAlreadyScanned);
129127
}
130128

131129
const workloadName = rollout.metadata.name || FALSY_WORKLOAD_NAME_MARKER;

src/supervisor/watchers/handlers/cron-job.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -110,16 +110,14 @@ export async function cronJobWatchHandler(
110110
const workloadAlreadyScanned =
111111
kubernetesObjectToWorkloadAlreadyScanned(cronJob);
112112
if (workloadAlreadyScanned !== undefined) {
113-
await Promise.all([
114-
deleteWorkloadAlreadyScanned(workloadAlreadyScanned),
115-
deleteWorkloadImagesAlreadyScanned({
116-
...workloadAlreadyScanned,
117-
imageIds: cronJob.spec.jobTemplate.spec.template.spec.containers
118-
.filter((container) => container.image !== undefined)
119-
.map((container) => container.image!),
120-
}),
121-
deleteWorkloadFromScanQueue(workloadAlreadyScanned),
122-
]);
113+
deleteWorkloadAlreadyScanned(workloadAlreadyScanned);
114+
deleteWorkloadImagesAlreadyScanned({
115+
...workloadAlreadyScanned,
116+
imageIds: cronJob.spec.jobTemplate.spec.template.spec.containers
117+
.filter((container) => container.image !== undefined)
118+
.map((container) => container.image!),
119+
});
120+
deleteWorkloadFromScanQueue(workloadAlreadyScanned);
123121
}
124122

125123
const workloadName = cronJob.metadata.name || FALSY_WORKLOAD_NAME_MARKER;

src/supervisor/watchers/handlers/daemon-set.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,14 @@ export async function daemonSetWatchHandler(
6464
const workloadAlreadyScanned =
6565
kubernetesObjectToWorkloadAlreadyScanned(daemonSet);
6666
if (workloadAlreadyScanned !== undefined) {
67-
await Promise.all([
68-
deleteWorkloadAlreadyScanned(workloadAlreadyScanned),
69-
deleteWorkloadImagesAlreadyScanned({
70-
...workloadAlreadyScanned,
71-
imageIds: daemonSet.spec.template.spec.containers
72-
.filter((container) => container.image !== undefined)
73-
.map((container) => container.image!),
74-
}),
75-
deleteWorkloadFromScanQueue(workloadAlreadyScanned),
76-
]);
67+
deleteWorkloadAlreadyScanned(workloadAlreadyScanned);
68+
deleteWorkloadImagesAlreadyScanned({
69+
...workloadAlreadyScanned,
70+
imageIds: daemonSet.spec.template.spec.containers
71+
.filter((container) => container.image !== undefined)
72+
.map((container) => container.image!),
73+
});
74+
deleteWorkloadFromScanQueue(workloadAlreadyScanned);
7775
}
7876

7977
const workloadName = daemonSet.metadata.name || FALSY_WORKLOAD_NAME_MARKER;

src/supervisor/watchers/handlers/deployment-config.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -116,16 +116,14 @@ export async function deploymentConfigWatchHandler(
116116
const workloadAlreadyScanned =
117117
kubernetesObjectToWorkloadAlreadyScanned(deploymentConfig);
118118
if (workloadAlreadyScanned !== undefined) {
119-
await Promise.all([
120-
deleteWorkloadAlreadyScanned(workloadAlreadyScanned),
121-
deleteWorkloadImagesAlreadyScanned({
122-
...workloadAlreadyScanned,
123-
imageIds: deploymentConfig.spec.template.spec.containers
124-
.filter((container) => container.image !== undefined)
125-
.map((container) => container.image!),
126-
}),
127-
deleteWorkloadFromScanQueue(workloadAlreadyScanned),
128-
]);
119+
deleteWorkloadAlreadyScanned(workloadAlreadyScanned);
120+
deleteWorkloadImagesAlreadyScanned({
121+
...workloadAlreadyScanned,
122+
imageIds: deploymentConfig.spec.template.spec.containers
123+
.filter((container) => container.image !== undefined)
124+
.map((container) => container.image!),
125+
});
126+
deleteWorkloadFromScanQueue(workloadAlreadyScanned);
129127
}
130128

131129
const workloadName =

src/supervisor/watchers/handlers/deployment.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,14 @@ export async function deploymentWatchHandler(
6464
const workloadAlreadyScanned =
6565
kubernetesObjectToWorkloadAlreadyScanned(deployment);
6666
if (workloadAlreadyScanned !== undefined) {
67-
await Promise.all([
68-
deleteWorkloadAlreadyScanned(workloadAlreadyScanned),
69-
deleteWorkloadImagesAlreadyScanned({
70-
...workloadAlreadyScanned,
71-
imageIds: deployment.spec.template.spec.containers
72-
.filter((container) => container.image !== undefined)
73-
.map((container) => container.image!),
74-
}),
75-
deleteWorkloadFromScanQueue(workloadAlreadyScanned),
76-
]);
67+
deleteWorkloadAlreadyScanned(workloadAlreadyScanned);
68+
deleteWorkloadImagesAlreadyScanned({
69+
...workloadAlreadyScanned,
70+
imageIds: deployment.spec.template.spec.containers
71+
.filter((container) => container.image !== undefined)
72+
.map((container) => container.image!),
73+
});
74+
deleteWorkloadFromScanQueue(workloadAlreadyScanned);
7775
}
7876

7977
const workloadName = deployment.metadata.name || FALSY_WORKLOAD_NAME_MARKER;

src/supervisor/watchers/handlers/job.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,14 @@ export async function jobWatchHandler(job: V1Job): Promise<void> {
5858

5959
const workloadAlreadyScanned = kubernetesObjectToWorkloadAlreadyScanned(job);
6060
if (workloadAlreadyScanned !== undefined) {
61-
await Promise.all([
62-
deleteWorkloadAlreadyScanned(workloadAlreadyScanned),
63-
deleteWorkloadImagesAlreadyScanned({
64-
...workloadAlreadyScanned,
65-
imageIds: job.spec.template.spec.containers
66-
.filter((container) => container.image !== undefined)
67-
.map((container) => container.image!),
68-
}),
69-
deleteWorkloadFromScanQueue(workloadAlreadyScanned),
70-
]);
61+
deleteWorkloadAlreadyScanned(workloadAlreadyScanned);
62+
deleteWorkloadImagesAlreadyScanned({
63+
...workloadAlreadyScanned,
64+
imageIds: job.spec.template.spec.containers
65+
.filter((container) => container.image !== undefined)
66+
.map((container) => container.image!),
67+
});
68+
deleteWorkloadFromScanQueue(workloadAlreadyScanned);
7169
}
7270

7371
const workloadName = job.metadata.name || FALSY_WORKLOAD_NAME_MARKER;

src/supervisor/watchers/handlers/pod.ts

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export async function handleReadyPod(
3030
): Promise<void> {
3131
const workloadToScan: IWorkload[] = [];
3232
for (const workload of workloadMetadata) {
33-
const scanned = await getWorkloadImageAlreadyScanned(
33+
const scanned = getWorkloadImageAlreadyScanned(
3434
workload,
3535
workload.imageName,
3636
workload.imageId,
@@ -49,7 +49,7 @@ export async function handleReadyPod(
4949
{ workloadToScan, imageId: workload.imageId },
5050
'image has not been scanned',
5151
);
52-
await setWorkloadImageAlreadyScanned(
52+
setWorkloadImageAlreadyScanned(
5353
workload,
5454
workload.imageName,
5555
workload.imageId,
@@ -147,12 +147,12 @@ export async function podWatchHandler(pod: V1Pod): Promise<void> {
147147
const workloadRevision = workloadMember.revision
148148
? workloadMember.revision.toString()
149149
: '';
150-
const scannedRevision = await getWorkloadAlreadyScanned(workloadMember);
150+
const scannedRevision = getWorkloadAlreadyScanned(workloadMember);
151151
const isRevisionDifferent =
152152
scannedRevision === undefined ||
153153
Number(workloadRevision) > Number(scannedRevision);
154154
if (isRevisionDifferent) {
155-
await setWorkloadAlreadyScanned(workloadMember, workloadRevision);
155+
setWorkloadAlreadyScanned(workloadMember, workloadRevision);
156156
await sendWorkloadMetadata(workloadMetadataPayload);
157157
}
158158

@@ -169,16 +169,14 @@ export async function podDeletedHandler(pod: V1Pod): Promise<void> {
169169

170170
const workloadAlreadyScanned = kubernetesObjectToWorkloadAlreadyScanned(pod);
171171
if (workloadAlreadyScanned !== undefined) {
172-
await Promise.all([
173-
deleteWorkloadAlreadyScanned(workloadAlreadyScanned),
174-
deleteWorkloadImagesAlreadyScanned({
175-
...workloadAlreadyScanned,
176-
imageIds: pod.spec.containers
177-
.filter((container) => container.image !== undefined)
178-
.map((container) => container.image!),
179-
}),
180-
deleteWorkloadFromScanQueue(workloadAlreadyScanned),
181-
]);
172+
deleteWorkloadAlreadyScanned(workloadAlreadyScanned);
173+
deleteWorkloadImagesAlreadyScanned({
174+
...workloadAlreadyScanned,
175+
imageIds: pod.spec.containers
176+
.filter((container) => container.image !== undefined)
177+
.map((container) => container.image!),
178+
});
179+
deleteWorkloadFromScanQueue(workloadAlreadyScanned);
182180
}
183181

184182
const workloadName = pod.metadata.name || FALSY_WORKLOAD_NAME_MARKER;

src/supervisor/watchers/handlers/queue.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ async function queueWorkerWorkloadScan(
5858
...workloadMetadata[0],
5959
imageIds,
6060
};
61-
await deleteWorkloadImagesAlreadyScanned(workload);
61+
deleteWorkloadImagesAlreadyScanned(workload);
6262
}
6363
}
6464

0 commit comments

Comments
 (0)