crossplane-function-pythonic 0.2.0__py3-none-any.whl → 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
1
  # This is set at build time, using "hatch version"
2
- __version__ = "0.2.0"
2
+ __version__ = "0.3.0"
@@ -0,0 +1,153 @@
1
+
2
+
3
+ def process(composite):
4
+ for name, resource in composite.resources:
5
+ if resource.observed:
6
+ if resource.autoReady or (resource.autoReady is None and composite.autoReady):
7
+ if resource.ready is None:
8
+ if _checks.get((resource.apiVersion, resource.kind), _check_default).ready(resource):
9
+ resource.ready = True
10
+
11
+
12
+ class ConditionReady:
13
+ def ready(self, resource):
14
+ return bool(resource.conditions.Ready.status)
15
+
16
+ _checks = {}
17
+ _check_default = ConditionReady()
18
+
19
+ class Check:
20
+ @classmethod
21
+ def __init_subclass__(cls, **kwargs):
22
+ super().__init_subclass__(**kwargs)
23
+ if hasattr(cls, 'apiVersion'):
24
+ _checks[(cls.apiVersion, cls.__name__)] = cls()
25
+
26
+ def ready(self, resource):
27
+ raise NotImplementedError()
28
+
29
+ class AlwaysReady(Check):
30
+ def ready(self, resource):
31
+ return True
32
+
33
+
34
+ class ClusterRole(AlwaysReady):
35
+ apiVersion = 'rbac.authorization.k8s.io/v1'
36
+
37
+ class ClusterRoleBinding(AlwaysReady):
38
+ apiVersion = 'rbac.authorization.k8s.io/v1'
39
+
40
+ class ConfigMap(AlwaysReady):
41
+ apiVersion = 'v1'
42
+
43
+ class CronJob(Check):
44
+ apiVersion = 'batch/v1'
45
+ def ready(self, resource):
46
+ if resource.observed.spec.suspend and len(resource.observed.spec.suspend):
47
+ return True
48
+ if not resource.status.lastScheduleTime:
49
+ return False
50
+ if resource.status.active:
51
+ return True
52
+ if not resource.status.lastSuccessfulTime:
53
+ return False
54
+ return str(resource.status.lastSuccessfulTime) >= str(resource.status.lastScheduleTime)
55
+
56
+ class DaemonSet(Check):
57
+ apiVersion = 'apps/v1'
58
+ def ready(self, resource):
59
+ if not resource.status.desiredNumberScheduled:
60
+ return False
61
+ scheduled = resource.status.desiredNumberScheduled
62
+ return (scheduled == resource.status.numberReady and
63
+ scheduled == resource.status.updatedNumberScheduled and
64
+ scheduled == resource.status.numberAvailable
65
+ )
66
+
67
+ class Deployment(Check):
68
+ apiVersion = 'apps/v1'
69
+ def ready(self, resource):
70
+ replicas = resource.observed.spec.replicas or 1
71
+ if resource.status.updatedReplicas != replicas or resource.status.availableReplicas != replicas:
72
+ return False
73
+ return bool(resource.conditions.Available.status)
74
+
75
+ class HorizontalPodAutoscaler(Check):
76
+ apiVersion = 'autoscaling/v2'
77
+ def ready(self, resource):
78
+ for type in ('FailedGetScale', 'FailedUpdateScale', 'FailedGetResourceMetric', 'InvalidSelector'):
79
+ if resource.conditions[type].status:
80
+ return False
81
+ for type in ('ScalingActive', 'ScalingLimited'):
82
+ if resource.conditions[type].status:
83
+ return True
84
+ return False
85
+
86
+ class Ingress(Check):
87
+ apiVersion = 'networking.k8s.io/v1'
88
+ def ready(self, resource):
89
+ return len(resource.status.loadBalancer.ingress) > 0
90
+
91
+ class Job(Check):
92
+ apiVersion = 'batch/v1'
93
+ def ready(self, resource):
94
+ for type in ('Failed', 'Suspended'):
95
+ if resource.conditions[type].status:
96
+ return False
97
+ return bool(resource.conditions.Complete.status)
98
+
99
+ class Namespace(AlwaysReady):
100
+ apiVersion = 'v1'
101
+
102
+ class PersistentVolumeClaim(Check):
103
+ apiVersion = 'v1'
104
+ def ready(self, resource):
105
+ return resource.status.phase == 'Bound'
106
+
107
+ class Pod(Check):
108
+ apiVersion = 'v1'
109
+ def ready(self, resource):
110
+ if resource.status.phase == 'Succeeded':
111
+ return True
112
+ if resource.status.phase == 'Running':
113
+ if resource.observed.spec.restartPolicy == 'Always':
114
+ if resource.conditions.Ready.status:
115
+ return True
116
+ return False
117
+
118
+ class ReplicaSet(Check):
119
+ apiVersion = 'v1'
120
+ def ready(self, resource):
121
+ if int(resource.status.observedGeneration) < int(resource.observed.metadata.generation):
122
+ return False
123
+ if resource.conditions.ReplicaFailure.status:
124
+ return False
125
+ return int(resource.status.availableReplicas) >= int(resource.observed.spec.replicas or 1)
126
+
127
+ class Role(AlwaysReady):
128
+ apiVersion = 'rbac.authorization.k8s.io/v1'
129
+
130
+ class RoleBinding(AlwaysReady):
131
+ apiVersion = 'rbac.authorization.k8s.io/v1'
132
+
133
+ class Secret(AlwaysReady):
134
+ apiVersion = 'v1'
135
+
136
+ class Service(Check):
137
+ apiVersion = 'v1'
138
+ def ready(self, resource):
139
+ if resource.observed.spec.type != 'LoadBalancer':
140
+ return True
141
+ return len(resource.status.loadBalancer.ingress) > 0
142
+
143
+ class ServiceAccount(AlwaysReady):
144
+ apiVersion = 'v1'
145
+
146
+ class StatefulSet(Check):
147
+ apiVersion = 'apps/v1'
148
+ def ready(self, resource):
149
+ replicas = resource.observed.spec.replicas or 1
150
+ return (resource.status.readyReplicas == replicas and
151
+ resource.status.currentReplicas == replicas and
152
+ resource.status.currentRevision == resource.status.updateRevision
153
+ )
@@ -50,6 +50,11 @@ class Command:
50
50
  action='store_true',
