From a8f83eba4c5afa27835289d47650ef37ef4ee4b2 Mon Sep 17 00:00:00 2001 From: Watson Yuuma Sato Date: Tue, 1 Sep 2026 22:07:32 +0200 Subject: [PATCH 1/2] Wait for TailoredProfile to be READY before creating ScanSettingBinding TestPlatformCompliance and TestNodeCompliance create a TailoredProfile and then immediately create a ScanSettingBinding that references it. The compliance-operator's ScanSettingBinding controller can fail to generate a ComplianceSuite when it reconciles a binding whose referenced TailoredProfile has not finished rendering (i.e. has not reached the READY state). When that happens the suite is never created and the test polls for it until it times out: the Compliance Suite 'platform-scan-binding' didn't get to DONE phase: compliancesuites.compliance.openshift.io "platform-scan-binding" not found This is an intermittent, timing-dependent failure: whether the suite is created depends on whether the TailoredProfile happens to be rendered before the binding is reconciled. Wait for each TailoredProfile to reach READY at the end of createTailoredProfile, so no caller can create a ScanSettingBinding against an unrendered profile. The wait fails fast (via backoff.Permanent) with the operator's error message if the profile lands in ERROR. Co-Authored-By: Claude Opus 4.8 --- helpers/utilities.go | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/helpers/utilities.go b/helpers/utilities.go index cbb975e9..ea166d51 100644 --- a/helpers/utilities.go +++ b/helpers/utilities.go @@ -510,6 +510,39 @@ func createTailoredProfile(tc *testConfig.TestConfig, c dynclient.Client, name s return err } log.Printf("Created %s tailored profile with %d rules", name, len(rules)) + + // Wait for the profile to be rendered before returning. The operator's + // ScanSettingBinding controller can fail to generate a ComplianceSuite if it + // reconciles a binding whose referenced TailoredProfile has not reached READY + // yet, so callers must not create the binding before the profile is ready. + return waitForTailoredProfileReady(c, tc, name) +} + +// waitForTailoredProfileReady blocks until the named TailoredProfile reports the +// READY state, failing fast if it lands in ERROR. +func waitForTailoredProfileReady(c dynclient.Client, tc *testConfig.TestConfig, name string) error { + key := types.NamespacedName{Name: name, Namespace: tc.OperatorNamespace.Namespace} + tp := &cmpv1alpha1.TailoredProfile{} + bo := backoff.WithMaxRetries(backoff.NewConstantBackOff(tc.APIPollInterval), 180) + err := backoff.RetryNotify(func() error { + if err := c.Get(goctx.TODO(), key, tp); err != nil { + return err + } + if tp.Status.State == cmpv1alpha1.TailoredProfileStateReady { + return nil + } + if tp.Status.State == cmpv1alpha1.TailoredProfileStateError { + return backoff.Permanent( + fmt.Errorf("TailoredProfile %s is in ERROR state: %s", name, tp.Status.ErrorMessage)) + } + return fmt.Errorf("TailoredProfile %s is not READY yet (state: %q)", name, tp.Status.State) + }, bo, func(err error, d time.Duration) { + log.Printf("Waiting for TailoredProfile %s to be READY after %s: %s", name, d.String(), err) + }) + if err != nil { + return fmt.Errorf("failed waiting for TailoredProfile %s to be READY: %w", name, err) + } + log.Printf("TailoredProfile %s is READY", name) return nil } From 0a6eb76aca3d98e1cc6b0a211bfbe88231b0e510 Mon Sep 17 00:00:00 2001 From: Watson Yuuma Sato Date: Wed, 2 Sep 2026 09:56:50 +0200 Subject: [PATCH 2/2] CMP-4656: Exclude CEL rules from the platform tailored profile The platform TailoredProfile is built from every Platform rule in the ocp4 bundle. Newer content ships CEL-typed platform rules alongside the OpenSCAP ones, and the compliance-operator rejects a TailoredProfile that mixes the two: TailoredProfile cannot mix CEL-typed Rules with OpenSCAP Rules The profile then lands in ERROR, no ComplianceSuite is generated, and the test times out (now surfaced immediately by the READY wait added in the previous commit). Filter out CEL-typed rules in findPlatformRules so the platform tailored profile only contains OpenSCAP rules. CEL checks are Platform-only, so the node rule selection is unaffected. Co-Authored-By: Claude Opus 4.8 --- helpers/utilities.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/helpers/utilities.go b/helpers/utilities.go index ea166d51..20e093d8 100644 --- a/helpers/utilities.go +++ b/helpers/utilities.go @@ -587,7 +587,9 @@ func CreateNodeTailoredProfile(tc *testConfig.TestConfig, c dynclient.Client) er return nil } -// findPlatformRules finds all Rule custom resources of type Platform and returns them. +// findPlatformRules finds all OpenSCAP Rule custom resources of type Platform +// and returns them. CEL-typed rules are excluded because a TailoredProfile +// cannot mix CEL-typed rules with OpenSCAP rules. func findPlatformRules(c dynclient.Client, tc *testConfig.TestConfig) ([]cmpv1alpha1.Rule, error) { ruleList := &cmpv1alpha1.RuleList{} err := c.List(goctx.TODO(), ruleList) @@ -601,7 +603,8 @@ func findPlatformRules(c dynclient.Client, tc *testConfig.TestConfig) ([]cmpv1al // Only include rules from the e2e profile bundle bundleName, exists := ruleList.Items[i].Labels["compliance.openshift.io/profile-bundle"] if exists && bundleName == "ocp4" { - if ruleList.Items[i].CheckType == cmpv1alpha1.CheckTypePlatform { + if ruleList.Items[i].CheckType == cmpv1alpha1.CheckTypePlatform && + ruleList.Items[i].ScannerType != cmpv1alpha1.ScannerTypeCEL { platformRules = append(platformRules, ruleList.Items[i]) } }