Skip to content

Commit 873f26b

Browse files
committed
fix: run patches in order for composed resources
Signed-off-by: Philippe Scorsolini <p.scorsolini@gmail.com>
1 parent f6c8462 commit 873f26b

File tree

3 files changed

+80
-48
lines changed

3 files changed

+80
-48
lines changed

fn.go

Lines changed: 8 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -196,58 +196,19 @@ func (f *Function) RunFunction(ctx context.Context, req *fnv1beta1.RunFunctionRe
196196
log.Debug("Found corresponding observed resource",
197197
"ready", ready,
198198
"name", ocd.Resource.GetName())
199-
200-
// TODO(negz): Should failures to patch the XR be terminal? It could
201-
// indicate a required patch failed. A required patch means roughly
202-
// "this patch has to succeed before you mutate the resource". This
203-
// is useful to make sure we never create a composed resource in the
204-
// wrong state. It's less clear how useful it is for the XR, given
205-
// we'll only ever be updating it, not creating it.
206-
207-
// We want to patch the XR from observed composed resources, not
208-
// from desired state. This is because folks will typically be
209-
// patching from a field that is set once the observed resource is
210-
// applied such as its status.
211-
if err := RenderToCompositePatches(dxr.Resource, ocd.Resource, t.Patches); err != nil {
212-
response.Warning(rsp, errors.Wrapf(err, "cannot render ToComposite patches for composed resource %q", t.Name))
213-
log.Info("Cannot render ToComposite patches for composed resource", "warning", err)
214-
warnings++
215-
}
216-
217-
// TODO(negz): Same as above, but for the Environment. What does it
218-
// mean for a required patch to the environment to fail? Should it
219-
// be terminal?
220-
221-
// Run all patches that are from the (observed) composed resource to
222-
// the environment.
223-
if err := RenderToEnvironmentPatches(env, ocd.Resource, t.Patches); err != nil {
224-
response.Warning(rsp, errors.Wrapf(err, "cannot render ToEnvironment patches for composed resource %q", t.Name))
225-
log.Info("Cannot render ToEnvironment patches for composed resource", "warning", err)
226-
warnings++
227-
}
228199
}
229200

230-
// If either of the below renderings return an error, most likely a
231-
// required FromComposite or FromEnvironment patch failed. A required
232-
// patch means roughly "this patch has to succeed before you mutate the
233-
// resource." This is useful to make sure we never create a composed
234-
// resource in the wrong state. To that end, we don't want to add this
235-
// resource to our accumulated desired state.
236-
if err := RenderFromCompositePatches(dcd.Resource, oxr.Resource, t.Patches); err != nil {
237-
response.Warning(rsp, errors.Wrapf(err, "cannot render FromComposite patches for composed resource %q", t.Name))
238-
log.Info("Cannot render FromComposite patches for composed resource", "warning", err)
201+
errs, store := RenderComposedPatches(ocd.Resource, dcd.Resource, oxr.Resource, dxr.Resource, env, t.Patches)
202+
for _, err := range errs {
203+
response.Warning(rsp, errors.Wrapf(err, "cannot render patches for composed resource %q", t.Name))
204+
log.Info("Cannot render patches for composed resource", "warning", err)
239205
warnings++
240-
continue
241-
}
242-
if err := RenderFromEnvironmentPatches(dcd.Resource, env, t.Patches); err != nil {
243-
response.Warning(rsp, errors.Wrapf(err, "cannot render FromEnvironment patches for composed resource %q", t.Name))
244-
log.Info("Cannot render FromEnvironment patches for composed resource", "warning", err)
245-
warnings++
246-
continue
247206
}
248207

249-
// Add or replace our desired resource.
250-
desired[resource.Name(t.Name)] = dcd
208+
if store {
209+
// Add or replace our desired resource.
210+
desired[resource.Name(t.Name)] = dcd
211+
}
251212
}
252213

253214
if err := response.SetDesiredCompositeResource(rsp, dxr); err != nil {

fn_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,7 @@ func TestRunFunction(t *testing.T) {
447447
Results: []*fnv1beta1.Result{
448448
{
449449
Severity: fnv1beta1.Severity_SEVERITY_WARNING,
450-
Message: fmt.Sprintf("cannot render FromComposite patches for composed resource %q: cannot apply the %q patch at index 1: spec.doesNotExist: no such field", "cool-resource", "FromCompositeFieldPath"),
450+
Message: fmt.Sprintf("cannot render patches for composed resource %q: cannot apply the %q patch at index 1: spec.doesNotExist: no such field", "cool-resource", "FromCompositeFieldPath"),
451451
},
452452
},
453453
Context: &structpb.Struct{Fields: map[string]*structpb.Value{fncontext.KeyEnvironment: structpb.NewStructValue(nil)}},

render.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ import (
99
"github.com/crossplane/crossplane-runtime/pkg/errors"
1010
"github.com/crossplane/crossplane-runtime/pkg/resource"
1111

12+
"github.com/crossplane/function-sdk-go/resource/composed"
13+
"github.com/crossplane/function-sdk-go/resource/composite"
14+
1215
"github.com/crossplane-contrib/function-patch-and-transform/input/v1beta1"
1316
)
1417

@@ -102,3 +105,71 @@ func RenderToEnvironmentPatches(env *unstructured.Unstructured, o runtime.Object
102105
}
103106
return nil
104107
}
108+
109+
// RenderComposedPatches renders the supplied composed resource by applying all
110+
// patches that are to or from the supplied composite resource and environment
111+
// in the order they were defined. Properly selecting the right source or
112+
// destination between observed and desired resources.
113+
func RenderComposedPatches( //nolint:gocyclo // just a switch
114+
ocd *composed.Unstructured,
115+
dcd *composed.Unstructured,
116+
oxr *composite.Unstructured,
117+
dxr *composite.Unstructured,
118+
env *unstructured.Unstructured,
119+
ps []v1beta1.Patch,
120+
) (errs []error, store bool) {
121+
for i, p := range ps {
122+
switch t := p.Type; t {
123+
case v1beta1.PatchTypeToCompositeFieldPath, v1beta1.PatchTypeCombineToComposite:
124+
// TODO(negz): Should failures to patch the XR be terminal? It could
125+
// indicate a required patch failed. A required patch means roughly
126+
// "this patch has to succeed before you mutate the resource". This
127+
// is useful to make sure we never create a composed resource in the
128+
// wrong state. It's less clear how useful it is for the XR, given
129+
// we'll only ever be updating it, not creating it.
130+
131+
// We want to patch the XR from observed composed resources, not
132+
// from desired state. This is because folks will typically be
133+
// patching from a field that is set once the observed resource is
134+
// applied such as its status.
135+
if ocd == nil {
136+
continue
137+
}
138+
if err := ApplyToObjects(p, dxr, ocd); err != nil {
139+
errs = append(errs, errors.Wrapf(err, errFmtPatch, t, i))
140+
}
141+
case v1beta1.PatchTypeToEnvironmentFieldPath, v1beta1.PatchTypeCombineToEnvironment:
142+
// TODO(negz): Same as above, but for the Environment. What does it
143+
// mean for a required patch to the environment to fail? Should it
144+
// be terminal?
145+
146+
// Run all patches that are from the (observed) composed resource to
147+
// the environment.
148+
if ocd == nil {
149+
continue
150+
}
151+
if err := ApplyToObjects(p, env, ocd); err != nil {
152+
errs = append(errs, errors.Wrapf(err, errFmtPatch, t, i))
153+
}
154+
// If either of the below renderings return an error, most likely a
155+
// required FromComposite or FromEnvironment patch failed. A required
156+
// patch means roughly "this patch has to succeed before you mutate the
157+
// resource." This is useful to make sure we never create a composed
158+
// resource in the wrong state. To that end, we don't want to add this
159+
// resource to our accumulated desired state.
160+
case v1beta1.PatchTypeFromCompositeFieldPath, v1beta1.PatchTypeCombineFromComposite:
161+
if err := ApplyToObjects(p, oxr, dcd); err != nil {
162+
errs = append(errs, errors.Wrapf(err, errFmtPatch, t, i))
163+
return errs, false
164+
}
165+
case v1beta1.PatchTypeFromEnvironmentFieldPath, v1beta1.PatchTypeCombineFromEnvironment:
166+
if err := ApplyToObjects(p, env, dcd); err != nil {
167+
errs = append(errs, errors.Wrapf(err, errFmtPatch, t, i))
168+
return errs, false
169+
}
170+
case v1beta1.PatchTypePatchSet:
171+
// Already resolved - nothing to do.
172+
}
173+
}
174+
return errs, true
175+
}

0 commit comments

Comments
 (0)