Skip to content

Commit b75bfb9

Browse files
committed
✨ Enable organization-scoped garbage collection by updating MRN handling and related tests
1 parent 2901c34 commit b75bfb9

6 files changed

Lines changed: 37 additions & 79 deletions

File tree

cmd/mondoo-operator/garbage_collect/cmd.go

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ func init() {
3030
filterPlatformRuntime := Cmd.Flags().String("filter-platform-runtime", "", "Cleanup assets by an asset's PlatformRuntime (k8s-cluster or docker-image).")
3131
filterManagedBy := Cmd.Flags().String("filter-managed-by", "", "Cleanup assets with matching ManagedBy field.")
3232
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).")
33-
spaceMrnOverride := Cmd.Flags().String("space-mrn", "", "Override the space MRN for garbage collection (used when spaceId is set in MondooAuditConfig).")
33+
scopeMrnOverride := Cmd.Flags().String("scope-mrn", "", "Override the scope MRN (space or org) for garbage collection.")
34+
Cmd.Flags().String("space-mrn", "", "Deprecated: use --scope-mrn instead.")
35+
_ = Cmd.Flags().MarkDeprecated("space-mrn", "use --scope-mrn instead")
3436
Cmd.RunE = func(cmd *cobra.Command, args []string) error {
3537
log.SetLogger(logger.NewLogger())
3638
logger := log.Log.WithName("garbage-collect")
@@ -78,20 +80,25 @@ func init() {
7880
return fmt.Errorf("no filters provided to garbage collect by")
7981
}
8082

81-
spaceMrn := *spaceMrnOverride
82-
if spaceMrn == "" {
83-
spaceMrn = serviceAccount.SpaceMrn
84-
if spaceMrn == "" {
85-
spaceMrn = mondoo.SpaceMrnFromServiceAccountMrn(serviceAccount.Mrn)
83+
scopeMrn := *scopeMrnOverride
84+
if scopeMrn == "" {
85+
if spaceMrn, _ := cmd.Flags().GetString("space-mrn"); spaceMrn != "" {
86+
scopeMrn = spaceMrn
8687
}
8788
}
88-
return GarbageCollectCmd(ctx, client, spaceMrn, *filterPlatformRuntime, *filterOlderThan, *filterManagedBy, logger)
89+
if scopeMrn == "" {
90+
scopeMrn = serviceAccount.ScopeMrn
91+
}
92+
if scopeMrn == "" {
93+
scopeMrn = serviceAccount.SpaceMrn
94+
}
95+
return GarbageCollectCmd(ctx, client, scopeMrn, *filterPlatformRuntime, *filterOlderThan, *filterManagedBy, logger)
8996
}
9097
}
9198

92-
func GarbageCollectCmd(ctx context.Context, client mondooclient.MondooClient, spaceMrn, platformRuntime, olderThan, managedBy string, logger logr.Logger) error {
99+
func GarbageCollectCmd(ctx context.Context, client mondooclient.MondooClient, scopeMrn, platformRuntime, olderThan, managedBy string, logger logr.Logger) error {
93100
req := &mondooclient.GarbageCollectAssetsRequest{
94-
SpaceMrn: spaceMrn,
101+
ScopeMrn: scopeMrn,
95102
ManagedBy: managedBy,
96103
}
97104

controllers/k8s_scan/deployment_handler_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1683,6 +1683,7 @@ func (s *DeploymentHandlerSuite) createDeploymentHandlerWithGCMock(gcFunc func(c
16831683
key := credentials.MondooServiceAccount(s.T())
16841684
mockSA := mondooclient.ServiceAccountCredentials{
16851685
Mrn: "//agents.api.mondoo.app/spaces/test/serviceaccounts/test",
1686+
ScopeMrn: "//captain.api.mondoo.app/spaces/test",
16861687
PrivateKey: key,
16871688
ApiEndpoint: "https://us.api.mondoo.com",
16881689
}

controllers/nodes/deployment_handler_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,7 @@ func (s *DeploymentHandlerSuite) createDeploymentHandlerWithGCMock(gcFunc func(c
899899
key := generateTestPrivateKey(s.T())
900900
mockSA := mondooclient.ServiceAccountCredentials{
901901
Mrn: "//agents.api.mondoo.app/spaces/test/serviceaccounts/test",
902+
ScopeMrn: "//captain.api.mondoo.app/spaces/test",
902903
PrivateKey: key,
903904
ApiEndpoint: "https://us.api.mondoo.com",
904905
}

pkg/client/mondooclient/types.go

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ type ServiceAccountCredentials struct {
5151
PrivateKey string `protobuf:"bytes,3,opt,name=private_key,json=privateKey,proto3" json:"private_key,omitempty"` //nolint:gosec
5252
Certificate string `protobuf:"bytes,4,opt,name=certificate,proto3" json:"certificate,omitempty"`
5353
ApiEndpoint string `protobuf:"bytes,5,opt,name=api_endpoint,json=apiEndpoint,proto3" json:"api_endpoint,omitempty"`
54+
ScopeMrn string `json:"scope_mrn,omitempty"`
5455
}
5556
type IntegrationCheckInInput struct {
5657
// Mrn should hold the MRN of the integration that is having the CheckIn() called for
@@ -119,17 +120,20 @@ const (
119120
// GarbageCollectAssetsRequest matches the server-side PurgeAssetsRequest proto
120121
// on the PolicyResolver service (/PolicyResolver/PurgeAssets).
121122
type GarbageCollectAssetsRequest struct {
122-
SpaceMrn string `json:"spaceMrn,omitempty"`
123-
DateFilter *DateFilter `json:"date_filter,omitempty"`
124-
ManagedBy string `json:"managed_by,omitempty"`
125-
PlatformRuntime string `json:"platform_runtime,omitempty"` // optional filter (k8s-cluster, docker-image, etc.)
126-
Labels map[string]string `json:"labels,omitempty"`
123+
SpaceMrn string `protobuf:"bytes,1,opt,name=spaceMrn,proto3" json:"spaceMrn,omitempty"` // Deprecated: use ScopeMrn
124+
AssetMrns []string `protobuf:"bytes,2,rep,name=asset_mrns,json=assetMrns,proto3" json:"asset_mrns,omitempty"`
125+
PurgeAll bool `protobuf:"varint,3,opt,name=purge_all,json=purgeAll,proto3" json:"purge_all,omitempty"`
126+
DateFilter *DateFilter `protobuf:"bytes,4,opt,name=date_filter,json=dateFilter,proto3" json:"date_filter,omitempty"`
127+
ManagedBy string `protobuf:"bytes,5,opt,name=managed_by,json=managedBy,proto3" json:"managed_by,omitempty"`
128+
PlatformRuntime string `protobuf:"bytes,6,opt,name=platform_runtime,json=platformRuntime,proto3" json:"platform_runtime,omitempty"`
129+
Labels map[string]string `protobuf:"bytes,7,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
130+
ScopeMrn string `protobuf:"bytes,8,opt,name=scope_mrn,json=scopeMrn,proto3" json:"scope_mrn,omitempty"`
127131
}
128132

129133
type DateFilter struct {
130-
Timestamp string `json:"timestamp,omitempty"` // RFC3339
131-
Comparison Comparison `json:"comparison"`
132-
Field DateFilterField `json:"field"`
134+
Timestamp string `protobuf:"bytes,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
135+
Comparison Comparison `protobuf:"varint,2,opt,name=comparison,proto3,enum=cnspec.policy.v1.Comparison" json:"comparison,omitempty"`
136+
Field DateFilterField `protobuf:"varint,3,opt,name=field,proto3,enum=cnspec.policy.v1.DateFilterField" json:"field,omitempty"`
133137
}
134138

135139
type Comparison int32

pkg/utils/mondoo/gc.go

Lines changed: 7 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"context"
88
"fmt"
99
"os"
10-
"strings"
1110
"time"
1211

1312
"github.com/go-logr/logr"
@@ -115,26 +114,24 @@ func GarbageCollectAssets(
115114

116115
// Use spaceId override from MondooAuditConfig if set, otherwise derive from SA credentials.
117116
if spaceMrn := k8s.SpaceMrnForAuditConfig(*mondoo); spaceMrn != "" {
118-
req.SpaceMrn = spaceMrn
117+
req.ScopeMrn = spaceMrn
119118
if saSpaceMrn := sa.SpaceMrn; saSpaceMrn != "" && saSpaceMrn != spaceMrn {
120119
logger.V(1).Info("spaceId override targets a different space than the service account",
121120
"saSpaceMrn", saSpaceMrn, "targetSpaceMrn", spaceMrn)
122121
}
123122
} else {
124-
req.SpaceMrn = sa.SpaceMrn
125-
if req.SpaceMrn == "" {
126-
req.SpaceMrn = SpaceMrnFromServiceAccountMrn(sa.Mrn)
123+
req.ScopeMrn = sa.ScopeMrn
124+
if req.ScopeMrn == "" {
125+
req.ScopeMrn = sa.SpaceMrn
127126
}
128127
}
129128

130-
// Org-level service accounts without spaceId have no determinable space for GC.
131-
// This happens when asset routing is used (server-side routing, no operator-side space).
132-
if req.SpaceMrn == "" {
133-
logger.Info("Skipping garbage collection: no space MRN determinable (org-level SA without spaceId)")
129+
if req.ScopeMrn == "" {
130+
logger.Info("Skipping garbage collection: no scope MRN determinable from service account")
134131
return nil
135132
}
136133

137-
logger.Info("Preparing GarbageCollectAssets request", "spaceMrn", req.SpaceMrn, "managedBy", req.ManagedBy)
134+
logger.Info("Preparing GarbageCollectAssets request", "scopeMrn", req.ScopeMrn, "managedBy", req.ManagedBy)
138135

139136
opts := mondooclient.MondooClientOptions{
140137
ApiEndpoint: sa.ApiEndpoint,
@@ -158,20 +155,3 @@ func GarbageCollectAssets(
158155
logger.Info("GarbageCollectAssets completed successfully")
159156
return nil
160157
}
161-
162-
// SpaceMrnFromServiceAccountMrn extracts the space MRN from a service account MRN.
163-
// SA MRN format: //agents.api.mondoo.app/spaces/<space-id>/serviceaccounts/<sa-id>
164-
// Space MRN format: //captain.api.mondoo.app/spaces/<space-id>
165-
func SpaceMrnFromServiceAccountMrn(saMrn string) string {
166-
const spacesSegment = "/spaces/"
167-
idx := strings.Index(saMrn, spacesSegment)
168-
if idx < 0 {
169-
return ""
170-
}
171-
after := saMrn[idx+len(spacesSegment):]
172-
spaceID, _, _ := strings.Cut(after, "/")
173-
if spaceID == "" {
174-
return ""
175-
}
176-
return "//captain.api.mondoo.app/spaces/" + spaceID
177-
}

pkg/utils/mondoo/gc_test.go

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -54,38 +54,3 @@ func TestGCOlderThanFromSchedule(t *testing.T) {
5454
})
5555
}
5656
}
57-
58-
func TestSpaceMrnFromServiceAccountMrn(t *testing.T) {
59-
tests := []struct {
60-
name string
61-
saMrn string
62-
expect string
63-
}{
64-
{
65-
name: "standard SA MRN",
66-
saMrn: "//agents.api.mondoo.app/spaces/abc123/serviceaccounts/sa456",
67-
expect: "//captain.api.mondoo.app/spaces/abc123",
68-
},
69-
{
70-
name: "empty MRN",
71-
saMrn: "",
72-
expect: "",
73-
},
74-
{
75-
name: "no spaces segment",
76-
saMrn: "//agents.api.mondoo.app/organizations/org1",
77-
expect: "",
78-
},
79-
{
80-
name: "spaces segment with no ID after it",
81-
saMrn: "//agents.api.mondoo.app/spaces/",
82-
expect: "",
83-
},
84-
}
85-
86-
for _, tt := range tests {
87-
t.Run(tt.name, func(t *testing.T) {
88-
assert.Equal(t, tt.expect, SpaceMrnFromServiceAccountMrn(tt.saMrn))
89-
})
90-
}
91-
}

0 commit comments

Comments
 (0)