@@ -99,7 +99,7 @@ async def run_function(self, request):
9999 return self .fatal (request , logger , 'Instantiate' , e )
100100
101101 step = composite .context ._pythonic [step ]
102- iteration = (step .iteration or 0 ) + 1
102+ iteration = int (step .iteration ) + 1
103103 step .iteration = iteration
104104 composite .context .iteration = iteration
105105 logger .debug (f"Starting compose, { ordinal (len (composite .context ._pythonic ))} step, { ordinal (iteration )} pass" )
@@ -111,7 +111,40 @@ async def run_function(self, request):
111111 except Exception as e :
112112 return self .fatal (request , logger , 'Compose' , e )
113113
114- requested = []
114+ if requireds := self .get_requireds (step , composite ):
115+ logger .info (f"Requireds requested: { ',' .join (requireds )} " )
116+ else :
117+ self .process_usages (composite )
118+ self .process_unknowns (composite )
119+ self .process_auto_readies (composite )
120+ logger .info ('Completed compose' )
121+
122+ return composite .response ._message
123+
124+ def fatal (self , request , logger , message , exception = None ):
125+ if exception :
126+ message += ' exceptiion'
127+ logger .exception (message )
128+ m = str (exception )
129+ if not m :
130+ m = exception .__class__ .__name__
131+ message += ': ' + m
132+ else :
133+ logger .error (message )
134+ return fnv1 .RunFunctionResponse (
135+ meta = fnv1 .ResponseMeta (
136+ tag = request .meta .tag ,
137+ ),
138+ results = [
139+ fnv1 .Result (
140+ severity = fnv1 .SEVERITY_FATAL ,
141+ message = message ,
142+ )
143+ ]
144+ )
145+
146+ def get_requireds (self , step , composite ):
147+ requireds = []
115148 for name , required in composite .requireds :
116149 if required .apiVersion and required .kind :
117150 r = pythonic .Map (apiVersion = required .apiVersion , kind = required .kind )
@@ -123,11 +156,82 @@ async def run_function(self, request):
123156 r .matchLabels [key ] = value
124157 if r != step .requireds [name ]:
125158 step .requireds [name ] = r
126- requested .append (name )
127- if requested :
128- logger .info (f"Requireds requested: { ',' .join (requested )} " )
129- return composite .response ._message
159+ requireds .append (name )
160+ return requireds
161+
162+ def process_usages (self , composite ):
163+ for _ , resource in sorted (entry for entry in composite .resources ):
164+ dependencies = resource .desired ._getDependencies
165+ if dependencies :
166+ if self .debug :
167+ for destination , source in sorted (dependencies .items ()):
168+ destination = self .trimFullName (destination )
169+ source = self .trimFullName (source )
170+ logger .debug (f"Dependency: { destination } = { source } " )
171+ if resource .usages or (resource .usages is None and composite .usages ):
172+ resources = {}
173+ requireds = {}
174+ for destination , source in sorted (dependencies .items ()):
175+ name = source .split ('.' )
176+ if (len (name ) > 5 and
177+ name [0 ] == 'request' and
178+ name [1 ] == 'observed' and
179+ name [2 ] == 'resources' and
180+ name [4 ] == 'resource'
181+ ):
182+ if name [3 ] not in resources :
183+ resources [name [3 ]] = []
184+ resources [name [3 ]].append (f"{ '.' .join (destination .split ('.' )[5 :])} = { '.' .join (name [5 :])} " )
185+ elif (len (name ) > 5 and
186+ name [0 ] == 'request' and
187+ name [1 ] == 'extra_resources' and
188+ name [3 ].startswith ('items[' ) and name [3 ][- 1 ] == ']' and
189+ name [4 ] == 'resource'
190+ ):
191+ key = (name [2 ], int (name [3 ][6 :- 1 ]))
192+ if key not in requireds :
193+ requireds [key ] = []
194+ requireds [key ].append (f"{ '.' .join (destination .split ('.' )[5 :])} = [{ key [1 ]} ]{ '.' .join (name [5 :])} " )
195+ for name , dependencies in resources .items ():
196+ source = composite .resources [name ]
197+ name = [resource .name , str (source .kind )]
198+ if source .metadata .namespace :
199+ name .append (str (source .metadata .namespace ))
200+ name .append (str (source .observed .metadata .name ))
201+ usage = composite .resources ['_' .join (name )]('protection.crossplane.io/v1beta1' , 'Usage' )
202+ if resource .metadata .namespace :
203+ usage .metadata .namespace = resource .metadata .namespace
204+ usage .spec .description = '\n ' .join (dependencies )
205+ usage .spec .replayDeletion = True
206+ usage .spec .of .apiVersion = resource .apiVersion
207+ usage .spec .of .kind = resource .kind
208+ usage .spec .of .resourceRef .name = resource .observed .metadata .name
209+ usage .spec .by .apiVersion = source .apiVersion
210+ usage .spec .by .kind = source .kind
211+ if source .metadata .namespace :
212+ usage .spec .by .resourceRef .namespace = source .metadata .namespace
213+ usage .spec .by .resourceRef .name = source .observed .metadata .name
214+ for key , dependencies in requireds .items ():
215+ source = composite .requireds [key [0 ]][key [1 ]]
216+ name = [resource .name , str (source .kind )]
217+ if source .metadata .namespace :
218+ name .append (str (source .metadata .namespace ))
219+ name .append (str (source .metadata .name ))
220+ usage = composite .resources ['_' .join (name )]('protection.crossplane.io/v1beta1' , 'Usage' )
221+ if resource .metadata .namespace :
222+ usage .metadata .namespace = resource .metadata .namespace
223+ usage .spec .description = '\n ' .join (dependencies )
224+ usage .spec .replayDeletion = True
225+ usage .spec .of .apiVersion = resource .apiVersion
226+ usage .spec .of .kind = resource .kind
227+ usage .spec .of .resourceRef .name = resource .observed .metadata .name
228+ usage .spec .by .apiVersion = source .apiVersion
229+ usage .spec .by .kind = source .kind
230+ if source .metadata .namespace :
231+ usage .spec .by .resourceRef .namespace = source .metadata .namespace
232+ usage .spec .by .resourceRef .name = source .observed .metadata .name
130233
234+ def process_unknowns (self , composite ):
131235 unknownResources = []
132236 warningResources = []
133237 fatalResources = []
@@ -190,42 +294,19 @@ async def run_function(self, request):
190294 if event :
191295 event (reason , message )
192296
297+ def process_auto_readies (self , composite ):
193298 for name , resource in composite .resources :
194299 if resource .autoReady or (resource .autoReady is None and composite .autoReady ):
195300 if resource .ready is None :
196301 if resource .conditions .Ready .status :
197302 resource .ready = True
198303
199- logger .info ('Completed compose' )
200- return composite .response ._message
201-
202- def fatal (self , request , logger , message , exception = None ):
203- if exception :
204- message += ' exceptiion'
205- logger .exception (message )
206- m = str (exception )
207- if not m :
208- m = exception .__class__ .__name__
209- message += ': ' + m
210- else :
211- logger .error (message )
212- return fnv1 .RunFunctionResponse (
213- meta = fnv1 .ResponseMeta (
214- tag = request .meta .tag ,
215- ),
216- results = [
217- fnv1 .Result (
218- severity = fnv1 .SEVERITY_FATAL ,
219- message = message ,
220- )
221- ]
222- )
223-
224304 def trimFullName (self , name ):
225305 name = name .split ('.' )
226306 for values in (
307+ ('request' , 'observed' , 'composite' , 'resource' ),
227308 ('request' , 'observed' , 'resources' , None , 'resource' ),
228- ('request' , 'extra_resources' , None , 'items' , 'resource' ),
309+ ('request' , 'extra_resources' , None , 'items' , None , 'resource' ),
229310 ('response' , 'desired' , 'resources' , None , 'resource' ),
230311 ):
231312 if len (values ) < len (name ):
0 commit comments