forked from crossplane-contrib/function-patch-and-transform
-
Notifications
You must be signed in to change notification settings - Fork 1
/
render.go
151 lines (134 loc) · 6.09 KB
/
render.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package main
import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/json"
"github.com/crossplane/crossplane-runtime/pkg/errors"
"github.com/crossplane/crossplane-runtime/pkg/resource"
"github.com/crossplane/function-sdk-go/resource/composed"
"github.com/crossplane/function-sdk-go/resource/composite"
"github.com/stevendborrelli/function-conditional-patch-and-transform/input/v1beta1"
)
// Error strings
const (
errUnmarshalJSON = "cannot unmarshal JSON data"
errFmtKindChanged = "cannot change the kind of a composed resource from %s to %s (possible composed resource template mismatch)"
errFmtNamePrefixLabel = "cannot find top-level composite resource name label %q in composite resource metadata"
// TODO(negz): Include more detail such as field paths if they exist.
// Perhaps require each patch type to have a String() method to help
// identify it.
errFmtPatch = "cannot apply the %q patch at index %d"
)
// RenderFromJSON renders the supplied resource from JSON bytes.
func RenderFromJSON(o resource.Object, data []byte) error {
gvk := o.GetObjectKind().GroupVersionKind()
name := o.GetName()
namespace := o.GetNamespace()
if err := json.Unmarshal(data, o); err != nil {
return errors.Wrap(err, errUnmarshalJSON)
}
// TODO(negz): Should we return an error if the name or namespace change,
// rather than just silently re-setting it? Presumably these _changing_ is a
// sign that something has gone wrong, similar to the GVK changing. What
// about the UID changing?
// Unmarshalling the template will overwrite any existing fields, so we must
// restore the existing name, if any.
o.SetName(name)
o.SetNamespace(namespace)
// This resource already had a GVK (probably because it already exists), but
// when we rendered its template it changed. This shouldn't happen. Either
// someone changed the kind in the template or we're trying to use the wrong
// template (e.g. because the order of an array of anonymous templates
// changed).
empty := schema.GroupVersionKind{}
if gvk != empty && o.GetObjectKind().GroupVersionKind() != gvk {
return errors.Errorf(errFmtKindChanged, gvk, o.GetObjectKind().GroupVersionKind())
}
return nil
}
// RenderEnvironmentPatches renders the supplied environment by applying all
// patches that are to the environment, from the supplied XR.
func RenderEnvironmentPatches(env *unstructured.Unstructured, oxr, dxr *composite.Unstructured, ps []v1beta1.EnvironmentPatch) error {
for i, p := range ps {
p := p
switch p.Type {
case v1beta1.PatchTypeToEnvironmentFieldPath, v1beta1.PatchTypeCombineToEnvironment:
if err := ApplyToObjects(&p, env, oxr); err != nil {
return errors.Wrapf(err, errFmtPatch, p.Type, i)
}
case v1beta1.PatchTypeFromEnvironmentFieldPath, v1beta1.PatchTypeCombineFromEnvironment:
if err := ApplyToObjects(&p, env, dxr); err != nil {
return errors.Wrapf(err, errFmtPatch, p.Type, i)
}
case v1beta1.PatchTypePatchSet, v1beta1.PatchTypeFromCompositeFieldPath, v1beta1.PatchTypeCombineFromComposite, v1beta1.PatchTypeToCompositeFieldPath, v1beta1.PatchTypeCombineToComposite:
// nothing to do
}
}
return nil
}
// RenderComposedPatches renders the supplied composed resource by applying all
// patches that are to or from the supplied composite resource and environment
// in the order they were defined. Properly selecting the right source or
// destination between observed and desired resources.
func RenderComposedPatches( //nolint:gocyclo // just a switch
ocd *composed.Unstructured,
dcd *composed.Unstructured,
oxr *composite.Unstructured,
dxr *composite.Unstructured,
env *unstructured.Unstructured,
ps []v1beta1.ComposedPatch,
) (errs []error, store bool) {
for i, p := range ps {
p := p
switch t := p.Type; t {
case v1beta1.PatchTypeToCompositeFieldPath, v1beta1.PatchTypeCombineToComposite:
// TODO(negz): Should failures to patch the XR be terminal? It could
// indicate a required patch failed. A required patch means roughly
// "this patch has to succeed before you mutate the resource". This
// is useful to make sure we never create a composed resource in the
// wrong state. It's less clear how useful it is for the XR, given
// we'll only ever be updating it, not creating it.
// We want to patch the XR from observed composed resources, not
// from desired state. This is because folks will typically be
// patching from a field that is set once the observed resource is
// applied such as its status.
if ocd == nil {
continue
}
if err := ApplyToObjects(&p, dxr, ocd); err != nil {
errs = append(errs, errors.Wrapf(err, errFmtPatch, t, i))
}
case v1beta1.PatchTypeToEnvironmentFieldPath, v1beta1.PatchTypeCombineToEnvironment:
// TODO(negz): Same as above, but for the Environment. What does it
// mean for a required patch to the environment to fail? Should it
// be terminal?
// Run all patches that are from the (observed) composed resource to
// the environment.
if ocd == nil {
continue
}
if err := ApplyToObjects(&p, env, ocd); err != nil {
errs = append(errs, errors.Wrapf(err, errFmtPatch, t, i))
}
// If either of the below renderings return an error, most likely a
// required FromComposite or FromEnvironment patch failed. A required
// patch means roughly "this patch has to succeed before you mutate the
// resource." This is useful to make sure we never create a composed
// resource in the wrong state. To that end, we don't want to add this
// resource to our accumulated desired state.
case v1beta1.PatchTypeFromCompositeFieldPath, v1beta1.PatchTypeCombineFromComposite:
if err := ApplyToObjects(&p, oxr, dcd); err != nil {
errs = append(errs, errors.Wrapf(err, errFmtPatch, t, i))
return errs, false
}
case v1beta1.PatchTypeFromEnvironmentFieldPath, v1beta1.PatchTypeCombineFromEnvironment:
if err := ApplyToObjects(&p, env, dcd); err != nil {
errs = append(errs, errors.Wrapf(err, errFmtPatch, t, i))
return errs, false
}
case v1beta1.PatchTypePatchSet:
// Already resolved - nothing to do.
}
}
return errs, true
}