Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 16 additions & 9 deletions cmd/mondoo-operator/garbage_collect/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ func init() {
filterPlatformRuntime := Cmd.Flags().String("filter-platform-runtime", "", "Cleanup assets by an asset's PlatformRuntime (k8s-cluster or docker-image).")
Comment thread
mondoo-code-review[bot] marked this conversation as resolved.
filterManagedBy := Cmd.Flags().String("filter-managed-by", "", "Cleanup assets with matching ManagedBy field.")
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).")
spaceMrnOverride := Cmd.Flags().String("space-mrn", "", "Override the space MRN for garbage collection (used when spaceId is set in MondooAuditConfig).")
scopeMrnOverride := Cmd.Flags().String("scope-mrn", "", "Override the scope MRN (space or org) for garbage collection.")
Cmd.Flags().String("space-mrn", "", "Deprecated: use --scope-mrn instead.")
_ = Cmd.Flags().MarkDeprecated("space-mrn", "use --scope-mrn instead")
Cmd.RunE = func(cmd *cobra.Command, args []string) error {
log.SetLogger(logger.NewLogger())
logger := log.Log.WithName("garbage-collect")
Expand Down Expand Up @@ -78,20 +80,25 @@ func init() {
return fmt.Errorf("no filters provided to garbage collect by")
}

spaceMrn := *spaceMrnOverride
if spaceMrn == "" {
spaceMrn = serviceAccount.SpaceMrn
if spaceMrn == "" {
spaceMrn = mondoo.SpaceMrnFromServiceAccountMrn(serviceAccount.Mrn)
scopeMrn := *scopeMrnOverride
if scopeMrn == "" {
if spaceMrn, _ := cmd.Flags().GetString("space-mrn"); spaceMrn != "" {
scopeMrn = spaceMrn
}
}
return GarbageCollectCmd(ctx, client, spaceMrn, *filterPlatformRuntime, *filterOlderThan, *filterManagedBy, logger)
if scopeMrn == "" {
scopeMrn = serviceAccount.ScopeMrn
}
if scopeMrn == "" {
scopeMrn = serviceAccount.SpaceMrn
}
return GarbageCollectCmd(ctx, client, scopeMrn, *filterPlatformRuntime, *filterOlderThan, *filterManagedBy, logger)
}
}

func GarbageCollectCmd(ctx context.Context, client mondooclient.MondooClient, spaceMrn, platformRuntime, olderThan, managedBy string, logger logr.Logger) error {
func GarbageCollectCmd(ctx context.Context, client mondooclient.MondooClient, scopeMrn, platformRuntime, olderThan, managedBy string, logger logr.Logger) error {
req := &mondooclient.GarbageCollectAssetsRequest{
SpaceMrn: spaceMrn,
ScopeMrn: scopeMrn,
ManagedBy: managedBy,
}

Expand Down
1 change: 1 addition & 0 deletions controllers/k8s_scan/deployment_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1683,6 +1683,7 @@ func (s *DeploymentHandlerSuite) createDeploymentHandlerWithGCMock(gcFunc func(c
key := credentials.MondooServiceAccount(s.T())
mockSA := mondooclient.ServiceAccountCredentials{
Mrn: "//agents.api.mondoo.app/spaces/test/serviceaccounts/test",
ScopeMrn: "//captain.api.mondoo.app/spaces/test",
PrivateKey: key,
ApiEndpoint: "https://us.api.mondoo.com",
}
Expand Down
1 change: 1 addition & 0 deletions controllers/nodes/deployment_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -899,6 +899,7 @@ func (s *DeploymentHandlerSuite) createDeploymentHandlerWithGCMock(gcFunc func(c
key := generateTestPrivateKey(s.T())
mockSA := mondooclient.ServiceAccountCredentials{
Mrn: "//agents.api.mondoo.app/spaces/test/serviceaccounts/test",
ScopeMrn: "//captain.api.mondoo.app/spaces/test",
PrivateKey: key,
ApiEndpoint: "https://us.api.mondoo.com",
}
Expand Down
20 changes: 12 additions & 8 deletions pkg/client/mondooclient/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ type ServiceAccountCredentials struct {
PrivateKey string `protobuf:"bytes,3,opt,name=private_key,json=privateKey,proto3" json:"private_key,omitempty"` //nolint:gosec
Certificate string `protobuf:"bytes,4,opt,name=certificate,proto3" json:"certificate,omitempty"`
ApiEndpoint string `protobuf:"bytes,5,opt,name=api_endpoint,json=apiEndpoint,proto3" json:"api_endpoint,omitempty"`
ScopeMrn string `json:"scope_mrn,omitempty"`
Comment thread
mondoo-code-review[bot] marked this conversation as resolved.
}
type IntegrationCheckInInput struct {
// Mrn should hold the MRN of the integration that is having the CheckIn() called for
Expand Down Expand Up @@ -119,17 +120,20 @@ const (
// GarbageCollectAssetsRequest matches the server-side PurgeAssetsRequest proto
// on the PolicyResolver service (/PolicyResolver/PurgeAssets).
type GarbageCollectAssetsRequest struct {
SpaceMrn string `json:"spaceMrn,omitempty"`
DateFilter *DateFilter `json:"date_filter,omitempty"`
ManagedBy string `json:"managed_by,omitempty"`
PlatformRuntime string `json:"platform_runtime,omitempty"` // optional filter (k8s-cluster, docker-image, etc.)
Labels map[string]string `json:"labels,omitempty"`
SpaceMrn string `protobuf:"bytes,1,opt,name=spaceMrn,proto3" json:"spaceMrn,omitempty"` // Deprecated: use ScopeMrn
AssetMrns []string `protobuf:"bytes,2,rep,name=asset_mrns,json=assetMrns,proto3" json:"asset_mrns,omitempty"`
PurgeAll bool `protobuf:"varint,3,opt,name=purge_all,json=purgeAll,proto3" json:"purge_all,omitempty"`
DateFilter *DateFilter `protobuf:"bytes,4,opt,name=date_filter,json=dateFilter,proto3" json:"date_filter,omitempty"`
ManagedBy string `protobuf:"bytes,5,opt,name=managed_by,json=managedBy,proto3" json:"managed_by,omitempty"`
PlatformRuntime string `protobuf:"bytes,6,opt,name=platform_runtime,json=platformRuntime,proto3" json:"platform_runtime,omitempty"`
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"`
ScopeMrn string `protobuf:"bytes,8,opt,name=scope_mrn,json=scopeMrn,proto3" json:"scope_mrn,omitempty"`
}

type DateFilter struct {
Timestamp string `json:"timestamp,omitempty"` // RFC3339
Comparison Comparison `json:"comparison"`
Field DateFilterField `json:"field"`
Timestamp string `protobuf:"bytes,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
Comparison Comparison `protobuf:"varint,2,opt,name=comparison,proto3,enum=cnspec.policy.v1.Comparison" json:"comparison,omitempty"`
Field DateFilterField `protobuf:"varint,3,opt,name=field,proto3,enum=cnspec.policy.v1.DateFilterField" json:"field,omitempty"`
}

type Comparison int32
Expand Down
36 changes: 8 additions & 28 deletions pkg/utils/mondoo/gc.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"context"
"fmt"
"os"
"strings"
"time"

"github.com/go-logr/logr"
Expand Down Expand Up @@ -113,28 +112,26 @@ func GarbageCollectAssets(
return fmt.Errorf("failed to generate token: %w", err)
}

// Use spaceId override from MondooAuditConfig if set, otherwise derive from SA credentials.
// Use spaceId override from MondooAuditConfig if set, otherwise use scope from SA credentials.
if spaceMrn := k8s.SpaceMrnForAuditConfig(*mondoo); spaceMrn != "" {
req.SpaceMrn = spaceMrn
req.ScopeMrn = spaceMrn
if saSpaceMrn := sa.SpaceMrn; saSpaceMrn != "" && saSpaceMrn != spaceMrn {
logger.V(1).Info("spaceId override targets a different space than the service account",
"saSpaceMrn", saSpaceMrn, "targetSpaceMrn", spaceMrn)
}
} else {
req.SpaceMrn = sa.SpaceMrn
if req.SpaceMrn == "" {
req.SpaceMrn = SpaceMrnFromServiceAccountMrn(sa.Mrn)
req.ScopeMrn = sa.ScopeMrn
if req.ScopeMrn == "" {
req.ScopeMrn = sa.SpaceMrn
}
Comment thread
mondoo-code-review[bot] marked this conversation as resolved.
}

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

logger.Info("Preparing GarbageCollectAssets request", "spaceMrn", req.SpaceMrn, "managedBy", req.ManagedBy)
logger.Info("Preparing GarbageCollectAssets request", "scopeMrn", req.ScopeMrn, "managedBy", req.ManagedBy)

opts := mondooclient.MondooClientOptions{
ApiEndpoint: sa.ApiEndpoint,
Expand All @@ -158,20 +155,3 @@ func GarbageCollectAssets(
logger.Info("GarbageCollectAssets completed successfully")
return nil
}

// SpaceMrnFromServiceAccountMrn extracts the space MRN from a service account MRN.
// SA MRN format: //agents.api.mondoo.app/spaces/<space-id>/serviceaccounts/<sa-id>
// Space MRN format: //captain.api.mondoo.app/spaces/<space-id>
func SpaceMrnFromServiceAccountMrn(saMrn string) string {
const spacesSegment = "/spaces/"
idx := strings.Index(saMrn, spacesSegment)
if idx < 0 {
return ""
}
after := saMrn[idx+len(spacesSegment):]
spaceID, _, _ := strings.Cut(after, "/")
if spaceID == "" {
return ""
}
return "//captain.api.mondoo.app/spaces/" + spaceID
}
35 changes: 0 additions & 35 deletions pkg/utils/mondoo/gc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,38 +54,3 @@ func TestGCOlderThanFromSchedule(t *testing.T) {
})
}
}

func TestSpaceMrnFromServiceAccountMrn(t *testing.T) {
tests := []struct {
name string
saMrn string
expect string
}{
{
name: "standard SA MRN",
saMrn: "//agents.api.mondoo.app/spaces/abc123/serviceaccounts/sa456",
expect: "//captain.api.mondoo.app/spaces/abc123",
},
{
name: "empty MRN",
saMrn: "",
expect: "",
},
{
name: "no spaces segment",
saMrn: "//agents.api.mondoo.app/organizations/org1",
expect: "",
},
{
name: "spaces segment with no ID after it",
saMrn: "//agents.api.mondoo.app/spaces/",
expect: "",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.expect, SpaceMrnFromServiceAccountMrn(tt.saMrn))
})
}
}
Loading