Skip to content

Commit 73a85ca

Browse files
committed
✨ add garbage collection for stale node scan assets
fix: update MondooClientBuilder reference and clarify garbage collection comment refactor: streamline garbage collection logic for nodes and K8s resources refactor: simplify garbage collection logic by removing cluster UID dependency
1 parent 525d6ae commit 73a85ca

16 files changed

Lines changed: 570 additions & 112 deletions

File tree

api/v1alpha2/mondooauditconfig_types.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,11 @@ type MondooAuditConfigStatus struct {
483483
// garbage collection of stale K8s resource scan assets.
484484
// +optional
485485
LastK8sResourceGarbageCollectionTime *metav1.Time `json:"lastK8sResourceGarbageCollectionTime,omitempty"`
486+
487+
// LastNodeScanGarbageCollectionTime tracks the last time the operator performed
488+
// garbage collection of stale node scan assets.
489+
// +optional
490+
LastNodeScanGarbageCollectionTime *metav1.Time `json:"lastNodeScanGarbageCollectionTime,omitempty"`
486491
}
487492

488493
type MondooAuditConfigCondition struct {

api/v1alpha2/zz_generated.deepcopy.go

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

charts/mondoo-operator/crds/k8s.mondoo.com_mondooauditconfigs.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1366,6 +1366,12 @@ spec:
13661366
garbage collection of stale K8s resource scan assets.
13671367
format: date-time
13681368
type: string
1369+
lastNodeScanGarbageCollectionTime:
1370+
description: |-
1371+
LastNodeScanGarbageCollectionTime tracks the last time the operator performed
1372+
garbage collection of stale node scan assets.
1373+
format: date-time
1374+
type: string
13691375
pods:
13701376
description: Pods store the name of the pods which are running mondoo
13711377
instances

charts/mondoo-operator/files/crds/k8s.mondoo.com_mondooauditconfigs.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1366,6 +1366,12 @@ spec:
13661366
garbage collection of stale K8s resource scan assets.
13671367
format: date-time
13681368
type: string
1369+
lastNodeScanGarbageCollectionTime:
1370+
description: |-
1371+
LastNodeScanGarbageCollectionTime tracks the last time the operator performed
1372+
garbage collection of stale node scan assets.
1373+
format: date-time
1374+
type: string
13691375
pods:
13701376
description: Pods store the name of the pods which are running mondoo
13711377
instances

cmd/mondoo-operator/garbage_collect/cmd.go

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"errors"
99
"fmt"
1010
"os"
11-
"strings"
1211
"time"
1312

1413
"github.com/go-logr/logr"
@@ -31,7 +30,6 @@ func init() {
3130
filterPlatformRuntime := Cmd.Flags().String("filter-platform-runtime", "", "Cleanup assets by an asset's PlatformRuntime (k8s-cluster or docker-image).")
3231
filterManagedBy := Cmd.Flags().String("filter-managed-by", "", "Cleanup assets with matching ManagedBy field.")
3332
filterOlderThan := Cmd.Flags().String("filter-older-than", "", "Cleanup assets which have not been updated in over the time provided (eg 12m or 48h or anything time.ParseDuration() accepts).")
34-
labelsInput := Cmd.Flags().StringSlice("labels", []string{}, "Cleanup assets with matching labels (eg --labels key1=value1,key2=value2).")
3533
Cmd.RunE = func(cmd *cobra.Command, args []string) error {
3634
log.SetLogger(logger.NewLogger())
3735
logger := log.Log.WithName("garbage-collect")
@@ -43,15 +41,6 @@ func init() {
4341
return fmt.Errorf("--timeout must be greater than 0")
4442
}
4543

46-
labels := make(map[string]string)
47-
for _, l := range *labelsInput {
48-
split := strings.Split(l, "=")
49-
if len(split) != 2 {
50-
return fmt.Errorf("invalid label provided %s. Labels should be in the form of key=value", l)
51-
}
52-
labels[split[0]] = split[1]
53-
}
54-
5544
// Read the service account credentials from the config file
5645
configData, err := os.ReadFile(*configPath)
5746
if err != nil {
@@ -88,14 +77,18 @@ func init() {
8877
return fmt.Errorf("no filters provided to garbage collect by")
8978
}
9079

91-
return GarbageCollectCmd(ctx, client, *filterPlatformRuntime, *filterOlderThan, *filterManagedBy, labels, logger)
80+
spaceMrn := serviceAccount.SpaceMrn
81+
if spaceMrn == "" {
82+
spaceMrn = mondoo.SpaceMrnFromServiceAccountMrn(serviceAccount.Mrn)
83+
}
84+
return GarbageCollectCmd(ctx, client, spaceMrn, *filterPlatformRuntime, *filterOlderThan, *filterManagedBy, logger)
9285
}
9386
}
9487

95-
func GarbageCollectCmd(ctx context.Context, client mondooclient.MondooClient, platformRuntime, olderThan, managedBy string, labels map[string]string, logger logr.Logger) error {
96-
gcOpts := &mondooclient.GarbageCollectOptions{
88+
func GarbageCollectCmd(ctx context.Context, client mondooclient.MondooClient, spaceMrn, platformRuntime, olderThan, managedBy string, logger logr.Logger) error {
89+
req := &mondooclient.DeleteAssetsRequest{
90+
SpaceMrn: spaceMrn,
9791
ManagedBy: managedBy,
98-
Labels: labels,
9992
}
10093

10194
if olderThan != "" {
@@ -105,19 +98,23 @@ func GarbageCollectCmd(ctx context.Context, client mondooclient.MondooClient, pl
10598
return err
10699
}
107100

108-
gcOpts.OlderThan = timestamp
101+
req.DateFilter = &mondooclient.DateFilter{
102+
Timestamp: timestamp,
103+
Comparison: mondooclient.Comparison_LESS_THAN,
104+
Field: mondooclient.DateFilterField_FILTER_LAST_UPDATED,
105+
}
109106
}
110107

111108
if platformRuntime != "" {
112109
switch platformRuntime {
113110
case "k8s-cluster", "docker-image":
114-
gcOpts.PlatformRuntime = platformRuntime
111+
req.PlatformRuntime = platformRuntime
115112
default:
116113
return fmt.Errorf("no matching platform runtime found for (%s)", platformRuntime)
117114
}
118115
}
119116

120-
err := client.GarbageCollectAssets(ctx, gcOpts)
117+
resp, err := client.DeleteAssets(ctx, req)
121118
if err != nil {
122119
if errors.Is(err, context.DeadlineExceeded) {
123120
logger.Error(err, "failed to receive a response before timeout was exceeded")
@@ -127,6 +124,13 @@ func GarbageCollectCmd(ctx context.Context, client mondooclient.MondooClient, pl
127124
return err
128125
}
129126

127+
if len(resp.AssetMrns) > 0 {
128+
logger.Info("Deleted assets", "count", len(resp.AssetMrns))
129+
}
130+
if len(resp.Errors) > 0 {
131+
logger.Info("DeleteAssets completed with errors", "errors", resp.Errors)
132+
}
133+
130134
return nil
131135
}
132136

config/crd/bases/k8s.mondoo.com_mondooauditconfigs.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1366,6 +1366,12 @@ spec:
13661366
garbage collection of stale K8s resource scan assets.
13671367
format: date-time
13681368
type: string
1369+
lastNodeScanGarbageCollectionTime:
1370+
description: |-
1371+
LastNodeScanGarbageCollectionTime tracks the last time the operator performed
1372+
garbage collection of stale node scan assets.
1373+
format: date-time
1374+
type: string
13691375
pods:
13701376
description: Pods store the name of the pods which are running mondoo
13711377
instances

controllers/k8s_scan/deployment_handler.go

Lines changed: 9 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import (
2020

2121
"go.mondoo.com/mondoo-operator/api/v1alpha2"
2222
"go.mondoo.com/mondoo-operator/pkg/client/mondooclient"
23-
"go.mondoo.com/mondoo-operator/pkg/constants"
2423
"go.mondoo.com/mondoo-operator/pkg/utils/k8s"
2524
"go.mondoo.com/mondoo-operator/pkg/utils/mondoo"
2625
)
@@ -610,61 +609,20 @@ func (n *DeploymentHandler) garbageCollectIfNeeded(ctx context.Context, clusterU
610609
n.Mondoo.Status.LastK8sResourceGarbageCollectionTime = &now
611610
}
612611

613-
// performGarbageCollection calls the Mondoo API to garbage collect stale K8s resource scan assets.
612+
// performGarbageCollection calls the Mondoo API to delete stale K8s resource scan assets.
614613
func (n *DeploymentHandler) performGarbageCollection(ctx context.Context, managedBy string) error {
615-
if n.MondooClientBuilder == nil {
616-
logger.Info("MondooClientBuilder not configured, skipping garbage collection")
617-
return nil
618-
}
619-
620-
// Read service account credentials from the creds secret
621-
credsSecret := &corev1.Secret{}
622-
credsSecretKey := client.ObjectKey{
623-
Namespace: n.Mondoo.Namespace,
624-
Name: n.Mondoo.Spec.MondooCredsSecretRef.Name,
625-
}
626-
if err := n.KubeClient.Get(ctx, credsSecretKey, credsSecret); err != nil {
627-
return fmt.Errorf("failed to get credentials secret: %w", err)
628-
}
629-
630-
saData, ok := credsSecret.Data[constants.MondooCredsSecretServiceAccountKey]
631-
if !ok {
632-
return fmt.Errorf("credentials secret missing key %q", constants.MondooCredsSecretServiceAccountKey)
633-
}
634-
635-
sa, err := mondoo.LoadServiceAccountFromFile(saData)
636-
if err != nil {
637-
return fmt.Errorf("failed to load service account: %w", err)
638-
}
639-
640-
token, err := mondoo.GenerateTokenFromServiceAccount(*sa, logger)
641-
if err != nil {
642-
return fmt.Errorf("failed to generate token: %w", err)
643-
}
644-
645-
opts := mondooclient.MondooClientOptions{
646-
ApiEndpoint: sa.ApiEndpoint,
647-
Token: token,
648-
}
649-
if n.MondooOperatorConfig != nil {
650-
opts.HttpProxy = n.MondooOperatorConfig.Spec.HttpProxy
651-
opts.HttpsProxy = n.MondooOperatorConfig.Spec.HttpsProxy
652-
opts.NoProxy = n.MondooOperatorConfig.Spec.NoProxy
653-
}
654-
655-
mondooClient, err := n.MondooClientBuilder(opts)
656-
if err != nil {
657-
return fmt.Errorf("failed to create mondoo client: %w", err)
658-
}
659-
660-
gcOpts := &mondooclient.GarbageCollectOptions{
614+
req := &mondooclient.DeleteAssetsRequest{
661615
ManagedBy: managedBy,
662616
PlatformRuntime: "k8s-cluster",
663-
OlderThan: time.Now().Add(-2 * time.Hour).Format(time.RFC3339),
617+
DateFilter: &mondooclient.DateFilter{
618+
Timestamp: time.Now().Add(-mondoo.GCOlderThan()).Format(time.RFC3339),
619+
Comparison: mondooclient.Comparison_LESS_THAN,
620+
Field: mondooclient.DateFilterField_FILTER_LAST_UPDATED,
621+
},
664622
}
665623

666-
if err := mondooClient.GarbageCollectAssets(ctx, gcOpts); err != nil {
667-
return fmt.Errorf("garbage collection API call failed: %w", err)
624+
if err := mondoo.DeleteStaleAssets(ctx, n.KubeClient, n.Mondoo, n.MondooOperatorConfig, n.MondooClientBuilder, req, logger); err != nil {
625+
return err
668626
}
669627

670628
logger.Info("Successfully performed garbage collection of K8s resource scan assets")

controllers/k8s_scan/deployment_handler_test.go

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1528,11 +1528,14 @@ func TestExternalClusterNaming(t *testing.T) {
15281528

15291529
func (s *DeploymentHandlerSuite) TestGarbageCollection_RunsAfterSuccessfulScan() {
15301530
gcCalled := false
1531-
d := s.createDeploymentHandlerWithGCMock(func(ctx context.Context, opts *mondooclient.GarbageCollectOptions) error {
1531+
d := s.createDeploymentHandlerWithGCMock(func(ctx context.Context, req *mondooclient.DeleteAssetsRequest) error {
15321532
gcCalled = true
1533-
s.Equal("k8s-cluster", opts.PlatformRuntime)
1534-
s.Contains(opts.ManagedBy, "mondoo-operator-")
1535-
s.NotEmpty(opts.OlderThan)
1533+
s.Equal("k8s-cluster", req.PlatformRuntime)
1534+
s.Contains(req.ManagedBy, "mondoo-operator-")
1535+
s.NotNil(req.DateFilter)
1536+
s.NotEmpty(req.DateFilter.Timestamp)
1537+
s.Equal(mondooclient.Comparison_LESS_THAN, req.DateFilter.Comparison)
1538+
s.Equal(mondooclient.DateFilterField_FILTER_LAST_UPDATED, req.DateFilter.Field)
15361539
return nil
15371540
})
15381541
s.NoError(d.KubeClient.Create(s.ctx, &s.auditConfig))
@@ -1556,13 +1559,13 @@ func (s *DeploymentHandlerSuite) TestGarbageCollection_RunsAfterSuccessfulScan()
15561559
s.NoError(err)
15571560
s.True(result.IsZero())
15581561

1559-
s.True(gcCalled, "GarbageCollectAssets should have been called")
1562+
s.True(gcCalled, "DeleteAssets should have been called")
15601563
s.NotNil(d.Mondoo.Status.LastK8sResourceGarbageCollectionTime, "GC timestamp should be set in status")
15611564
}
15621565

15631566
func (s *DeploymentHandlerSuite) TestGarbageCollection_SkipsWhenAlreadyRun() {
15641567
gcCalled := false
1565-
d := s.createDeploymentHandlerWithGCMock(func(ctx context.Context, opts *mondooclient.GarbageCollectOptions) error {
1568+
d := s.createDeploymentHandlerWithGCMock(func(ctx context.Context, opts *mondooclient.DeleteAssetsRequest) error {
15661569
gcCalled = true
15671570
return nil
15681571
})
@@ -1591,11 +1594,11 @@ func (s *DeploymentHandlerSuite) TestGarbageCollection_SkipsWhenAlreadyRun() {
15911594
s.NoError(err)
15921595
s.True(result.IsZero())
15931596

1594-
s.False(gcCalled, "GarbageCollectAssets should NOT have been called")
1597+
s.False(gcCalled, "DeleteAssets should NOT have been called")
15951598
}
15961599

15971600
func (s *DeploymentHandlerSuite) TestGarbageCollection_FailureStillUpdatesTimestamp() {
1598-
d := s.createDeploymentHandlerWithGCMock(func(ctx context.Context, opts *mondooclient.GarbageCollectOptions) error {
1601+
d := s.createDeploymentHandlerWithGCMock(func(ctx context.Context, opts *mondooclient.DeleteAssetsRequest) error {
15991602
return fmt.Errorf("API error")
16001603
})
16011604
s.NoError(d.KubeClient.Create(s.ctx, &s.auditConfig))
@@ -1624,8 +1627,8 @@ func (s *DeploymentHandlerSuite) TestGarbageCollection_FailureStillUpdatesTimest
16241627
}
16251628

16261629
// createDeploymentHandlerWithGCMock creates a DeploymentHandler with a mock MondooClientBuilder
1627-
// that captures calls to GarbageCollectAssets.
1628-
func (s *DeploymentHandlerSuite) createDeploymentHandlerWithGCMock(gcFunc func(context.Context, *mondooclient.GarbageCollectOptions) error) DeploymentHandler {
1630+
// that captures calls to DeleteAssets.
1631+
func (s *DeploymentHandlerSuite) createDeploymentHandlerWithGCMock(gcFunc func(context.Context, *mondooclient.DeleteAssetsRequest) error) DeploymentHandler {
16291632
// Create a mock credentials secret so GC can read it
16301633
key := credentials.MondooServiceAccount(s.T())
16311634
mockSA := mondooclient.ServiceAccountCredentials{
@@ -1660,14 +1663,14 @@ func (s *DeploymentHandlerSuite) createDeploymentHandlerWithGCMock(gcFunc func(c
16601663
// fakeMondooClient implements just enough of MondooClient to test GC
16611664
type fakeMondooClient struct {
16621665
mondooclient.MondooClient
1663-
gcFunc func(context.Context, *mondooclient.GarbageCollectOptions) error
1666+
gcFunc func(context.Context, *mondooclient.DeleteAssetsRequest) error
16641667
}
16651668

1666-
func (f *fakeMondooClient) GarbageCollectAssets(ctx context.Context, opts *mondooclient.GarbageCollectOptions) error {
1669+
func (f *fakeMondooClient) DeleteAssets(ctx context.Context, req *mondooclient.DeleteAssetsRequest) (*mondooclient.DeleteAssetsConfirmation, error) {
16671670
if f.gcFunc != nil {
1668-
return f.gcFunc(ctx, opts)
1671+
return &mondooclient.DeleteAssetsConfirmation{}, f.gcFunc(ctx, req)
16691672
}
1670-
return nil
1673+
return &mondooclient.DeleteAssetsConfirmation{}, nil
16711674
}
16721675

16731676
func TestDeploymentHandlerSuite(t *testing.T) {

controllers/mondooauditconfig_controller.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ func (r *MondooAuditConfigReconciler) Reconcile(ctx context.Context, req ctrl.Re
277277
KubeClient: r.Client,
278278
MondooOperatorConfig: config,
279279
ContainerImageResolver: imageResolver,
280+
MondooClientBuilder: r.MondooClientBuilder,
280281
IsOpenshift: r.RunningOnOpenShift,
281282
}
282283

0 commit comments

Comments
 (0)