Skip to content

CMP-4550: Deliver Variable values to CEL rules, honoring TailoredProfile setValues - #1381

Open
Vincent056 wants to merge 1 commit into
ComplianceAsCode:masterfrom
Vincent056:cmp-4550-cel-setvalues
Open

CMP-4550: Deliver Variable values to CEL rules, honoring TailoredProfile setValues#1381
Vincent056 wants to merge 1 commit into
ComplianceAsCode:masterfrom
Vincent056:cmp-4550-cel-setvalues

Conversation

@Vincent056

@Vincent056 Vincent056 commented Sep 2, 2026

Copy link
Copy Markdown

What

CEL rules can now consume Variable CRs, and a TailoredProfile's setValues override those values per scan. Previously (CMP-4550) the CustomRule admission compiled expressions with inputs-only declarations, so any variable reference was rejected as UNDECLARED_REFERENCE, and setValues were silently inert for CEL scans — a rule keyed to the tailored value evaluated against nothing.

How

Every Variable in the scan namespace is bound under an auto-derived CEL identifier: dashes become underscores, so the Variable ocp4-var-max-pods is referenced in expressions as ocp4_var_max_pods. Kubernetes resource names cannot contain underscores, so the mapping is unambiguous and reversible (the same munging kube-state-metrics applies to label keys for PromQL).

  • Scan time (cel-scanner): buildScanVariables lists the namespace's Variables, overlays the scan-resolved values (TailoredProfile setValues take precedence over the stored value; the Variable CR is never mutated), and feeds the derived bindings through the SDK's existing config.Variables pipe. Each rule is evaluated with the variables filtered against its own input names, so an input wins exactly as admission declared it for that rule. Names that derive to something unusable (digit-led, CEL keywords, CEL standard type names such as list/int that would shadow the type in type(x) == list) are skipped.
  • Admission (celvalidation, CustomRule controller, ProfileBundle CEL parser): the namespace's Variable names are passed into validation, which declares the derived identifiers through the SDK's exported ValidateCELExpressionWithInputs. A rule referencing an existing Variable admits Ready; an unknown identifier is rejected with UNDECLARED_REFERENCE. Bundle CEL rules get the same treatment at parse time (their own variables: list is included), so content can adopt the convention without tripping the parser.
  • Variable watch: the CustomRule controller revalidates the rules whose expression references a changed Variable — a rule created before its Variable becomes Ready once it appears, and a Ready rule whose Variable is deleted is marked Error. A transient failure to list Variables is retried as a reconcile error rather than misreported.

No SDK changes and no CRD schema changes — the delivery uses only existing, exported surfaces.

Testing

  • Unit: 17 new tests — identifier derivation (incl. digit-led and reserved-word rejection), validation accept/reject, input-name precedence, scan-variable assembly (universe, setValues override, setValues-only variables, per-rule input filtering, namespace scoping, CEL type-name exclusion), the controller's revalidation paths (Variable present → Ready, Variable deleted → Error, List failure retried with status untouched), and the targeted Variable-watch mapper.
  • e2e: TestCustomRuleVariableSetValues (parallel lane) — CustomRule referencing a Variable via the derived identifier admits Ready, an unknown identifier admits Error, a rule created before its Variable flips Error → Ready once the Variable is created, the scan passes with the stored value, adding setValues and rescanning flips the same check to FAIL, and the Variable CR is asserted unmutated afterwards.
  • Validated live on a 6-node bare-metal 4.22 cluster with this branch's operator: Ready/Error admission split, DONE COMPLIANT at the stored value (100000), DONE NON-COMPLIANT with the check FAIL after setValues: "0", Variable CR still 100000 afterwards.

