go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/_motor/discovery/k8s/list_workloads_test.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package k8s 5 6 import ( 7 "strings" 8 "testing" 9 10 "github.com/golang/mock/gomock" 11 "github.com/stretchr/testify/assert" 12 "github.com/stretchr/testify/require" 13 "go.mondoo.com/cnquery/motor/providers" 14 "go.mondoo.com/cnquery/motor/providers/k8s" 15 appsv1 "k8s.io/api/apps/v1" 16 batchv1 "k8s.io/api/batch/v1" 17 corev1 "k8s.io/api/core/v1" 18 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 19 ) 20 21 func TestListCronJobs(t *testing.T) { 22 mockCtrl := gomock.NewController(t) 23 defer mockCtrl.Finish() 24 25 p := k8s.NewMockKubernetesProvider(mockCtrl) 26 27 // Seed namespaces 28 nss := []corev1.Namespace{ 29 {ObjectMeta: metav1.ObjectMeta{Name: "default"}}, 30 } 31 p.EXPECT().Namespaces().Return(nss, nil) 32 // called for each CronJob 33 p.EXPECT().Runtime().Return("k8s-cluster") 34 p.EXPECT().Runtime().Return("k8s-cluster") 35 36 // pretend cronjob owned by deployment 37 parent := appsv1.Deployment{ 38 TypeMeta: metav1.TypeMeta{ 39 Kind: "Deployment", 40 APIVersion: "apps/v1", 41 }, 42 ObjectMeta: metav1.ObjectMeta{ 43 Name: "nginx-deployment", 44 Namespace: nss[0].Name, 45 UID: "000", 46 }, 47 Spec: appsv1.DeploymentSpec{ 48 Template: corev1.PodTemplateSpec{ 49 Spec: corev1.PodSpec{ 50 Containers: []corev1.Container{{Image: "nginx:1.22.0-alpine"}}, 51 }, 52 }, 53 }, 54 } 55 56 // Seed CronJobs 57 cronjobs := []*batchv1.CronJob{ 58 { 59 TypeMeta: metav1.TypeMeta{ 60 Kind: "CronJob", 61 APIVersion: "batch/v1", 62 }, 63 ObjectMeta: metav1.ObjectMeta{ 64 Name: "nginx", 65 Namespace: nss[0].Name, 66 UID: "123", 67 OwnerReferences: []metav1.OwnerReference{ 68 { 69 APIVersion: parent.APIVersion, 70 Kind: parent.Kind, 71 Name: parent.Name, 72 UID: parent.UID, 73 }, 74 }, 75 }, 76 Spec: batchv1.CronJobSpec{ 77 Schedule: "*/1 * * * *", 78 JobTemplate: batchv1.JobTemplateSpec{ 79 Spec: batchv1.JobSpec{ 80 Template: corev1.PodTemplateSpec{ 81 ObjectMeta: metav1.ObjectMeta{ 82 Labels: map[string]string{ 83 "app": "nginx", 84 }, 85 }, 86 Spec: corev1.PodSpec{ 87 Containers: []corev1.Container{{Image: "nginx:1.22.0-alpine"}}, 88 }, 89 }, 90 }, 91 }, 92 }, 93 }, 94 { 95 TypeMeta: metav1.TypeMeta{ 96 Kind: "CronJob", 97 APIVersion: "batch/v1", 98 }, 99 ObjectMeta: metav1.ObjectMeta{ 100 Name: "nginx2", 101 Namespace: nss[0].Name, 102 UID: "456", 103 }, 104 Spec: batchv1.CronJobSpec{ 105 Schedule: "*/1 * * * *", 106 JobTemplate: batchv1.JobTemplateSpec{ 107 Spec: batchv1.JobSpec{ 108 Template: corev1.PodTemplateSpec{ 109 ObjectMeta: metav1.ObjectMeta{ 110 Labels: map[string]string{ 111 "app": "nginx", 112 }, 113 }, 114 Spec: corev1.PodSpec{ 115 Containers: []corev1.Container{{Image: "nginx:1.22.0-alpine"}}, 116 }, 117 }, 118 }, 119 }, 120 }, 121 }, 122 } 123 124 p.EXPECT().CronJobs(nss[0]).Return(cronjobs, nil) 125 126 expectedAssetNames := []string{ 127 nss[0].Name + "/" + cronjobs[0].Name, 128 nss[0].Name + "/" + cronjobs[1].Name, 129 } 130 131 clusterIdentifier := "//platformid.api.mondoo.app/runtime/k8s/uid/e26043bb-8669-48a2-b684-b1e132198cdc" 132 133 expectedAssetPlatformIds := []string{ 134 clusterIdentifier + "/namespace/" + nss[0].Name + "/cronjobs/name/" + cronjobs[0].Name, 135 clusterIdentifier + "/namespace/" + nss[0].Name + "/cronjobs/name/" + cronjobs[1].Name, 136 } 137 138 pCfg := &providers.Config{} 139 ownershipDir := k8s.NewEmptyPlatformIdOwnershipDirectory(clusterIdentifier) 140 assets, err := ListCronJobs(p, pCfg, clusterIdentifier, NamespaceFilterOpts{}, make(map[string][]K8sResourceIdentifier), ownershipDir) 141 require.NoError(t, err) 142 require.Equal(t, []string{k8s.NewPlatformWorkloadId(clusterIdentifier, 143 strings.ToLower(parent.Kind), 144 parent.Namespace, 145 parent.Name, 146 "")}, 147 ownershipDir.OwnedBy(expectedAssetPlatformIds[0])) 148 149 var assetNames []string 150 for _, a := range assets { 151 assetNames = append(assetNames, a.Name) 152 } 153 154 var assetPlatformIds []string 155 for _, a := range assets { 156 assetPlatformIds = append(assetPlatformIds, a.PlatformIds[0]) 157 } 158 159 assert.ElementsMatch(t, expectedAssetNames, assetNames) 160 assert.ElementsMatch(t, expectedAssetPlatformIds, assetPlatformIds) 161 assert.Equal(t, "batch/v1", assets[0].Platform.Version) 162 assert.Equal(t, "k8s-cronjob", assets[0].Platform.Name) 163 assert.ElementsMatch(t, []string{"k8s", "k8s-workload"}, assets[0].Platform.Family) 164 assert.Equal(t, nss[0].Name, assets[0].Labels["k8s.mondoo.com/namespace"]) 165 } 166 167 func TestListCronJobs_Filter(t *testing.T) { 168 mockCtrl := gomock.NewController(t) 169 defer mockCtrl.Finish() 170 171 p := k8s.NewMockKubernetesProvider(mockCtrl) 172 173 // called for each CronJob 174 p.EXPECT().Runtime().Return("k8s-cluster") 175 176 // Seed CronJobs 177 cronjobs := []*batchv1.CronJob{ 178 { 179 TypeMeta: metav1.TypeMeta{ 180 Kind: "CronJob", 181 APIVersion: "batch/v1", 182 }, 183 ObjectMeta: metav1.ObjectMeta{ 184 Name: "nginx", 185 Namespace: "default", 186 UID: "123", 187 }, 188 Spec: batchv1.CronJobSpec{ 189 Schedule: "*/1 * * * *", 190 JobTemplate: batchv1.JobTemplateSpec{ 191 Spec: batchv1.JobSpec{ 192 Template: corev1.PodTemplateSpec{ 193 ObjectMeta: metav1.ObjectMeta{ 194 Labels: map[string]string{ 195 "app": "nginx", 196 }, 197 }, 198 Spec: corev1.PodSpec{ 199 Containers: []corev1.Container{{Image: "nginx:1.22.0-alpine"}}, 200 }, 201 }, 202 }, 203 }, 204 }, 205 }, 206 { 207 TypeMeta: metav1.TypeMeta{ 208 Kind: "CronJob", 209 APIVersion: "batch/v1", 210 }, 211 ObjectMeta: metav1.ObjectMeta{ 212 Name: "nginx2", 213 Namespace: "default", 214 UID: "456", 215 }, 216 Spec: batchv1.CronJobSpec{ 217 Schedule: "*/1 * * * *", 218 JobTemplate: batchv1.JobTemplateSpec{ 219 Spec: batchv1.JobSpec{ 220 Template: corev1.PodTemplateSpec{ 221 ObjectMeta: metav1.ObjectMeta{ 222 Labels: map[string]string{ 223 "app": "nginx", 224 }, 225 }, 226 Spec: corev1.PodSpec{ 227 Containers: []corev1.Container{{Image: "nginx:1.22.0-alpine"}}, 228 }, 229 }, 230 }, 231 }, 232 }, 233 }, 234 } 235 236 p.EXPECT().CronJob(cronjobs[0].Namespace, cronjobs[0].Name).Return(cronjobs[0], nil) 237 238 expectedAssetNames := []string{ 239 cronjobs[0].Namespace + "/" + cronjobs[0].Name, 240 } 241 242 clusterIdentifier := "//platformid.api.mondoo.app/runtime/k8s/uid/e26043bb-8669-48a2-b684-b1e132198cdc" 243 244 expectedAssetPlatformIds := []string{ 245 clusterIdentifier + "/namespace/" + cronjobs[0].Namespace + "/cronjobs/name/" + cronjobs[0].Name, 246 } 247 248 pCfg := &providers.Config{} 249 ownershipDir := k8s.NewEmptyPlatformIdOwnershipDirectory(clusterIdentifier) 250 resFilter := map[string][]K8sResourceIdentifier{ 251 "cronjob": { 252 {Type: "cronjob", Name: cronjobs[0].Name, Namespace: cronjobs[0].Namespace}, 253 }, 254 } 255 assets, err := ListCronJobs(p, pCfg, clusterIdentifier, NamespaceFilterOpts{}, resFilter, ownershipDir) 256 require.NoError(t, err) 257 258 var assetNames []string 259 for _, a := range assets { 260 assetNames = append(assetNames, a.Name) 261 } 262 263 var assetPlatformIds []string 264 for _, a := range assets { 265 assetPlatformIds = append(assetPlatformIds, a.PlatformIds[0]) 266 } 267 268 assert.ElementsMatch(t, expectedAssetNames, assetNames) 269 assert.ElementsMatch(t, expectedAssetPlatformIds, assetPlatformIds) 270 assert.Equal(t, "batch/v1", assets[0].Platform.Version) 271 assert.Equal(t, "k8s-cronjob", assets[0].Platform.Name) 272 assert.ElementsMatch(t, []string{"k8s", "k8s-workload"}, assets[0].Platform.Family) 273 assert.Equal(t, cronjobs[0].Namespace, assets[0].Labels["k8s.mondoo.com/namespace"]) 274 } 275 276 func TestListDaemonsets(t *testing.T) { 277 mockCtrl := gomock.NewController(t) 278 defer mockCtrl.Finish() 279 280 p := k8s.NewMockKubernetesProvider(mockCtrl) 281 282 // Seed namespaces 283 nss := []corev1.Namespace{ 284 {ObjectMeta: metav1.ObjectMeta{Name: "default"}}, 285 } 286 p.EXPECT().Namespaces().Return(nss, nil) 287 // called for each DaemonSet 288 p.EXPECT().Runtime().Return("k8s-cluster") 289 p.EXPECT().Runtime().Return("k8s-cluster") 290 291 // pretend daemon set owned by deployment 292 parent := appsv1.Deployment{ 293 TypeMeta: metav1.TypeMeta{ 294 Kind: "Deployment", 295 APIVersion: "apps/v1", 296 }, 297 ObjectMeta: metav1.ObjectMeta{ 298 Name: "nginx-deployment", 299 Namespace: nss[0].Name, 300 UID: "000", 301 }, 302 Spec: appsv1.DeploymentSpec{ 303 Template: corev1.PodTemplateSpec{ 304 Spec: corev1.PodSpec{ 305 Containers: []corev1.Container{{Image: "nginx:1.22.0-alpine"}}, 306 }, 307 }, 308 }, 309 } 310 311 // Seed DaemonSets 312 daemonsets := []*appsv1.DaemonSet{ 313 { 314 TypeMeta: metav1.TypeMeta{ 315 Kind: "DaemonSet", 316 APIVersion: "apps/v1", 317 }, 318 ObjectMeta: metav1.ObjectMeta{ 319 Name: "nginx", 320 Namespace: nss[0].Name, 321 UID: "123", 322 OwnerReferences: []metav1.OwnerReference{ 323 { 324 APIVersion: parent.APIVersion, 325 Kind: parent.Kind, 326 Name: parent.Name, 327 UID: parent.UID, 328 }, 329 }, 330 }, 331 Spec: appsv1.DaemonSetSpec{ 332 Template: corev1.PodTemplateSpec{ 333 Spec: corev1.PodSpec{ 334 Containers: []corev1.Container{{Image: "nginx:1.22.0-alpine"}}, 335 }, 336 }, 337 }, 338 }, 339 { 340 TypeMeta: metav1.TypeMeta{ 341 Kind: "DaemonSet", 342 APIVersion: "apps/v1", 343 }, 344 ObjectMeta: metav1.ObjectMeta{ 345 Name: "nginx2", 346 Namespace: nss[0].Name, 347 UID: "456", 348 }, 349 Spec: appsv1.DaemonSetSpec{ 350 Template: corev1.PodTemplateSpec{ 351 Spec: corev1.PodSpec{ 352 Containers: []corev1.Container{{Image: "nginx:1.22.0-alpine"}}, 353 }, 354 }, 355 }, 356 }, 357 } 358 359 p.EXPECT().DaemonSets(nss[0]).Return(daemonsets, nil) 360 361 expectedAssetNames := []string{ 362 nss[0].Name + "/" + daemonsets[0].Name, 363 nss[0].Name + "/" + daemonsets[1].Name, 364 } 365 366 clusterIdentifier := "//platformid.api.mondoo.app/runtime/k8s/uid/e26043bb-8669-48a2-b684-b1e132198cdc" 367 368 expectedAssetPlatformIds := []string{ 369 clusterIdentifier + "/namespace/" + nss[0].Name + "/daemonsets/name/" + daemonsets[0].Name, 370 clusterIdentifier + "/namespace/" + nss[0].Name + "/daemonsets/name/" + daemonsets[1].Name, 371 } 372 373 pCfg := &providers.Config{} 374 ownershipDir := k8s.NewEmptyPlatformIdOwnershipDirectory(clusterIdentifier) 375 assets, err := ListDaemonSets(p, pCfg, clusterIdentifier, NamespaceFilterOpts{}, make(map[string][]K8sResourceIdentifier), ownershipDir) 376 require.NoError(t, err) 377 require.Equal(t, []string{k8s.NewPlatformWorkloadId(clusterIdentifier, 378 strings.ToLower(parent.Kind), 379 parent.Namespace, 380 parent.Name, 381 "")}, 382 ownershipDir.OwnedBy(expectedAssetPlatformIds[0])) 383 384 var assetNames []string 385 for _, a := range assets { 386 assetNames = append(assetNames, a.Name) 387 } 388 389 var assetPlatformIds []string 390 for _, a := range assets { 391 assetPlatformIds = append(assetPlatformIds, a.PlatformIds[0]) 392 } 393 394 assert.ElementsMatch(t, expectedAssetNames, assetNames) 395 assert.ElementsMatch(t, expectedAssetPlatformIds, assetPlatformIds) 396 assert.Equal(t, "apps/v1", assets[0].Platform.Version) 397 assert.Equal(t, "k8s-daemonset", assets[0].Platform.Name) 398 assert.ElementsMatch(t, []string{"k8s", "k8s-workload"}, assets[0].Platform.Family) 399 assert.Equal(t, nss[0].Name, assets[0].Labels["k8s.mondoo.com/namespace"]) 400 } 401 402 func TestListDaemonsets_Filter(t *testing.T) { 403 mockCtrl := gomock.NewController(t) 404 defer mockCtrl.Finish() 405 406 p := k8s.NewMockKubernetesProvider(mockCtrl) 407 408 // called for each DaemonSet 409 p.EXPECT().Runtime().Return("k8s-cluster") 410 411 // Seed DaemonSets 412 daemonsets := []*appsv1.DaemonSet{ 413 { 414 TypeMeta: metav1.TypeMeta{ 415 Kind: "DaemonSet", 416 APIVersion: "apps/v1", 417 }, 418 ObjectMeta: metav1.ObjectMeta{ 419 Name: "nginx", 420 Namespace: "default", 421 UID: "123", 422 }, 423 Spec: appsv1.DaemonSetSpec{ 424 Template: corev1.PodTemplateSpec{ 425 Spec: corev1.PodSpec{ 426 Containers: []corev1.Container{{Image: "nginx:1.22.0-alpine"}}, 427 }, 428 }, 429 }, 430 }, 431 { 432 TypeMeta: metav1.TypeMeta{ 433 Kind: "DaemonSet", 434 APIVersion: "apps/v1", 435 }, 436 ObjectMeta: metav1.ObjectMeta{ 437 Name: "nginx2", 438 Namespace: "default", 439 UID: "456", 440 }, 441 Spec: appsv1.DaemonSetSpec{ 442 Template: corev1.PodTemplateSpec{ 443 Spec: corev1.PodSpec{ 444 Containers: []corev1.Container{{Image: "nginx:1.22.0-alpine"}}, 445 }, 446 }, 447 }, 448 }, 449 } 450 451 p.EXPECT().DaemonSet(daemonsets[0].Namespace, daemonsets[0].Name).Return(daemonsets[0], nil) 452 453 expectedAssetNames := []string{ 454 daemonsets[0].Namespace + "/" + daemonsets[0].Name, 455 } 456 457 clusterIdentifier := "//platformid.api.mondoo.app/runtime/k8s/uid/e26043bb-8669-48a2-b684-b1e132198cdc" 458 459 expectedAssetPlatformIds := []string{ 460 clusterIdentifier + "/namespace/" + daemonsets[0].Namespace + "/daemonsets/name/" + daemonsets[0].Name, 461 } 462 463 pCfg := &providers.Config{} 464 ownershipDir := k8s.NewEmptyPlatformIdOwnershipDirectory(clusterIdentifier) 465 resFilter := map[string][]K8sResourceIdentifier{ 466 "daemonset": { 467 {Type: "daemonset", Name: daemonsets[0].Name, Namespace: daemonsets[0].Namespace}, 468 }, 469 } 470 assets, err := ListDaemonSets(p, pCfg, clusterIdentifier, NamespaceFilterOpts{}, resFilter, ownershipDir) 471 require.NoError(t, err) 472 473 var assetNames []string 474 for _, a := range assets { 475 assetNames = append(assetNames, a.Name) 476 } 477 478 var assetPlatformIds []string 479 for _, a := range assets { 480 assetPlatformIds = append(assetPlatformIds, a.PlatformIds[0]) 481 } 482 483 assert.ElementsMatch(t, expectedAssetNames, assetNames) 484 assert.ElementsMatch(t, expectedAssetPlatformIds, assetPlatformIds) 485 assert.Equal(t, "apps/v1", assets[0].Platform.Version) 486 assert.Equal(t, "k8s-daemonset", assets[0].Platform.Name) 487 assert.ElementsMatch(t, []string{"k8s", "k8s-workload"}, assets[0].Platform.Family) 488 assert.Equal(t, daemonsets[0].Namespace, assets[0].Labels["k8s.mondoo.com/namespace"]) 489 } 490 491 func TestListDeployments(t *testing.T) { 492 mockCtrl := gomock.NewController(t) 493 defer mockCtrl.Finish() 494 495 p := k8s.NewMockKubernetesProvider(mockCtrl) 496 497 // Seed namespaces 498 nss := []corev1.Namespace{ 499 {ObjectMeta: metav1.ObjectMeta{Name: "default"}}, 500 } 501 p.EXPECT().Namespaces().Return(nss, nil) 502 // called for each Deployment 503 p.EXPECT().Runtime().Return("k8s-cluster") 504 p.EXPECT().Runtime().Return("k8s-cluster") 505 506 // pretend the deployment is owned by something 507 parent := appsv1.Deployment{ 508 TypeMeta: metav1.TypeMeta{ 509 Kind: "Deployment", 510 APIVersion: "apps/v1", 511 }, 512 ObjectMeta: metav1.ObjectMeta{ 513 Name: "nginx-deployment-deployment", 514 Namespace: nss[0].Name, 515 UID: "000", 516 }, 517 } 518 519 // Seed Deployments 520 deployments := []*appsv1.Deployment{ 521 { 522 TypeMeta: metav1.TypeMeta{ 523 Kind: "Deployment", 524 APIVersion: "apps/v1", 525 }, 526 ObjectMeta: metav1.ObjectMeta{ 527 Name: "nginx", 528 Namespace: nss[0].Name, 529 UID: "123", 530 OwnerReferences: []metav1.OwnerReference{ 531 { 532 APIVersion: parent.APIVersion, 533 Kind: parent.Kind, 534 Name: parent.Name, 535 UID: parent.UID, 536 }, 537 }, 538 }, 539 Spec: appsv1.DeploymentSpec{ 540 Template: corev1.PodTemplateSpec{ 541 Spec: corev1.PodSpec{ 542 Containers: []corev1.Container{{Image: "nginx:1.22.0-alpine"}}, 543 }, 544 }, 545 }, 546 }, 547 { 548 TypeMeta: metav1.TypeMeta{ 549 Kind: "Deployment", 550 APIVersion: "apps/v1", 551 }, 552 ObjectMeta: metav1.ObjectMeta{ 553 Name: "nginx2", 554 Namespace: nss[0].Name, 555 UID: "456", 556 }, 557 Spec: appsv1.DeploymentSpec{ 558 Template: corev1.PodTemplateSpec{ 559 Spec: corev1.PodSpec{ 560 Containers: []corev1.Container{{Image: "nginx:1.22.0-alpine"}}, 561 }, 562 }, 563 }, 564 }, 565 } 566 567 p.EXPECT().Deployments(nss[0]).Return(deployments, nil) 568 569 expectedAssetNames := []string{ 570 nss[0].Name + "/" + deployments[0].Name, 571 nss[0].Name + "/" + deployments[1].Name, 572 } 573 574 clusterIdentifier := "//platformid.api.mondoo.app/runtime/k8s/uid/e26043bb-8669-48a2-b684-b1e132198cdc" 575 576 expectedAssetPlatformIds := []string{ 577 clusterIdentifier + "/namespace/" + nss[0].Name + "/deployments/name/" + deployments[0].Name, 578 clusterIdentifier + "/namespace/" + nss[0].Name + "/deployments/name/" + deployments[1].Name, 579 } 580 581 pCfg := &providers.Config{} 582 ownershipDir := k8s.NewEmptyPlatformIdOwnershipDirectory(clusterIdentifier) 583 assets, err := ListDeployments(p, pCfg, clusterIdentifier, NamespaceFilterOpts{}, make(map[string][]K8sResourceIdentifier), ownershipDir) 584 require.NoError(t, err) 585 require.Equal(t, []string{k8s.NewPlatformWorkloadId(clusterIdentifier, 586 strings.ToLower(parent.Kind), 587 parent.Namespace, 588 parent.Name, 589 "")}, 590 ownershipDir.OwnedBy(expectedAssetPlatformIds[0])) 591 592 var assetNames []string 593 for _, a := range assets { 594 assetNames = append(assetNames, a.Name) 595 } 596 597 var assetPlatformIds []string 598 for _, a := range assets { 599 assetPlatformIds = append(assetPlatformIds, a.PlatformIds[0]) 600 } 601 602 assert.ElementsMatch(t, expectedAssetNames, assetNames) 603 assert.ElementsMatch(t, expectedAssetPlatformIds, assetPlatformIds) 604 assert.Equal(t, "apps/v1", assets[0].Platform.Version) 605 assert.Equal(t, "k8s-deployment", assets[0].Platform.Name) 606 assert.ElementsMatch(t, []string{"k8s", "k8s-workload"}, assets[0].Platform.Family) 607 assert.Equal(t, nss[0].Name, assets[0].Labels["k8s.mondoo.com/namespace"]) 608 } 609 610 func TestListDeployments_Filter(t *testing.T) { 611 mockCtrl := gomock.NewController(t) 612 defer mockCtrl.Finish() 613 614 p := k8s.NewMockKubernetesProvider(mockCtrl) 615 616 // called for each Deployment 617 p.EXPECT().Runtime().Return("k8s-cluster") 618 619 // Seed Deployments 620 deployments := []*appsv1.Deployment{ 621 { 622 TypeMeta: metav1.TypeMeta{ 623 Kind: "Deployment", 624 APIVersion: "apps/v1", 625 }, 626 ObjectMeta: metav1.ObjectMeta{ 627 Name: "nginx", 628 Namespace: "default", 629 UID: "123", 630 }, 631 Spec: appsv1.DeploymentSpec{ 632 Template: corev1.PodTemplateSpec{ 633 Spec: corev1.PodSpec{ 634 Containers: []corev1.Container{{Image: "nginx:1.22.0-alpine"}}, 635 }, 636 }, 637 }, 638 }, 639 { 640 TypeMeta: metav1.TypeMeta{ 641 Kind: "Deployment", 642 APIVersion: "apps/v1", 643 }, 644 ObjectMeta: metav1.ObjectMeta{ 645 Name: "nginx2", 646 Namespace: "default", 647 UID: "456", 648 }, 649 Spec: appsv1.DeploymentSpec{ 650 Template: corev1.PodTemplateSpec{ 651 Spec: corev1.PodSpec{ 652 Containers: []corev1.Container{{Image: "nginx:1.22.0-alpine"}}, 653 }, 654 }, 655 }, 656 }, 657 } 658 659 p.EXPECT().Deployment(deployments[0].Namespace, deployments[0].Name).Return(deployments[0], nil) 660 661 expectedAssetNames := []string{ 662 deployments[0].Namespace + "/" + deployments[0].Name, 663 } 664 665 clusterIdentifier := "//platformid.api.mondoo.app/runtime/k8s/uid/e26043bb-8669-48a2-b684-b1e132198cdc" 666 667 expectedAssetPlatformIds := []string{ 668 clusterIdentifier + "/namespace/" + deployments[0].Namespace + "/deployments/name/" + deployments[0].Name, 669 } 670 671 pCfg := &providers.Config{} 672 ownershipDir := k8s.NewEmptyPlatformIdOwnershipDirectory(clusterIdentifier) 673 resFilter := map[string][]K8sResourceIdentifier{ 674 "deployment": { 675 {Type: "deployment", Name: deployments[0].Name, Namespace: deployments[0].Namespace}, 676 }, 677 } 678 assets, err := ListDeployments(p, pCfg, clusterIdentifier, NamespaceFilterOpts{}, resFilter, ownershipDir) 679 require.NoError(t, err) 680 681 var assetNames []string 682 for _, a := range assets { 683 assetNames = append(assetNames, a.Name) 684 } 685 686 var assetPlatformIds []string 687 for _, a := range assets { 688 assetPlatformIds = append(assetPlatformIds, a.PlatformIds[0]) 689 } 690 691 assert.ElementsMatch(t, expectedAssetNames, assetNames) 692 assert.ElementsMatch(t, expectedAssetPlatformIds, assetPlatformIds) 693 assert.Equal(t, "apps/v1", assets[0].Platform.Version) 694 assert.Equal(t, "k8s-deployment", assets[0].Platform.Name) 695 assert.ElementsMatch(t, []string{"k8s", "k8s-workload"}, assets[0].Platform.Family) 696 assert.Equal(t, deployments[0].Namespace, assets[0].Labels["k8s.mondoo.com/namespace"]) 697 } 698 699 func TestListJobs(t *testing.T) { 700 mockCtrl := gomock.NewController(t) 701 defer mockCtrl.Finish() 702 703 p := k8s.NewMockKubernetesProvider(mockCtrl) 704 705 // Seed namespaces 706 nss := []corev1.Namespace{ 707 {ObjectMeta: metav1.ObjectMeta{Name: "default"}}, 708 } 709 p.EXPECT().Namespaces().Return(nss, nil) 710 // called for each Job 711 p.EXPECT().Runtime().Return("k8s-cluster") 712 p.EXPECT().Runtime().Return("k8s-cluster") 713 714 // pretend the job has a parent 715 parent := appsv1.ReplicaSet{ 716 TypeMeta: metav1.TypeMeta{ 717 Kind: "ReplicaSet", 718 APIVersion: "apps/v1", 719 }, 720 ObjectMeta: metav1.ObjectMeta{ 721 Name: "nginx-replicaset", 722 Namespace: nss[0].Name, 723 UID: "000", 724 }, 725 } 726 727 // Seed Jobs 728 jobs := []*batchv1.Job{ 729 { 730 TypeMeta: metav1.TypeMeta{ 731 Kind: "Job", 732 APIVersion: "batch/v1", 733 }, 734 ObjectMeta: metav1.ObjectMeta{ 735 Name: "nginx", 736 Namespace: nss[0].Name, 737 UID: "123", 738 OwnerReferences: []metav1.OwnerReference{ 739 { 740 APIVersion: parent.APIVersion, 741 Kind: parent.Kind, 742 Name: parent.Name, 743 UID: parent.UID, 744 }, 745 }, 746 }, 747 Spec: batchv1.JobSpec{ 748 Template: corev1.PodTemplateSpec{ 749 ObjectMeta: metav1.ObjectMeta{ 750 Labels: map[string]string{ 751 "app": "nginx", 752 }, 753 }, 754 Spec: corev1.PodSpec{ 755 Containers: []corev1.Container{{Image: "nginx:1.22.0-alpine"}}, 756 }, 757 }, 758 }, 759 }, 760 { 761 TypeMeta: metav1.TypeMeta{ 762 Kind: "Job", 763 APIVersion: "batch/v1", 764 }, 765 ObjectMeta: metav1.ObjectMeta{ 766 Name: "nginx2", 767 Namespace: nss[0].Name, 768 UID: "456", 769 }, 770 Spec: batchv1.JobSpec{ 771 Template: corev1.PodTemplateSpec{ 772 ObjectMeta: metav1.ObjectMeta{ 773 Labels: map[string]string{ 774 "app": "nginx", 775 }, 776 }, 777 Spec: corev1.PodSpec{ 778 Containers: []corev1.Container{{Image: "nginx:1.22.0-alpine"}}, 779 }, 780 }, 781 }, 782 }, 783 } 784 785 p.EXPECT().Jobs(nss[0]).Return(jobs, nil) 786 787 expectedAssetNames := []string{ 788 nss[0].Name + "/" + jobs[0].Name, 789 nss[0].Name + "/" + jobs[1].Name, 790 } 791 792 clusterIdentifier := "//platformid.api.mondoo.app/runtime/k8s/uid/e26043bb-8669-48a2-b684-b1e132198cdc" 793 794 expectedAssetPlatformIds := []string{ 795 clusterIdentifier + "/namespace/" + nss[0].Name + "/jobs/name/" + jobs[0].Name, 796 clusterIdentifier + "/namespace/" + nss[0].Name + "/jobs/name/" + jobs[1].Name, 797 } 798 799 pCfg := &providers.Config{} 800 ownershipDir := k8s.NewEmptyPlatformIdOwnershipDirectory(clusterIdentifier) 801 ownershipDir.Add(&parent) 802 assets, err := ListJobs(p, pCfg, clusterIdentifier, NamespaceFilterOpts{}, make(map[string][]K8sResourceIdentifier), ownershipDir) 803 require.NoError(t, err) 804 require.Equal(t, []string{k8s.NewPlatformWorkloadId(clusterIdentifier, 805 strings.ToLower(parent.Kind), 806 parent.Namespace, 807 parent.Name, 808 "")}, 809 ownershipDir.OwnedBy(expectedAssetPlatformIds[0])) 810 811 var assetNames []string 812 for _, a := range assets { 813 assetNames = append(assetNames, a.Name) 814 } 815 816 var assetPlatformIds []string 817 for _, a := range assets { 818 assetPlatformIds = append(assetPlatformIds, a.PlatformIds[0]) 819 } 820 821 assert.ElementsMatch(t, expectedAssetNames, assetNames) 822 assert.ElementsMatch(t, expectedAssetPlatformIds, assetPlatformIds) 823 assert.Equal(t, "batch/v1", assets[0].Platform.Version) 824 assert.Equal(t, "k8s-job", assets[0].Platform.Name) 825 assert.ElementsMatch(t, []string{"k8s", "k8s-workload"}, assets[0].Platform.Family) 826 assert.Equal(t, nss[0].Name, assets[0].Labels["k8s.mondoo.com/namespace"]) 827 } 828 829 func TestListJobs_Filter(t *testing.T) { 830 mockCtrl := gomock.NewController(t) 831 defer mockCtrl.Finish() 832 833 p := k8s.NewMockKubernetesProvider(mockCtrl) 834 835 // called for each Job 836 p.EXPECT().Runtime().Return("k8s-cluster") 837 838 // Seed Jobs 839 jobs := []*batchv1.Job{ 840 { 841 TypeMeta: metav1.TypeMeta{ 842 Kind: "Job", 843 APIVersion: "batch/v1", 844 }, 845 ObjectMeta: metav1.ObjectMeta{ 846 Name: "nginx", 847 Namespace: "default", 848 UID: "123", 849 }, 850 Spec: batchv1.JobSpec{ 851 Template: corev1.PodTemplateSpec{ 852 ObjectMeta: metav1.ObjectMeta{ 853 Labels: map[string]string{ 854 "app": "nginx", 855 }, 856 }, 857 Spec: corev1.PodSpec{ 858 Containers: []corev1.Container{{Image: "nginx:1.22.0-alpine"}}, 859 }, 860 }, 861 }, 862 }, 863 { 864 TypeMeta: metav1.TypeMeta{ 865 Kind: "Job", 866 APIVersion: "batch/v1", 867 }, 868 ObjectMeta: metav1.ObjectMeta{ 869 Name: "nginx2", 870 Namespace: "default", 871 UID: "456", 872 }, 873 Spec: batchv1.JobSpec{ 874 Template: corev1.PodTemplateSpec{ 875 ObjectMeta: metav1.ObjectMeta{ 876 Labels: map[string]string{ 877 "app": "nginx", 878 }, 879 }, 880 Spec: corev1.PodSpec{ 881 Containers: []corev1.Container{{Image: "nginx:1.22.0-alpine"}}, 882 }, 883 }, 884 }, 885 }, 886 } 887 888 p.EXPECT().Job(jobs[0].Namespace, jobs[0].Name).Return(jobs[0], nil) 889 890 expectedAssetNames := []string{ 891 jobs[0].Namespace + "/" + jobs[0].Name, 892 } 893 894 clusterIdentifier := "//platformid.api.mondoo.app/runtime/k8s/uid/e26043bb-8669-48a2-b684-b1e132198cdc" 895 896 expectedAssetPlatformIds := []string{ 897 clusterIdentifier + "/namespace/" + jobs[0].Namespace + "/jobs/name/" + jobs[0].Name, 898 } 899 900 pCfg := &providers.Config{} 901 ownershipDir := k8s.NewEmptyPlatformIdOwnershipDirectory(clusterIdentifier) 902 resFilter := map[string][]K8sResourceIdentifier{ 903 "job": { 904 {Type: "job", Name: jobs[0].Name, Namespace: jobs[0].Namespace}, 905 }, 906 } 907 assets, err := ListJobs(p, pCfg, clusterIdentifier, NamespaceFilterOpts{}, resFilter, ownershipDir) 908 require.NoError(t, err) 909 910 var assetNames []string 911 for _, a := range assets { 912 assetNames = append(assetNames, a.Name) 913 } 914 915 var assetPlatformIds []string 916 for _, a := range assets { 917 assetPlatformIds = append(assetPlatformIds, a.PlatformIds[0]) 918 } 919 920 assert.ElementsMatch(t, expectedAssetNames, assetNames) 921 assert.ElementsMatch(t, expectedAssetPlatformIds, assetPlatformIds) 922 assert.Equal(t, "batch/v1", assets[0].Platform.Version) 923 assert.Equal(t, "k8s-job", assets[0].Platform.Name) 924 assert.ElementsMatch(t, []string{"k8s", "k8s-workload"}, assets[0].Platform.Family) 925 assert.Equal(t, jobs[0].Namespace, assets[0].Labels["k8s.mondoo.com/namespace"]) 926 } 927 928 func TestListPods(t *testing.T) { 929 mockCtrl := gomock.NewController(t) 930 defer mockCtrl.Finish() 931 932 p := k8s.NewMockKubernetesProvider(mockCtrl) 933 934 // Seed namespaces 935 nss := []corev1.Namespace{ 936 {ObjectMeta: metav1.ObjectMeta{Name: "default"}}, 937 } 938 p.EXPECT().Namespaces().Return(nss, nil) 939 // called for each Pod 940 p.EXPECT().Runtime().Return("k8s-cluster") 941 p.EXPECT().Runtime().Return("k8s-cluster") 942 943 parent := appsv1.ReplicaSet{ 944 TypeMeta: metav1.TypeMeta{ 945 Kind: "ReplicaSet", 946 APIVersion: "apps/v1", 947 }, 948 ObjectMeta: metav1.ObjectMeta{ 949 Name: "nginx-replicaset", 950 Namespace: nss[0].Name, 951 UID: "000", 952 }, 953 } 954 955 // Seed Pods 956 pods := []*corev1.Pod{ 957 { 958 TypeMeta: metav1.TypeMeta{ 959 Kind: "Pod", 960 APIVersion: "v1", 961 }, 962 ObjectMeta: metav1.ObjectMeta{ 963 Name: "nginx", 964 Namespace: nss[0].Name, 965 UID: "123", 966 OwnerReferences: []metav1.OwnerReference{ 967 { 968 APIVersion: parent.APIVersion, 969 Kind: parent.Kind, 970 Name: parent.Name, 971 UID: parent.UID, 972 }, 973 }, 974 }, 975 Spec: corev1.PodSpec{ 976 Containers: []corev1.Container{{Image: "nginx:1.22.0-alpine"}}, 977 }, 978 }, 979 { 980 TypeMeta: metav1.TypeMeta{ 981 Kind: "Pod", 982 APIVersion: "v1", 983 }, 984 ObjectMeta: metav1.ObjectMeta{ 985 Name: "nginx2", 986 Namespace: nss[0].Name, 987 UID: "456", 988 }, 989 Spec: corev1.PodSpec{ 990 Containers: []corev1.Container{{Image: "nginx:1.22.0-alpine"}}, 991 }, 992 }, 993 } 994 995 p.EXPECT().Pods(nss[0]).Return(pods, nil) 996 997 expectedAssetNames := []string{ 998 nss[0].Name + "/" + pods[0].Name, 999 nss[0].Name + "/" + pods[1].Name, 1000 } 1001 1002 clusterIdentifier := "//platformid.api.mondoo.app/runtime/k8s/uid/e26043bb-8669-48a2-b684-b1e132198cdc" 1003 1004 expectedAssetPlatformIds := []string{ 1005 clusterIdentifier + "/namespace/" + nss[0].Name + "/pods/name/" + pods[0].Name, 1006 clusterIdentifier + "/namespace/" + nss[0].Name + "/pods/name/" + pods[1].Name, 1007 } 1008 1009 pCfg := &providers.Config{} 1010 ownershipDir := k8s.NewEmptyPlatformIdOwnershipDirectory(clusterIdentifier) 1011 assets, err := ListPods(p, pCfg, clusterIdentifier, NamespaceFilterOpts{}, make(map[string][]K8sResourceIdentifier), ownershipDir) 1012 require.NoError(t, err) 1013 require.Equal(t, []string{k8s.NewPlatformWorkloadId(clusterIdentifier, 1014 strings.ToLower(parent.Kind), 1015 parent.Namespace, 1016 parent.Name, 1017 "")}, 1018 ownershipDir.OwnedBy(expectedAssetPlatformIds[0])) 1019 var assetNames []string 1020 for _, a := range assets { 1021 assetNames = append(assetNames, a.Name) 1022 } 1023 1024 var assetPlatformIds []string 1025 for _, a := range assets { 1026 assetPlatformIds = append(assetPlatformIds, a.PlatformIds[0]) 1027 } 1028 1029 assert.ElementsMatch(t, expectedAssetNames, assetNames) 1030 assert.ElementsMatch(t, expectedAssetPlatformIds, assetPlatformIds) 1031 assert.Equal(t, "v1", assets[0].Platform.Version) 1032 assert.Equal(t, "k8s-pod", assets[0].Platform.Name) 1033 assert.ElementsMatch(t, []string{"k8s", "k8s-workload"}, assets[0].Platform.Family) 1034 assert.Equal(t, nss[0].Name, assets[0].Labels["k8s.mondoo.com/namespace"]) 1035 } 1036 1037 func TestListPods_Filter(t *testing.T) { 1038 mockCtrl := gomock.NewController(t) 1039 defer mockCtrl.Finish() 1040 1041 p := k8s.NewMockKubernetesProvider(mockCtrl) 1042 1043 // called for each Pod 1044 p.EXPECT().Runtime().Return("k8s-cluster") 1045 1046 // Seed Pods 1047 pods := []*corev1.Pod{ 1048 { 1049 TypeMeta: metav1.TypeMeta{ 1050 Kind: "Pod", 1051 APIVersion: "v1", 1052 }, 1053 ObjectMeta: metav1.ObjectMeta{ 1054 Name: "nginx", 1055 Namespace: "default", 1056 UID: "123", 1057 }, 1058 Spec: corev1.PodSpec{ 1059 Containers: []corev1.Container{{Image: "nginx:1.22.0-alpine"}}, 1060 }, 1061 }, 1062 { 1063 TypeMeta: metav1.TypeMeta{ 1064 Kind: "Pod", 1065 APIVersion: "v1", 1066 }, 1067 ObjectMeta: metav1.ObjectMeta{ 1068 Name: "nginx2", 1069 Namespace: "default", 1070 UID: "456", 1071 }, 1072 Spec: corev1.PodSpec{ 1073 Containers: []corev1.Container{{Image: "nginx:1.22.0-alpine"}}, 1074 }, 1075 }, 1076 } 1077 1078 p.EXPECT().Pod(pods[0].Namespace, pods[0].Name).Return(pods[0], nil) 1079 1080 expectedAssetNames := []string{ 1081 pods[0].Namespace + "/" + pods[0].Name, 1082 } 1083 1084 clusterIdentifier := "//platformid.api.mondoo.app/runtime/k8s/uid/e26043bb-8669-48a2-b684-b1e132198cdc" 1085 1086 expectedAssetPlatformIds := []string{ 1087 clusterIdentifier + "/namespace/" + pods[0].Namespace + "/pods/name/" + pods[0].Name, 1088 } 1089 1090 pCfg := &providers.Config{} 1091 ownershipDir := k8s.NewEmptyPlatformIdOwnershipDirectory(clusterIdentifier) 1092 resFilter := map[string][]K8sResourceIdentifier{ 1093 "pod": { 1094 {Type: "pod", Name: pods[0].Name, Namespace: pods[0].Namespace}, 1095 }, 1096 } 1097 assets, err := ListPods(p, pCfg, clusterIdentifier, NamespaceFilterOpts{}, resFilter, ownershipDir) 1098 require.NoError(t, err) 1099 var assetNames []string 1100 for _, a := range assets { 1101 assetNames = append(assetNames, a.Name) 1102 } 1103 1104 var assetPlatformIds []string 1105 for _, a := range assets { 1106 assetPlatformIds = append(assetPlatformIds, a.PlatformIds[0]) 1107 } 1108 1109 assert.ElementsMatch(t, expectedAssetNames, assetNames) 1110 assert.ElementsMatch(t, expectedAssetPlatformIds, assetPlatformIds) 1111 assert.Equal(t, "v1", assets[0].Platform.Version) 1112 assert.Equal(t, "k8s-pod", assets[0].Platform.Name) 1113 assert.ElementsMatch(t, []string{"k8s", "k8s-workload"}, assets[0].Platform.Family) 1114 assert.Equal(t, pods[0].Namespace, assets[0].Labels["k8s.mondoo.com/namespace"]) 1115 } 1116 1117 func TestListReplicaSets(t *testing.T) { 1118 mockCtrl := gomock.NewController(t) 1119 defer mockCtrl.Finish() 1120 1121 p := k8s.NewMockKubernetesProvider(mockCtrl) 1122 1123 // Seed namespaces 1124 nss := []corev1.Namespace{ 1125 {ObjectMeta: metav1.ObjectMeta{Name: "default"}}, 1126 } 1127 p.EXPECT().Namespaces().Return(nss, nil) 1128 // called for each ReplicaSet 1129 p.EXPECT().Runtime().Return("k8s-cluster") 1130 p.EXPECT().Runtime().Return("k8s-cluster") 1131 parent := appsv1.Deployment{ 1132 TypeMeta: metav1.TypeMeta{ 1133 Kind: "Deployment", 1134 APIVersion: "apps/v1", 1135 }, 1136 ObjectMeta: metav1.ObjectMeta{ 1137 Name: "nginx-deployment", 1138 Namespace: nss[0].Name, 1139 UID: "000", 1140 }, 1141 Spec: appsv1.DeploymentSpec{ 1142 Template: corev1.PodTemplateSpec{ 1143 Spec: corev1.PodSpec{ 1144 Containers: []corev1.Container{{Image: "nginx:1.22.0-alpine"}}, 1145 }, 1146 }, 1147 }, 1148 } 1149 // Seed ReplicaSets 1150 replicaSets := []*appsv1.ReplicaSet{ 1151 { 1152 TypeMeta: metav1.TypeMeta{ 1153 Kind: "ReplicaSet", 1154 APIVersion: "apps/v1", 1155 }, 1156 ObjectMeta: metav1.ObjectMeta{ 1157 Name: "nginx", 1158 Namespace: nss[0].Name, 1159 UID: "123", 1160 OwnerReferences: []metav1.OwnerReference{ 1161 { 1162 APIVersion: parent.APIVersion, 1163 Kind: parent.Kind, 1164 Name: parent.Name, 1165 UID: parent.UID, 1166 }, 1167 }, 1168 }, 1169 Spec: appsv1.ReplicaSetSpec{ 1170 Template: corev1.PodTemplateSpec{ 1171 Spec: corev1.PodSpec{ 1172 Containers: []corev1.Container{{Image: "nginx:1.22.0-alpine"}}, 1173 }, 1174 }, 1175 }, 1176 }, 1177 { 1178 TypeMeta: metav1.TypeMeta{ 1179 Kind: "ReplicaSet", 1180 APIVersion: "apps/v1", 1181 }, 1182 ObjectMeta: metav1.ObjectMeta{ 1183 Name: "nginx2", 1184 Namespace: nss[0].Name, 1185 UID: "456", 1186 }, 1187 Spec: appsv1.ReplicaSetSpec{ 1188 Template: corev1.PodTemplateSpec{ 1189 Spec: corev1.PodSpec{ 1190 Containers: []corev1.Container{{Image: "nginx:1.22.0-alpine"}}, 1191 }, 1192 }, 1193 }, 1194 }, 1195 } 1196 1197 p.EXPECT().ReplicaSets(nss[0]).Return(replicaSets, nil) 1198 1199 expectedAssetNames := []string{ 1200 nss[0].Name + "/" + replicaSets[0].Name, 1201 nss[0].Name + "/" + replicaSets[1].Name, 1202 } 1203 1204 clusterIdentifier := "//platformid.api.mondoo.app/runtime/k8s/uid/e26043bb-8669-48a2-b684-b1e132198cdc" 1205 1206 expectedAssetPlatformIds := []string{ 1207 clusterIdentifier + "/namespace/" + nss[0].Name + "/replicasets/name/" + replicaSets[0].Name, 1208 clusterIdentifier + "/namespace/" + nss[0].Name + "/replicasets/name/" + replicaSets[1].Name, 1209 } 1210 1211 pCfg := &providers.Config{} 1212 ownershipDir := k8s.NewEmptyPlatformIdOwnershipDirectory(clusterIdentifier) 1213 assets, err := ListReplicaSets(p, pCfg, clusterIdentifier, NamespaceFilterOpts{}, make(map[string][]K8sResourceIdentifier), ownershipDir) 1214 require.NoError(t, err) 1215 require.Equal(t, []string{k8s.NewPlatformWorkloadId(clusterIdentifier, 1216 strings.ToLower(parent.Kind), 1217 parent.Namespace, 1218 parent.Name, 1219 "")}, 1220 ownershipDir.OwnedBy(expectedAssetPlatformIds[0])) 1221 1222 var assetNames []string 1223 for _, a := range assets { 1224 assetNames = append(assetNames, a.Name) 1225 } 1226 1227 var assetPlatformIds []string 1228 for _, a := range assets { 1229 assetPlatformIds = append(assetPlatformIds, a.PlatformIds[0]) 1230 } 1231 1232 assert.ElementsMatch(t, expectedAssetNames, assetNames) 1233 assert.ElementsMatch(t, expectedAssetPlatformIds, assetPlatformIds) 1234 assert.Equal(t, "apps/v1", assets[0].Platform.Version) 1235 assert.Equal(t, "k8s-replicaset", assets[0].Platform.Name) 1236 assert.ElementsMatch(t, []string{"k8s", "k8s-workload"}, assets[0].Platform.Family) 1237 assert.Equal(t, nss[0].Name, assets[0].Labels["k8s.mondoo.com/namespace"]) 1238 } 1239 1240 func TestListReplicaSets_Filter(t *testing.T) { 1241 mockCtrl := gomock.NewController(t) 1242 defer mockCtrl.Finish() 1243 1244 p := k8s.NewMockKubernetesProvider(mockCtrl) 1245 1246 // called for each ReplicaSet 1247 p.EXPECT().Runtime().Return("k8s-cluster") 1248 1249 // Seed ReplicaSets 1250 replicaSets := []*appsv1.ReplicaSet{ 1251 { 1252 TypeMeta: metav1.TypeMeta{ 1253 Kind: "ReplicaSet", 1254 APIVersion: "apps/v1", 1255 }, 1256 ObjectMeta: metav1.ObjectMeta{ 1257 Name: "nginx", 1258 Namespace: "default", 1259 UID: "123", 1260 }, 1261 Spec: appsv1.ReplicaSetSpec{ 1262 Template: corev1.PodTemplateSpec{ 1263 Spec: corev1.PodSpec{ 1264 Containers: []corev1.Container{{Image: "nginx:1.22.0-alpine"}}, 1265 }, 1266 }, 1267 }, 1268 }, 1269 { 1270 TypeMeta: metav1.TypeMeta{ 1271 Kind: "ReplicaSet", 1272 APIVersion: "apps/v1", 1273 }, 1274 ObjectMeta: metav1.ObjectMeta{ 1275 Name: "nginx2", 1276 Namespace: "default", 1277 UID: "456", 1278 }, 1279 Spec: appsv1.ReplicaSetSpec{ 1280 Template: corev1.PodTemplateSpec{ 1281 Spec: corev1.PodSpec{ 1282 Containers: []corev1.Container{{Image: "nginx:1.22.0-alpine"}}, 1283 }, 1284 }, 1285 }, 1286 }, 1287 } 1288 1289 p.EXPECT().ReplicaSet(replicaSets[0].Namespace, replicaSets[0].Name).Return(replicaSets[0], nil) 1290 1291 expectedAssetNames := []string{ 1292 replicaSets[0].Namespace + "/" + replicaSets[0].Name, 1293 } 1294 1295 clusterIdentifier := "//platformid.api.mondoo.app/runtime/k8s/uid/e26043bb-8669-48a2-b684-b1e132198cdc" 1296 1297 expectedAssetPlatformIds := []string{ 1298 clusterIdentifier + "/namespace/" + replicaSets[0].Namespace + "/replicasets/name/" + replicaSets[0].Name, 1299 } 1300 1301 pCfg := &providers.Config{} 1302 ownershipDir := k8s.NewEmptyPlatformIdOwnershipDirectory(clusterIdentifier) 1303 resFilter := map[string][]K8sResourceIdentifier{ 1304 "replicaset": { 1305 {Type: "replicaset", Name: replicaSets[0].Name, Namespace: replicaSets[0].Namespace}, 1306 }, 1307 } 1308 assets, err := ListReplicaSets(p, pCfg, clusterIdentifier, NamespaceFilterOpts{}, resFilter, ownershipDir) 1309 require.NoError(t, err) 1310 1311 var assetNames []string 1312 for _, a := range assets { 1313 assetNames = append(assetNames, a.Name) 1314 } 1315 1316 var assetPlatformIds []string 1317 for _, a := range assets { 1318 assetPlatformIds = append(assetPlatformIds, a.PlatformIds[0]) 1319 } 1320 1321 assert.ElementsMatch(t, expectedAssetNames, assetNames) 1322 assert.ElementsMatch(t, expectedAssetPlatformIds, assetPlatformIds) 1323 assert.Equal(t, "apps/v1", assets[0].Platform.Version) 1324 assert.Equal(t, "k8s-replicaset", assets[0].Platform.Name) 1325 assert.ElementsMatch(t, []string{"k8s", "k8s-workload"}, assets[0].Platform.Family) 1326 assert.Equal(t, replicaSets[0].Namespace, assets[0].Labels["k8s.mondoo.com/namespace"]) 1327 } 1328 1329 func TestListStatefulSets(t *testing.T) { 1330 mockCtrl := gomock.NewController(t) 1331 defer mockCtrl.Finish() 1332 1333 p := k8s.NewMockKubernetesProvider(mockCtrl) 1334 1335 // Seed namespaces 1336 nss := []corev1.Namespace{ 1337 {ObjectMeta: metav1.ObjectMeta{Name: "default"}}, 1338 } 1339 p.EXPECT().Namespaces().Return(nss, nil) 1340 // called for each StatefulSet 1341 p.EXPECT().Runtime().Return("k8s-cluster") 1342 p.EXPECT().Runtime().Return("k8s-cluster") 1343 1344 // pretend stateful set owned by deployment 1345 parent := appsv1.Deployment{ 1346 TypeMeta: metav1.TypeMeta{ 1347 Kind: "Deployment", 1348 APIVersion: "apps/v1", 1349 }, 1350 ObjectMeta: metav1.ObjectMeta{ 1351 Name: "nginx-deployment", 1352 Namespace: nss[0].Name, 1353 UID: "000", 1354 }, 1355 Spec: appsv1.DeploymentSpec{ 1356 Template: corev1.PodTemplateSpec{ 1357 Spec: corev1.PodSpec{ 1358 Containers: []corev1.Container{{Image: "nginx:1.22.0-alpine"}}, 1359 }, 1360 }, 1361 }, 1362 } 1363 1364 // Seed StatefulSets 1365 statefulsets := []*appsv1.StatefulSet{ 1366 { 1367 TypeMeta: metav1.TypeMeta{ 1368 Kind: "StatefulSet", 1369 APIVersion: "apps/v1", 1370 }, 1371 ObjectMeta: metav1.ObjectMeta{ 1372 Name: "nginx", 1373 Namespace: nss[0].Name, 1374 UID: "123", 1375 OwnerReferences: []metav1.OwnerReference{ 1376 { 1377 APIVersion: parent.APIVersion, 1378 Kind: parent.Kind, 1379 Name: parent.Name, 1380 UID: parent.UID, 1381 }, 1382 }, 1383 }, 1384 Spec: appsv1.StatefulSetSpec{ 1385 Template: corev1.PodTemplateSpec{ 1386 ObjectMeta: metav1.ObjectMeta{ 1387 Labels: map[string]string{ 1388 "app": "nginx", 1389 }, 1390 }, 1391 Spec: corev1.PodSpec{ 1392 Containers: []corev1.Container{{Image: "nginx:1.22.0-alpine"}}, 1393 }, 1394 }, 1395 }, 1396 }, 1397 { 1398 TypeMeta: metav1.TypeMeta{ 1399 Kind: "StatefulSet", 1400 APIVersion: "apps/v1", 1401 }, 1402 ObjectMeta: metav1.ObjectMeta{ 1403 Name: "nginx2", 1404 Namespace: nss[0].Name, 1405 UID: "456", 1406 }, 1407 Spec: appsv1.StatefulSetSpec{ 1408 Template: corev1.PodTemplateSpec{ 1409 ObjectMeta: metav1.ObjectMeta{ 1410 Labels: map[string]string{ 1411 "app": "nginx", 1412 }, 1413 }, 1414 Spec: corev1.PodSpec{ 1415 Containers: []corev1.Container{{Image: "nginx:1.22.0-alpine"}}, 1416 }, 1417 }, 1418 }, 1419 }, 1420 } 1421 1422 p.EXPECT().StatefulSets(nss[0]).Return(statefulsets, nil) 1423 1424 expectedAssetNames := []string{ 1425 nss[0].Name + "/" + statefulsets[0].Name, 1426 nss[0].Name + "/" + statefulsets[1].Name, 1427 } 1428 1429 clusterIdentifier := "//platformid.api.mondoo.app/runtime/k8s/uid/e26043bb-8669-48a2-b684-b1e132198cdc" 1430 1431 expectedAssetPlatformIds := []string{ 1432 clusterIdentifier + "/namespace/" + nss[0].Name + "/statefulsets/name/" + statefulsets[0].Name, 1433 clusterIdentifier + "/namespace/" + nss[0].Name + "/statefulsets/name/" + statefulsets[1].Name, 1434 } 1435 1436 pCfg := &providers.Config{} 1437 ownershipDir := k8s.NewEmptyPlatformIdOwnershipDirectory(clusterIdentifier) 1438 assets, err := ListStatefulSets(p, pCfg, clusterIdentifier, NamespaceFilterOpts{}, make(map[string][]K8sResourceIdentifier), ownershipDir) 1439 require.NoError(t, err) 1440 require.Equal(t, []string{k8s.NewPlatformWorkloadId(clusterIdentifier, 1441 strings.ToLower(parent.Kind), 1442 parent.Namespace, 1443 parent.Name, 1444 "")}, 1445 ownershipDir.OwnedBy(expectedAssetPlatformIds[0])) 1446 1447 var assetNames []string 1448 for _, a := range assets { 1449 assetNames = append(assetNames, a.Name) 1450 } 1451 1452 var assetPlatformIds []string 1453 for _, a := range assets { 1454 assetPlatformIds = append(assetPlatformIds, a.PlatformIds[0]) 1455 } 1456 1457 assert.ElementsMatch(t, expectedAssetNames, assetNames) 1458 assert.ElementsMatch(t, expectedAssetPlatformIds, assetPlatformIds) 1459 assert.Equal(t, "apps/v1", assets[0].Platform.Version) 1460 assert.Equal(t, "k8s-statefulset", assets[0].Platform.Name) 1461 assert.ElementsMatch(t, []string{"k8s", "k8s-workload"}, assets[0].Platform.Family) 1462 assert.Equal(t, nss[0].Name, assets[0].Labels["k8s.mondoo.com/namespace"]) 1463 } 1464 1465 func TestListStatefulSets_Filter(t *testing.T) { 1466 mockCtrl := gomock.NewController(t) 1467 defer mockCtrl.Finish() 1468 1469 p := k8s.NewMockKubernetesProvider(mockCtrl) 1470 1471 // called for each StatefulSet 1472 p.EXPECT().Runtime().Return("k8s-cluster") 1473 1474 // Seed StatefulSets 1475 statefulsets := []*appsv1.StatefulSet{ 1476 { 1477 TypeMeta: metav1.TypeMeta{ 1478 Kind: "StatefulSet", 1479 APIVersion: "apps/v1", 1480 }, 1481 ObjectMeta: metav1.ObjectMeta{ 1482 Name: "nginx", 1483 Namespace: "default", 1484 UID: "123", 1485 }, 1486 Spec: appsv1.StatefulSetSpec{ 1487 Template: corev1.PodTemplateSpec{ 1488 ObjectMeta: metav1.ObjectMeta{ 1489 Labels: map[string]string{ 1490 "app": "nginx", 1491 }, 1492 }, 1493 Spec: corev1.PodSpec{ 1494 Containers: []corev1.Container{{Image: "nginx:1.22.0-alpine"}}, 1495 }, 1496 }, 1497 }, 1498 }, 1499 { 1500 TypeMeta: metav1.TypeMeta{ 1501 Kind: "StatefulSet", 1502 APIVersion: "apps/v1", 1503 }, 1504 ObjectMeta: metav1.ObjectMeta{ 1505 Name: "nginx2", 1506 Namespace: "default", 1507 UID: "456", 1508 }, 1509 Spec: appsv1.StatefulSetSpec{ 1510 Template: corev1.PodTemplateSpec{ 1511 ObjectMeta: metav1.ObjectMeta{ 1512 Labels: map[string]string{ 1513 "app": "nginx", 1514 }, 1515 }, 1516 Spec: corev1.PodSpec{ 1517 Containers: []corev1.Container{{Image: "nginx:1.22.0-alpine"}}, 1518 }, 1519 }, 1520 }, 1521 }, 1522 } 1523 1524 p.EXPECT().StatefulSet(statefulsets[0].Namespace, statefulsets[0].Name).Return(statefulsets[0], nil) 1525 1526 expectedAssetNames := []string{ 1527 statefulsets[0].Namespace + "/" + statefulsets[0].Name, 1528 } 1529 1530 clusterIdentifier := "//platformid.api.mondoo.app/runtime/k8s/uid/e26043bb-8669-48a2-b684-b1e132198cdc" 1531 1532 expectedAssetPlatformIds := []string{ 1533 clusterIdentifier + "/namespace/" + statefulsets[0].Namespace + "/statefulsets/name/" + statefulsets[0].Name, 1534 } 1535 1536 pCfg := &providers.Config{} 1537 ownershipDir := k8s.NewEmptyPlatformIdOwnershipDirectory(clusterIdentifier) 1538 resFilter := map[string][]K8sResourceIdentifier{ 1539 "statefulset": { 1540 {Type: "statefulset", Name: statefulsets[0].Name, Namespace: statefulsets[0].Namespace}, 1541 }, 1542 } 1543 assets, err := ListStatefulSets(p, pCfg, clusterIdentifier, NamespaceFilterOpts{}, resFilter, ownershipDir) 1544 require.NoError(t, err) 1545 1546 var assetNames []string 1547 for _, a := range assets { 1548 assetNames = append(assetNames, a.Name) 1549 } 1550 1551 var assetPlatformIds []string 1552 for _, a := range assets { 1553 assetPlatformIds = append(assetPlatformIds, a.PlatformIds[0]) 1554 } 1555 1556 assert.ElementsMatch(t, expectedAssetNames, assetNames) 1557 assert.ElementsMatch(t, expectedAssetPlatformIds, assetPlatformIds) 1558 assert.Equal(t, "apps/v1", assets[0].Platform.Version) 1559 assert.Equal(t, "k8s-statefulset", assets[0].Platform.Name) 1560 assert.ElementsMatch(t, []string{"k8s", "k8s-workload"}, assets[0].Platform.Family) 1561 assert.Equal(t, statefulsets[0].Namespace, assets[0].Labels["k8s.mondoo.com/namespace"]) 1562 } 1563 1564 func TestListFiltering(t *testing.T) { 1565 mockCtrl := gomock.NewController(t) 1566 defer mockCtrl.Finish() 1567 1568 p := k8s.NewMockKubernetesProvider(mockCtrl) 1569 1570 // Seed namespaces 1571 nss := []corev1.Namespace{ 1572 {ObjectMeta: metav1.ObjectMeta{Name: "default"}}, 1573 {ObjectMeta: metav1.ObjectMeta{Name: "kube-system"}}, 1574 {ObjectMeta: metav1.ObjectMeta{Name: "kube-system-alternative"}}, 1575 } 1576 p.EXPECT().Namespaces().Return(nss, nil).AnyTimes() 1577 1578 // Seed pods 1579 defaultNamespacePods := []*corev1.Pod{ 1580 { 1581 TypeMeta: metav1.TypeMeta{Kind: "Pod", APIVersion: "v1"}, 1582 ObjectMeta: metav1.ObjectMeta{Name: "nginx", Namespace: nss[0].Name}, 1583 }, 1584 { 1585 TypeMeta: metav1.TypeMeta{Kind: "Pod", APIVersion: "v1"}, 1586 ObjectMeta: metav1.ObjectMeta{Name: "nginx2", Namespace: nss[0].Name}, 1587 }, 1588 } 1589 1590 kubeSystemPods := []*corev1.Pod{ 1591 { 1592 TypeMeta: metav1.TypeMeta{Kind: "Pod", APIVersion: "v1"}, 1593 ObjectMeta: metav1.ObjectMeta{Name: "kube-proxy", Namespace: nss[1].Name}, 1594 }, 1595 } 1596 1597 otherNamespacePods := []*corev1.Pod{ 1598 { 1599 TypeMeta: metav1.TypeMeta{Kind: "Pod", APIVersion: "v1"}, 1600 ObjectMeta: metav1.ObjectMeta{Name: "some-workload", Namespace: nss[2].Name}, 1601 }, 1602 { 1603 TypeMeta: metav1.TypeMeta{Kind: "Pod", APIVersion: "v1"}, 1604 ObjectMeta: metav1.ObjectMeta{Name: "some-workload2", Namespace: nss[2].Name}, 1605 }, 1606 { 1607 TypeMeta: metav1.TypeMeta{Kind: "Pod", APIVersion: "v1"}, 1608 ObjectMeta: metav1.ObjectMeta{Name: "some-workload3", Namespace: nss[2].Name}, 1609 }, 1610 } 1611 p.EXPECT().Pods(nss[0]).Return(defaultNamespacePods, nil).AnyTimes() 1612 p.EXPECT().Pods(nss[1]).Return(kubeSystemPods, nil).AnyTimes() 1613 p.EXPECT().Pods(nss[2]).Return(otherNamespacePods, nil).AnyTimes() 1614 p.EXPECT().Runtime().Return("k8s-cluster").AnyTimes() 1615 1616 clusterIdentifier := "//platformid.api.mondoo.app/runtime/k8s/uid/e26043bb-8669-48a2-b684-b1e132198cdc" 1617 ownershipDir := k8s.NewEmptyPlatformIdOwnershipDirectory(clusterIdentifier) 1618 pCfg := &providers.Config{} 1619 1620 // List with no filtering 1621 assets, err := ListPods(p, pCfg, clusterIdentifier, NamespaceFilterOpts{}, make(map[string][]K8sResourceIdentifier), ownershipDir) 1622 require.NoError(t, err) 1623 assert.Equal(t, 6, len(assets), "expected all Pods to be found when no filter specified") 1624 1625 // List only 'kube-system' 1626 assets, err = ListPods(p, pCfg, clusterIdentifier, NamespaceFilterOpts{include: []string{nss[1].Name}}, make(map[string][]K8sResourceIdentifier), ownershipDir) 1627 require.NoError(t, err) 1628 assert.Equal(t, 1, len(assets), "expected only 1 Pod to be returned") 1629 1630 // List 'kube-system' and 'other-namespace' 1631 assets, err = ListPods(p, pCfg, clusterIdentifier, NamespaceFilterOpts{include: []string{nss[1].Name, nss[2].Name}}, make(map[string][]K8sResourceIdentifier), ownershipDir) 1632 require.NoError(t, err) 1633 assert.Equal(t, 4, len(assets), "expected only 4 Pods to be returned") 1634 1635 // Exclude kube-system 1636 assets, err = ListPods(p, pCfg, clusterIdentifier, NamespaceFilterOpts{exclude: []string{nss[1].Name}}, make(map[string][]K8sResourceIdentifier), ownershipDir) 1637 require.NoError(t, err) 1638 assert.Equal(t, 5, len(assets), "expected only 5 Pods to be returned") 1639 1640 // Include and exclude list should behave like only include list 1641 assets, err = ListPods(p, pCfg, clusterIdentifier, NamespaceFilterOpts{include: []string{nss[1].Name}, exclude: []string{nss[1].Name}}, make(map[string][]K8sResourceIdentifier), ownershipDir) 1642 require.NoError(t, err) 1643 assert.Equal(t, 1, len(assets), "expected only 1 Pod to be returned") 1644 1645 // List w/glob 'kube*' 1646 assets, err = ListPods(p, pCfg, clusterIdentifier, NamespaceFilterOpts{include: []string{"kube*"}}, make(map[string][]K8sResourceIdentifier), ownershipDir) 1647 require.NoError(t, err) 1648 assert.Equal(t, 4, len(assets), "expected 4 Pods to be returned from matched Namespaces") 1649 1650 // List w/glob '*alt*' 1651 assets, err = ListPods(p, pCfg, clusterIdentifier, NamespaceFilterOpts{include: []string{"*alt*"}}, make(map[string][]K8sResourceIdentifier), ownershipDir) 1652 require.NoError(t, err) 1653 assert.Equal(t, 3, len(assets), "expected 3 Pods to be returned from matched Namespaces") 1654 1655 // Exclude w/glob '*default*' 1656 assets, err = ListPods(p, pCfg, clusterIdentifier, NamespaceFilterOpts{exclude: []string{"*default*"}}, make(map[string][]K8sResourceIdentifier), ownershipDir) 1657 require.NoError(t, err) 1658 assert.Equal(t, 4, len(assets), "expected 4 Pods to be returned from non-excluded Namespaces") 1659 }