Skip to content

Commit 901f6a0

Browse files
authored
Merge pull request #122 from phisco/allow-only-env-patches
fix: allow using function only to patch to and from the environment
2 parents aac361b + 3896496 commit 901f6a0

File tree

4 files changed

+66
-16
lines changed

4 files changed

+66
-16
lines changed

fn.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,14 @@ func (f *Function) RunFunction(ctx context.Context, req *fnv1beta1.RunFunctionRe
115115
log.Debug("Loaded Composition environment from Function context", "context-key", fncontext.KeyEnvironment)
116116
}
117117

118+
// Patching code assumes that the environment has a GVK, as it uses
119+
// runtime.DefaultUnstructuredConverter.FromUnstructured. This is a bit odd,
120+
// but it's what we've done in the past. We'll set a default GVK here if one
121+
// isn't set.
122+
if env.GroupVersionKind().Empty() {
123+
env.SetGroupVersionKind(internalEnvironmentGVK)
124+
}
125+
118126
if input.Environment != nil {
119127
// Run all patches that are from the (observed) XR to the environment or
120128
// from the environment to the (desired) XR.

fn_test.go

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212
"google.golang.org/protobuf/types/known/structpb"
1313
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1414
"k8s.io/apimachinery/pkg/runtime"
15-
"k8s.io/apimachinery/pkg/runtime/schema"
1615
"k8s.io/utils/ptr"
1716

1817
"github.com/crossplane/crossplane-runtime/pkg/logging"
@@ -52,7 +51,7 @@ func TestRunFunction(t *testing.T) {
5251
Results: []*fnv1beta1.Result{
5352
{
5453
Severity: fnv1beta1.Severity_SEVERITY_FATAL,
55-
Message: "invalid Function input: resources: Required value: resources is required",
54+
Message: "invalid Function input: resources: Required value: resources or environment patches are required",
5655
},
5756
},
5857
},
@@ -95,7 +94,7 @@ func TestRunFunction(t *testing.T) {
9594
},
9695
},
9796
},
98-
Context: &structpb.Struct{Fields: map[string]*structpb.Value{fncontext.KeyEnvironment: structpb.NewStructValue(nil)}},
97+
Context: contextWithEnvironment(nil),
9998
},
10099
},
101100
},
@@ -126,7 +125,7 @@ func TestRunFunction(t *testing.T) {
126125
},
127126
},
128127
},
129-
Context: &structpb.Struct{Fields: map[string]*structpb.Value{fncontext.KeyEnvironment: structpb.NewStructValue(nil)}},
128+
Context: contextWithEnvironment(nil),
130129
},
131130
},
132131
want: want{
@@ -145,7 +144,7 @@ func TestRunFunction(t *testing.T) {
145144
},
146145
},
147146
},
148-
Context: &structpb.Struct{Fields: map[string]*structpb.Value{fncontext.KeyEnvironment: structpb.NewStructValue(nil)}},
147+
Context: contextWithEnvironment(nil),
149148
},
150149
},
151150
},
@@ -210,7 +209,7 @@ func TestRunFunction(t *testing.T) {
210209
},
211210
},
212211
},
213-
Context: &structpb.Struct{Fields: map[string]*structpb.Value{fncontext.KeyEnvironment: structpb.NewStructValue(nil)}},
212+
Context: contextWithEnvironment(nil),
214213
},
215214
},
216215
},
@@ -282,7 +281,7 @@ func TestRunFunction(t *testing.T) {
282281
},
283282
},
284283
},
285-
Context: &structpb.Struct{Fields: map[string]*structpb.Value{fncontext.KeyEnvironment: structpb.NewStructValue(nil)}},
284+
Context: contextWithEnvironment(nil),
286285
},
287286
},
288287
},
@@ -380,7 +379,7 @@ func TestRunFunction(t *testing.T) {
380379
},
381380
},
382381
},
383-
Context: &structpb.Struct{Fields: map[string]*structpb.Value{fncontext.KeyEnvironment: structpb.NewStructValue(nil)}},
382+
Context: contextWithEnvironment(nil),
384383
},
385384
},
386385
},
@@ -436,7 +435,7 @@ func TestRunFunction(t *testing.T) {
436435
},
437436
},
438437
},
439-
Context: &structpb.Struct{Fields: map[string]*structpb.Value{fncontext.KeyEnvironment: structpb.NewStructValue(nil)}},
438+
Context: contextWithEnvironment(nil),
440439
},
441440
},
442441
},
@@ -537,7 +536,7 @@ func TestRunFunction(t *testing.T) {
537536
// Note "new-resource" doesn't appear here.
538537
},
539538
},
540-
Context: &structpb.Struct{Fields: map[string]*structpb.Value{fncontext.KeyEnvironment: structpb.NewStructValue(nil)}},
539+
Context: contextWithEnvironment(nil),
541540
Results: []*fnv1beta1.Result{
542541
{
543542
Severity: fnv1beta1.Severity_SEVERITY_WARNING,
@@ -652,7 +651,7 @@ func TestRunFunction(t *testing.T) {
652651
},
653652
},
654653
},
655-
Context: &structpb.Struct{Fields: map[string]*structpb.Value{fncontext.KeyEnvironment: structpb.NewStructValue(nil)}},
654+
Context: contextWithEnvironment(nil),
656655
},
657656
},
658657
},
@@ -715,7 +714,7 @@ func TestRunFunction(t *testing.T) {
715714
},
716715
},
717716
},
718-
Context: &structpb.Struct{Fields: map[string]*structpb.Value{fncontext.KeyEnvironment: structpb.NewStructValue(nil)}},
717+
Context: contextWithEnvironment(nil),
719718
},
720719
},
721720
},
@@ -785,7 +784,7 @@ func TestRunFunction(t *testing.T) {
785784
},
786785
},
787786
},
788-
Context: &structpb.Struct{Fields: map[string]*structpb.Value{fncontext.KeyEnvironment: structpb.NewStructValue(nil)}},
787+
Context: contextWithEnvironment(nil),
789788
},
790789
},
791790
},
@@ -1198,6 +1197,44 @@ func TestRunFunction(t *testing.T) {
11981197
Context: contextWithEnvironment(map[string]interface{}{
11991198
"widgets": "10",
12001199
})}}},
1200+
"OnlyEnvironmentPatchesIsAllowed": {
1201+
reason: "Having only environment patches should be allowed and work as expected.",
1202+
args: args{
1203+
req: &fnv1beta1.RunFunctionRequest{
1204+
Input: resource.MustStructObject(&v1beta1.Resources{
1205+
Environment: &v1beta1.Environment{
1206+
Patches: []v1beta1.EnvironmentPatch{
1207+
{
1208+
Type: v1beta1.PatchTypeFromCompositeFieldPath,
1209+
Patch: v1beta1.Patch{
1210+
FromFieldPath: ptr.To[string]("spec.widgets"),
1211+
ToFieldPath: ptr.To[string]("envKey"),
1212+
},
1213+
},
1214+
},
1215+
},
1216+
}),
1217+
Observed: &fnv1beta1.State{
1218+
Composite: &fnv1beta1.Resource{
1219+
Resource: resource.MustStructJSON(`{"apiVersion":"example.org/v1","kind":"XR","spec":{"widgets":"10"}}`),
1220+
},
1221+
},
1222+
},
1223+
},
1224+
want: want{
1225+
rsp: &fnv1beta1.RunFunctionResponse{
1226+
Meta: &fnv1beta1.ResponseMeta{Ttl: durationpb.New(response.DefaultTTL)},
1227+
Desired: &fnv1beta1.State{
1228+
Composite: &fnv1beta1.Resource{
1229+
Resource: resource.MustStructJSON(`{"apiVersion":"example.org/v1","kind":"XR"}`),
1230+
},
1231+
},
1232+
Context: contextWithEnvironment(map[string]interface{}{
1233+
"envKey": "10",
1234+
}),
1235+
},
1236+
},
1237+
},
12011238
}
12021239

12031240
for name, tc := range cases {
@@ -1227,7 +1264,7 @@ func contextWithEnvironment(data map[string]interface{}) *structpb.Struct {
12271264
data = map[string]interface{}{}
12281265
}
12291266
u := unstructured.Unstructured{Object: data}
1230-
u.SetGroupVersionKind(schema.GroupVersionKind{Group: "internal.crossplane.io", Version: "v1alpha1", Kind: "Environment"})
1267+
u.SetGroupVersionKind(internalEnvironmentGVK)
12311268
d, err := structpb.NewStruct(u.UnstructuredContent())
12321269
if err != nil {
12331270
panic(err)

patches.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/pkg/errors"
88
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
99
"k8s.io/apimachinery/pkg/runtime"
10+
"k8s.io/apimachinery/pkg/runtime/schema"
1011
"k8s.io/apimachinery/pkg/util/json"
1112
"k8s.io/utils/ptr"
1213

@@ -30,6 +31,10 @@ const (
3031
errFmtInvalidPatchPolicy = "invalid patch policy %s"
3132
)
3233

34+
var (
35+
internalEnvironmentGVK = schema.GroupVersionKind{Group: "internal.crossplane.io", Version: "v1alpha1", Kind: "Environment"}
36+
)
37+
3338
// A PatchInterface is a patch that can be applied between resources.
3439
type PatchInterface interface {
3540
GetType() v1beta1.PatchType

validate.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ func ValidateResources(r *v1beta1.Resources) *field.Error {
3939
return err
4040
}
4141
}
42-
if len(r.Resources) == 0 {
43-
return field.Required(field.NewPath("resources"), "resources is required")
42+
if len(r.Resources) == 0 && (r.Environment == nil || len(r.Environment.Patches) == 0) {
43+
return field.Required(field.NewPath("resources"), "resources or environment patches are required")
4444
}
4545
for i, r := range r.Resources {
4646
if err := ValidateComposedTemplate(r); err != nil {

0 commit comments

Comments
 (0)