Skip to content

Commit 43024a5

Browse files
committed
WIP CEl stuff, but need to stop because it seems to be kube 1.33+ only
On-behalf-of: @SAP [email protected]
1 parent 0227d23 commit 43024a5

File tree

3 files changed

+120
-3
lines changed

3 files changed

+120
-3
lines changed

internal/mutation/mutator.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,12 @@ func createAggregatedTransformer(mutations []syncagentv1alpha1.ResourceMutation)
111111
return nil, err
112112
}
113113

114+
case mut.ApplyConfiguration != nil:
115+
trans, err = transformer.NewApplyConfiguration(mut.ApplyConfiguration)
116+
if err != nil {
117+
return nil, err
118+
}
119+
114120
default:
115121
return nil, errors.New("no valid mutation mechanism provided")
116122
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
Copyright 2025 The KCP Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package transformer
18+
19+
import (
20+
"bytes"
21+
"fmt"
22+
"html/template"
23+
"strings"
24+
25+
"github.com/tidwall/gjson"
26+
"github.com/tidwall/sjson"
27+
28+
syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1"
29+
plugincel "k8s.io/apiserver/pkg/admission/plugin/cel"
30+
"k8s.io/apiserver/pkg/admission/plugin/policy/mutating/patch"
31+
32+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
33+
"k8s.io/apiserver/pkg/cel/environment"
34+
)
35+
36+
type applyConfigTransformer struct {
37+
path string
38+
tpl *template.Template
39+
}
40+
41+
func NewApplyConfiguration(mut *syncagentv1alpha1.ResourceApplyConfigurationMutation) (*applyConfigTransformer, error) {
42+
opts := plugincel.OptionalVariableDeclarations{HasParams: false, StrictCost: true, HasAuthorizer: true}
43+
compiler, err := plugincel.NewCompositedCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true))
44+
if err != nil {
45+
return nil, fmt.Errorf("failed to initialize CEL compiler: %w", err)
46+
}
47+
48+
// Compile and store variables
49+
compiler.CompileAndStoreVariables(convertv1alpha1Variables(policy.Spec.Variables), opts, environment.StoredExpressions)
50+
51+
accessor := &patch.ApplyConfigurationCondition{Expression: m.ApplyConfiguration.Expression}
52+
compileResult := compiler.CompileMutatingEvaluator(accessor, patchOptions, environment.StoredExpressions)
53+
patcher := patch.NewApplyConfigurationPatcher(compileResult)
54+
55+
return &applyConfigTransformer{
56+
path: mut.Path,
57+
tpl: tpl,
58+
}, nil
59+
}
60+
61+
func (m *applyConfigTransformer) Apply(toMutate *unstructured.Unstructured, otherObj *unstructured.Unstructured) (*unstructured.Unstructured, error) {
62+
encoded, err := EncodeObject(toMutate)
63+
if err != nil {
64+
return nil, fmt.Errorf("failed to JSON encode object: %w", err)
65+
}
66+
67+
ctx := templateMutationContext{
68+
Value: gjson.Get(encoded, m.path),
69+
LocalObject: toMutate.Object,
70+
}
71+
72+
if otherObj != nil {
73+
ctx.RemoteObject = otherObj.Object
74+
}
75+
76+
var buf bytes.Buffer
77+
if err := m.tpl.Execute(&buf, ctx); err != nil {
78+
return nil, fmt.Errorf("failed to execute template: %w", err)
79+
}
80+
81+
replacement := strings.TrimSpace(buf.String())
82+
83+
updated, err := sjson.Set(encoded, m.path, replacement)
84+
if err != nil {
85+
return nil, fmt.Errorf("failed to set updated value: %w", err)
86+
}
87+
88+
return DecodeObject(updated)
89+
}
90+
91+
// type rawGoVariable struct {
92+
// name string
93+
// expression string
94+
// }
95+
96+
// func (t *rawGoVariable) GetExpression() string {
97+
// return t.expression
98+
// }
99+
100+
// func (t *rawGoVariable) ReturnTypes() []*cel.Type {
101+
// return []*cel.Type{cel.AnyType}
102+
// }
103+
104+
// func (t *rawGoVariable) GetName() string {
105+
// return t.name
106+
// }

sdk/apis/syncagent/v1alpha1/published_resource.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,10 @@ type ResourceMutation struct {
160160
// Must use exactly one of these options, never more, never fewer.
161161
// TODO: Add validation code for this somewhere.
162162

163-
Delete *ResourceDeleteMutation `json:"delete,omitempty"`
164-
Regex *ResourceRegexMutation `json:"regex,omitempty"`
165-
Template *ResourceTemplateMutation `json:"template,omitempty"`
163+
Delete *ResourceDeleteMutation `json:"delete,omitempty"`
164+
Regex *ResourceRegexMutation `json:"regex,omitempty"`
165+
Template *ResourceTemplateMutation `json:"template,omitempty"`
166+
ApplyConfiguration *ResourceApplyConfigurationMutation `json:"applyConfig,omitempty"`
166167
}
167168

168169
type ResourceDeleteMutation struct {
@@ -182,6 +183,10 @@ type ResourceTemplateMutation struct {
182183
Template string `json:"template"`
183184
}
184185

186+
type ResourceApplyConfigurationMutation struct {
187+
Expression string `json:"expression"`
188+
}
189+
185190
type RelatedResourceOrigin string
186191

187192
const (

0 commit comments

Comments
 (0)