Notes

  • Identifiers derive from the Variable CR name, which the profileparser prefixes with the ProfileBundle name (ocp4-var-xocp4_var_x). This is the same coupling TailoredProfile.setValues already has (it names the prefixed CR too); a bundle-independent derivation would need a collision policy for two bundles of the same content and is left for the declarations follow-up.
  • SDK follow-up (not blocking): the SDK's compile-error diagnostics (getDetailedCompilationError) re-validate with inputs only, so a rule whose input fetch fails is diagnosed as an undeclared reference to its (existing, bound) variable. The verdict is right, the message is misleading; fix belongs in the SDK.
  • Pre-existing, unchanged: expression-less CustomRules fail validation with a SYNTAX_ERROR (manual CustomRules were never reachable); the parse-time issue rendering in celvalidation mirrors the SDK's CompileCELExpression until the SDK exports a declaration-taking variant.
  • pkg/profileparser/manual-bundle.yaml is added to .gitignore: cel_content_test.go writes it into the package directory on every run (Ginkgo v1's TempDir() is a no-op), which is how it nearly slipped into this PR.
  • Follow-ups under CMP-4550 (all additive): an explicit per-rule variables: declaration tier (chosen short names, defaults, bundle-portable content rules, typed binding) and a reserved vars map for existence checks / in-expression defaults.

🤖 Generated with Claude Code

@openshift-ci-robot

Copy link
Copy Markdown
Collaborator

@Vincent056: This pull request references CMP-4550 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the bug to target either version "5.1.0." or "openshift-5.1.0.", but it targets "compliance-operator-1.10.0" instead.

Details

In response to this:

What

CEL rules can now consume Variable CRs, and a TailoredProfile's setValues override those values per scan. Previously (CMP-4550) the CustomRule admission compiled expressions with inputs-only declarations, so any variable reference was rejected as UNDECLARED_REFERENCE, and setValues were silently inert for CEL scans — a rule keyed to the tailored value evaluated against nothing.

How

Every Variable in the scan namespace is bound under an auto-derived CEL identifier: dashes become underscores, so the Variable ocp4-var-max-pods is referenced in expressions as ocp4_var_max_pods. Kubernetes resource names cannot contain underscores, so the mapping is unambiguous and reversible (the same munging kube-state-metrics applies to label keys for PromQL).

  • Scan time (cel-scanner): buildScanVariables lists the namespace's Variables, overlays the scan-resolved values (TailoredProfile setValues take precedence over the stored value; the Variable CR is never mutated), and feeds the derived bindings through the SDK's existing config.Variables pipe. Names that derive to something unusable (digit-led, CEL reserved words) are skipped, and an input name wins over a variable identifier — enforced scan-wide so no rule can hit an overlapping-identifier failure at environment creation.
  • Admission (celvalidation + CustomRule controller): the controller passes the namespace's Variable names into validation, which declares the derived identifiers through the SDK's exported ValidateCELExpressionWithInputs. A rule referencing an existing Variable admits Ready; an unknown identifier is rejected with UNDECLARED_REFERENCE.

No SDK changes and no CRD schema changes — the delivery uses only existing, exported surfaces.

Testing

  • Unit: 11 new tests — identifier derivation (incl. digit-led and reserved-word rejection), validation accept/reject, input-name precedence, scan-variable assembly (universe, setValues override, setValues-only variables, scan-wide input exclusion, dedupe).
  • e2e: TestCustomRuleVariableSetValues (parallel lane) — CustomRule referencing a Variable via the derived identifier admits Ready, an unknown identifier admits Error, the scan passes with the stored value, adding setValues and rescanning flips the same check to FAIL, and the Variable CR is asserted unmutated afterwards.
  • Validated live on a 6-node bare-metal 4.22 cluster with this branch's operator: Ready/Error admission split, DONE COMPLIANT at the stored value (100000), DONE NON-COMPLIANT with the check FAIL after setValues: "0", Variable CR still 100000 afterwards.

Notes

  • Rules created before their Variable exists go to Error until re-reconciled; a Variable-watch requeue on the CustomRule controller is a planned follow-up.
  • Follow-ups under CMP-4550 (all additive): an explicit per-rule variables: declaration tier (chosen short names, defaults, bundle-portable content rules, typed binding) and a reserved vars map for existence checks / in-expression defaults.

🤖 Generated with Claude Code

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci

openshift-ci Bot commented Sep 2, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: Vincent056

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci Bot added the approved label Sep 2, 2026
@Vincent056
Vincent056 force-pushed the cmp-4550-cel-setvalues branch from a170ebd to ce1e830 Compare September 2, 2026 04:32
@Vincent056 Vincent056 added this to the 1.10.0 milestone Sep 2, 2026
@Vincent056
Vincent056 force-pushed the cmp-4550-cel-setvalues branch from ce1e830 to 1e1cb9b Compare September 2, 2026 04:42
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown

🤖 To deploy this PR, run the following command:

make catalog-deploy CATALOG_IMG=ghcr.io/complianceascode/compliance-operator-catalog:1381-1e1cb9b00f3a7a5f5cee330d89df5c1f5303020e

@Vincent056
Vincent056 force-pushed the cmp-4550-cel-setvalues branch 2 times, most recently from 52146ce to e3148c5 Compare September 2, 2026 05:46
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown

🤖 To deploy this PR, run the following command:

make catalog-deploy CATALOG_IMG=ghcr.io/complianceascode/compliance-operator-catalog:1381-52146ced1dc61960ad89819acb29595cc936a144

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown

🤖 To deploy this PR, run the following command:

make catalog-deploy CATALOG_IMG=ghcr.io/complianceascode/compliance-operator-catalog:1381-e3148c51c6f931e97184facebf732c8ef2acf31a

@yuumasato yuumasato modified the milestones: 1.10.0, 1.11.0 Sep 2, 2026
…ile setValues

CEL rules could not consume Variable CRs: admission compiled expressions
with inputs-only declarations, so any variable reference was rejected as
UNDECLARED_REFERENCE, and a TailoredProfile's setValues were silently
inert for CEL scans.

Bind every Variable in the scan namespace under an auto-derived CEL
identifier: dashes become underscores (ocp4-var-max-pods is referenced
as ocp4_var_max_pods). Kubernetes names cannot contain underscores, so
the mapping is unambiguous and reversible. Values resolve per scan with
setValues taking precedence over the stored Variable value, and the
Variable CR is never mutated. Each rule is evaluated with the variables
filtered against its own input names, so an input always wins exactly
the way admission validation declares it. CEL keywords and standard type
names are never used as identifiers.

Validation gains the same knowledge through the SDK's exported
validator: the CustomRule controller and the ProfileBundle CEL parser
pass the namespace's Variable names, so a rule referencing a Variable
that exists admits Ready while an unknown identifier is rejected with
UNDECLARED_REFERENCE. The CustomRule controller watches Variables and
revalidates the rules whose expression references one — a rule created
before its Variable becomes Ready once it appears, and a rule whose
Variable disappears is marked Error — and a transient Variable listing
failure is retried instead of being reported as an unknown identifier.
No SDK or CRD schema changes.

Adds unit coverage for the derivation, validation, per-rule variable
filtering, controller revalidation paths, and the Variable watch mapper,
plus an e2e test covering the full flow including the setValues
override, the negative admission case, and late Variable creation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Vincent056
Vincent056 force-pushed the cmp-4550-cel-setvalues branch from e3148c5 to 7d20e68 Compare September 3, 2026 15:17
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

🤖 To deploy this PR, run the following command:

make catalog-deploy CATALOG_IMG=ghcr.io/complianceascode/compliance-operator-catalog:1381-7d20e6867e552e136c0107cd6cd16c00a3e704fa

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants