Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,7 @@ If the condition is not met:
$ kpt fn render wordpress
Package "wordpress":

[SKIPPED] "ghcr.io/kptdev/krm-functions-catalog/set-labels:latest" (condition not met)
[SKIPPED] "ghcr.io/kptdev/krm-functions-catalog/set-labels:latest" (celCondition not met)

Successfully executed 0 function(s) in 1 package(s).
```
Expand Down
7 changes: 7 additions & 0 deletions documentation/content/en/reference/cli/fn/eval/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,13 @@ fn-args:
--save, s:
Save the function image and fn-config to Kptfile. Require ` + "`" + `--image` + "`" + `.

--when:
A CEL expression that determines whether the function should be run.
The expression is evaluated against the input resources and must return a boolean.
If it returns true (or is not specified), the function executes normally.
If it returns false, the function is skipped.
Example: --when "resources.exists(r, r.kind == 'ConfigMap')"

```

#### Environment Variables
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright 2026 The kpt Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

testType: eval
image: ghcr.io/kptdev/krm-functions-catalog/no-op
when: "this is not valid CEL"
exitCode: 1
stdErr: "failed to compile CEL expression"
1 change: 1 addition & 0 deletions e2e/testdata/fn-eval/condition-invalid-cel/.krmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.expected
19 changes: 19 additions & 0 deletions e2e/testdata/fn-eval/condition-invalid-cel/resources.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright 2026 The kpt Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 1
17 changes: 17 additions & 0 deletions e2e/testdata/fn-eval/condition-met/.expected/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright 2026 The kpt Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

testType: eval
image: ghcr.io/kptdev/krm-functions-catalog/no-op
when: "resources.exists(r, r.kind == 'ConfigMap' && r.metadata.name == 'app-config')"
1 change: 1 addition & 0 deletions e2e/testdata/fn-eval/condition-met/.krmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.expected
26 changes: 26 additions & 0 deletions e2e/testdata/fn-eval/condition-met/resources.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Copyright 2026 The kpt Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
env: production
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 1
18 changes: 18 additions & 0 deletions e2e/testdata/fn-eval/condition-not-met/.expected/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright 2026 The kpt Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

testType: eval
image: ghcr.io/kptdev/krm-functions-catalog/no-op
when: "resources.exists(r, r.kind == 'ConfigMap' && r.metadata.name == 'app-config')"
stdErr: "[SKIPPED]"
1 change: 1 addition & 0 deletions e2e/testdata/fn-eval/condition-not-met/.krmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.expected
19 changes: 19 additions & 0 deletions e2e/testdata/fn-eval/condition-not-met/resources.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright 2026 The kpt Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
stdErr: |
Package "condition-mixed":
[RUNNING] "ghcr.io/kptdev/krm-functions-catalog/no-op:latest" on package "condition-mixed"
[PASS] "ghcr.io/kptdev/krm-functions-catalog/no-op:latest" in 0s
[SKIPPED] "ghcr.io/kptdev/krm-functions-catalog/no-op:latest" (celCondition not met)

Successfully executed 1 function(s) in 1 package(s).
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
diff --git a/Kptfile b/Kptfile
index ea7d5a8..4da1d54 100644
--- a/Kptfile
+++ b/Kptfile
@@ -8,3 +8,17 @@ pipeline:
when: resources.exists(r, r.kind == 'ConfigMap')
- image: ghcr.io/kptdev/krm-functions-catalog/no-op
when: resources.exists(r, r.kind == 'Secret')
+status:
+ conditions:
+ - type: Rendered
+ status: "True"
+ reason: RenderSuccess
+ renderStatus:
+ mutationSteps:
+ - image: ghcr.io/kptdev/krm-functions-catalog/no-op:latest
+ exitCode: 0
+ when: resources.exists(r, r.kind == 'ConfigMap')
+ - image: ghcr.io/kptdev/krm-functions-catalog/no-op:latest
+ exitCode: 0
+ when: resources.exists(r, r.kind == 'Secret')
+ skipped: true
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.expected
10 changes: 10 additions & 0 deletions e2e/testdata/fn-render/condition/condition-mixed/Kptfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
apiVersion: kpt.dev/v1
kind: Kptfile
metadata:
name: condition-mixed
pipeline:
mutators:
- image: ghcr.io/kptdev/krm-functions-catalog/no-op
when: resources.exists(r, r.kind == 'ConfigMap')
- image: ghcr.io/kptdev/krm-functions-catalog/no-op
when: resources.exists(r, r.kind == 'Secret')
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
env: prod
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
diff --git a/Kptfile b/Kptfile
index 06ea70e..7b4e0f5 100644
--- a/Kptfile
+++ b/Kptfile
@@ -6,3 +6,12 @@ pipeline:
validators:
- image: ghcr.io/kptdev/krm-functions-catalog/starlark:latest
configPath: starlark-val.yaml
+status:
+ conditions:
+ - type: Rendered
+ status: "True"
+ reason: RenderSuccess
+ renderStatus:
+ validationSteps:
+ - image: ghcr.io/kptdev/krm-functions-catalog/starlark:latest
+ exitCode: 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
apiVersion: kpt.dev/v1
kind: Kptfile
metadata:
name: validator-excludes-fnconfig
pipeline:
validators:
- image: ghcr.io/kptdev/krm-functions-catalog/starlark:latest
configPath: starlark-val.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
spec:
replicas: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
apiVersion: fn.kpt.dev/v1alpha1
kind: StarlarkRun
metadata:
name: starlark-val
source: |-
def validate(resource_list):
for r in resource_list["items"]:
if r["metadata"]["name"] == "starlark-val" and r["kind"] == "StarlarkRun":
fail("validator should not see its own fn-config in items")
validate(ctx.resource_list)
9 changes: 8 additions & 1 deletion internal/docs/generated/fndocs/docs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 6 additions & 6 deletions pkg/fn/runtime/condition_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,46 +43,46 @@ func TestFunctionRunner_Conditions(t *testing.T) {
testCases := []struct {
name string
fn *kptfile.Function
condition string
celCond string
expectRun bool
}{
{
name: "builtin runtime - condition met",
fn: &kptfile.Function{
Image: runneroptions.FuncGenPkgContext,
},
condition: "resources.exists(r, r.kind == 'ConfigMap')",
celCond: "resources.exists(r, r.kind == 'ConfigMap')",
expectRun: true,
},
{
name: "builtin runtime - condition not met",
fn: &kptfile.Function{
Image: runneroptions.FuncGenPkgContext,
},
condition: "resources.exists(r, r.kind == 'Deployment')",
celCond: "resources.exists(r, r.kind == 'Deployment')",
expectRun: false,
},
{
name: "executable runtime - condition met",
fn: &kptfile.Function{
Exec: "my-exec",
},
condition: "resources.size() > 0",
celCond: "resources.size() > 0",
expectRun: true,
},
{
name: "executable runtime - condition not met",
fn: &kptfile.Function{
Exec: "my-exec",
},
condition: "resources.size() == 0",
celCond: "resources.size() == 0",
expectRun: false,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
tc.fn.CelCondition = tc.condition
tc.fn.CelCondition = tc.celCond
results := fnresult.NewResultList()

// Mock runner options
Expand Down
20 changes: 8 additions & 12 deletions pkg/lib/runneroptions/celenv.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ type CELEnvironment struct {
}

// NewCELEnvironment creates a new CELEnvironment with the standard KRM variable bindings.
// Includes cel-go built-in extensions and k8s-specific validators (IP, CIDR, Quantity, SemVer)
// from k8s.io/apiserver/pkg/cel/library for full Kubernetes CEL compatibility.
// Includes cel-go built-in extensions (strings, sets, lists, comprehensions) for
// evaluating CEL expressions against KRM resources.
func NewCELEnvironment() (*CELEnvironment, error) {
env, err := cel.NewEnv(
cel.Variable("resources", cel.ListType(cel.DynType)),
Expand All @@ -54,12 +54,12 @@ func NewCELEnvironment() (*CELEnvironment, error) {
// EvaluateCondition compiles and evaluates a CEL condition against a list of KRM resources.
// Returns true if the condition is met, false otherwise.
// An empty condition always returns true (function executes unconditionally).
func (e *CELEnvironment) EvaluateCondition(ctx context.Context, condition string, resources []*yaml.RNode, checkFrequency uint, costLimit uint64) (bool, error) {
if condition == "" {
func (e *CELEnvironment) EvaluateCondition(ctx context.Context, celCond string, resources []*yaml.RNode, checkFrequency uint, costLimit uint64) (bool, error) {
if celCond == "" {
return true, nil
}

ast, issues := e.env.Compile(condition)
ast, issues := e.env.Compile(celCond)
if issues != nil && issues.Err() != nil {
return false, fmt.Errorf("failed to compile CEL expression: %w", issues.Err())
}
Expand Down Expand Up @@ -109,13 +109,9 @@ func resourcesToList(resources []*yaml.RNode) ([]any, error) {
}

func resourceToMap(resource *yaml.RNode) (map[string]any, error) {
node := resource.YNode()
if node == nil {
return nil, fmt.Errorf("resource has nil yaml.Node")
}
var result map[string]any
if err := node.Decode(&result); err != nil {
return nil, fmt.Errorf("failed to decode resource: %w", err)
result, err := resource.Map()
if err != nil {
return nil, err
}
ensureMetadata(result)
return result, nil
Expand Down
Loading