diff --git a/controllers/status/operator_status.go b/controllers/status/operator_status.go index 135a15090..8f2941390 100644 --- a/controllers/status/operator_status.go +++ b/controllers/status/operator_status.go @@ -46,7 +46,9 @@ type MondooAuditConfig struct { } func ReportStatusRequestFromAuditConfig( - ctx context.Context, integrationMrn string, m v1alpha2.MondooAuditConfig, nodes []v1.Node, k8sVersion *k8sversion.Info, containerImageResolver mondoo.ContainerImageResolver, log logr.Logger, + ctx context.Context, integrationMrn string, m v1alpha2.MondooAuditConfig, + nodes []v1.Node, k8sVersion *k8sversion.Info, containerImageResolver mondoo.ContainerImageResolver, + skipContainerResolution bool, log logr.Logger, ) mondooclient.ReportStatusRequest { nodeNames := make([]string, len(nodes)) for i := range nodes { @@ -169,7 +171,7 @@ func ReportStatusRequestFromAuditConfig( // Resolve cnspec and operator images to get version and digest var cnspecVersion, cnspecImageDigest, operatorImageDigest string if containerImageResolver != nil { - resolvedImage, err := containerImageResolver.CnspecImage(m.Spec.Scanner.Image.Name, m.Spec.Scanner.Image.Tag, m.Spec.Scanner.Image.Digest, false) + resolvedImage, err := containerImageResolver.CnspecImage(m.Spec.Scanner.Image.Name, m.Spec.Scanner.Image.Tag, m.Spec.Scanner.Image.Digest, skipContainerResolution) if err != nil { log.Error(err, "Failed to resolve cnspec image for status reporting") } else { @@ -185,7 +187,7 @@ func ReportStatusRequestFromAuditConfig( } // Resolve operator image digest - resolvedOperator, err := containerImageResolver.MondooOperatorImage(ctx, "", "", "", false) + resolvedOperator, err := containerImageResolver.MondooOperatorImage(ctx, "", "", "", skipContainerResolution) if err != nil { log.Error(err, "Failed to resolve operator image for status reporting") } else { diff --git a/controllers/status/operator_status_test.go b/controllers/status/operator_status_test.go index 73c78d5f6..8c00172ea 100644 --- a/controllers/status/operator_status_test.go +++ b/controllers/status/operator_status_test.go @@ -16,6 +16,7 @@ import ( "go.mondoo.com/mondoo-operator/api/v1alpha2" "go.mondoo.com/mondoo-operator/pkg/client/mondooclient" + mondooutils "go.mondoo.com/mondoo-operator/pkg/utils/mondoo" "go.mondoo.com/mondoo-operator/pkg/version" "go.mondoo.com/mondoo-operator/tests/framework/utils" ) @@ -30,7 +31,7 @@ func TestReportStatusRequestFromAuditConfig_AllDisabled(t *testing.T) { v := &k8sversion.Info{GitVersion: "v1.24.0"} m := testMondooAuditConfig() - reportStatus := ReportStatusRequestFromAuditConfig(context.Background(), integrationMrn, m, nodes, v, nil, logger) + reportStatus := ReportStatusRequestFromAuditConfig(context.Background(), integrationMrn, m, nodes, v, nil, false, logger) assert.Equal(t, integrationMrn, reportStatus.Mrn) assert.Equal(t, mondooclient.Status_ACTIVE, reportStatus.Status) assert.Equal(t, OperatorCustomState{ @@ -74,7 +75,7 @@ func TestReportStatusRequestFromAuditConfig_AllEnabled(t *testing.T) { {Message: "Mondoo Operator controller is available", Status: v1.ConditionFalse, Type: v1alpha2.MondooOperatorDegraded}, } - reportStatus := ReportStatusRequestFromAuditConfig(context.Background(), integrationMrn, m, nodes, v, nil, logger) + reportStatus := ReportStatusRequestFromAuditConfig(context.Background(), integrationMrn, m, nodes, v, nil, false, logger) assert.Equal(t, integrationMrn, reportStatus.Mrn) assert.Equal(t, mondooclient.Status_ACTIVE, reportStatus.Status) assert.Equal(t, OperatorCustomState{ @@ -126,7 +127,7 @@ func TestReportStatusRequestFromAuditConfig_AllEnabled_DeprecatedFields(t *testi {Message: "Mondoo Operator controller is available", Status: v1.ConditionFalse, Type: v1alpha2.MondooOperatorDegraded}, } - reportStatus := ReportStatusRequestFromAuditConfig(context.Background(), integrationMrn, m, nodes, v, nil, logger) + reportStatus := ReportStatusRequestFromAuditConfig(context.Background(), integrationMrn, m, nodes, v, nil, false, logger) assert.Equal(t, integrationMrn, reportStatus.Mrn) assert.Equal(t, mondooclient.Status_ACTIVE, reportStatus.Status) assert.Equal(t, OperatorCustomState{ @@ -174,7 +175,7 @@ func TestReportStatusRequestFromAuditConfig_AllError(t *testing.T) { {Message: "Mondoo Operator controller is unavailable", Status: v1.ConditionTrue, Type: v1alpha2.MondooOperatorDegraded}, } - reportStatus := ReportStatusRequestFromAuditConfig(context.Background(), integrationMrn, m, nodes, v, nil, logger) + reportStatus := ReportStatusRequestFromAuditConfig(context.Background(), integrationMrn, m, nodes, v, nil, false, logger) assert.Equal(t, integrationMrn, reportStatus.Mrn) assert.Equal(t, mondooclient.Status_ERROR, reportStatus.Status) assert.Equal(t, OperatorCustomState{ @@ -196,6 +197,72 @@ func TestReportStatusRequestFromAuditConfig_AllError(t *testing.T) { assert.ElementsMatch(t, messages, reportStatus.Messages.Messages) } +type mockContainerImageResolver struct { + cnspecSkipValues []bool + operatorSkipValues []bool +} + +func (m *mockContainerImageResolver) CnspecImage(_, _, _ string, skipImageResolution bool) (string, error) { + m.cnspecSkipValues = append(m.cnspecSkipValues, skipImageResolution) + if skipImageResolution { + return "ghcr.io/mondoohq/mondoo-operator/cnspec:11-rootless", nil + } + return "ghcr.io/mondoohq/mondoo-operator/cnspec@sha256:abc123", nil +} + +func (m *mockContainerImageResolver) MondooOperatorImage(_ context.Context, _, _, _ string, skipImageResolution bool) (string, error) { + m.operatorSkipValues = append(m.operatorSkipValues, skipImageResolution) + if skipImageResolution { + return "ghcr.io/mondoohq/mondoo-operator:latest", nil + } + return "ghcr.io/mondoohq/mondoo-operator@sha256:def456", nil +} + +func (m *mockContainerImageResolver) WithImageRegistry(_ string) mondooutils.ContainerImageResolver { + return m +} + +func (m *mockContainerImageResolver) WithRegistryMirrors(_ map[string]string) mondooutils.ContainerImageResolver { + return m +} + +func (m *mockContainerImageResolver) WithImagePullSecrets(_ []v1.LocalObjectReference) mondooutils.ContainerImageResolver { + return m +} + +func TestReportStatusRequestFromAuditConfig_SkipContainerResolution(t *testing.T) { + logger := logr.Logger{} + integrationMrn := utils.RandString(10) + nodes := []v1.Node{{ObjectMeta: metav1.ObjectMeta{Name: "node1"}}} + v := &k8sversion.Info{GitVersion: "v1.24.0"} + m := testMondooAuditConfig() + + t.Run("skipContainerResolution=false resolves digests", func(t *testing.T) { + resolver := &mockContainerImageResolver{} + reportStatus := ReportStatusRequestFromAuditConfig(context.Background(), integrationMrn, m, nodes, v, resolver, false, logger) + + state := reportStatus.LastState.(OperatorCustomState) + assert.Equal(t, "sha256:abc123", state.CnspecImageDigest) + assert.Equal(t, "sha256:def456", state.OperatorImageDigest) + // CnspecImage called twice: once for digest, once for tag (always skip=true) + assert.Equal(t, []bool{false, true}, resolver.cnspecSkipValues) + assert.Equal(t, []bool{false}, resolver.operatorSkipValues) + }) + + t.Run("skipContainerResolution=true skips digest resolution", func(t *testing.T) { + resolver := &mockContainerImageResolver{} + reportStatus := ReportStatusRequestFromAuditConfig(context.Background(), integrationMrn, m, nodes, v, resolver, true, logger) + + state := reportStatus.LastState.(OperatorCustomState) + assert.Empty(t, state.CnspecImageDigest) + assert.Empty(t, state.OperatorImageDigest) + assert.Equal(t, "11-rootless", state.CnspecVersion) + // Both calls should pass skipImageResolution=true + assert.Equal(t, []bool{true, true}, resolver.cnspecSkipValues) + assert.Equal(t, []bool{true}, resolver.operatorSkipValues) + }) +} + func testMondooAuditConfig() v1alpha2.MondooAuditConfig { return v1alpha2.MondooAuditConfig{ ObjectMeta: metav1.ObjectMeta{ diff --git a/controllers/status/status_reporter.go b/controllers/status/status_reporter.go index 835d40e98..13da460ae 100644 --- a/controllers/status/status_reporter.go +++ b/controllers/status/status_reporter.go @@ -58,7 +58,7 @@ func (r *StatusReporter) Report(ctx context.Context, m v1alpha2.MondooAuditConfi return err } - operatorStatus := ReportStatusRequestFromAuditConfig(ctx, integrationMrn, m, nodes.Items, r.k8sVersion, r.containerImageResolver, logger) + operatorStatus := ReportStatusRequestFromAuditConfig(ctx, integrationMrn, m, nodes.Items, r.k8sVersion, r.containerImageResolver, cfg.Spec.SkipContainerResolution, logger) r.mu.RLock() statusUnchanged := reflect.DeepEqual(operatorStatus, r.lastReportedStatus)