Skip to content

Commit 19aea3a

Browse files
author
Tal Kaptsan
authored
Merge pull request #154 from snyk/feat/workload-config-meta
Feat/workload config meta
2 parents 56a75a8 + 5b8e166 commit 19aea3a

15 files changed

+150
-20
lines changed

src/kube-scanner/metadata-extractor.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ export function buildImageMetadata(
1313
workloadMeta: KubeObjectMetadata,
1414
containerStatuses: V1ContainerStatus[],
1515
): IWorkload[] {
16-
const { kind, objectMeta, specMeta, containers, revision } = workloadMeta;
16+
const { kind, objectMeta, specMeta, revision, podSpec } = workloadMeta;
1717
const { name, namespace, labels, annotations, uid } = objectMeta;
1818

1919
const containerNameToSpec: {[key: string]: V1Container} = {};
20-
for (const container of containers) {
20+
for (const container of podSpec.containers) {
2121
containerNameToSpec[container.name] = container;
2222
}
2323

@@ -40,6 +40,7 @@ export function buildImageMetadata(
4040
imageId: containerNameToStatus[containerName].imageID,
4141
cluster: currentClusterName,
4242
revision,
43+
podSpec,
4344
} as IWorkload),
4445
);
4546
return images;
@@ -113,7 +114,7 @@ export async function buildMetadataForWorkload(pod: V1Pod): Promise<IWorkload[]
113114
// do not have the "template" property.
114115
specMeta: pod.metadata,
115116
ownerRefs: [],
116-
containers: pod.spec.containers,
117+
podSpec: pod.spec,
117118
},
118119
pod.status.containerStatuses,
119120
);

src/kube-scanner/types.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { AppsV1Api, BatchV1Api, BatchV1beta1Api, CoreV1Api, KubeConfig,
2-
V1Container, V1ObjectMeta, V1OwnerReference } from '@kubernetes/client-node';
2+
V1ObjectMeta, V1OwnerReference, V1PodSpec } from '@kubernetes/client-node';
33

44
export enum WorkloadKind {
55
Deployment = 'Deployment',
@@ -35,7 +35,7 @@ export interface KubeObjectMetadata {
3535
kind: string;
3636
objectMeta: V1ObjectMeta;
3737
specMeta: V1ObjectMeta;
38-
containers: V1Container[];
38+
podSpec: V1PodSpec;
3939
ownerRefs: V1OwnerReference[] | undefined;
4040
revision?: number;
4141
}

src/kube-scanner/watchers/handlers/cron-job.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export async function cronJobWatchHandler(cronJob: V1beta1CronJob) {
1616
kind: WorkloadKind.CronJob,
1717
objectMeta: cronJob.metadata,
1818
specMeta: cronJob.spec.jobTemplate.metadata,
19-
containers: cronJob.spec.jobTemplate.spec.template.spec.containers,
2019
ownerRefs: cronJob.metadata.ownerReferences,
20+
podSpec: cronJob.spec.jobTemplate.spec.template.spec,
2121
}, workloadName);
2222
}

src/kube-scanner/watchers/handlers/daemon-set.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ export async function daemonSetWatchHandler(daemonSet: V1DaemonSet) {
1616
kind: WorkloadKind.DaemonSet,
1717
objectMeta: daemonSet.metadata,
1818
specMeta: daemonSet.spec.template.metadata,
19-
containers: daemonSet.spec.template.spec.containers,
2019
ownerRefs: daemonSet.metadata.ownerReferences,
2120
revision: daemonSet.status.observedGeneration,
21+
podSpec: daemonSet.spec.template.spec,
2222
}, workloadName);
2323
}

src/kube-scanner/watchers/handlers/deployment.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ export async function deploymentWatchHandler(deployment: V1Deployment) {
1616
kind: WorkloadKind.Deployment,
1717
objectMeta: deployment.metadata,
1818
specMeta: deployment.spec.template.metadata,
19-
containers: deployment.spec.template.spec.containers,
2019
ownerRefs: deployment.metadata.ownerReferences,
2120
revision: deployment.status.observedGeneration,
21+
podSpec: deployment.spec.template.spec,
2222
}, workloadName);
2323
}

src/kube-scanner/watchers/handlers/job.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export async function jobWatchHandler(job: V1Job) {
1515
kind: WorkloadKind.Job,
1616
objectMeta: job.metadata,
1717
specMeta: job.spec.template.metadata,
18-
containers: job.spec.template.spec.containers,
1918
ownerRefs: job.metadata.ownerReferences,
19+
podSpec: job.spec.template.spec,
2020
}, workloadName);
2121
}

