Skip to content

feat: Add CEL-based conditional function execution (#4388)#4469

Open
SurbhiAgarwal1 wants to merge 1 commit intokptdev:mainfrom
SurbhiAgarwal1:feat/cel-clean-v2
Open

feat: Add CEL-based conditional function execution (#4388)#4469
SurbhiAgarwal1 wants to merge 1 commit intokptdev:mainfrom
SurbhiAgarwal1:feat/cel-clean-v2

Conversation

@SurbhiAgarwal1
Copy link
Copy Markdown
Contributor

@SurbhiAgarwal1 SurbhiAgarwal1 commented Apr 7, 2026

Description

This PR adds CEL-based conditional function execution to kpt, implementing #4388.

A new optional condition field is added to the Function type in the Kptfile pipeline. When specified, the CEL expression is evaluated against the current list of KRM resources. If the expression returns false, the function is skipped. If omitted or returns true, the function executes normally.

Motivation

In many real-world scenarios, you want a pipeline function to run only when certain resources exist or meet specific criteria. Without this feature, users had to maintain separate Kptfiles or manually manage which functions run. The condition field makes pipelines more dynamic and reduces the need for workarounds.

Changes

  • Added condition field to Function type in pkg/api/kptfile/v1/types.go
  • Added CELEnvironment in pkg/lib/runneroptions/celenv.go using google/cel-go
  • Integrated condition check in FunctionRunner.Filter() in internal/fnruntime/runner.go
  • Functions skipped due to condition show [SKIPPED] in CLI output
  • Added InitCELEnvironment() method to RunnerOptions for proper error handling
  • Updated all callers of InitDefaults() to also call InitCELEnvironment()
  • Added E2E testdata for condition-met and condition-not-met cases
  • Added unit tests for CEL evaluation covering all 3 runtimes (builtin, exec, container)
  • Updated documentation: kptfile schema reference and book/04-using-functions

Example Usage

pipeline:
  mutators:
    - image: ghcr.io/kptdev/krm-functions-catalog/set-namespace:v0.4
      condition: "resources.exists(r, r.kind == 'ConfigMap' && r.metadata.name == 'env-config')"
      configMap:
        namespace: production

Copilot AI review requested due to automatic review settings April 7, 2026 18:29
@netlify
Copy link
Copy Markdown

netlify Bot commented Apr 7, 2026

Deploy Preview for kptdocs ready!

Name Link
🔨 Latest commit 0457465
🔍 Latest deploy log https://app.netlify.com/projects/kptdocs/deploys/69eca2d298e5500008a02435
😎 Deploy Preview https://deploy-preview-4469--kptdocs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@dosubot dosubot Bot added size:XL This PR changes 500-999 lines, ignoring generated files. documentation Improvements or additions to documentation enhancement New feature or request go Pull requests that update Go code labels Apr 7, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@liamfallon
Copy link
Copy Markdown
Contributor

Comment from #4391:

Closing this PR in favor of a clean rebase. The branch had accumulated 61 commits including upstream commits from other contributors, making it messy to review.

All the feedback from this review has been addressed in the new PR which has a single clean commit:

Removed unnecessary type alias file (celeval.go)
Grouped constants in const() block
Replaced interface{} with any
Removed panic from InitDefaults, added InitCELEnvironment() error method
Added e2e tests for condition-met and condition-not-met
Added immutability test
Added documentation (kptfile schema + book/04-using-functions)
Removed all unwanted files (krm-functions-catalog, output.txt, etc.)
Fixed diff.patch for renderStatus field

Thank you all for the thorough review!

Copilot AI review requested due to automatic review settings April 8, 2026 16:58
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 31 out of 32 changed files in this pull request and generated 6 comments.

Comments suppressed due to low confidence (1)

thirdparty/kyaml/runfn/runfn.go:1

  • NewFunctionRunner’s error is discarded and SetCondition is called without validating opts.CELEnvironment. This can both (a) mask construction failures and (b) silently ignore the condition at runtime when CELEnvironment is nil (since Filter() only evaluates conditions when celEnv != nil). Handle the err from NewFunctionRunner, and if r.Condition is set, return an error when opts.CELEnvironment is nil (or make SetCondition return an error and enforce it here).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/util/render/executor.go Outdated
Comment thread pkg/lib/runneroptions/runneroptions.go Outdated
Comment thread commands/fn/render/cmdrender.go
Comment thread documentation/content/en/book/04-using-functions/_index.md Outdated
Comment thread e2e/testdata/live-apply/json-output/config.yaml
Comment thread e2e/testdata/live-apply/apply-depends-on/config.yaml
Copilot AI review requested due to automatic review settings April 8, 2026 18:02
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 29 out of 30 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (1)

documentation/content/en/book/04-using-functions/_index.md:1

  • Two doc mismatches with the implementation: (1) the CEL resource map normalization in resourceToMap guarantees apiVersion, kind, and metadata keys, but not spec/status—either update the docs or ensure those keys are present; (2) the skipped example says “Successfully executed 1 function(s)” but the new behavior and e2e expectations indicate skipped functions should not count as executed (so this should be 0).
---

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread thirdparty/kyaml/runfn/runfn.go
Comment thread pkg/fn/runtime/runner.go
Comment thread pkg/fn/runtime/runner.go
Comment thread documentation/content/en/book/04-using-functions/_index.md Outdated
@liamfallon
Copy link
Copy Markdown
Contributor

liamfallon commented Apr 8, 2026

@SurbhiAgarwal1

The tests are failing because we have merged the conditions and renederstatus to kpt recently. I thought that by deleting the diff.patch files, the comparison would just check the output and the tests would pass. However we also need to add the difference in the Kprfile after render to the diff.patch files. Now that I deleted the diff.patch files in the PR, I can't see any option to restore them on the Github GUI.

The following `diff.patch files work for me on the tests locally on my machine:

e2e/testdata/fn-render/condition/condition-met/.expected/diff.patch

diff --git a/Kptfile b/Kptfile
index eb90ac3..d305dc0 100644
--- a/Kptfile
+++ b/Kptfile
@@ -5,4 +5,14 @@ metadata:
 pipeline:
   mutators:
     - image: ghcr.io/kptdev/krm-functions-catalog/no-op
-      condition: "resources.exists(r, r.kind == 'ConfigMap' && r.metadata.name == 'app-config')"
+      condition: resources.exists(r, r.kind == 'ConfigMap' && r.metadata.name == 'app-config')
+status:
+  conditions:
+    - type: Rendered
+      status: "True"
+      reason: RenderSuccess
+  renderStatus:
+    mutationSteps:
+      - image: ghcr.io/kptdev/krm-functions-catalog/no-op
+        exitCode: 0

e2e/testdata/fn-render/condition/condition-not-met/.expected/diff.patch

diff --git a/Kptfile b/Kptfile
index eb90ac3..b055f32 100644
--- a/Kptfile
+++ b/Kptfile
@@ -5,4 +5,14 @@ metadata:
 pipeline:
   mutators:
     - image: ghcr.io/kptdev/krm-functions-catalog/no-op
-      condition: "resources.exists(r, r.kind == 'ConfigMap' && r.metadata.name == 'app-config')"
+      condition: resources.exists(r, r.kind == 'ConfigMap' && r.metadata.name == 'app-config')
+status:
+  conditions:
+    - type: Rendered
+      status: "True"
+      reason: RenderSuccess
+  renderStatus:
+    mutationSteps:
+      - image: ghcr.io/kptdev/krm-functions-catalog/no-op
+        exitCode: 0
+        skipped: true

Can you try restoring the two diff.patch files with the contents above and see if that fixes the tests?

Sorry for mucking with your PR 😢

@SurbhiAgarwal1
Copy link
Copy Markdown
Contributor Author

Thank you @liamfallon! I've restored both diff.patch files with exactly the contents you provided in commit df1189f. The tests should pass now. Sorry for the confusion!

Copilot AI review requested due to automatic review settings April 8, 2026 19:04
@SurbhiAgarwal1 SurbhiAgarwal1 force-pushed the feat/cel-clean-v2 branch 2 times, most recently from 298cc5d to 6d4aed1 Compare April 8, 2026 23:04
@SurbhiAgarwal1
Copy link
Copy Markdown
Contributor Author

Got it, makes sense. Thanks!
Yeah I noticed the text comparison was causing these issues. Glad it’s passing now.
Appreciate the help on this — I’ll keep it in mind if I touch similar tests later.

@liamfallon
Copy link
Copy Markdown
Contributor

If you have time to look at the copilot comments that would be great. We can move to formally review and merge the PR then.

Copy link
Copy Markdown
Contributor

@liamfallon liamfallon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me.

Could you have a look at that last copilot comment and see if anything needs to be done?

Copy link
Copy Markdown
Contributor

@nagygergo nagygergo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Thanks for the contribution @SurbhiAgarwal1

+ renderStatus:
+ mutationSteps:
+ - image: ghcr.io/kptdev/krm-functions-catalog/no-op:latest
+ exitCode: 0
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would also be worth capturing the condition field in status.renderStatus, so the skip condition is recorded alongside the render result.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @nagygergo and @aravindtga - the render condition should reflect the overall pipeline result, not individual function execution. Since the pipeline completed successfully (just with some functions skipped), keeping status: "True" makes sense.

I can update the reason to RenderedWithSkippedFunctions when at least one function was skipped, so consumers can distinguish between a fully executed render and one with skipped functions. I can also add the condition field to status.renderStatus so the skip reason is recorded alongside the result.

Let me know if you'd like me to go ahead with these changes

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would also be worth capturing the condition field in status.renderStatus, so the skip condition is recorded alongside the render result.

A condition field has been introduced in the mutation pipeline configuration:

mutators:
  - image: ghcr.io/kptdev/krm-functions-catalog/no-op
    condition: resources.exists(r, r.kind == 'ConfigMap' && r.metadata.name == 'app-config')

Can you also add the condition field to status.renderStatus.mutationSteps so it gets recorded when the pipeline execution is captured? e.g:

status:
  conditions:
    - type: Rendered
      status: "True"
      reason: RenderSuccess
  renderStatus:
    mutationSteps:
      - image: ghcr.io/kptdev/krm-functions-catalog/no-op:latest
        condition: resources.exists(r, r.kind == 'ConfigMap' && r.metadata.name == 'app-config')
        exitCode: 0
      ....

@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. and removed size:XL This PR changes 500-999 lines, ignoring generated files. labels Apr 14, 2026
@SurbhiAgarwal1 SurbhiAgarwal1 force-pushed the feat/cel-clean-v2 branch 3 times, most recently from 5101835 to 39f43fb Compare April 19, 2026 00:06
@dosubot dosubot Bot added size:XXL This PR changes 1000+ lines, ignoring generated files. and removed size:L This PR changes 100-499 lines, ignoring generated files. labels Apr 19, 2026
@SurbhiAgarwal1
Copy link
Copy Markdown
Contributor Author

Hi @liamfallon, the condition tests are now passing. The remaining Docker failure is TestFnRender/testdata/fn-render/subpkg-fn-failure which expects "statefulset-filter:4:42: got newline, want primary expression" but gets a different starlark error format. This test file is unchanged from upstream - is this a known flaky test?

@SurbhiAgarwal1 SurbhiAgarwal1 force-pushed the feat/cel-clean-v2 branch 2 times, most recently from dcc296f to fbfedca Compare April 24, 2026 23:15
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. and removed size:XXL This PR changes 1000+ lines, ignoring generated files. labels Apr 24, 2026
- Add condition field to Function type in Kptfile pipeline
- Add CELEnvironment in pkg/lib/runneroptions/celenv.go
- Integrate condition check in FunctionRunner.Filter()
- Functions skipped due to condition show [SKIPPED] in CLI output
- Add condition field to status.renderStatus.mutationSteps
- Add E2E tests for condition-met and condition-not-met (docker+podman verified)
- Add unit tests for CEL evaluation
- Update documentation
- Fix imports after fnruntime moved to pkg/fn/runtime
- Add IsEmpty() to Status struct, DisplayName to Renderer struct
- Fix symlink test skip on Windows/WSL
- Remove apply-setters from internal/kptops (dependency removed upstream)
- Apply gofmt formatting fixes
- Fix InitCELEnvironment error propagation in get.go
- Fix doc: executed count when condition not met is 0
- Fix doc: spec/status fields may be absent in resources
- Fix comment: CELEnvironment initialized by InitCELEnvironment not InitDefaults
- Update subpkg-fn-failure diff.patch with renderStatus output

Signed-off-by: SurbhiAgarwal1 <agarwalsurbhi1807@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation enhancement New feature or request go Pull requests that update Go code lgtm size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants