feat: add builtin runtime support#4430
Conversation
✅ Deploy Preview for kptdocs ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
There was a problem hiding this comment.
Pull request overview
This PR implements a built-in runtime for curated KRM functions (apply-replacements and starlark) inside kpt itself, allowing them to run without Docker by registering them in a thread-safe in-process registry. When a function image matches a registered builtin (by normalized name, ignoring tags/digests), the builtin is used; otherwise the existing Docker/WASM fallback is preserved.
Changes:
- New self-registration registry (
internal/builtins/registry/) with thread-safeRegister/Lookupusing image name normalization - Built-in implementations for
apply-replacementsandstarlarkfunctions, wired viainit()blank imports - Integration into
fnruntime/runner.goto check the builtin registry before falling back to Docker/WASM
Reviewed changes
Copilot reviewed 9 out of 10 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
internal/builtins/registry/registry.go |
Thread-safe builtin function registry with image name normalization |
internal/builtins/applyreplacements/apply_replacements.go |
Built-in apply-replacements KRM function implementation |
internal/builtins/starlark/starlark.go |
Built-in starlark KRM function entry point |
internal/builtins/starlark/config.go |
Starlark config parser supporting StarlarkRun, Run, and ConfigMap kinds |
internal/builtins/starlark/processor.go |
Starlark processing logic |
internal/builtins/starlark/config_test.go |
Tests for starlark config parsing |
internal/builtins/BuiltinRuntime.go |
Blank-imports all builtins to trigger init() registration |
internal/fnruntime/runner.go |
Wires builtin registry lookup between pkg-context and Docker/WASM |
go.mod / go.sum |
Adds starlark catalog dependency and upgrades fn SDK to v1.0.2 |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
446b055 to
4bdb97c
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 10 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
d83136d to
267c21e
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 13 changed files in this pull request and generated 6 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| func (r *Replacements) Process(rl *fn.ResourceList) (bool, error) { | ||
| if err := r.Config(rl.FunctionConfig); err != nil { | ||
| rl.LogResult(err) | ||
| return false, nil | ||
| } | ||
| transformedItems, err := r.Transform(rl.Items) | ||
| if err != nil { | ||
| rl.LogResult(err) | ||
| return false, nil | ||
| } |
There was a problem hiding this comment.
Replacements.Process logs errors to the ResourceList but then returns (false, nil) on both config and transform failures. That makes the builtin appear successful to fnruntime (exit code 0 / [PASS]) even when the function reports an error result. To match container/WASM behavior and the starlark builtin, propagate the error (while still logging/writing the ResourceList) so fn render fails appropriately.
| import ( | ||
| "io" | ||
| "log" | ||
| "strings" | ||
| "sync" | ||
| ) |
There was a problem hiding this comment.
Lookup uses the standard library log package for warnings. The rest of this repo typically uses k8s.io/klog/v2 for warnings/info, which supports verbosity levels and integrates with existing logging flags. Consider switching this to klog.Warningf (and possibly gating behind a V-level) to keep logging consistent and avoid unexpected stdout/stderr output.
| if fn != nil && imageName != normalized { | ||
| log.Printf("WARNING: builtin function %q is being used instead of the requested image %q. "+ | ||
| "The built-in implementation may differ from the pinned version.", normalized, imageName) | ||
| } |
There was a problem hiding this comment.
This warning currently triggers for any tag/digest mismatch but is emitted unconditionally once a builtin is found. If you keep the warning, consider downgrading it to a verbose log level (or only warning when the request was explicitly pinned with a tag/digest) to avoid noisy logs for users who intentionally rely on the builtin fallback.
internal/builtins/starlark/config.go
Outdated
| func (sr *Run) Config(fnCfg *fn.KubeObject) error { | ||
| switch { | ||
| case fnCfg.IsEmpty(): | ||
| return fmt.Errorf("FunctionConfig is missing. Expect `ConfigMap` or `StarlarkRun`") | ||
| case fnCfg.IsGVK("", configMapAPIVersion, configMapKind): |
There was a problem hiding this comment.
The empty-functionConfig error message is now inaccurate: this builtin accepts ConfigMap, StarlarkRun, and Run kinds, but the message only mentions ConfigMap/StarlarkRun. Please update the message so users know Run is supported (or remove mention of specific kinds and point to docs).
internal/builtins/starlark/config.go
Outdated
| case fnCfg.IsGVK(starlarkRunGroup, starlarkRunVersion, starlarkRunKind), | ||
| fnCfg.IsGVK(starlarkRunGroup, starlarkRunVersion, "Run"): | ||
| if err := fnCfg.As(sr); err != nil { | ||
| return err | ||
| } | ||
| default: | ||
| return fmt.Errorf("`functionConfig` must be either %v or %v, but we got: %v", | ||
| schema.FromAPIVersionAndKind(configMapAPIVersion, configMapKind).String(), | ||
| schema.FromAPIVersionAndKind(starlarkRunAPIVersion, starlarkRunKind).String(), | ||
| schema.FromAPIVersionAndKind(fnCfg.GetAPIVersion(), fnCfg.GetKind()).String()) | ||
| } |
There was a problem hiding this comment.
The type-mismatch error message only lists ConfigMap and StarlarkRun as supported, but Run is also accepted (see the IsGVK(..., "Run") branch). Please include Run in the allowed kinds so the error output matches actual behavior.
internal/builtins/starlark/config.go
Outdated
| import ( | ||
| "fmt" | ||
|
|
||
| starlarkruntime "github.com/kptdev/krm-functions-catalog/functions/go/starlark/third_party/sigs.k8s.io/kustomize/kyaml/fn/runtime/starlark" |
There was a problem hiding this comment.
This file imports a third_party/.../fn/runtime/starlark package from krm-functions-catalog, which forces kpt to take a fairly large new direct dependency (github.com/kptdev/krm-functions-catalog/functions/go/starlark). If possible, prefer importing the upstream kyaml runtime directly (sigs.k8s.io/kustomize/kyaml/fn/runtime/starlark) to reduce transitive deps and avoid relying on a third_party path that may not be API-stable.
| starlarkruntime "github.com/kptdev/krm-functions-catalog/functions/go/starlark/third_party/sigs.k8s.io/kustomize/kyaml/fn/runtime/starlark" | |
| starlarkruntime "sigs.k8s.io/kustomize/kyaml/fn/runtime/starlark" |
Signed-off-by: Abdulrahman Fikry <abdulrahmanfikry1@gmail.com>
Signed-off-by: Abdulrahman Fikry <abdulrahmanfikry1@gmail.com> Signed-off-by: abdulrahman11a <abdulrahmanfikry1@gmail.com>
Signed-off-by: abdulrahman11a <abdulrahmanfikry1@gmail.com>
9abb21d to
c4f465b
Compare
|
Do you need to duplicate function code here because otherwise there would be a circular dependency (kpt -> kpt-fn-sdk-> apply-replacements->kpt)? |
After reviewing the go mod graph, the current dependency chain is: |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 17 out of 19 changed files in this pull request and generated 8 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } else if builtinFn := builtinsregistry.Lookup(f.Image); builtinFn != nil { | ||
| fltr.Run = func(r io.Reader, w io.Writer) error { | ||
| var stderrBuf strings.Builder | ||
| err := builtinFn.Run(r, w, &stderrBuf) | ||
| if stderrBuf.Len() > 0 { | ||
| fnResult.Stderr = stderrBuf.String() | ||
| } | ||
| return err | ||
| } |
There was a problem hiding this comment.
Builtin functions are executed in-process here, but the runner currently doesn’t apply RunnerOptions permissions (notably AllowNetwork) that are enforced for container execution. This means the starlark builtin can still fetch URL scripts and use starlib’s http module even when AllowNetwork is false, which is a behavior + security regression versus the Docker runtime. Consider enforcing the same permission model for builtins (e.g., reject URL/http usage when AllowNetwork is false, or plumb permissions into builtin runners).
| // for builtin functions, print stderr from fnResult if available | ||
| if fr.fnResult.Stderr != "" { | ||
| printFnStderr(fr.ctx, fr.fnResult.Stderr) | ||
| pr.Printf(" Exit code: %d\n\n", fr.fnResult.ExitCode) | ||
| return nil, errors.ErrAlreadyHandled | ||
| } |
There was a problem hiding this comment.
This error path treats any non-empty fr.fnResult.Stderr as a builtin-function failure and returns errors.ErrAlreadyHandled. fnResult.Stderr can also be populated on successful execution (warnings) or left over when a later post-processing step fails (e.g., pkg-path annotation / path invariants), which would cause the real error to be masked and reported as already-handled. Gate this on a more reliable signal (e.g., a dedicated error type/flag for builtin execution failures, or only when err came from the builtin runner wrapper).
internal/fnruntime/runner.go
Outdated
| // builtin functions don't return ExecError, populate stderr from error message | ||
| fnResult.Stderr = err.Error() |
There was a problem hiding this comment.
In the builtin runner wrapper, stderr is captured into fnResult.Stderr, but on error this block overwrites fnResult.Stderr with err.Error(), losing the function’s actual stderr output. Preserve the captured stderr (e.g., only set fnResult.Stderr = err.Error() if it is still empty, or append the message) so builtin failures surface useful diagnostics.
| // builtin functions don't return ExecError, populate stderr from error message | |
| fnResult.Stderr = err.Error() | |
| // builtin functions don't return ExecError, preserve any captured stderr and | |
| // fall back to the error message only when stderr is empty. | |
| if fnResult.Stderr == "" { | |
| fnResult.Stderr = err.Error() | |
| } else if !strings.Contains(fnResult.Stderr, err.Error()) { | |
| if strings.HasSuffix(fnResult.Stderr, "\n") { | |
| fnResult.Stderr += err.Error() | |
| } else { | |
| fnResult.Stderr += "\n" + err.Error() | |
| } | |
| } |
| if sf.URL != "" { | ||
| err := func() error { | ||
| resp, err := http.Get(sf.URL) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| defer func() { | ||
| _ = resp.Body.Close() | ||
| }() | ||
|
|
||
| b, err := io.ReadAll(resp.Body) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| sf.Program = string(b) | ||
| return nil |
There was a problem hiding this comment.
When loading a starlark program from URL, the code doesn’t check resp.StatusCode (so 404/500 bodies will be treated as valid programs) and uses http.Get with the default client (no timeout / no context). Handle non-2xx responses explicitly and use an http.Client with a reasonable timeout (and ideally context) to avoid hangs and improve error messages.
| // The resources are provided to the starlark program through the global variable "resourceList". | ||
| // "resourceList" is a dictionary containing an "items" field with a list of resources. | ||
| // The starlark modified "resourceList" is the Filter output. | ||
| // | ||
| // After being run through the starlark program, the filter will copy the comments from the input | ||
| // resources to restore them -- due to them being dropped as a result of serializing the resources | ||
| // as starlark values. | ||
| // | ||
| // "resourceList" may also contain a "functionConfig" entry to configure the starlark script itself. | ||
| // Changes made by the starlark program to the "functionConfig" will be reflected in the | ||
| // Filter.FunctionConfig value. | ||
| // | ||
| // The Filter will also format the output so that output has the preferred field ordering | ||
| // rather than an alphabetical field ordering. | ||
| // | ||
| // The resourceList variable adheres to the kustomize function spec as specified by: | ||
| // https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md | ||
| // | ||
| // All items in the resourceList are resources represented as starlark dictionaries/ | ||
| // The items in the resourceList respect the io spec specified by: |
There was a problem hiding this comment.
Package docs describe a global variable resourceList, but the runtime actually exposes ctx.resource_list (see Context.predeclared). Please update the doc to match the real API so users don’t write scripts against a non-existent global.
| // The resources are provided to the starlark program through the global variable "resourceList". | |
| // "resourceList" is a dictionary containing an "items" field with a list of resources. | |
| // The starlark modified "resourceList" is the Filter output. | |
| // | |
| // After being run through the starlark program, the filter will copy the comments from the input | |
| // resources to restore them -- due to them being dropped as a result of serializing the resources | |
| // as starlark values. | |
| // | |
| // "resourceList" may also contain a "functionConfig" entry to configure the starlark script itself. | |
| // Changes made by the starlark program to the "functionConfig" will be reflected in the | |
| // Filter.FunctionConfig value. | |
| // | |
| // The Filter will also format the output so that output has the preferred field ordering | |
| // rather than an alphabetical field ordering. | |
| // | |
| // The resourceList variable adheres to the kustomize function spec as specified by: | |
| // https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md | |
| // | |
| // All items in the resourceList are resources represented as starlark dictionaries/ | |
| // The items in the resourceList respect the io spec specified by: | |
| // The resources are provided to the starlark program through ctx.resource_list. | |
| // ctx.resource_list is a dictionary containing an "items" field with a list of resources. | |
| // The starlark-modified ctx.resource_list is the Filter output. | |
| // | |
| // After being run through the starlark program, the filter will copy the comments from the input | |
| // resources to restore them -- due to them being dropped as a result of serializing the resources | |
| // as starlark values. | |
| // | |
| // ctx.resource_list may also contain a "functionConfig" entry to configure the starlark script | |
| // itself. Changes made by the starlark program to the "functionConfig" will be reflected in the | |
| // Filter.FunctionConfig value. | |
| // | |
| // The Filter will also format the output so that output has the preferred field ordering | |
| // rather than an alphabetical field ordering. | |
| // | |
| // The ctx.resource_list value adheres to the kustomize function spec as specified by: | |
| // https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md | |
| // | |
| // All items in ctx.resource_list are resources represented as starlark dictionaries/ | |
| // The items in ctx.resource_list respect the io spec specified by: |
| "sigs.k8s.io/kustomize/kyaml/yaml" | ||
| ) | ||
|
|
||
| func parseResourceList(t *testing.T, input string) *framework.ResourceList { | ||
| t.Helper() | ||
| rw := &kio.ByteReader{Reader: strings.NewReader(input)} | ||
| nodes, err := rw.Read() | ||
| assert.NoError(t, err) | ||
|
|
||
| rl := &framework.ResourceList{Items: nodes} | ||
| // استخرج الـ functionConfig من الـ ResourceList YAML | ||
| node, err := yaml.Parse(input) | ||
| if err == nil { | ||
| fc := node.Field("functionConfig") | ||
| if fc != nil && fc.Value != nil { | ||
| rl.FunctionConfig = fc.Value | ||
| } | ||
| } | ||
| return rl | ||
| } | ||
|
|
There was a problem hiding this comment.
parseResourceList is never used in this test file, and it includes a non-English inline comment. Since it’s dead code, it should be removed to keep the test suite maintainable and consistent.
| "sigs.k8s.io/kustomize/kyaml/yaml" | |
| ) | |
| func parseResourceList(t *testing.T, input string) *framework.ResourceList { | |
| t.Helper() | |
| rw := &kio.ByteReader{Reader: strings.NewReader(input)} | |
| nodes, err := rw.Read() | |
| assert.NoError(t, err) | |
| rl := &framework.ResourceList{Items: nodes} | |
| // استخرج الـ functionConfig من الـ ResourceList YAML | |
| node, err := yaml.Parse(input) | |
| if err == nil { | |
| fc := node.Field("functionConfig") | |
| if fc != nil && fc.Value != nil { | |
| rl.FunctionConfig = fc.Value | |
| } | |
| } | |
| return rl | |
| } | |
| ) |
| func env() (starlark.Value, error) { | ||
| env := map[string]interface{}{} | ||
| for _, e := range os.Environ() { | ||
| pair := strings.SplitN(e, "=", 2) | ||
| if len(pair) < 2 { | ||
| continue | ||
| } | ||
| env[pair[0]] = pair[1] | ||
| } | ||
| value, err := util.Marshal(env) | ||
| if err != nil { | ||
| return nil, errors.Wrap(err) | ||
| } | ||
| return value, nil |
There was a problem hiding this comment.
ctx.environment is built from os.Environ(), which (with the new in-process builtin runtime) exposes the full kpt process environment to arbitrary starlark scripts. This is a significant privilege increase compared to container execution (where only the container env is visible). Consider gating this behind an explicit option, exposing only a safe allowlist, or omitting environment entirely to avoid accidental secret leakage (e.g., cloud credentials in env vars).
…ptdev#4307) Closes kptdev#4307 Implements a built-in runtime for curated KRM functions inside kpt, allowing apply-replacements and starlark to run without pulling images from Docker Hub, eliminating the external SDK dependency. ## Approach Avoids circular dependency between Porch and kpt by moving the built-in runtime concept into kpt directly using kyaml/fn/framework instead of krm-functions-sdk. ## Architecture A thread-safe self-registration registry (internal/builtins/registry) allows KRM function implementations to register themselves via init(). The fnruntime runner checks this registry before falling back to Docker or WASM, preserving existing behavior for unregistered functions. Priority order in fnruntime/runner.go: 1. pkg-context builtin (existing) 2. Builtin registry (new, no Docker needed) 3. Docker / WASM (fallback, unchanged) ## Functions included - apply-replacements: ghcr.io/kptdev/krm-functions-catalog/apply-replacements - starlark: ghcr.io/kptdev/krm-functions-catalog/starlark ## Implementation notes - Removed dependency on krm-functions-sdk/go/fn and krm-functions-catalog/starlark - Vendored starlark runtime locally using kyaml/yaml instead of SDK ## Verified locally [PASS] apply-replacements in 0s (no Docker) [PASS] starlark in 0s (no Docker) Signed-off-by: abdulrahman11a <abdulrahmanfikry1@gmail.com>
70f474c to
a1fce5e
Compare
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 17 out of 19 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| case krmfn.ModuleName: | ||
| return starlark.StringDict{"krmfn": krmfn.Module}, nil | ||
| } | ||
| return nil, nil | ||
| } |
There was a problem hiding this comment.
load returns (nil, nil) for unknown modules. Starlark’s Thread.Load is expected to return a non-nil error when a module can’t be resolved; returning nil without error can lead to confusing failures (or panics) when a script tries to load() an unsupported module. Please return a descriptive error for the default case.
| // Package starlark contains a kio.Filter which can be applied to resources to transform | ||
| // them through starlark program. | ||
| // | ||
| // Starlark has become a popular runtime embedding in go programs, especially for Kubernetes | ||
| // and data processing. | ||
| // Examples: https://github.com/cruise-automation/isopod, https://qri.io/docs/starlark/starlib, | ||
| // https://github.com/stripe/skycfg, https://github.com/k14s/ytt | ||
| // | ||
| // The resources are provided to the starlark program through the global variable "resourceList". | ||
| // "resourceList" is a dictionary containing an "items" field with a list of resources. | ||
| // The starlark modified "resourceList" is the Filter output. | ||
| // | ||
| // After being run through the starlark program, the filter will copy the comments from the input | ||
| // resources to restore them -- due to them being dropped as a result of serializing the resources | ||
| // as starlark values. | ||
| // | ||
| // "resourceList" may also contain a "functionConfig" entry to configure the starlark script itself. | ||
| // Changes made by the starlark program to the "functionConfig" will be reflected in the | ||
| // Filter.FunctionConfig value. | ||
| // | ||
| // The Filter will also format the output so that output has the preferred field ordering | ||
| // rather than an alphabetical field ordering. | ||
| // | ||
| // The resourceList variable adheres to the kustomize function spec as specified by: | ||
| // https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md | ||
| // | ||
| // All items in the resourceList are resources represented as starlark dictionaries/ | ||
| // The items in the resourceList respect the io spec specified by: | ||
| // https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/config-io.md | ||
| // | ||
| // The starlark language spec can be found here: |
There was a problem hiding this comment.
Package doc is inconsistent with the actual runtime API: it says the package is starlark and that scripts receive a global resourceList, but the implementation exposes ctx.resource_list (see Context.predeclared) and the package name is starlarkruntime. Please update the doc comment to match the actual symbols users should reference.
| // Package starlark contains a kio.Filter which can be applied to resources to transform | |
| // them through starlark program. | |
| // | |
| // Starlark has become a popular runtime embedding in go programs, especially for Kubernetes | |
| // and data processing. | |
| // Examples: https://github.com/cruise-automation/isopod, https://qri.io/docs/starlark/starlib, | |
| // https://github.com/stripe/skycfg, https://github.com/k14s/ytt | |
| // | |
| // The resources are provided to the starlark program through the global variable "resourceList". | |
| // "resourceList" is a dictionary containing an "items" field with a list of resources. | |
| // The starlark modified "resourceList" is the Filter output. | |
| // | |
| // After being run through the starlark program, the filter will copy the comments from the input | |
| // resources to restore them -- due to them being dropped as a result of serializing the resources | |
| // as starlark values. | |
| // | |
| // "resourceList" may also contain a "functionConfig" entry to configure the starlark script itself. | |
| // Changes made by the starlark program to the "functionConfig" will be reflected in the | |
| // Filter.FunctionConfig value. | |
| // | |
| // The Filter will also format the output so that output has the preferred field ordering | |
| // rather than an alphabetical field ordering. | |
| // | |
| // The resourceList variable adheres to the kustomize function spec as specified by: | |
| // https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md | |
| // | |
| // All items in the resourceList are resources represented as starlark dictionaries/ | |
| // The items in the resourceList respect the io spec specified by: | |
| // https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/config-io.md | |
| // | |
| // The starlark language spec can be found here: | |
| // Package starlarkruntime contains a kio.Filter which can be applied to resources to transform | |
| // them through a Starlark program. | |
| // | |
| // Starlark has become a popular runtime embedding in Go programs, especially for Kubernetes | |
| // and data processing. | |
| // Examples: https://github.com/cruise-automation/isopod, https://qri.io/docs/starlark/starlib, | |
| // https://github.com/stripe/skycfg, https://github.com/k14s/ytt | |
| // | |
| // The resources are provided to the Starlark program through ctx.resource_list. | |
| // ctx.resource_list is a dictionary containing an "items" field with a list of resources. | |
| // The Starlark-modified ctx.resource_list is the Filter output. | |
| // | |
| // After being run through the Starlark program, the filter will copy the comments from the input | |
| // resources to restore them -- due to them being dropped as a result of serializing the resources | |
| // as Starlark values. | |
| // | |
| // ctx.resource_list may also contain a "functionConfig" entry to configure the Starlark script | |
| // itself. Changes made by the Starlark program to the "functionConfig" will be reflected in the | |
| // Filter.FunctionConfig value. | |
| // | |
| // The Filter will also format the output so that output has the preferred field ordering | |
| // rather than an alphabetical field ordering. | |
| // | |
| // The ctx.resource_list value adheres to the kustomize function spec as specified by: | |
| // https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md | |
| // | |
| // All items in ctx.resource_list are resources represented as Starlark dictionaries/ | |
| // The items in ctx.resource_list respect the io spec specified by: | |
| // https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/config-io.md | |
| // | |
| // The Starlark language spec can be found here: |
f745a1a to
df6c9ee
Compare
…ptdev#4307) Closes kptdev#4307 Implements a built-in runtime for curated KRM functions inside kpt, allowing apply-replacements and starlark to run without pulling images from Docker Hub, eliminating the external SDK dependency. Avoids circular dependency between Porch and kpt by moving the built-in runtime concept into kpt directly using kyaml/fn/framework instead of krm-functions-sdk. A thread-safe self-registration registry (internal/builtins/registry) allows KRM function implementations to register themselves via init(). The fnruntime runner checks this registry before falling back to Docker or WASM, preserving existing behavior for unregistered functions. Priority order in fnruntime/runner.go: 1. pkg-context builtin (existing) 2. Builtin registry (new, no Docker needed) 3. Docker / WASM (fallback, unchanged) - apply-replacements: ghcr.io/kptdev/krm-functions-catalog/apply-replacements - starlark: ghcr.io/kptdev/krm-functions-catalog/starlark - Removed dependency on krm-functions-sdk/go/fn and krm-functions-catalog/starlark - Vendored starlark runtime locally using kyaml/yaml instead of SDK [PASS] apply-replacements in 0s (no Docker) [PASS] starlark in 0s (no Docker) Signed-off-by: abdulrahman11a <abdulrahmanfikry1@gmail.com>
df6c9ee to
839d2ff
Compare
Summary
Closes #4307
This PR implements a built-in runtime for curated KRM functions inside kpt itself,
allowing
apply-replacementsandstarlarkto run without pulling images from Docker Hub.Approach
As discussed in the issue thread, this implementation avoids reintroducing a circular
dependency between Porch and kpt by moving the built-in runtime concept into kpt directly.
The architecture is intentionally simple and extensible — new functions can be added
incrementally by registering them in the builtin registry.
How it works
A thread-safe self-registration registry (
internal/builtins/registry) allows KRM functionimplementations to register themselves via
init(). Thefnruntimerunner checks thisregistry before falling back to Docker or WASM, so existing behavior is fully preserved
for unregistered functions.
Priority order in
fnruntime/runner.go:pkg-contextbuiltin (existing)Functions included
ghcr.io/kptdev/krm-functions-catalog/apply-replacementsghcr.io/kptdev/krm-functions-catalog/starlarkFiles changed
internal/builtins/registry/registry.gointernal/builtins/applyreplacements/apply_replacements.gointernal/builtins/starlark/starlark.gointernal/builtins/starlark/config.goStarlarkRunandRunkinds)internal/builtins/starlark/processor.gointernal/builtins/BuiltinRuntime.goinit()internal/fnruntime/runner.goVerified locally
Both functions execute without Docker running.

Next steps