From cf8cbaaa4e992c7ad731e3df88a99be348418403 Mon Sep 17 00:00:00 2001 From: Truong Nguyen Date: Wed, 29 May 2024 15:05:53 +0200 Subject: [PATCH] Add support for To|FromEnvironmentFieldPath type in environment patches Resolves #123 Signed-off-by: Truong Nguyen --- fn_test.go | 108 ++++++++++++++++++ input/v1beta1/resources_patches.go | 2 +- .../input/pt.fn.crossplane.io_resources.yaml | 2 + patches.go | 10 +- validate.go | 4 +- 5 files changed, 119 insertions(+), 7 deletions(-) diff --git a/fn_test.go b/fn_test.go index 59afee3..565c801 100644 --- a/fn_test.go +++ b/fn_test.go @@ -896,6 +896,114 @@ func TestRunFunction(t *testing.T) { Context: contextWithEnvironment(map[string]interface{}{ "widgets": "30", })}}}, + "PatchToCompositeSupportsFromEnvironmentFieldPath": { + reason: "A basic FromEnvironmentFieldPath patch should work with environment.patches.", + args: args{ + req: &fnv1beta1.RunFunctionRequest{ + Input: resource.MustStructObject(&v1beta1.Resources{ + Resources: []v1beta1.ComposedTemplate{ + { + Name: "cool-resource", + Base: &runtime.RawExtension{Raw: []byte(`{"apiVersion":"example.org/v1","kind":"CD"}`)}, + }}, + Environment: &v1beta1.Environment{ + Patches: []v1beta1.EnvironmentPatch{ + { + Type: v1beta1.PatchTypeFromEnvironmentFieldPath, + Patch: v1beta1.Patch{ + FromFieldPath: ptr.To[string]("widgets"), + ToFieldPath: ptr.To[string]("spec.watchers"), + Transforms: []v1beta1.Transform{ + { + Type: v1beta1.TransformTypeConvert, + Convert: &v1beta1.ConvertTransform{ + ToType: v1beta1.TransformIOTypeInt64, + }, + }, + { + Type: v1beta1.TransformTypeMath, + Math: &v1beta1.MathTransform{ + Type: v1beta1.MathTransformTypeMultiply, + Multiply: ptr.To[int64](3), + }}}}}}}}), + Observed: &fnv1beta1.State{ + Composite: &fnv1beta1.Resource{ + Resource: resource.MustStructJSON(`{"apiVersion":"example.org/v1","kind":"CD","spec":{}}`), + }, + Resources: map[string]*fnv1beta1.Resource{}, + }, + Context: contextWithEnvironment(map[string]interface{}{ + "widgets": "10", + })}, + }, + want: want{ + rsp: &fnv1beta1.RunFunctionResponse{ + Meta: &fnv1beta1.ResponseMeta{Ttl: durationpb.New(response.DefaultTTL)}, + Desired: &fnv1beta1.State{ + Composite: &fnv1beta1.Resource{ + // spec.watchers = 10 * 3 = 30 + Resource: resource.MustStructJSON(`{"apiVersion":"example.org/v1","kind":"CD","spec":{"watchers":30}}`), + }, + Resources: map[string]*fnv1beta1.Resource{ + "cool-resource": { + Resource: resource.MustStructJSON(`{"apiVersion":"example.org/v1","kind":"CD"}`), + }}}, + Context: contextWithEnvironment(map[string]interface{}{ + "widgets": "10", + })}}}, + "EnvironmentPatchSupportsToEnvironmentFieldPath": { + reason: "ToEnvironmentFieldPath patch should work with environment.patches.", + args: args{ + req: &fnv1beta1.RunFunctionRequest{ + Input: resource.MustStructObject(&v1beta1.Resources{ + Resources: []v1beta1.ComposedTemplate{ + { + Name: "cool-resource", + Base: &runtime.RawExtension{Raw: []byte(`{"apiVersion":"example.org/v1","kind":"CD"}`)}, + }}, + Environment: &v1beta1.Environment{ + Patches: []v1beta1.EnvironmentPatch{ + { + Type: v1beta1.PatchTypeToEnvironmentFieldPath, + Patch: v1beta1.Patch{ + FromFieldPath: ptr.To[string]("spec.watchers"), + ToFieldPath: ptr.To[string]("widgets"), + Transforms: []v1beta1.Transform{ + { + Type: v1beta1.TransformTypeMath, + Math: &v1beta1.MathTransform{ + Type: v1beta1.MathTransformTypeMultiply, + Multiply: ptr.To[int64](3), + }, + }, + { + Type: v1beta1.TransformTypeConvert, + Convert: &v1beta1.ConvertTransform{ + ToType: v1beta1.TransformIOTypeString, + }, + }}}}}}}), + Observed: &fnv1beta1.State{ + Composite: &fnv1beta1.Resource{ + Resource: resource.MustStructJSON(`{"apiVersion":"example.org/v1","kind":"CD","spec":{"watchers":10}}`), + }, + Resources: map[string]*fnv1beta1.Resource{}, + }, + Context: contextWithEnvironment(nil)}, + }, + want: want{ + rsp: &fnv1beta1.RunFunctionResponse{ + Meta: &fnv1beta1.ResponseMeta{Ttl: durationpb.New(response.DefaultTTL)}, + Desired: &fnv1beta1.State{ + Composite: &fnv1beta1.Resource{ + Resource: resource.MustStructJSON(`{"apiVersion":"example.org/v1","kind":"CD"}`), + }, + Resources: map[string]*fnv1beta1.Resource{ + "cool-resource": { + Resource: resource.MustStructJSON(`{"apiVersion":"example.org/v1","kind":"CD"}`), + }}}, + Context: contextWithEnvironment(map[string]interface{}{ + "widgets": "30", + })}}}, "EnvironmentPatchOptionalNotFoundSkipped": { reason: "A basic ToEnvironment patch with optional or not field path policy should be skipped", args: args{ diff --git a/input/v1beta1/resources_patches.go b/input/v1beta1/resources_patches.go index 2dc2959..17b8774 100644 --- a/input/v1beta1/resources_patches.go +++ b/input/v1beta1/resources_patches.go @@ -109,7 +109,7 @@ type EnvironmentPatch struct { // Type sets the patching behaviour to be used. Each patch type may require // its own fields to be set on the Patch object. // +optional - // +kubebuilder:validation:Enum=FromCompositeFieldPath;ToCompositeFieldPath;CombineFromComposite;CombineToComposite + // +kubebuilder:validation:Enum=FromCompositeFieldPath;ToCompositeFieldPath;CombineFromComposite;CombineToComposite;FromEnvironmentFieldPath;ToEnvironmentFieldPath // +kubebuilder:default=FromCompositeFieldPath Type PatchType `json:"type,omitempty"` diff --git a/package/input/pt.fn.crossplane.io_resources.yaml b/package/input/pt.fn.crossplane.io_resources.yaml index d58d4a8..a077022 100644 --- a/package/input/pt.fn.crossplane.io_resources.yaml +++ b/package/input/pt.fn.crossplane.io_resources.yaml @@ -371,6 +371,8 @@ spec: - ToCompositeFieldPath - CombineFromComposite - CombineToComposite + - FromEnvironmentFieldPath + - ToEnvironmentFieldPath type: string type: object type: array diff --git a/patches.go b/patches.go index 6ffecf0..69c10a1 100644 --- a/patches.go +++ b/patches.go @@ -198,21 +198,21 @@ func ApplyCombineFromVariablesPatch(p PatchInterface, from, to runtime.Object) e func ApplyEnvironmentPatch(p *v1beta1.EnvironmentPatch, env *unstructured.Unstructured, oxr, dxr *composite.Unstructured) error { switch p.GetType() { // From observed XR to environment. - case v1beta1.PatchTypeFromCompositeFieldPath: + case v1beta1.PatchTypeFromCompositeFieldPath, + v1beta1.PatchTypeToEnvironmentFieldPath: return ApplyFromFieldPathPatch(p, oxr, env) case v1beta1.PatchTypeCombineFromComposite: return ApplyCombineFromVariablesPatch(p, oxr, env) // From environment to desired XR. - case v1beta1.PatchTypeToCompositeFieldPath: + case v1beta1.PatchTypeToCompositeFieldPath, + v1beta1.PatchTypeFromEnvironmentFieldPath: return ApplyFromFieldPathPatch(p, env, dxr) case v1beta1.PatchTypeCombineToComposite: return ApplyCombineFromVariablesPatch(p, env, dxr) // Invalid patch types in this context. - case v1beta1.PatchTypeFromEnvironmentFieldPath, - v1beta1.PatchTypeCombineFromEnvironment, - v1beta1.PatchTypeToEnvironmentFieldPath, + case v1beta1.PatchTypeCombineFromEnvironment, v1beta1.PatchTypeCombineToEnvironment: // Nothing to do. diff --git a/validate.go b/validate.go index 4290bff..6165f3a 100644 --- a/validate.go +++ b/validate.go @@ -103,7 +103,9 @@ func ValidateEnvironment(e *v1beta1.Environment) *field.Error { v1beta1.PatchTypeFromCompositeFieldPath, v1beta1.PatchTypeToCompositeFieldPath, v1beta1.PatchTypeCombineFromComposite, - v1beta1.PatchTypeCombineToComposite: + v1beta1.PatchTypeCombineToComposite, + v1beta1.PatchTypeFromEnvironmentFieldPath, + v1beta1.PatchTypeToEnvironmentFieldPath: default: return field.Invalid(field.NewPath("patches").Index(i).Key("type"), p.GetType(), "invalid environment patch type") }