Skip to content

Commit 446b055

Browse files
feat: add builtin runtime support
Signed-off-by: Abdulrahman Fikry <[email protected]>
1 parent 712ada1 commit 446b055

10 files changed

Lines changed: 639 additions & 1 deletion

File tree

go.mod

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ require (
1111
github.com/google/go-containerregistry v0.20.6
1212
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
1313
github.com/kptdev/krm-functions-catalog/functions/go/apply-setters v0.2.2
14-
github.com/kptdev/krm-functions-sdk/go/fn v1.0.0
14+
github.com/kptdev/krm-functions-sdk/go/fn v1.0.2
1515
github.com/otiai10/copy v1.14.1
1616
github.com/philopon/go-toposort v0.0.0-20170620085441-9be86dbd762f
1717
github.com/pkg/errors v0.9.1
@@ -43,8 +43,11 @@ require (
4343

4444
require (
4545
cloud.google.com/go/compute/metadata v0.9.0 // indirect
46+
github.com/360EntSecGroup-Skylar/excelize v1.4.1 // indirect
4647
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
4748
github.com/MakeNowJust/heredoc v1.0.0 // indirect
49+
github.com/PuerkitoBio/goquery v1.5.1 // indirect
50+
github.com/andybalholm/cascadia v1.1.0 // indirect
4851
github.com/beorn7/perks v1.0.1 // indirect
4952
github.com/blang/semver/v4 v4.0.0 // indirect
5053
github.com/cespare/xxhash/v2 v2.3.0 // indirect
@@ -55,6 +58,7 @@ require (
5558
github.com/docker/distribution v2.8.3+incompatible // indirect
5659
github.com/docker/docker-credential-helpers v0.9.4 // indirect
5760
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
61+
github.com/dustmop/soup v1.1.2-0.20190516214245-38228baa104e // indirect
5862
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
5963
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
6064
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
@@ -85,26 +89,30 @@ require (
8589
github.com/jonboulle/clockwork v0.5.0 // indirect
8690
github.com/json-iterator/go v1.1.12 // indirect
8791
github.com/klauspost/compress v1.18.2 // indirect
92+
github.com/kptdev/krm-functions-catalog/functions/go/starlark v0.5.5 // indirect
8893
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
8994
github.com/mitchellh/go-homedir v1.1.0 // indirect
9095
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
9196
github.com/moby/spdystream v0.5.0 // indirect
9297
github.com/moby/term v0.5.2 // indirect
9398
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
9499
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
100+
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
95101
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
96102
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
97103
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
98104
github.com/onsi/gomega v1.37.0 // indirect
99105
github.com/opencontainers/go-digest v1.0.0 // indirect
100106
github.com/opencontainers/image-spec v1.1.1 // indirect
101107
github.com/otiai10/mint v1.6.3 // indirect
108+
github.com/paulmach/orb v0.1.5 // indirect
102109
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
103110
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
104111
github.com/prometheus/client_golang v1.23.2 // indirect
105112
github.com/prometheus/client_model v0.6.2 // indirect
106113
github.com/prometheus/common v0.67.2 // indirect
107114
github.com/prometheus/procfs v0.19.2 // indirect
115+
github.com/qri-io/starlib v0.5.0 // indirect
108116
github.com/russross/blackfriday/v2 v2.1.0 // indirect
109117
github.com/sergi/go-diff v1.4.0 // indirect
110118
github.com/sirupsen/logrus v1.9.3 // indirect
@@ -114,6 +122,7 @@ require (
114122
github.com/x448/float16 v0.8.4 // indirect
115123
go.opentelemetry.io/otel v1.38.0 // indirect
116124
go.opentelemetry.io/otel/trace v1.38.0 // indirect
125+
go.starlark.net v0.0.0-20250417143717-f57e51f710eb // indirect
117126
go.yaml.in/yaml/v2 v2.4.3 // indirect
118127
go.yaml.in/yaml/v3 v3.0.4 // indirect
119128
golang.org/x/net v0.47.0 // indirect

go.sum

Lines changed: 94 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2026 The kpt Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Package builtins registers all built-in KRM functions into the builtin registry.
16+
package builtins
17+
18+
import (
19+
// Register built-in functions via init()
20+
_ "github.com/kptdev/kpt/internal/builtins/applyreplacements"
21+
_ "github.com/kptdev/kpt/internal/builtins/starlark"
22+
)
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
// Copyright 2026 The kpt Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
package applyreplacements
15+
16+
import (
17+
"fmt"
18+
"io"
19+
20+
"github.com/kptdev/kpt/internal/builtins/registry"
21+
"github.com/kptdev/krm-functions-sdk/go/fn"
22+
"sigs.k8s.io/kustomize/api/filters/replacement"
23+
"sigs.k8s.io/kustomize/api/types"
24+
"sigs.k8s.io/kustomize/kyaml/yaml"
25+
)
26+
27+
const (
28+
ImageName = "ghcr.io/kptdev/krm-functions-catalog/apply-replacements"
29+
fnConfigKind = "ApplyReplacements"
30+
fnConfigAPIVersion = "fn.kpt.dev/v1alpha1"
31+
)
32+
33+
func init() {
34+
registry.Register(&ApplyReplacementsRunner{})
35+
}
36+
37+
type ApplyReplacementsRunner struct{}
38+
39+
func (a *ApplyReplacementsRunner) ImageName() string { return ImageName }
40+
41+
func (a *ApplyReplacementsRunner) Run(r io.Reader, w io.Writer) error {
42+
input, err := io.ReadAll(r)
43+
if err != nil {
44+
return fmt.Errorf("reading input: %w", err)
45+
}
46+
rl, err := fn.ParseResourceList(input)
47+
if err != nil {
48+
return fmt.Errorf("parsing ResourceList: %w", err)
49+
}
50+
if _, err := applyReplacements(rl); err != nil {
51+
return err
52+
}
53+
out, err := rl.ToYAML()
54+
if err != nil {
55+
return err
56+
}
57+
_, err = w.Write(out)
58+
return err
59+
}
60+
func applyReplacements(rl *fn.ResourceList) (bool, error) {
61+
r := &Replacements{}
62+
return r.Process(rl)
63+
}
64+
65+
type Replacements struct {
66+
Replacements []types.Replacement `json:"replacements,omitempty" yaml:"replacements,omitempty"`
67+
}
68+
69+
func (r *Replacements) Config(functionConfig *fn.KubeObject) error {
70+
if functionConfig.IsEmpty() {
71+
return fmt.Errorf("FunctionConfig is missing. Expect `ApplyReplacements`")
72+
}
73+
if functionConfig.GetKind() != fnConfigKind || functionConfig.GetAPIVersion() != fnConfigAPIVersion {
74+
return fmt.Errorf("received functionConfig of kind %s and apiVersion %s, only functionConfig of kind %s and apiVersion %s is supported",
75+
functionConfig.GetKind(), functionConfig.GetAPIVersion(), fnConfigKind, fnConfigAPIVersion)
76+
}
77+
r.Replacements = []types.Replacement{}
78+
if err := functionConfig.As(r); err != nil {
79+
return fmt.Errorf("unable to convert functionConfig to replacements:\n%w", err)
80+
}
81+
return nil
82+
}
83+
84+
func (r *Replacements) Process(rl *fn.ResourceList) (bool, error) {
85+
if err := r.Config(rl.FunctionConfig); err != nil {
86+
rl.LogResult(err)
87+
return false, nil
88+
}
89+
transformedItems, err := r.Transform(rl.Items)
90+
if err != nil {
91+
rl.LogResult(err)
92+
return false, nil
93+
}
94+
rl.Items = transformedItems
95+
return true, nil
96+
}
97+
98+
func (r *Replacements) Transform(items []*fn.KubeObject) ([]*fn.KubeObject, error) {
99+
var transformedItems []*fn.KubeObject
100+
var nodes []*yaml.RNode
101+
for _, obj := range items {
102+
objRN, err := yaml.Parse(obj.String())
103+
if err != nil {
104+
return nil, err
105+
}
106+
nodes = append(nodes, objRN)
107+
}
108+
transformedNodes, err := replacement.Filter{
109+
Replacements: r.Replacements,
110+
}.Filter(nodes)
111+
if err != nil {
112+
return nil, err
113+
}
114+
for _, n := range transformedNodes {
115+
obj, err := fn.ParseKubeObject([]byte(n.MustString()))
116+
if err != nil {
117+
return nil, err
118+
}
119+
transformedItems = append(transformedItems, obj)
120+
}
121+
return transformedItems, nil
122+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright 2026 The kpt Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package registry
16+
17+
import (
18+
"io"
19+
"strings"
20+
"sync"
21+
)
22+
23+
type BuiltinFunction interface {
24+
ImageName() string
25+
Run(r io.Reader, w io.Writer) error
26+
}
27+
28+
var (
29+
mu sync.RWMutex
30+
registry = map[string]BuiltinFunction{}
31+
)
32+
33+
func Register(fn BuiltinFunction) {
34+
mu.Lock()
35+
defer mu.Unlock()
36+
registry[normalizeImage(fn.ImageName())] = fn
37+
}
38+
39+
func Lookup(imageName string) BuiltinFunction {
40+
mu.RLock()
41+
defer mu.RUnlock()
42+
return registry[normalizeImage(imageName)]
43+
}
44+
45+
func List() []string {
46+
mu.RLock()
47+
defer mu.RUnlock()
48+
names := make([]string, 0, len(registry))
49+
for name := range registry {
50+
names = append(names, name)
51+
}
52+
return names
53+
}
54+
55+
func normalizeImage(image string) string {
56+
if idx := strings.Index(image, "@"); idx != -1 {
57+
image = image[:idx]
58+
}
59+
parts := strings.Split(image, "/")
60+
if len(parts) > 0 {
61+
last := parts[len(parts)-1]
62+
if idx := strings.Index(last, ":"); idx != -1 {
63+
parts[len(parts)-1] = last[:idx]
64+
}
65+
}
66+
return strings.Join(parts, "/")
67+
}

0 commit comments

Comments
 (0)