Skip to content
This repository was archived by the owner on Dec 10, 2025. It is now read-only.

Commit 4dd567f

Browse files
authored
Merge branch 'fortra:main' into main
2 parents fe4782b + c3d2799 commit 4dd567f

19 files changed

Lines changed: 78 additions & 77 deletions

README.md

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ spec:
2828
apiVersion: pythonic.fn.crossplane.io/v1beta1
2929
kind: Composite
3030
composite: |
31-
class Composite(BaseComposite):
31+
class VpcComposite(BaseComposite):
3232
def compose(self):
3333
vpc = self.resources.vpc('ec2.aws.crossplane.io/v1beta1', 'VPC')
3434
vpc.spec.forProvider.region = self.spec.region
@@ -57,7 +57,7 @@ kind: Function
5757
metadata:
5858
name: function-pythonic
5959
spec:
60-
package: ghcr.io/fortra/function-pythonic:v0.0.7
60+
package: ghcr.io/fortra/function-pythonic:v0.0.10
6161
```
6262
## Composed Resource Dependencies
6363
@@ -158,13 +158,13 @@ proto = format(request, 'protobuf') # get the request as a protobuf string
158158
Composite composition is performed from a Composite orientation. A `BaseComposite` class
159159
is subclassed and the `compose` method is implemented.
160160
```python
161-
class Composite(BaseComposite):
161+
class MyComposite(BaseComposite):
162162
def compose(self):
163163
# Compose the Composite
164164
```
165165
The compose method can also declare itself as performing async io:
166166
```python
167-
class Composite(BaseComposite):
167+
class MyAsyncComposite(BaseComposite):
168168
async def compose(self):
169169
# Compose the Composite using async io when needed
170170
```
@@ -266,7 +266,7 @@ Each resource in the list is the following RequiredResource class:
266266

267267
### Conditions
268268

269-
The `BaseCompsite.conditions`, `Resource.conditions`, and `RequiredResource.conditions` fields
269+
The `BaseComposite.conditions`, `Resource.conditions`, and `RequiredResource.conditions` fields
270270
are maps of that entity's status conditions array, with the map key being the condition type.
271271
The fields are read only for `Resource.conditions` and `RequiredResource.conditions`.
272272

@@ -307,7 +307,7 @@ metadata:
307307
name: composite-example
308308
spec:
309309
composite: |
310-
class Composite(BaseComposite):
310+
class HelloComposite(BaseComposite):
311311
def compose(self):
312312
self.status.composite = 'Hello, World!'
313313
```
@@ -349,7 +349,7 @@ spec:
349349
apiVersion: pythonic.fn.fortra.com/v1alpha1
350350
kind: Composite
351351
composite: |
352-
class Composite(BaseComposite):
352+
class GreetingComposite(BaseComposite):
353353
def compose(self):
354354
self.status.greeting = f"Hello, {self.spec.who}!"
355355
```
@@ -362,7 +362,7 @@ metadata:
362362
annotations:
363363
render.crossplane.io/runtime: Development
364364
spec:
365-
package: ghcr.io/fortra/function-pythonic:v0.0.7
365+
package: ghcr.io/fortra/function-pythonic:v0.0.10
366366
```
367367
In one terminal session, run function-pythonic:
368368
```shell
@@ -422,7 +422,7 @@ Then, in your Composition:
422422
kind: Composite
423423
composite: |
424424
from example.pythonic import features
425-
class Composite(BaseComposite):
425+
class FetureComposite(BaseComposite):
426426
def compose(self):
427427
anything = features.anything()
428428
...
@@ -464,7 +464,7 @@ kind: Function
464464
metadata:
465465
name: function-pythonic
466466
spec:
467-
package: ghcr.io/fortra/function-pythonic:v0.0.7
467+
package: ghcr.io/fortra/function-pythonic:v0.0.10
468468
runtimeConfigRef:
469469
name: function-pythonic
470470
---
@@ -493,12 +493,6 @@ kind: ClusterRole
493493
metadata:
494494
name: function-pythonic
495495
rules:
496-
- apiGroups:
497-
- ''
498-
resources:
499-
- events
500-
verbs:
501-
- create
502496
- apiGroups:
503497
- ''
504498
resources:
@@ -507,6 +501,12 @@ rules:
507501
- list
508502
- watch
509503
- patch
504+
- apiGroups:
505+
- ''
506+
resources:
507+
- events
508+
verbs:
509+
- create
510510
---
511511
apiVersion: rbac.authorization.k8s.io/v1
512512
kind: ClusterRoleBinding

crossplane/pythonic/function.py

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,18 @@ async def run_function(self, request):
8787
logger.exception('Exec exception')
8888
crossplane.function.response.fatal(response, f"Exec exception: {e}")
8989
return response
90-
composite = ['<script>', 'Composite']
90+
for field in dir(module):
91+
value = getattr(module, field)
92+
if inspect.isclass(value) and issubclass(value, BaseComposite) and value != BaseComposite:
93+
if clazz:
94+
logger.error('Composite script has multiple BaseComposite classes')
95+
crossplane.function.response.fatal(response, 'Composite script has multiple BaseComposite classes')
96+
return response
97+
clazz = value
98+
if not clazz:
99+
logger.error('Composite script does not have have a BaseComposite class')
100+
crossplane.function.response.fatal(response, 'Composite script does have have a BaseComposite class')
101+
return response
91102
else:
92103
composite = composite.rsplit('.', 1)
93104
if len(composite) == 1:
@@ -100,20 +111,20 @@ async def run_function(self, request):
100111
logger.error(str(e))
101112
crossplane.function.response.fatal(response, f"Import module exception: {e}")
102113
return response
103-
clazz = getattr(module, composite[1], None)
104-
if not clazz:
105-
logger.error(f"{composite[0]} did not define: {composite[1]}")
106-
crossplane.function.response.fatal(response, f"{composite[0]} did not define: {composite[1]}")
107-
return response
108-
composite = '.'.join(composite)
109-
if not inspect.isclass(clazz):
110-
logger.error(f"{composite} is not a class")
111-
crossplane.function.response.fatal(response, f"{composite} is not a class")
112-
return response
113-
if not issubclass(clazz, BaseComposite):
114-
logger.error(f"{composite} is not a subclass of BaseComposite")
115-
crossplane.function.response.fatal(response, f"{composite} is not a subclass of BaseComposite")
116-
return response
114+
clazz = getattr(module, composite[1], None)
115+
if not clazz:
116+
logger.error(f"{composite[0]} did not define: {composite[1]}")
117+
crossplane.function.response.fatal(response, f"{composite[0]} did not define: {composite[1]}")
118+
return response
119+
composite = '.'.join(composite)
120+
if not inspect.isclass(clazz):
121+
logger.error(f"{composite} is not a class")
122+
crossplane.function.response.fatal(response, f"{composite} is not a class")
123+
return response
124+
if not issubclass(clazz, BaseComposite):
125+
logger.error(f"{composite} is not a subclass of BaseComposite")
126+
crossplane.function.response.fatal(response, f"{composite} is not a subclass of BaseComposite")
127+
return response
117128
self.clazzes[composite] = clazz
118129

119130
try:

crossplane/pythonic/protobuf.py

Lines changed: 19 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,16 @@
2525

2626

2727
def Map(**kwargs):
28-
return Values(None, None, None, Values.Type.MAP)(**kwargs)
28+
map = Values(None, None, None, Values.Type.MAP)
29+
for name, value in kwargs.items():
30+
map[name] = value
31+
return map
2932

3033
def List(*args):
31-
return Values(None, None, None, Values.Type.LIST)(*args)
34+
list = Values(None, None, None, Values.Type.LIST)
35+
for ix, value in enumerate(args):
36+
list[ix] = value
37+
return list
3238

3339
def Unknown():
3440
return Values(None, None, None, Values.Type.UNKNOWN)
@@ -683,36 +689,16 @@ def _create_child(self, key, type):
683689
def __call__(self, *args, **kwargs):
684690
if self._readOnly:
685691
raise ValueError(f"{self._readOnly} is read only")
692+
self.__dict__['_values'] = None
693+
self.__dict__['_type'] = self.Type.UNKNOWN
686694
self._cache.clear()
687695
self._unknowns.clear()
688696
if len(kwargs):
689-
if not self._isMap:
690-
if not self._isUnknown:
691-
raise ValueError('Cannot specify kwargs on lists')
692-
self.__dict__['_type'] = self.Type.MAP
693697
if len(args):
694-
raise ValueError('Connect specify args on maps')
695-
if self._values is None:
696-
if self._parent is None:
697-
self.__dict__['_values'] = google.protobuf.struct_pb2.Struct()
698-
else:
699-
self.__dict__['_values'] = self._parent._create_child(self._key, self._type)
700-
self._values.Clear()
698+
raise ValueError('Connect specify both kwargs and args')
701699
for key, value in kwargs.items():
702700
self[key] = value
703701
elif len(args):
704-
if not self._isList:
705-
if not self._isUnknown:
706-
raise ValueError('Cannot specify args on maps')
707-
self.__dict__['_type'] = self.Type.LIST
708-
if len(kwargs):
709-
raise ValueError('Connect specify kwargs on lists')
710-
if self._values is None:
711-
if self._parent is None:
712-
self.__dict__['_values'] = google.protobuf.struct_pb2.ListValue()
713-
else:
714-
self.__dict__['_values'] = self._parent._create_child(self._key, self._type)
715-
self._values.Clear()
716702
for key in range(len(args)):
717703
self[key] = args[key]
718704
return self
@@ -767,17 +753,21 @@ def __setitem__(self, key, value):
767753
values[key].number_value = value
768754
elif isinstance(value, dict):
769755
values[key].struct_value.Clear()
770-
self[key](**value)
756+
for k, v in value.items():
757+
self[key][k] = v
771758
elif isinstance(value, (list, tuple)):
772759
values[key].list_value.Clear()
773-
self[key](*value)
760+
for ix, v in enumerate(value):
761+
self[key][ix] = v
774762
elif isinstance(value, Values):
775763
if value._isMap:
776764
values[key].struct_value.Clear()
777-
self[key](**{k:v for k,v in value})
765+
for k, v in value:
766+
self[key][k] = v
778767
elif value._isList:
779768
values[key].list_value.Clear()
780-
self[key](*[v for v in value])
769+
for ix, v in enumerate(value):
770+
self[key][ix] = v
781771
else:
782772
self._unknowns[key] = value
783773
if self._isMap:

examples/eks-cluster/functions.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ metadata:
55
annotations:
66
render.crossplane.io/runtime: Development
77
spec:
8-
package: ghcr.io/fortra/function-pythonic:v0.0.7
8+
package: ghcr.io/fortra/function-pythonic:v0.0.10

examples/filing-system/function.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ kind: Function
33
metadata:
44
name: function-pythonic
55
spec:
6-
package: ghcr.io/iciclespider/function-pythonic:v0.0.0-20250811193238-f8d5c82deb9b
6+
package: ghcr.io/fortra/function-pythonic:v0.0.10
77
runtimeConfigRef:
88
apiVersion: pkg.crossplane.io/v1beta1
99
kind: DeploymentRuntimeConfig

examples/helm-copy-secret/functions.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ metadata:
55
annotations:
66
render.crossplane.io/runtime: Development
77
spec:
8-
package: ghcr.io/fortra/function-pythonic:v0.0.7
8+
package: ghcr.io/fortra/function-pythonic:v0.0.10

scripts/setup-local.sh

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,15 +189,14 @@ spec:
189189
EOF
190190

191191
# package: ghcr.io/fortra/function-pythonic:v0.0.0-20250819201108-49cfb066579f
192-
# package: ghcr.io/fortra/function-pythonic:v0.0.7
193192

194193
kubectl apply -f - <<EOF
195194
apiVersion: pkg.crossplane.io/v1
196195
kind: Function
197196
metadata:
198197
name: function-pythonic
199198
spec:
200-
package: ghcr.io/fortra/function-pythonic:v0.0.7
199+
package: ghcr.io/fortra/function-pythonic:v0.0.10
201200
runtimeConfigRef:
202201
apiVersion: pkg.crossplane.io/v1beta1
203202
kind: DeploymentRuntimeConfig

tests/fn_cases/buckets.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
request:
22
input:
33
composite: |
4-
class Composite(BaseComposite):
4+
class BucketComposite(BaseComposite):
55
def compose(self):
66
77
self.resources.bucket1.apiVersion = 's3.aws.upbound.io/v1beta2'

tests/fn_cases/composed.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ request:
2020
input:
2121
composite: |
2222
apiVersion = 'dbforpostgresql.azure.upbound.io/v1beta1'
23-
class Composite(BaseComposite):
23+
class ComposedComposite(BaseComposite):
2424
def compose(self):
2525
self.ttl = (5 * 60) + 30
2626
s = self.resources.flexServer(apiVersion, 'FlexibleServer')

tests/fn_cases/conditions.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ request:
2121
lastTransitionTime: '2023-11-03T09:07:31Z'
2222
input:
2323
composite: |
24-
class Composite(BaseComposite):
24+
class ConditionsComposite(BaseComposite):
2525
def compose(self):
2626
self.status.compositeCondition = self.conditions.Ready
2727
self.status.compositeNotFound = self.conditions.Other

0 commit comments

Comments
 (0)