@@ -104,7 +104,23 @@ func (f *Function) parseInputAndCredentials(req *fnv1.RunFunctionRequest, rsp *f
104104
105105// getXRAndStatus retrieves status and desired XR, handling initialization if needed
106106func (f * Function ) getXRAndStatus (req * fnv1.RunFunctionRequest ) (map [string ]interface {}, * resource.Composite , error ) {
107- // Get both observed and desired XR
107+ // Get composite resources
108+ oxr , dxr , err := f .getObservedAndDesired (req )
109+ if err != nil {
110+ return nil , nil , err
111+ }
112+
113+ // Initialize and copy data
114+ f .initializeAndCopyData (oxr , dxr )
115+
116+ // Get status
117+ xrStatus := f .getStatusFromResources (oxr , dxr )
118+
119+ return xrStatus , dxr , nil
120+ }
121+
122+ // getObservedAndDesired gets both observed and desired XR resources
123+ func (f * Function ) getObservedAndDesired (req * fnv1.RunFunctionRequest ) (* resource.Composite , * resource.Composite , error ) {
108124 oxr , err := request .GetObservedCompositeResource (req )
109125 if err != nil {
110126 return nil , nil , errors .Wrap (err , "cannot get observed composite resource" )
@@ -115,31 +131,47 @@ func (f *Function) getXRAndStatus(req *fnv1.RunFunctionRequest) (map[string]inte
115131 return nil , nil , errors .Wrap (err , "cannot get desired composite resource" )
116132 }
117133
118- xrStatus := make (map [string ]interface {})
134+ return oxr , dxr , nil
135+ }
119136
137+ // initializeAndCopyData initializes metadata and copies spec
138+ func (f * Function ) initializeAndCopyData (oxr , dxr * resource.Composite ) {
120139 // Initialize dxr from oxr if needed
121140 if dxr .Resource .GetKind () == "" {
122141 dxr .Resource .SetAPIVersion (oxr .Resource .GetAPIVersion ())
123142 dxr .Resource .SetKind (oxr .Resource .GetKind ())
124143 dxr .Resource .SetName (oxr .Resource .GetName ())
125144 }
126145
146+ // Copy spec from observed to desired XR to preserve it
147+ xrSpec := make (map [string ]interface {})
148+ if err := oxr .Resource .GetValueInto ("spec" , & xrSpec ); err == nil && len (xrSpec ) > 0 {
149+ if err := dxr .Resource .SetValue ("spec" , xrSpec ); err != nil {
150+ f .log .Debug ("Cannot set spec in desired XR" , "error" , err )
151+ }
152+ }
153+ }
154+
155+ // getStatusFromResources gets status from desired or observed XR
156+ func (f * Function ) getStatusFromResources (oxr , dxr * resource.Composite ) map [string ]interface {} {
157+ xrStatus := make (map [string ]interface {})
158+
127159 // First try to get status from desired XR (pipeline changes)
128160 if dxr .Resource .GetKind () != "" {
129- err = dxr .Resource .GetValueInto ("status" , & xrStatus )
161+ err : = dxr .Resource .GetValueInto ("status" , & xrStatus )
130162 if err == nil && len (xrStatus ) > 0 {
131- return xrStatus , dxr , nil
163+ return xrStatus
132164 }
133165 f .log .Debug ("Cannot get status from Desired XR or it's empty" )
134166 }
135167
136168 // Fallback to observed XR status
137- err = oxr .Resource .GetValueInto ("status" , & xrStatus )
169+ err : = oxr .Resource .GetValueInto ("status" , & xrStatus )
138170 if err != nil {
139171 f .log .Debug ("Cannot get status from Observed XR" )
140172 }
141173
142- return xrStatus , dxr , nil
174+ return xrStatus
143175}
144176
145177// checkStatusTargetHasData checks if the status target has data.
@@ -1026,7 +1058,7 @@ func (f *Function) checkContextTargetHasData(req *fnv1.RunFunctionRequest, in *v
10261058 return false
10271059}
10281060
1029- // resolveGroupRef resolves the group name from a reference in status or context.
1061+ // resolveGroupRef resolves the group name from a reference in spec, status or context.
10301062func (f * Function ) resolveGroupRef (req * fnv1.RunFunctionRequest , groupRef * string ) (string , error ) {
10311063 if groupRef == nil || * groupRef == "" {
10321064 return "" , errors .New ("empty groupRef provided" )
@@ -1040,6 +1072,8 @@ func (f *Function) resolveGroupRef(req *fnv1.RunFunctionRequest, groupRef *strin
10401072 return f .resolveFromStatus (req , refKey )
10411073 case strings .HasPrefix (refKey , "context." ):
10421074 return f .resolveFromContext (req , refKey )
1075+ case strings .HasPrefix (refKey , "spec." ):
1076+ return f .resolveFromSpec (req , refKey )
10431077 default :
10441078 return "" , errors .Errorf ("unsupported groupRef format: %s" , refKey )
10451079 }
@@ -1071,7 +1105,30 @@ func (f *Function) resolveFromContext(req *fnv1.RunFunctionRequest, refKey strin
10711105 return value , nil
10721106}
10731107
1074- // resolveStringArrayRef resolves a list of string values from a reference in status or context
1108+ // resolveFromSpec resolves a reference from XR spec
1109+ func (f * Function ) resolveFromSpec (req * fnv1.RunFunctionRequest , refKey string ) (string , error ) {
1110+ // Use getXRAndStatus to ensure spec is copied to desired XR
1111+ _ , dxr , err := f .getXRAndStatus (req )
1112+ if err != nil {
1113+ return "" , errors .Wrap (err , "cannot get XR status and desired XR" )
1114+ }
1115+
1116+ // Get spec from the desired XR (which now has the spec copied from observed)
1117+ xrSpec := make (map [string ]interface {})
1118+ err = dxr .Resource .GetValueInto ("spec" , & xrSpec )
1119+ if err != nil {
1120+ return "" , errors .Wrap (err , "cannot get XR spec" )
1121+ }
1122+
1123+ specField := strings .TrimPrefix (refKey , "spec." )
1124+ value , ok := GetNestedKey (xrSpec , specField )
1125+ if ! ok {
1126+ return "" , errors .Errorf ("cannot resolve groupRef: %s not found" , refKey )
1127+ }
1128+ return value , nil
1129+ }
1130+
1131+ // resolveStringArrayRef resolves a list of string values from a reference in spec, status or context
10751132func (f * Function ) resolveStringArrayRef (req * fnv1.RunFunctionRequest , ref * string , refType string ) ([]* string , error ) {
10761133 if ref == nil || * ref == "" {
10771134 return nil , errors .Errorf ("empty %s provided" , refType )
@@ -1090,6 +1147,8 @@ func (f *Function) resolveStringArrayRef(req *fnv1.RunFunctionRequest, ref *stri
10901147 result , err = f .resolveStringArrayFromStatus (req , refKey )
10911148 case strings .HasPrefix (refKey , "context." ):
10921149 result , err = f .resolveStringArrayFromContext (req , refKey )
1150+ case strings .HasPrefix (refKey , "spec." ):
1151+ result , err = f .resolveStringArrayFromSpec (req , refKey )
10931152 default :
10941153 return nil , errors .Errorf ("unsupported %s format: %s" , refType , refKey )
10951154 }
@@ -1122,6 +1181,25 @@ func (f *Function) resolveStringArrayFromContext(req *fnv1.RunFunctionRequest, r
11221181 return f .extractStringArrayFromMap (contextMap , contextField , refKey )
11231182}
11241183
1184+ // resolveStringArrayFromSpec resolves a list of string values from XR spec
1185+ func (f * Function ) resolveStringArrayFromSpec (req * fnv1.RunFunctionRequest , refKey string ) ([]* string , error ) {
1186+ // Use getXRAndStatus to ensure spec is copied to desired XR
1187+ _ , dxr , err := f .getXRAndStatus (req )
1188+ if err != nil {
1189+ return nil , errors .Wrap (err , "cannot get XR status and desired XR" )
1190+ }
1191+
1192+ // Get spec from the desired XR (which now has the spec copied from observed)
1193+ xrSpec := make (map [string ]interface {})
1194+ err = dxr .Resource .GetValueInto ("spec" , & xrSpec )
1195+ if err != nil {
1196+ return nil , errors .Wrap (err , "cannot get XR spec" )
1197+ }
1198+
1199+ specField := strings .TrimPrefix (refKey , "spec." )
1200+ return f .extractStringArrayFromMap (xrSpec , specField , refKey )
1201+ }
1202+
11251203// resolveGroupsRef resolves a list of group names from a reference in status or context
11261204func (f * Function ) resolveGroupsRef (req * fnv1.RunFunctionRequest , groupsRef * string ) ([]* string , error ) {
11271205 return f .resolveStringArrayRef (req , groupsRef , "groupsRef" )
0 commit comments