src/kube-scanner/watchers/handlers/replica-set.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ export async function replicaSetWatchHandler(replicaSet: V1ReplicaSet) {
1616
kind: WorkloadKind.ReplicaSet,
1717
objectMeta: replicaSet.metadata,
1818
specMeta: replicaSet.spec.template.metadata,
19-
containers: replicaSet.spec.template.spec.containers,
2019
ownerRefs: replicaSet.metadata.ownerReferences,
2120
revision: replicaSet.status.observedGeneration,
21+
podSpec: replicaSet.spec.template.spec,
2222
}, workloadName);
2323
}

src/kube-scanner/watchers/handlers/replication-controller.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ export async function replicationControllerWatchHandler(replicationController: V
1717
kind: WorkloadKind.ReplicationController,
1818
objectMeta: replicationController.metadata,
1919
specMeta: replicationController.spec.template.metadata,
20-
containers: replicationController.spec.template.spec.containers,
2120
ownerRefs: replicationController.metadata.ownerReferences,
2221
revision: replicationController.status.observedGeneration,
22+
podSpec: replicationController.spec.template.spec,
2323
}, workloadName);
2424
}

src/kube-scanner/watchers/handlers/stateful-set.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ export async function statefulSetWatchHandler(statefulSet: V1StatefulSet) {
1616
kind: WorkloadKind.StatefulSet,
1717
objectMeta: statefulSet.metadata,
1818
specMeta: statefulSet.spec.template.metadata,
19-
containers: statefulSet.spec.template.spec.containers,
2019
ownerRefs: statefulSet.metadata.ownerReferences,
2120
revision: statefulSet.status.observedGeneration,
21+
podSpec: statefulSet.spec.template.spec,
2222
}, workloadName);
2323
}

src/kube-scanner/workload-reader.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ const deploymentReader: IWorkloadReaderFunc = async (workloadName, namespace) =>
2222
kind: WorkloadKind.Deployment,
2323
objectMeta: deployment.metadata,
2424
specMeta: deployment.spec.template.metadata,
25-
containers: deployment.spec.template.spec.containers,
2625
ownerRefs: deployment.metadata.ownerReferences,
2726
revision: deployment.status.observedGeneration,
27+
podSpec: deployment.spec.template.spec,
2828
};
2929
};
3030

@@ -43,9 +43,9 @@ const replicaSetReader: IWorkloadReaderFunc = async (workloadName, namespace) =>
4343
kind: WorkloadKind.ReplicaSet,
4444
objectMeta: replicaSet.metadata,
4545
specMeta: replicaSet.spec.template.metadata,
46-
containers: replicaSet.spec.template.spec.containers,
4746
ownerRefs: replicaSet.metadata.ownerReferences,
4847
revision: replicaSet.status.observedGeneration,
48+
podSpec: replicaSet.spec.template.spec,
4949
};
5050
};
5151

@@ -64,9 +64,9 @@ const statefulSetReader: IWorkloadReaderFunc = async (workloadName, namespace) =
6464
kind: WorkloadKind.StatefulSet,
6565
objectMeta: statefulSet.metadata,
6666
specMeta: statefulSet.spec.template.metadata,
67-
containers: statefulSet.spec.template.spec.containers,
6867
ownerRefs: statefulSet.metadata.ownerReferences,
6968
revision: statefulSet.status.observedGeneration,
69+
podSpec: statefulSet.spec.template.spec,
7070
};
7171
};
7272

@@ -85,9 +85,9 @@ const daemonSetReader: IWorkloadReaderFunc = async (workloadName, namespace) =>
8585
kind: WorkloadKind.DaemonSet,
8686
objectMeta: daemonSet.metadata,
8787
specMeta: daemonSet.spec.template.metadata,
88-
containers: daemonSet.spec.template.spec.containers,
8988
ownerRefs: daemonSet.metadata.ownerReferences,
9089
revision: daemonSet.status.observedGeneration,
90+
podSpec: daemonSet.spec.template.spec,
9191
};
9292
};
9393

@@ -105,8 +105,8 @@ const jobReader: IWorkloadReaderFunc = async (workloadName, namespace) => {
105105
kind: WorkloadKind.Job,
106106
objectMeta: job.metadata,
107107
specMeta: job.spec.template.metadata,
108-
containers: job.spec.template.spec.containers,
109108
ownerRefs: job.metadata.ownerReferences,
109+
podSpec: job.spec.template.spec,
110110
};
111111
};
112112

