|
7 | 7 | "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
8 | 8 |
|
9 | 9 | "github.com/crossplane/crossplane-runtime/pkg/errors"
|
| 10 | + "github.com/crossplane/crossplane-runtime/pkg/fieldpath" |
10 | 11 | "github.com/crossplane/crossplane-runtime/pkg/logging"
|
11 | 12 | "github.com/crossplane/crossplane-runtime/pkg/reconciler/managed"
|
12 | 13 |
|
@@ -114,10 +115,14 @@ func (f *Function) RunFunction(ctx context.Context, req *fnv1beta1.RunFunctionRe
|
114 | 115 | }
|
115 | 116 |
|
116 | 117 | if input.Environment != nil {
|
117 |
| - // Run all patches that are from the (observed) XR to the environment or from the environment to the (desired) XR. |
118 |
| - if err := RenderEnvironmentPatches(env, oxr.Resource, dxr.Resource, input.Environment.Patches); err != nil { |
119 |
| - response.Fatal(rsp, errors.Wrapf(err, "cannot render ToEnvironment patches from the composite resource")) |
120 |
| - return rsp, nil |
| 118 | + // Run all patches that are from the (observed) XR to the environment or |
| 119 | + // from the environment to the (desired) XR. |
| 120 | + for i := range input.Environment.Patches { |
| 121 | + p := &input.Environment.Patches[i] |
| 122 | + if err := ApplyEnvironmentPatch(p, env, oxr.Resource, dxr.Resource); err != nil { |
| 123 | + response.Fatal(rsp, errors.Wrapf(err, "cannot apply the %q environment patch at index %d", p.GetType(), i)) |
| 124 | + return rsp, nil |
| 125 | + } |
121 | 126 | }
|
122 | 127 | }
|
123 | 128 |
|
@@ -155,8 +160,8 @@ func (f *Function) RunFunction(ctx context.Context, req *fnv1beta1.RunFunctionRe
|
155 | 160 | }
|
156 | 161 | }
|
157 | 162 |
|
158 |
| - ocd, ok := observed[resource.Name(t.Name)] |
159 |
| - if ok { |
| 163 | + ocd, exists := observed[resource.Name(t.Name)] |
| 164 | + if exists { |
160 | 165 | existing++
|
161 | 166 | log.Debug("Resource template corresponds to existing composed resource", "metadata-name", ocd.Resource.GetName())
|
162 | 167 |
|
@@ -192,10 +197,53 @@ func (f *Function) RunFunction(ctx context.Context, req *fnv1beta1.RunFunctionRe
|
192 | 197 | "name", ocd.Resource.GetName())
|
193 | 198 | }
|
194 | 199 |
|
195 |
| - // TODO(negz): No need for multiple errors? |
196 |
| - if err := RenderComposedPatches(ocd.Resource, dcd.Resource, oxr.Resource, dxr.Resource, env, t.Patches); err != nil { |
197 |
| - response.Fatal(rsp, errors.Wrapf(err, "cannot render patches for composed resource %q", t.Name)) |
198 |
| - return rsp, nil |
| 200 | + // Run all patches that are to a desired composed resource, or from an |
| 201 | + // observed composed resource. |
| 202 | + skip := false |
| 203 | + for i := range t.Patches { |
| 204 | + p := &t.Patches[i] |
| 205 | + if err := ApplyComposedPatch(p, ocd.Resource, dcd.Resource, oxr.Resource, dxr.Resource, env); err != nil { |
| 206 | + if fieldpath.IsNotFound(err) { |
| 207 | + // This is a patch from a required field path that does not |
| 208 | + // exist. The point of FromFieldPathPolicyRequired is to |
| 209 | + // block creation of the new 'to' resource until the 'from' |
| 210 | + // field path exists. |
| 211 | + // |
| 212 | + // The only kind of resource we could be patching to that |
| 213 | + // might not exist at this point is a composed resource. So |
| 214 | + // if we're patching to a composed resource that doesn't |
| 215 | + // exist we want to avoid creating it. Otherwise, we just |
| 216 | + // treat the patch from a required field path the same way |
| 217 | + // we'd treat a patch from an optional field path and skip |
| 218 | + // it. |
| 219 | + if p.GetPolicy().GetFromFieldPathPolicy() == v1beta1.FromFieldPathPolicyRequired { |
| 220 | + if ToComposedResource(p) && !exists { |
| 221 | + response.Warning(rsp, errors.Wrapf(err, "not adding new composed resource %q to desired state because %q patch at index %d has 'policy.fromFieldPath: Required'", t.Name, p.GetType(), i)) |
| 222 | + |
| 223 | + // There's no point processing further patches. |
| 224 | + // They'll either be from an observed composed |
| 225 | + // resource that doesn't exist yet, or to a desired |
| 226 | + // composed resource that we'll discard. |
| 227 | + skip = true |
| 228 | + break |
| 229 | + } |
| 230 | + response.Warning(rsp, errors.Wrapf(err, "cannot render composed resource %q %q patch at index %d: ignoring 'policy.fromFieldPath: Required' because 'to' resource already exists", t.Name, p.GetType(), i)) |
| 231 | + } |
| 232 | + |
| 233 | + // If any optional field path isn't found we just skip this |
| 234 | + // patch and move on. The path may be populated by a |
| 235 | + // subsequent patch. |
| 236 | + continue |
| 237 | + } |
| 238 | + response.Fatal(rsp, errors.Wrapf(err, "cannot render composed resource %q %q patch at index %d", t.Name, p.GetType(), i)) |
| 239 | + return rsp, nil |
| 240 | + } |
| 241 | + } |
| 242 | + |
| 243 | + // Skip adding this resource to the desired state because it doesn't |
| 244 | + // exist yet, and a required FromFieldPath was not (yet) found. |
| 245 | + if skip { |
| 246 | + continue |
199 | 247 | }
|
200 | 248 |
|
201 | 249 | desired[resource.Name(t.Name)] = dcd
|
|
0 commit comments