51
51
  help='Allow oversized protobuf messages',
52
52
  )
53
+ parser.add_argument(
54
+ '--crossplane-v1',
55
+ action='store_true',
56
+ help='Enable Crossplane V1 compatibility mode',
57
+ )
53
58
 
54
59
  def __init__(self, args):
55
60
  self.args = args
@@ -9,8 +9,86 @@ from . import protobuf
9
9
  _notset = object()
10
10
 
11
11
 
12
+ class ConnectionSecret:
13
+ def __get__(self, composite, objtype=None):
14
+ if composite.crossplane_v1:
15
+ return composite.spec.writeConnectionSecretToRef
16
+ secret = getattr(composite, '_connectionSecret', None)
17
+ if not secret:
18
+ secret = protobuf.Map()
19
+ for key, value in composite.request.input.writeConnectionSecretToRef:
20
+ secret[key] = value
21
+ composite._connectionSecret = secret
22
+ return secret
23
+
24
+ def __set__(self, composite, values):
25
+ if composite.crossplane_v1:
26
+ if values != composite.spec.writeConnectionSecretToRef:
27
+ raise NotImplementedError('Connection Secret cannot be set in Crossplane V1')
28
+ return
29
+ secret = protobuf.Map()
30
+ for key, value in values:
31
+ secret[key] = value
32
+ composite._connectionSecret = secret
33
+
34
+
35
+ class Connection:
36
+ def __get__(self, composite, objtype=None):
37
+ connection = getattr(composite, '_connection', None)
38
+ if not connection:
39
+ connection = _Connection(composite)
40
+ composite._connection = connection
41
+ return connection
42
+
43
+ def __set__(self, composite, values):
44
+ connection = self.__get__(composite)
45
+ coneection()
46
+ for key, value in values:
47
+ connection[key] = value
48
+
49
+
50
+ class TTL:
51
+ def __get__(self, composite, objtype=None):
52
+ if composite.response.meta.ttl.nanos:
53
+ return float(composite.response.meta.ttl.seconds) + (float(composite.response.meta.ttl.nanos) / 1000000000.0)
54
+ return int(composite.response.meta.ttl.seconds)
55
+
56
+ def __set__(self, composite, ttl):
57
+ if isinstance(ttl, int):
58
+ composite.response.meta.ttl.seconds = ttl
59
+ composite.response.meta.ttl.nanos = 0
60
+ elif isinstance(ttl, float):
61
+ composite.response.meta.ttl.seconds = int(ttl)
62
+ if ttl.is_integer():
63
+ composite.response.meta.ttl.nanos = 0
64
+ else:
65
+ composite.response.meta.ttl.nanos = int((ttl - int(composite.response.meta.ttl.seconds)) * 1000000000)
66
+ else:
67
+ raise ValueError('ttl must be an int or float')
68
+
69
+
70
+ class Ready:
71
+ def __get__(self, composite, objtype=None):
72
+ ready = composite.desired._parent.ready
73
+ if ready == fnv1.Ready.READY_TRUE:
74
+ return True
75
+ if ready == fnv1.Ready.READY_FALSE:
76
+ return False
77
+ return None
78
+
79
+ def __set__(self, composite, ready):
80
+ if ready:
81
+ ready = fnv1.Ready.READY_TRUE
82
+ elif ready == None or (isinstance(ready, protobuf.Value) and ready._isUnknown):
83
+ ready = fnv1.Ready.READY_UNSPECIFIED
84
+ else:
85
+ ready = fnv1.Ready.READY_FALSE
86
+ composite.desired._parent.ready = ready
87
+
88
+
12
89
  class BaseComposite:
13
- def __init__(self, request, single_use, logger):
90
+ def __init__(self, crossplane_v1, request, single_use, logger):
91
+ self.crossplane_v1 = crossplane_v1
14
92
  self.request = protobuf.Message(None, 'request', request.DESCRIPTOR, request, 'Function Request')
15
93
  response = fnv1.RunFunctionResponse(
16
94
  meta=fnv1.ResponseMeta(
@@ -50,54 +128,10 @@ class BaseComposite:
50
128
  self.results = Results(self.response)
51
129
  self.events = Results(self.response) # Deprecated, use self.results
52
130
 
53
- @property
54
- def ttl(self):
55
- if self.response.meta.ttl.nanos:
56
- return float(self.response.meta.ttl.seconds) + (float(self.response.meta.ttl.nanos) / 1000000000.0)
57
- return int(self.response.meta.ttl.seconds)
58
-
59
- @ttl.setter
60
- def ttl(self, ttl):
61
- if isinstance(ttl, int):
62
- self.response.meta.ttl.seconds = ttl
63
- self.response.meta.ttl.nanos = 0
64
- elif isinstance(ttl, float):
65
- self.response.meta.ttl.seconds = int(ttl)
66
- if ttl.is_integer():
67
- self.response.meta.ttl.nanos = 0
68
- else:
69
- self.response.meta.ttl.nanos = int((ttl - int(self.response.meta.ttl.seconds)) * 1000000000)
70
- else:
71
- raise ValueError('ttl must be an int or float')
72
-
73
- @property
74
- def connection(self):
75
- return self.response.desired.composite.connection_details
76
-
77
- @connection.setter
78
- def connection(self, connection):
79
- self.response.desired.composite.connection_details()
80
- for key, value in connection:
81
- self.response.desired.composite.connection_details[key] = value
82
-
83
- @property
84
- def ready(self):
85
- ready = self.desired._parent.ready
86
- if ready == fnv1.Ready.READY_TRUE:
87
- return True
88
- if ready == fnv1.Ready.READY_FALSE:
89
- return False
90
- return None
91
-
92
- @ready.setter
93
- def ready(self, ready):
94
- if ready:
95
- ready = fnv1.Ready.READY_TRUE
96
- elif ready == None or (isinstance(ready, protobuf.Value) and ready._isUnknown):
97
- ready = fnv1.Ready.READY_UNSPECIFIED
98
- else:
99
- ready = fnv1.Ready.READY_FALSE
100
- self.desired._parent.ready = ready
131
+ ttl = TTL()
132
+ connectionSecret = ConnectionSecret()
133
+ connection = Connection()
134
+ ready = Ready()
101
135
 
102
136
  async def compose(self):
103
137
  raise NotImplementedError()
@@ -262,6 +296,14 @@ class Resource:
262
296
  def spec(self, spec):
263
297
  self.desired.spec = spec
264
298
 
299
+ @property
300
+ def type(self):
301
+ return self.desired.type
302
+
303
+ @type.setter
304
+ def type(self, type):
305
+ self.desired.type = type
306
+
265
307
  @property
266
308
  def data(self):
267
309
  return self.desired.data
@@ -314,25 +356,43 @@ class Requireds:
314
356
 
315
357
  def __len__(self):
316
358
  names = set()
317
- for name, resource in self._composite.request.extra_resources:
318
- names.add(name)
319
- for name, resource in self._composite.response.requirements.extra_resources:
320
- names.add(name)
359
+ if self._composite.crossplane_v1:
360
+ for name, resource in self._composite.request.extra_resources:
361
+ names.add(name)
362
+ for name, resource in self._composite.response.requirements.extra_resources:
363
+ names.add(name)
364
+ else:
365
+ for name, resource in self._composite.request.required_resources:
366
+ names.add(name)
367
+ for name, resource in self._composite.response.requirements.resources:
368
+ names.add(name)
321
369
  return len(names)
322
370
 
323
371
  def __contains__(self, key):
324
- if key in self._composite.request.extra_resources:
325
- return True
326
- if key in self._composite.response.desired.resources:
327
- return True
372
+ if self._composite.crossplane_v1:
373
+ if key in self._composite.request.extra_resources:
374
+ return True
375
+ if key in self._composite.response.requirements.extra_resources:
376
+ return True
377
+ else:
378
+ if key in self._composite.request.required_resources:
379
+ return True
380
+ if key in self._composite.response.requirements.resources:
381
+ return True
328
382
  return False
329
383
 
330
384
  def __iter__(self):
331
385
  names = set()
332
- for name, resource in self._composite.request.extra_resources:
333
- names.add(name)
334
- for name, resource in self._composite.response.requirements.extra_resources:
335
- names.add(name)
386
+ if self._composite.crossplane_v1:
387
+ for name, resource in self._composite.request.extra_resources:
388
+ names.add(name)
389
+ for name, resource in self._composite.response.requirements.extra_resources:
390
+ names.add(name)
391
+ else:
392
+ for name, resource in self._composite.request.required_resources:
393
+ names.add(name)
394
+ for name, resource in self._composite.response.requirements.resources:
395
+ names.add(name)
336
396
  for name in sorted(names):
337
397
  yield name, self[name]
338
398
 
@@ -340,8 +400,12 @@ class Requireds:
340
400
  class RequiredResources:
341
401
  def __init__(self, composite, name):
342
402
  self.name = name
343
- self._selector = composite.response.requirements.extra_resources[name]
344
- self._resources = composite.request.extra_resources[name]
403
+ if composite.crossplane_v1:
404
+ self._selector = composite.response.requirements.extra_resources[name]
405
+ self._resources = composite.request.extra_resources[name]
406
+ else:
407
+ self._selector = composite.response.requirements.resources[name]
408
+ self._resources = composite.request.required_resources[name]
345
409
  self._cache = {}
346
410
 
347
411
  def __call__(self, apiVersion=_notset, kind=_notset, namespace=_notset, name=_notset, labels=_notset):
@@ -431,9 +495,11 @@ class RequiredResource:
431
495
  self.kind = self.observed.kind
432
496
  self.metadata = self.observed.metadata
433
497
  self.spec = self.observed.spec
498
+ self.type = self.observed.type
434
499
  self.data = self.observed.data
435
500
  self.status = self.observed.status
436
501
  self.conditions = Conditions(resource)
502
+ self.connection = self.observed.connection_details
437
503
 
438
504
  def __bool__(self):
439
505
  return bool(self.observed)
@@ -721,3 +787,119 @@ class Result:
721
787
  self._result.target = fnv1.Target.TARGET_UNSPECIFIED
722
788
  else:
723
789
  self._result.target = fnv1.Target.TARGET_COMPOSITE
790
+
791
+
792
+ class _Connection:
793
+ def __init__(self, composite):
794
+ self._set_attribute('_composite', composite)
795
+
796
+ def _set_attribute(self, key, value):
797
+ self.__dict__[key] = value
798
+
799
+ @property
800
+ def _resource_name(self):
801
+ return self._composite.connectionSecret.resourceName or 'connection-secret'
802
+
803
+ @property
804
+ def observed(self):
805
+ if self._composite.crossplane_v1:
806
+ return self._composite.response.observed.composite.connection_details
807
+ data = protobuf.Map()
808
+ for key, value in self._composite.resources[self._resource_name].observed.data:
809
+ data[key] = protobuf.B64Decode(value)
810
+ return data
811
+
812
+ def __getattr__(self, key):
813
+ return self[key]
814
+
815
+ def __getitem__(self, key):
816
+ if self._composite.crossplane_v1:
817
+ return self._composite.response.desired.composite.connection_details[key]
818
+ value = self._composite.resources[self._resource_name].data[key]
819
+ if value:
820
+ value = protobuf.B64Decode(value)
821
+ return value
822
+
823
+ def __bool__(self):
824
+ if self._composite.crossplane_v1:
825
+ return bool(self._composite.response.desired.composite.connection_details)
826
+ return bool(self._composite.resources[self._resource_name].data)
827
+
828
+ def __len__(self):
829
+ if self._composite.crossplane_v1:
830
+ return len(self._composite.response.desired.composite.connection_details)
831
+ return len(self._composite.resources[self._resource_name].data)
832
+
833
+ def __contains__(self, key):
834
+ if self._composite.crossplane_v1:
835
+ return key in self._composite.response.desired.composite.connection_details
836
+
837
+ def __iter__(self):
838
+ keys = set()
839
+ if self._composite.crossplane_v1:
840
+ for key, value in self._composite.response.desired.composite.connection_details:
841
+ yield key, value
842
+ for key, value in self._composite.resources[self._resource_name].data:
843
+ yield key, protobuf.B64Decode(value)
844
+
845
+ def __str__(self):
846
+ return format(self)
847
+
848
+ def __format__(self, spec='yaml'):
849
+ if self._composite.crossplane_v1:
850
+ return format(self._composite.response.desired.composite.connection_details, spec)
851
+ data = protobuf.Map()
852
+ for key, value in self._composite.resources[self._resource_name].data:
853
+ data[key] = protobuf.B64Decode(value)
854
+ return format(data, spec)
855
+
856
+ def __call__(self, **kwargs):
857
+ if self._composite_v1:
858
+ self._composite.response.desired.composite.connection_details(**kwargs)
859
+ return
860
+ del self._composite.resources[self._resource_name]
861
+ for key, value in kwargs:
862
+ self[key] = value
863
+
864
+ def __setattr__(self, key, value):
865
+ self[key] = value
866
+
867
+ def __setitem__(self, key, value):
868
+ if not isinstance(value, str):
869
+ if value is None:
870
+ return
871
+ if isinstance(value, (protobuf.FieldMessage, protobuf.Value)):
872
+ if not value:
873
+ return
874
+ value = str(value)
875
+ if self._composite.crossplane_v1:
876
+ self._composite.response.desired.composite.connection_details[key] = value
877
+ return
878
+ #if not self._composite.connectionSecret.name:
879
+ # return
880
+ if self._resource_name in self._composite.resources:
881
+ secret = self._composite.resources[self._resource_name]
882
+ else:
883
+ secret = self._composite.resources[self._resource_name]('v1', 'Secret')
884
+ print(bool(self._composite.connectionSecret.name), len(self._composite.connectionSecret.name))
885
+ if self._composite.connectionSecret.name and len(self._composite.connectionSecret.name):
886
+ secret.metadata.name = self._composite.connectionSecret.name
887
+ if not self._composite.metadata.namespace:
888
+ if not self._composite.connectionSecret.namespace:
889
+ self._composite.results.fatal('ConnectionNoNamespace', 'Cluster scoped XR must specify connection secret namespace')
890
+ return
891
+ secret.metadata.namespace = self._composite.connectionSecret.namespace
892
+ secret.type = 'connection.crossplane.io/v1alpha1'
893
+ secret.data[key] = protobuf.B64Encode(value)
894
+
895
+ def __delattr__(self, key):
896
+ del self[key]
897
+
898
+ def __delitem__(self, key):
899
+ if self._composite.crossplane_v1:
900
+ del self._composite.response.desired.composite.connection_details[key]
901
+ return
902
+ if self._resource_name in self._composite.resources:
903
+ del self._composite.resources[self._resource_name].data[key]
904
+ if not len(self._composite.resources[self._resource_name].data):
905
+ del self._composite.resources[self._resource_name]
@@ -9,6 +9,7 @@ import sys
9
9
  import grpc
10
10
  from crossplane.function.proto.v1 import run_function_pb2 as fnv1
11
11
  from crossplane.function.proto.v1 import run_function_pb2_grpc as grpcv1
12
+ from . import auto_ready
12
13
  from .. import pythonic
13
14
 
14
15
  logger = logging.getLogger(__name__)
@@ -17,10 +18,11 @@ logger = logging.getLogger(__name__)
17
18
  class FunctionRunner(grpcv1.FunctionRunnerService):
18
19
  """A FunctionRunner handles gRPC RunFunctionRequests."""
19
20
 
20
- def __init__(self, debug=False, renderUnknowns=False):
21
+ def __init__(self, debug=False, renderUnknowns=False, crossplane_v1=False):
21
22
  """Create a new FunctionRunner."""
22
23
  self.debug = debug
23
24
  self.renderUnknowns = renderUnknowns
25
+ self.crossplane_v1 = crossplane_v1
24
26
  self.clazzes = {}
25
27
 
26
28
  def invalidate_module(self, module):
@@ -100,7 +102,7 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
100
102
  self.clazzes[composite] = clazz
101
103
 
102
104
  try:
103
- composite = clazz(request, single_use, logger)
105
+ composite = clazz(self.crossplane_v1, request, single_use, logger)
104
106
  except Exception as e:
105
107
  return self.fatal(request, logger, 'Instantiate', e)
106
108
 
@@ -122,7 +124,7 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
122
124
  else:
123
125
  self.process_usages(composite)
124
126
  self.process_unknowns(composite)
125
- self.process_auto_readies(composite)
127
+ auto_ready.process(composite)
126
128
  logger.info('Completed compose')
127
129
 
128
130
  return composite.response._message
@@ -152,11 +154,11 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
152
154
  def get_requireds(self, step, composite):
153
155
  requireds = []
154
156
  for name, required in composite.requireds:
155
- if required.apiVersion and required.kind:
157
+ if len(required.apiVersion) and len(required.kind):
156
158
  r = pythonic.Map(apiVersion=required.apiVersion, kind=required.kind)
157
- if required.namespace:
159
+ if len(required.namespace):
158
160
  r.namespace = required.namespace
159
- if required.matchName:
161
+ if len(required.matchName):
160
162
  r.matchName = required.matchName
161
163
  for key, value in required.matchLabels:
162
164
  r.matchLabels[key] = value
@@ -166,6 +168,10 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
166
168
  return requireds
167
169
 
168
170
  def process_usages(self, composite):
171
+ if self.crossplane_v1:
172
+ apiVersion = 'apiextensions.crossplane.io/v1beta1'
173
+ else:
174
+ apiVersion = 'protection.crossplane.io/v1beta1'
169
175
  for _, resource in sorted(entry for entry in composite.resources):
170
176
  dependencies = resource.desired._getDependencies
171
177
  if dependencies:
@@ -175,7 +181,6 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
175
181
  source = self.trimFullName(source)
176
182
  composite.logger.debug(f"Dependency: {destination} = {source}")
177
183
  if resource.usages or (resource.usages is None and composite.usages):
178
- apiVersion = 'protection.crossplane.io/v1beta1' if composite.metadata.namespace else 'apiextensions.crossplane.io/v1beta1'
179
184
  resources = {}
180
185
  requireds = {}
181
186
  for destination, source in sorted(dependencies.items()):
@@ -191,7 +196,7 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
191
196
  resources[name[3]].append(f"{'.'.join(destination.split('.')[5:])} = {'.'.join(name[5:])}")
192
197
  elif (len(name) > 5 and
193
198
  name[0] == 'request' and
194
- name[1] == 'extra_resources' and
199
+ name[1] in ('required_resources', 'extra_resources') and
195
200
  name[3].startswith('items[') and name[3][-1] == ']' and
196
201
  name[4] == 'resource'
197
202
  ):
@@ -206,8 +211,6 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
206
211
  name.append(str(source.metadata.namespace))
207
212
  name.append(str(source.observed.metadata.name))
208
213
  usage = composite.resources['_'.join(name)](apiVersion, 'Usage')
209
- if resource.metadata.namespace:
210
- usage.metadata.namespace = resource.metadata.namespace
211
214
  usage.spec.reason = '\n'.join(dependencies)
212
215
  usage.spec.replayDeletion = True
213
216
  usage.spec.by.apiVersion = resource.apiVersion
@@ -215,9 +218,17 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
215
218
  usage.spec.by.resourceRef.name = resource.observed.metadata.name
216
219
  usage.spec.of.apiVersion = source.apiVersion
217
220
  usage.spec.of.kind = source.kind
218
- if source.metadata.namespace:
219
- usage.spec.of.resourceRef.namespace = source.metadata.namespace
220
221
  usage.spec.of.resourceRef.name = source.observed.metadata.name
222
+ if not self.crossplane_v1:
223
+ if composite.metadata.namespace:
224
+ if source.metadata.namespace and source.metadata.namespace != composite.metadata.namespace:
225
+ usage.spec.of.resourceRef.namespace = source.metadata.namespace
226
+ elif resource.metadata.namespace:
227
+ usage.metadata.namespace = resource.metadata.namespace
228
+ if source.metadata.namespace and source.metadata.namespace != resource.metadata.namespace:
229
+ usage.spec.of.resourceRef.namespace = source.metadata.namespace
230
+ else:
231
+ usage.kind = 'ClusterUsage'
221
232
  for key, dependencies in requireds.items():
222
233
  source = composite.requireds[key[0]][key[1]]
223
234
  name = [resource.name, str(source.kind)]
@@ -225,8 +236,6 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
225
236
  name.append(str(source.metadata.namespace))
226
237
  name.append(str(source.metadata.name))
227
238
  usage = composite.resources['_'.join(name)](apiVersion, 'Usage')
228
- if resource.metadata.namespace:
229
- usage.metadata.namespace = resource.metadata.namespace
230
239
  usage.spec.reason = '\n'.join(dependencies)
231
240
  usage.spec.replayDeletion = True
232
241
  usage.spec.by.apiVersion = resource.apiVersion
@@ -234,9 +243,17 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
234
243
  usage.spec.by.resourceRef.name = resource.observed.metadata.name
235
244
  usage.spec.of.apiVersion = source.apiVersion
236
245
  usage.spec.of.kind = source.kind
237
- if source.metadata.namespace:
238
- usage.spec.of.resourceRef.namespace = source.metadata.namespace
239
246
  usage.spec.of.resourceRef.name = source.observed.metadata.name
247
+ if not self.crossplane_v1:
248
+ if composite.metadata.namespace:
249
+ if source.metadata.namespace and source.metadata.namespace != composite.metadata.namespace:
250
+ usage.spec.of.resourceRef.namespace = source.metadata.namespace
251
+ elif resource.metadata.namespace:
252
+ usage.metadata.namespace = resource.metadata.namespace
253
+ if source.metadata.namespace and source.metadata.namespace != resource.metadata.namespace:
254
+ usage.spec.of.resourceRef.namespace = source.metadata.namespace
255
+ else:
256
+ usage.kind = 'ClusterUsage'
240
257
 
241
258
  def process_unknowns(self, composite):
242
259
  unknownResources = []
@@ -301,13 +318,6 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
301
318
  if result:
302
319
  result(reason, message)
303
320
 
304
- def process_auto_readies(self, composite):
305
- for name, resource in composite.resources:
306
- if resource.autoReady or (resource.autoReady is None and composite.autoReady):
307
- if resource.ready is None:
308
- if resource.conditions.Ready.status:
309
- resource.ready = True
310
-
311
321
  def trimFullName(self, name):
312
322
  name = name.split('.')
313
323
  for values in (