@@ -128,8 +128,8 @@ const cronJobReader: IWorkloadReaderFunc = async (workloadName, namespace) => {
128128
kind: WorkloadKind.CronJob,
129129
objectMeta: cronJob.metadata,
130130
specMeta: cronJob.spec.jobTemplate.metadata,
131-
containers: cronJob.spec.jobTemplate.spec.template.spec.containers,
132131
ownerRefs: cronJob.metadata.ownerReferences,
132+
podSpec: cronJob.spec.jobTemplate.spec.template.spec,
133133
};
134134
};
135135

@@ -149,9 +149,9 @@ const replicationControllerReader: IWorkloadReaderFunc = async (workloadName, na
149149
kind: WorkloadKind.ReplicationController,
150150
objectMeta: replicationController.metadata,
151151
specMeta: replicationController.spec.template.metadata,
152-
containers: replicationController.spec.template.spec.containers,
153152
ownerRefs: replicationController.metadata.ownerReferences,
154153
revision: replicationController.status.observedGeneration,
154+
podSpec: replicationController.spec.template.spec,
155155
};
156156
};
157157

src/transmitter/payload.ts

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export function constructHomebaseWorkloadMetadataPayload(workload: IWorkload): I
5858
annotations: workload.annotations,
5959
specAnnotations: workload.specAnnotations,
6060
revision: workload.revision,
61+
podSpec: workload.podSpec,
6162
};
6263
return { workloadLocator, agentId: config.AGENT_ID, workloadMetadata };
6364
}

src/transmitter/types.ts

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { V1PodSpec } from '@kubernetes/client-node';
2+
13
interface StringMap { [key: string]: string; }
24

