crossplane-function-pythonic 0.2.1__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.
- crossplane/pythonic/__about__.py +1 -1
- crossplane/pythonic/auto_ready.py +153 -0
- crossplane/pythonic/command.py +5 -0
- crossplane/pythonic/composite.py +244 -64
- crossplane/pythonic/function.py +33 -23
- crossplane/pythonic/grpc.py +1 -1
- crossplane/pythonic/protobuf.py +26 -3
- crossplane/pythonic/render.py +2 -2
- {crossplane_function_pythonic-0.2.1.dist-info → crossplane_function_pythonic-0.3.0.dist-info}/METADATA +38 -6
- crossplane_function_pythonic-0.3.0.dist-info/RECORD +18 -0
- crossplane_function_pythonic-0.2.1.dist-info/RECORD +0 -17
- {crossplane_function_pythonic-0.2.1.dist-info → crossplane_function_pythonic-0.3.0.dist-info}/WHEEL +0 -0
- {crossplane_function_pythonic-0.2.1.dist-info → crossplane_function_pythonic-0.3.0.dist-info}/entry_points.txt +0 -0
- {crossplane_function_pythonic-0.2.1.dist-info → crossplane_function_pythonic-0.3.0.dist-info}/licenses/LICENSE +0 -0
crossplane/pythonic/__about__.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# This is set at build time, using "hatch version"
|
|
2
|
-
__version__ = "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
|
+
)
|
crossplane/pythonic/command.py
CHANGED
|
@@ -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
|
crossplane/pythonic/composite.py
CHANGED
|
@@ -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(
|
|
@@ -40,7 +118,6 @@ class BaseComposite:
|
|
|
40
118
|
observed = self.request.observed.composite
|
|
41
119
|
desired = self.response.desired.composite
|
|
42
120
|
self.observed = observed.resource
|
|
43
|
-
self.observed._set_attribute('connection', self.request.observed.composite.connection_details)
|
|
44
121
|
self.desired = desired.resource
|
|
45
122
|
self.apiVersion = self.observed.apiVersion
|
|
46
123
|
self.kind = self.observed.kind
|
|
@@ -51,54 +128,10 @@ class BaseComposite:
|
|
|
51
128
|
self.results = Results(self.response)
|
|
52
129
|
self.events = Results(self.response) # Deprecated, use self.results
|
|
53
130
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return int(self.response.meta.ttl.seconds)
|
|
59
|
-
|
|
60
|
-
@ttl.setter
|
|
61
|
-
def ttl(self, ttl):
|
|
62
|
-
if isinstance(ttl, int):
|
|
63
|
-
self.response.meta.ttl.seconds = ttl
|
|
64
|
-
self.response.meta.ttl.nanos = 0
|
|
65
|
-
elif isinstance(ttl, float):
|
|
66
|
-
self.response.meta.ttl.seconds = int(ttl)
|
|
67
|
-
if ttl.is_integer():
|
|
68
|
-
self.response.meta.ttl.nanos = 0
|
|
69
|
-
else:
|
|
70
|
-
self.response.meta.ttl.nanos = int((ttl - int(self.response.meta.ttl.seconds)) * 1000000000)
|
|
71
|
-
else:
|
|
72
|
-
raise ValueError('ttl must be an int or float')
|
|
73
|
-
|
|
74
|
-
@property
|
|
75
|
-
def connection(self):
|
|
76
|
-
return self.response.desired.composite.connection_details
|
|
77
|
-
|
|
78
|
-
@connection.setter
|
|
79
|
-
def connection(self, connection):
|
|
80
|
-
self.response.desired.composite.connection_details()
|
|
81
|
-
for key, value in connection:
|
|
82
|
-
self.response.desired.composite.connection_details[key] = value
|
|
83
|
-
|
|
84
|
-
@property
|
|
85
|
-
def ready(self):
|
|
86
|
-
ready = self.desired._parent.ready
|
|
87
|
-
if ready == fnv1.Ready.READY_TRUE:
|
|
88
|
-
return True
|
|
89
|
-
if ready == fnv1.Ready.READY_FALSE:
|
|
90
|
-
return False
|
|
91
|
-
return None
|
|
92
|
-
|
|
93
|
-
@ready.setter
|
|
94
|
-
def ready(self, ready):
|
|
95
|
-
if ready:
|
|
96
|
-
ready = fnv1.Ready.READY_TRUE
|
|
97
|
-
elif ready == None or (isinstance(ready, protobuf.Value) and ready._isUnknown):
|
|
98
|
-
ready = fnv1.Ready.READY_UNSPECIFIED
|
|
99
|
-
else:
|
|
100
|
-
ready = fnv1.Ready.READY_FALSE
|
|
101
|
-
self.desired._parent.ready = ready
|
|
131
|
+
ttl = TTL()
|
|
132
|
+
connectionSecret = ConnectionSecret()
|
|
133
|
+
connection = Connection()
|
|
134
|
+
ready = Ready()
|
|
102
135
|
|
|
103
136
|
async def compose(self):
|
|
104
137
|
raise NotImplementedError()
|
|
@@ -263,6 +296,14 @@ class Resource:
|
|
|
263
296
|
def spec(self, spec):
|
|
264
297
|
self.desired.spec = spec
|
|
265
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
|
+
|
|
266
307
|
@property
|
|
267
308
|
def data(self):
|
|
268
309
|
return self.desired.data
|
|
@@ -315,25 +356,43 @@ class Requireds:
|
|
|
315
356
|
|
|
316
357
|
def __len__(self):
|
|
317
358
|
names = set()
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
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)
|
|
322
369
|
return len(names)
|
|
323
370
|
|
|
324
371
|
def __contains__(self, key):
|
|
325
|
-
if
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
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
|
|
329
382
|
return False
|
|
330
383
|
|
|
331
384
|
def __iter__(self):
|
|
332
385
|
names = set()
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
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)
|
|
337
396
|
for name in sorted(names):
|
|
338
397
|
yield name, self[name]
|
|
339
398
|
|
|
@@ -341,8 +400,12 @@ class Requireds:
|
|
|
341
400
|
class RequiredResources:
|
|
342
401
|
def __init__(self, composite, name):
|
|
343
402
|
self.name = name
|
|
344
|
-
|
|
345
|
-
|
|
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]
|
|
346
409
|
self._cache = {}
|
|
347
410
|
|
|
348
411
|
def __call__(self, apiVersion=_notset, kind=_notset, namespace=_notset, name=_notset, labels=_notset):
|
|
@@ -432,6 +495,7 @@ class RequiredResource:
|
|
|
432
495
|
self.kind = self.observed.kind
|
|
433
496
|
self.metadata = self.observed.metadata
|
|
434
497
|
self.spec = self.observed.spec
|
|
498
|
+
self.type = self.observed.type
|
|
435
499
|
self.data = self.observed.data
|
|
436
500
|
self.status = self.observed.status
|
|
437
501
|
self.conditions = Conditions(resource)
|
|
@@ -723,3 +787,119 @@ class Result:
|
|
|
723
787
|
self._result.target = fnv1.Target.TARGET_UNSPECIFIED
|
|
724
788
|
else:
|
|
725
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]
|
crossplane/pythonic/function.py
CHANGED
|
@@ -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
|
-
|
|
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]
|
|
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 (
|
crossplane/pythonic/grpc.py
CHANGED
|
@@ -88,7 +88,7 @@ class Command(command.Command):
|
|
|
88
88
|
|
|
89
89
|
async def run(self):
|
|
90
90
|
grpc.aio.init_grpc_aio()
|
|
91
|
-
grpc_runner = function.FunctionRunner(self.args.debug, self.args.render_unknowns)
|
|
91
|
+
grpc_runner = function.FunctionRunner(self.args.debug, self.args.render_unknowns, self.args.crossplane_v1)
|
|
92
92
|
grpc_server = grpc.aio.server()
|
|
93
93
|
grpcv1.add_FunctionRunnerServiceServicer_to_server(grpc_runner, grpc_server)
|
|
94
94
|
if self.args.insecure:
|
crossplane/pythonic/protobuf.py
CHANGED
|
@@ -568,39 +568,57 @@ class FieldMessage:
|
|
|
568
568
|
self._value = value
|
|
569
569
|
|
|
570
570
|
def __bool__(self):
|
|
571
|
-
return
|
|
571
|
+
return self._value is not _Unknown
|
|
572
572
|
|
|
573
573
|
def __len__(self):
|
|
574
|
+
if self._value is _Unknown:
|
|
575
|
+
return 0
|
|
574
576
|
return len(self._value)
|
|
575
577
|
|
|
576
578
|
def __contains__(self, key):
|
|
579
|
+
if self._value is _Unknown:
|
|
580
|
+
return False
|
|
577
581
|
return key in self._value
|
|
578
582
|
|
|
579
583
|
def __hash__(self):
|
|
584
|
+
if self._value is _Unknown:
|
|
585
|
+
return 0
|
|
580
586
|
return hash(self._value)
|
|
581
587
|
|
|
582
588
|
def __eq__(self, other):
|
|
589
|
+
if self._value is _Unknown:
|
|
590
|
+
return False
|
|
583
591
|
if isinstance(other, FieldMessage):
|
|
584
592
|
return self._value == other._value
|
|
585
593
|
return self._value == other
|
|
586
594
|
|
|
587
595
|
def __bytes__(self):
|
|
596
|
+
if self._value is _Unknown:
|
|
597
|
+
return None
|
|
588
598
|
if isinstance(self._value, str):
|
|
589
599
|
return self._value.encode('utf-8')
|
|
590
600
|
return bytes(self._value)
|
|
591
601
|
|
|
592
602
|
def __str__(self):
|
|
603
|
+
if self._value is _Unknown:
|
|
604
|
+
return None
|
|
593
605
|
if isinstance(self._value, bytes):
|
|
594
606
|
return self._value.decode('utf-8')
|
|
595
607
|
return str(self._value)
|
|
596
608
|
|
|
597
609
|
def __format__(self, spec=''):
|
|
610
|
+
if self._value is _Unknown:
|
|
611
|
+
return None
|
|
598
612
|
return format(self._value, spec)
|
|
599
613
|
|
|
600
614
|
def __int__(self):
|
|
615
|
+
if self._value is _Unknown:
|
|
616
|
+
return None
|
|
601
617
|
return int(self._value)
|
|
602
618
|
|
|
603
619
|
def __float__(self):
|
|
620
|
+
if self._value is _Unknown:
|
|
621
|
+
return None
|
|
604
622
|
return float(self._value)
|
|
605
623
|
|
|
606
624
|
def _fullName(self, key=None):
|
|
@@ -715,6 +733,10 @@ class Value:
|
|
|
715
733
|
return len(self._value.list_value.values) + len(self._unknowns)
|
|
716
734
|
case 'ListValue':
|
|
717
735
|
return len(self._value.values) + len(self._unknowns)
|
|
736
|
+
case 'string_value':
|
|
737
|
+
return len(self._value.string_value)
|
|
738
|
+
case 'bool_value':
|
|
739
|
+
return 1 if self._value.bool_value else 0
|
|
718
740
|
return 0
|
|
719
741
|
|
|
720
742
|
def __contains__(self, item):
|
|
@@ -1224,13 +1246,14 @@ class Value:
|
|
|
1224
1246
|
for key, value in self:
|
|
1225
1247
|
if isinstance(value, Value) and len(value):
|
|
1226
1248
|
patch = patches[key]
|
|
1227
|
-
|
|
1249
|
+
print(patch.__class__, str(patch))
|
|
1250
|
+
if isinstance(patch, Value) and patch._kind == value._kind and len(patch):
|
|
1228
1251
|
value._patchUnknowns(patch)
|
|
1229
1252
|
elif self._isList:
|
|
1230
1253
|
for ix, value in enumerate(self):
|
|
1231
1254
|
if isinstance(value, Value) and len(value):
|
|
1232
1255
|
patch = patches[ix]
|
|
1233
|
-
if isinstance(patch, Value) and patch.
|
|
1256
|
+
if isinstance(patch, Value) and patch._kind == value._kind and len(patch):
|
|
1234
1257
|
value._patchUnknowns(patch)
|
|
1235
1258
|
|
|
1236
1259
|
def _renderUnknowns(self, trimFullName):
|
crossplane/pythonic/render.py
CHANGED
|
@@ -158,7 +158,7 @@ class Command(command.Command):
|
|
|
158
158
|
# Collect specified required/extra resources. Sort for stable order when processed.
|
|
159
159
|
requireds = sorted(
|
|
160
160
|
self.collect_resources(self.args.required_resources),
|
|
161
|
-
key=lambda required: str(
|
|
161
|
+
key=lambda required: str(required.metadata.name),
|
|
162
162
|
)
|
|
163
163
|
|
|
164
164
|
# Collect specified connection and credential secrets.
|
|
@@ -181,7 +181,7 @@ class Command(command.Command):
|
|
|
181
181
|
results = protobuf.List()
|
|
182
182
|
|
|
183
183
|
# Create a function-pythonic function runner used to run pipeline steps.
|
|
184
|
-
runner = function.FunctionRunner(self.args.debug, self.args.render_unknowns)
|
|
184
|
+
runner = function.FunctionRunner(self.args.debug, self.args.render_unknowns, self.args.crossplane_v1)
|
|
185
185
|
fatal = False
|
|
186
186
|
|
|
187
187
|
# Process the composition pipeline steps.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: crossplane-function-pythonic
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: A Python centric Crossplane Function
|
|
5
5
|
Project-URL: Documentation, https://github.com/crossplane-contrib/function-pythonic#readme
|
|
6
6
|
Project-URL: Issues, https://github.com/crossplane-contrib/function-pythonic/issues
|
|
@@ -17,7 +17,7 @@ Requires-Python: <3.15,>=3.12
|
|
|
17
17
|
Requires-Dist: crossplane-function-sdk-python==0.10.0
|
|
18
18
|
Requires-Dist: pyyaml==6.0.3
|
|
19
19
|
Provides-Extra: packages
|
|
20
|
-
Requires-Dist: kopf==1.
|
|
20
|
+
Requires-Dist: kopf==1.40.0; extra == 'packages'
|
|
21
21
|
Provides-Extra: pip-install
|
|
22
22
|
Requires-Dist: pip==25.3; extra == 'pip-install'
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
@@ -81,8 +81,39 @@ kind: Function
|
|
|
81
81
|
metadata:
|
|
82
82
|
name: function-pythonic
|
|
83
83
|
spec:
|
|
84
|
-
package: xpkg.upbound.io/crossplane-contrib/function-pythonic:v0.
|
|
84
|
+
package: xpkg.upbound.io/crossplane-contrib/function-pythonic:v0.3.0
|
|
85
85
|
```
|
|
86
|
+
|
|
87
|
+
### Crossplane V1
|
|
88
|
+
When running function-pythonic in Crossplane V1, the `--crossplane-v1` command line
|
|
89
|
+
option should be specified. This requires using a Crossplane DeploymentRuntimeConfig.
|
|
90
|
+
```yaml
|
|
91
|
+
apiVersion: pkg.crossplane.io/v1
|
|
92
|
+
kind: Function
|
|
93
|
+
metadata:
|
|
94
|
+
name: function-pythonic
|
|
95
|
+
spec:
|
|
96
|
+
package: xpkg.upbound.io/crossplane-contrib/function-pythonic:v0.3.0
|
|
97
|
+
runtimeConfigRef:
|
|
98
|
+
name: function-pythonic
|
|
99
|
+
--
|
|
100
|
+
apiVersion: pkg.crossplane.io/v1beta1
|
|
101
|
+
kind: DeploymentRuntimeConfig
|
|
102
|
+
metadata:
|
|
103
|
+
name: function-pythonic
|
|
104
|
+
spec:
|
|
105
|
+
deploymentTemplate:
|
|
106
|
+
spec:
|
|
107
|
+
selector: {}
|
|
108
|
+
template:
|
|
109
|
+
spec:
|
|
110
|
+
containers:
|
|
111
|
+
- name: package-runtime
|
|
112
|
+
args:
|
|
113
|
+
- --debug
|
|
114
|
+
- --crossplane-v1
|
|
115
|
+
```
|
|
116
|
+
|
|
86
117
|
## Composed Resource Dependencies
|
|
87
118
|
|
|
88
119
|
function-pythonic automatically handles dependencies between composed resources.
|
|
@@ -228,9 +259,10 @@ The BaseComposite class provides the following fields for manipulating the Compo
|
|
|
228
259
|
| self.status | Map | The composite desired and observed status, read from observed if not in desired |
|
|
229
260
|
| self.conditions | Conditions | The composite desired and observed conditions, read from observed if not in desired |
|
|
230
261
|
| self.results | Results | Returned results applied to the Composite and optionally on the Claim |
|
|
262
|
+
| self.connectionSecret | Map | The name, namespace, and resourceName to use when generating the connection secret in Crossplane v2 |
|
|
231
263
|
| self.connection | Map | The composite desired connection detials |
|
|
264
|
+
| self.connection.observed | Map | The composite observed connection detials |
|
|
232
265
|
| self.ready | Boolean | The composite desired ready state |
|
|
233
|
-
| self.observed.connection | Map | The composite observed connection detials |
|
|
234
266
|
|
|
235
267
|
The BaseComposite also provides access to the following Crossplane Function level features:
|
|
236
268
|
|
|
@@ -277,7 +309,7 @@ Resource class:
|
|
|
277
309
|
| Resource.usages | Boolean | Generate Crossplane Usages for this resource, default is Composite.autoReady |
|
|
278
310
|
| Resource.autoReady | Boolean | Perform auto ready processing on this resource, default is Composite.autoReady |
|
|
279
311
|
|
|
280
|
-
### Required Resources
|
|
312
|
+
### Required Resources
|
|
281
313
|
|
|
282
314
|
Creating and accessing required resources is performed using the `BaseComposite.requireds` field.
|
|
283
315
|
`BaseComposite.requireds` is a dictionary of the required resources whose key is the required
|
|
@@ -547,7 +579,7 @@ kind: Function
|
|
|
547
579
|
metadata:
|
|
548
580
|
name: function-pythonic
|
|
549
581
|
spec:
|
|
550
|
-
package: xpkg.upbound.io/crossplane-contrib/function-pythonic:v0.
|
|
582
|
+
package: xpkg.upbound.io/crossplane-contrib/function-pythonic:v0.3.0
|
|
551
583
|
runtimeConfigRef:
|
|
552
584
|
name: function-pythonic
|
|
553
585
|
---
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
crossplane/pythonic/__about__.py,sha256=aySM7NSterQ9iNm2zapljwvbJuWDyviv3Ku2kts5ThU,73
|
|
2
|
+
crossplane/pythonic/__init__.py,sha256=A9U4-azc4DjSsOnOnjQxCkoTzsZMRBb_AvqzR_Bd95A,268
|
|
3
|
+
crossplane/pythonic/__main__.py,sha256=6vYRlYDJtqFgLyiTamnl3htiNOtz8QlDl5WlIP98I8o,31
|
|
4
|
+
crossplane/pythonic/auto_ready.py,sha256=FrbIePmj96Jl51fjVt1ctj26jTksimRCXGsNTFaMh9A,5026
|
|
5
|
+
crossplane/pythonic/command.py,sha256=QC1Z4j5-MyfH41V7Zc9tJgaTZ7IVR0h8xL4bbXOE9Fg,3309
|
|
6
|
+
crossplane/pythonic/composite.py,sha256=WiDV5OCJ8-BDQ_4i-3FYvflpGxLoCLde-fRjDF0ib9w,29377
|
|
7
|
+
crossplane/pythonic/function.py,sha256=KYOFzCGp59trKGl5M5JxjnOWWEPDL3hmoXYXC6kGljw,17433
|
|
8
|
+
crossplane/pythonic/grpc.py,sha256=8hQZZsNrcbiCGfEdPWRrG9SYfTrmuPgMmAIHwWhCdt8,4468
|
|
9
|
+
crossplane/pythonic/main.py,sha256=ujUa_FYElQSGqnhZ-0NJrD3kSyYjfRbIp79FV2Yl7hs,599
|
|
10
|
+
crossplane/pythonic/packages.py,sha256=4TxyT6V79R0m4tJbC8R1gwU_vgHGLXKSBzeTTKd8xGo,5120
|
|
11
|
+
crossplane/pythonic/protobuf.py,sha256=Dc4gYWPusQYGIR0k0xawkHCV0a-sqX2y7eAAc7whqME,51304
|
|
12
|
+
crossplane/pythonic/render.py,sha256=h9S59R_cM1eerOuDL2h6fQ79f-cmaA9OYs8CE2AGj7E,22263
|
|
13
|
+
crossplane/pythonic/version.py,sha256=-RiB0p146ayqJj0SXfYxTNv49u9Fx9pPgm59Ji2blhc,214
|
|
14
|
+
crossplane_function_pythonic-0.3.0.dist-info/METADATA,sha256=Un9AsXhViJ_1YOx3wxXe1d6BDNzZgcf6_VczndW6OXg,29502
|
|
15
|
+
crossplane_function_pythonic-0.3.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
16
|
+
crossplane_function_pythonic-0.3.0.dist-info/entry_points.txt,sha256=jJ4baywFDviB9WyAhyhNYF2VOCb6XtbRSjKf7bnBwhg,68
|
|
17
|
+
crossplane_function_pythonic-0.3.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
18
|
+
crossplane_function_pythonic-0.3.0.dist-info/RECORD,,
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
crossplane/pythonic/__about__.py,sha256=QBcrW1XnbdqF5kVtJfA-yvTFAU-t00iGaaoo6kjjIDY,73
|
|
2
|
-
crossplane/pythonic/__init__.py,sha256=A9U4-azc4DjSsOnOnjQxCkoTzsZMRBb_AvqzR_Bd95A,268
|
|
3
|
-
crossplane/pythonic/__main__.py,sha256=6vYRlYDJtqFgLyiTamnl3htiNOtz8QlDl5WlIP98I8o,31
|
|
4
|
-
crossplane/pythonic/command.py,sha256=s69oOVCgbm39gowvTy7ieaE-Vosckf24vcznJ6fO0Q4,3146
|
|
5
|
-
crossplane/pythonic/composite.py,sha256=PJD3uRbanrkUgCUxnN_lKhg5n3QO1OpGfLC71UkEwRg,22129
|
|
6
|
-
crossplane/pythonic/function.py,sha256=ShHhSJYblTB9BRx7NEtHgiyOconax7Lro0tLzzRyZdY,16564
|
|
7
|
-
crossplane/pythonic/grpc.py,sha256=yc-hVnw7K0iuuJS25z6aZkfXjUuvciWfGCdHnWv4I78,4443
|
|
8
|
-
crossplane/pythonic/main.py,sha256=ujUa_FYElQSGqnhZ-0NJrD3kSyYjfRbIp79FV2Yl7hs,599
|
|
9
|
-
crossplane/pythonic/packages.py,sha256=4TxyT6V79R0m4tJbC8R1gwU_vgHGLXKSBzeTTKd8xGo,5120
|
|
10
|
-
crossplane/pythonic/protobuf.py,sha256=Wha3hMSDbJPALtMu-ZcZEHaV5CosfYW8SJh7ueAbqVk,50528
|
|
11
|
-
crossplane/pythonic/render.py,sha256=yqqk9DambTBRgQ51ZEQWOLg1xvos1KGETHei6rimus8,22238
|
|
12
|
-
crossplane/pythonic/version.py,sha256=-RiB0p146ayqJj0SXfYxTNv49u9Fx9pPgm59Ji2blhc,214
|
|
13
|
-
crossplane_function_pythonic-0.2.1.dist-info/METADATA,sha256=pIsTA_rzQ_HBpVlYZZI67AmHz27qcqAfxYoY5j1iNW0,28659
|
|
14
|
-
crossplane_function_pythonic-0.2.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
15
|
-
crossplane_function_pythonic-0.2.1.dist-info/entry_points.txt,sha256=jJ4baywFDviB9WyAhyhNYF2VOCb6XtbRSjKf7bnBwhg,68
|
|
16
|
-
crossplane_function_pythonic-0.2.1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
17
|
-
crossplane_function_pythonic-0.2.1.dist-info/RECORD,,
|
{crossplane_function_pythonic-0.2.1.dist-info → crossplane_function_pythonic-0.3.0.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|