@@ -74,24 +74,14 @@ func (f *Function) RunFunction(ctx context.Context, req *fnv1.RunFunctionRequest
7474 switch {
7575 case in .QueryRef == nil :
7676 case strings .HasPrefix (* in .QueryRef , "status." ):
77- // The composite resource that actually exists.
78- oxr , err := request .GetObservedCompositeResource (req )
77+ err := getQueryFromStatus (req , in )
7978 if err != nil {
80- response .Fatal (rsp , errors . Wrap ( err , "cannot get observed composite resource" ) )
79+ response .Fatal (rsp , err )
8180 return rsp , nil
8281 }
83- xrStatus := make (map [string ]interface {})
84- err = oxr .Resource .GetValueInto ("status" , & xrStatus )
85- if err != nil {
86- response .Fatal (rsp , errors .Wrap (err , "cannot get XR status" ))
87- return rsp , nil
88- }
89- if queryFromXRStatus , ok := GetNestedContextKey (xrStatus , strings .TrimPrefix (* in .QueryRef , "status." )); ok {
90- in .Query = queryFromXRStatus
91- }
9282 case strings .HasPrefix (* in .QueryRef , "context." ):
9383 functionContext := req .GetContext ().AsMap ()
94- if queryFromContext , ok := GetNestedContextKey (functionContext , strings .TrimPrefix (* in .QueryRef , "context." )); ok {
84+ if queryFromContext , ok := GetNestedKey (functionContext , strings .TrimPrefix (* in .QueryRef , "context." )); ok {
9585 in .Query = queryFromContext
9686 }
9787 default :
@@ -118,39 +108,17 @@ func (f *Function) RunFunction(ctx context.Context, req *fnv1.RunFunctionRequest
118108
119109 switch {
120110 case strings .HasPrefix (in .Target , "status." ):
121- // The composite resource that actually exists.
122- oxr , err := request .GetObservedCompositeResource (req )
123- if err != nil {
124- response .Fatal (rsp , errors .Wrap (err , "cannot get observed composite resource" ))
125- return rsp , nil
126- }
127- // The composite resource desired by previous functions in the pipeline.
128- dxr , err := request .GetDesiredCompositeResource (req )
129- if err != nil {
130- response .Fatal (rsp , errors .Wrap (err , "cannot get desired composite resource" ))
131- return rsp , nil
132- }
133- dxr .Resource .SetAPIVersion (oxr .Resource .GetAPIVersion ())
134- dxr .Resource .SetKind (oxr .Resource .GetKind ())
135- TargetXRStatusField := in .Target
136- err = dxr .Resource .SetValue (TargetXRStatusField , & results .Data )
111+ err = putQueryResultToStatus (req , rsp , in , results , f )
137112 if err != nil {
138- response .Fatal (rsp , errors .Wrapf (err , "cannot set field %s to %s for %s" , TargetXRStatusField , results .Data , dxr .Resource .GetKind ()))
139- return rsp , nil
140- }
141- if err := response .SetDesiredCompositeResource (rsp , dxr ); err != nil {
142- response .Fatal (rsp , errors .Wrapf (err , "cannot set desired composite resource in %T" , rsp ))
113+ response .Fatal (rsp , err )
143114 return rsp , nil
144115 }
145116 case strings .HasPrefix (in .Target , "context." ):
146- contextField := strings .TrimPrefix (in .Target , "context." )
147- data , err := structpb .NewValue (results .Data )
117+ err = putQueryResultToContext (req , rsp , in , results , f )
148118 if err != nil {
149- response .Fatal (rsp , errors . Wrap ( err , "cannot convert results data to structpb.Value" ) )
119+ response .Fatal (rsp , err )
150120 return rsp , nil
151121 }
152- f .log .Debug ("Updating Composition environment" , "key" , contextField , "data" , & results .Data )
153- response .SetContextKey (rsp , contextField , data )
154122 default :
155123 response .Fatal (rsp , errors .Errorf ("Unrecognized target field: %s" , in .Target ))
156124 return rsp , nil
@@ -229,24 +197,35 @@ func (a *AzureQuery) azQuery(ctx context.Context, azureCreds map[string]string,
229197 return results , nil
230198}
231199
232- // GetNestedContextKey retrieves a nested string value from a map using dot notation keys.
233- func GetNestedContextKey (context map [string ]interface {}, key string ) (string , bool ) {
200+ // ParseNestedKey enables the bracket and dot notation to key reference
201+ func ParseNestedKey (key string ) ([]string , error ) {
202+ var parts []string
234203 // Regular expression to extract keys, supporting both dot and bracket notation
235- keyRegex := regexp .MustCompile (`\[([^\[\]]+)\]|([^.\[\]]+)` )
236- matches := keyRegex .FindAllStringSubmatch (key , - 1 )
237-
238- // Extract all keys in the proper order
239- var keys []string
204+ regex := regexp .MustCompile (`\[([^\[\]]+)\]|([^.\[\]]+)` )
205+ matches := regex .FindAllStringSubmatch (key , - 1 )
240206 for _ , match := range matches {
241207 if match [1 ] != "" {
242- keys = append (keys , match [1 ]) // Bracket key
208+ parts = append (parts , match [1 ]) // Bracket notation
243209 } else if match [2 ] != "" {
244- keys = append (keys , match [2 ]) // Dot key
210+ parts = append (parts , match [2 ]) // Dot notation
245211 }
246212 }
247- currentValue := interface {}(context )
248213
249- for _ , k := range keys {
214+ if len (parts ) == 0 {
215+ return nil , errors .New ("invalid key" )
216+ }
217+ return parts , nil
218+ }
219+
220+ // GetNestedKey retrieves a nested string value from a map using dot notation keys.
221+ func GetNestedKey (context map [string ]interface {}, key string ) (string , bool ) {
222+ parts , err := ParseNestedKey (key )
223+ if err != nil {
224+ return "" , false
225+ }
226+
227+ currentValue := interface {}(context )
228+ for _ , k := range parts {
250229 // Check if the current value is a map
251230 if nestedMap , ok := currentValue .(map [string ]interface {}); ok {
252231 // Get the next value in the nested map
@@ -266,3 +245,119 @@ func GetNestedContextKey(context map[string]interface{}, key string) (string, bo
266245 }
267246 return "" , false
268247}
248+
249+ // SetNestedKey sets a value to a nested key from a map using dot notation keys.
250+ func SetNestedKey (root map [string ]interface {}, key string , value interface {}) error {
251+ parts , err := ParseNestedKey (key )
252+ if err != nil {
253+ return err
254+ }
255+
256+ current := root
257+ for i , part := range parts {
258+ if i == len (parts )- 1 {
259+ // Set the value at the final key
260+ current [part ] = value
261+ return nil
262+ }
263+
264+ // Traverse into nested maps or create them if they don't exist
265+ if next , exists := current [part ]; exists {
266+ if nextMap , ok := next .(map [string ]interface {}); ok {
267+ current = nextMap
268+ } else {
269+ return fmt .Errorf ("key %q exists but is not a map" , part )
270+ }
271+ } else {
272+ // Create a new map if the path doesn't exist
273+ newMap := make (map [string ]interface {})
274+ current [part ] = newMap
275+ current = newMap
276+ }
277+ }
278+
279+ return nil
280+ }
281+
282+ func getQueryFromStatus (req * fnv1.RunFunctionRequest , in * v1beta1.Input ) error {
283+ oxr , err := request .GetObservedCompositeResource (req )
284+ if err != nil {
285+ return errors .Wrap (err , "cannot get observed composite resource" )
286+ }
287+ xrStatus := make (map [string ]interface {})
288+ err = oxr .Resource .GetValueInto ("status" , & xrStatus )
289+ if err != nil {
290+ return errors .Wrap (err , "cannot get XR status" )
291+ }
292+ if queryFromXRStatus , ok := GetNestedKey (xrStatus , strings .TrimPrefix (* in .QueryRef , "status." )); ok {
293+ in .Query = queryFromXRStatus
294+ }
295+ return nil
296+ }
297+
298+ func putQueryResultToStatus (req * fnv1.RunFunctionRequest , rsp * fnv1.RunFunctionResponse , in * v1beta1.Input , results armresourcegraph.ClientResourcesResponse , f * Function ) error {
299+ oxr , err := request .GetObservedCompositeResource (req )
300+ if err != nil {
301+ return errors .Wrap (err , "cannot get observed composite resource" )
302+ }
303+ // The composite resource desired by previous functions in the pipeline.
304+ dxr , err := request .GetDesiredCompositeResource (req )
305+ if err != nil {
306+ return errors .Wrap (err , "cannot get desired composite resource" )
307+ }
308+ dxr .Resource .SetAPIVersion (oxr .Resource .GetAPIVersion ())
309+ dxr .Resource .SetKind (oxr .Resource .GetKind ())
310+
311+ xrStatus := make (map [string ]interface {})
312+ err = oxr .Resource .GetValueInto ("status" , & xrStatus )
313+ if err != nil {
314+ f .log .Debug ("Cannot get status from XR" )
315+ }
316+
317+ // Update the specific status field using the reusable function
318+ statusField := strings .TrimPrefix (in .Target , "status." )
319+ err = SetNestedKey (xrStatus , statusField , results .Data )
320+ if err != nil {
321+ return errors .Wrapf (err , "cannot set status field %s to %v" , statusField , results .Data )
322+ }
323+
324+ // Write the updated status field back into the composite resource
325+ if err := dxr .Resource .SetValue ("status" , xrStatus ); err != nil {
326+ return errors .Wrap (err , "cannot write updated status back into composite resource" )
327+ }
328+
329+ // Save the updated desired composite resource
330+ if err := response .SetDesiredCompositeResource (rsp , dxr ); err != nil {
331+ return errors .Wrapf (err , "cannot set desired composite resource in %T" , rsp )
332+ }
333+ return nil
334+ }
335+
336+ func putQueryResultToContext (req * fnv1.RunFunctionRequest , rsp * fnv1.RunFunctionResponse , in * v1beta1.Input , results armresourcegraph.ClientResourcesResponse , f * Function ) error {
337+
338+ contextField := strings .TrimPrefix (in .Target , "context." )
339+ data , err := structpb .NewValue (results .Data )
340+ if err != nil {
341+ return errors .Wrap (err , "cannot convert results data to structpb.Value" )
342+ }
343+
344+ // Convert existing context into a map[string]interface{}
345+ contextMap := req .GetContext ().AsMap ()
346+
347+ err = SetNestedKey (contextMap , contextField , data .AsInterface ())
348+ if err != nil {
349+ return errors .Wrap (err , "failed to update context key" )
350+ }
351+
352+ f .log .Debug ("Updating Composition Pipeline Context" , "key" , contextField , "data" , & results .Data )
353+
354+ // Convert the updated context back into structpb.Struct
355+ updatedContext , err := structpb .NewStruct (contextMap )
356+ if err != nil {
357+ return errors .Wrap (err , "failed to serialize updated context" )
358+ }
359+
360+ // Set the updated context
361+ rsp .Context = updatedContext
362+ return nil
363+ }
0 commit comments