35
export interface ILocalWorkloadLocator {
@@ -17,6 +19,7 @@ export interface IWorkloadMetadata {
1719
annotations: StringMap | undefined;
1820
specAnnotations: StringMap | undefined;
1921
revision: number | undefined;
22+
podSpec: V1PodSpec;
2023
}
2124

2225
export interface IImageLocator extends IWorkloadLocator {
@@ -54,4 +57,5 @@ export interface IWorkload {
5457
imageName: string;
5558
imageId: string;
5659
cluster: string;
60+
podSpec: V1PodSpec;
5761
}

test/fixtures/pod-spec.json

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
{
2+
"containers": [
3+
{
4+
"env": [
5+
{
6+
"name": "SNYK_INTEGRATION_ID",
7+
"valueFrom": {
8+
"secretKeyRef": {
9+
"key": "integrationId",
10+
"name": "snyk-monitor"
11+
}
12+
}
13+
},
14+
{
15+
"name": "SNYK_NAMESPACE"
16+
},
17+
{
18+
"name": "SNYK_INTEGRATION_API"
19+
},
20+
{
21+
"name": "SNYK_CLUSTER_NAME",
22+
"value": "Production cluster"
23+
},
24+
{
25+
"name": "SNYK_STATIC_ANALYSIS",
26+
"value": "true"
27+
}
28+
],
29+
"image": "snyk/kubernetes-monitor:1.8.5",
30+
"imagePullPolicy": "Always",
31+
"name": "snyk-monitor",
32+
"resources": {
33+
"limits": {
34+
"cpu": "1",
35+
"memory": "2Gi"
36+
},
37+
"requests": {
38+
"cpu": "250m",
39+
"memory": "400Mi"
40+
}
41+
},
42+
"terminationMessagePath": "/dev/termination-log",
43+
"terminationMessagePolicy": "File",
44+
"volumeMounts": [
45+
{
46+
"mountPath": "/root/.docker",
47+
"name": "docker-config",
48+
"readOnly": true
49+
},
50+
{
51+
"mountPath": "/snyk-monitor",
52+
"name": "temporary-storage"
53+
},
54+
{
55+
"mountPath": "/var/run/secrets/kubernetes.io/serviceaccount",
56+
"name": "snyk-monitor-token-ncps2",
57+
"readOnly": true
58+
}
59+
]
60+
}
61+
],
62+
"dnsPolicy": "ClusterFirst",
63+
"enableServiceLinks": true,
64+
"nodeName": "gke-test-node-123456",
65+
"priority": 0,
66+
"restartPolicy": "Always",
67+
"schedulerName": "default-scheduler",
68+
"securityContext": {},
69+
"serviceAccount": "snyk-monitor",
70+
"serviceAccountName": "snyk-monitor",
71+
"terminationGracePeriodSeconds": 30,
72+
"tolerations": [
73+
{
74+
"effect": "NoExecute",
75+
"key": "node.kubernetes.io/not-ready",
76+
"operator": "Exists",
77+
"tolerationSeconds": 300
78+
},
79+
{
80+
"effect": "NoExecute",
81+
"key": "node.kubernetes.io/unreachable",
82+
"operator": "Exists",
83+
"tolerationSeconds": 300
84+
}
85+
],
86+
"volumes": [
87+
{
88+
"name": "docker-config",
89+
"secret": {
90+
"defaultMode": 420,
91+
"items": [
92+
{
93+
"key": "dockercfg.json",
94+
"path": "config.json"
95+
}
96+
],
97+
"secretName": "snyk-monitor"
98+
}
99+
},
100+
{
101+
"emptyDir": {
102+
"sizeLimit": "50Gi"
103+
},
104+
"name": "temporary-storage"
105+
},
106+
{
107+
"name": "snyk-monitor-token-test",
108+
"secret": {
109+
"defaultMode": 420,
110+
"secretName": "snyk-monitor-token-test"
111+
}
112+
}
113+
]
114+
}

test/integration/kubernetes.test.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ tap.test('snyk-monitor sends data to homebase', async (t) => {
8787

8888
const metaValidator: WorkloadMetadataValidator = (workloadInfo) => {
8989
return workloadInfo !== undefined && 'revision' in workloadInfo && 'labels' in workloadInfo &&
90-
'specLabels' in workloadInfo && 'annotations' in workloadInfo && 'specAnnotations' in workloadInfo;
90+
'specLabels' in workloadInfo && 'annotations' in workloadInfo && 'specAnnotations' in workloadInfo &&
91+
'podSpec' in workloadInfo;
9192
};
9293

9394
// We don't want to spam Homebase with requests; do it infrequently

test/unit/transmitter-payload.test.ts

+9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as tap from 'tap';
33
import imageScanner = require('../../src/kube-scanner/image-scanner');
44
import payload = require('../../src/transmitter/payload');
55
import transmitterTypes = require('../../src/transmitter/types');
6+
const podSpecFixture = require('../fixtures/pod-spec.json');
67

78
tap.test('constructHomebaseDepGraphPayloads breaks when workloadMetadata is missing items', async (t) => {
89
const scannedImages: imageScanner.IScanResult[] = [
@@ -33,6 +34,7 @@ tap.test('constructHomebaseDepGraphPayloads breaks when workloadMetadata is miss
3334
imageId: 'does this matter?',
3435
cluster: 'grapefruit',
3536
revision: undefined,
37+
podSpec: podSpecFixture,
3638
},
3739
];
3840

@@ -64,6 +66,7 @@ tap.test('constructHomebaseDepGraphPayloads happy flow', async (t) => {
6466
imageId: 'does this matter?',
6567
cluster: 'grapefruit',
6668
revision: 1,
69+
podSpec: podSpecFixture,
6770
},
6871
];
6972

@@ -92,6 +95,7 @@ tap.test('constructHomebaseWorkloadMetadataPayload happy flow', async (t) => {
9295
imageId: 'does this matter?',
9396
cluster: 'grapefruit',
9497
revision: 1,
98+
podSpec: podSpecFixture,
9599
};
96100

97101
const workloadMetadataPayload = payload.constructHomebaseWorkloadMetadataPayload(workloadWithImages);
@@ -101,6 +105,11 @@ tap.test('constructHomebaseWorkloadMetadataPayload happy flow', async (t) => {
101105
t.equals(workloadMetadataPayload.workloadLocator.name, 'workloadName', 'workload name present in payload');
102106
t.equals(workloadMetadataPayload.workloadLocator.type, 'type', 'workload type present in payload');
103107
t.equals(workloadMetadataPayload.workloadMetadata.revision, 1, 'revision present in metadata');
108+
t.ok('podSpec' in workloadMetadataPayload.workloadMetadata, 'podSpec present in metadata');
109+
t.equals(workloadMetadataPayload.workloadMetadata.podSpec.containers[0].resources!.limits!.memory!, '2Gi',
110+
'memory limit present in metadata');
111+
t.equals(workloadMetadataPayload.workloadMetadata.podSpec.serviceAccountName, 'snyk-monitor',
112+
'service account name present in metadata');
104113
t.ok('annotations' in workloadMetadataPayload.workloadMetadata, 'annotations present in metadata');
105114
t.ok('specAnnotations' in workloadMetadataPayload.workloadMetadata, 'specAnnotations present in metadata');
106115
t.ok('labels' in workloadMetadataPayload.workloadMetadata, 'labels present in metadata');

0 commit comments

Comments
 (0)