crossplane-function-pythonic 0.6.2__tar.gz → 0.7.0__tar.gz
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_function_pythonic-0.6.2 → crossplane_function_pythonic-0.7.0}/PKG-INFO +23 -26
- {crossplane_function_pythonic-0.6.2 → crossplane_function_pythonic-0.7.0}/README.md +11 -9
- crossplane_function_pythonic-0.7.0/pyproject.toml +65 -0
- crossplane_function_pythonic-0.7.0/pyproject.toml.orig +66 -0
- crossplane_function_pythonic-0.7.0/src/crossplane/pythonic/__about__.py +6 -0
- crossplane_function_pythonic-0.7.0/src/crossplane/pythonic/__init__.py +25 -0
- {crossplane_function_pythonic-0.6.2 → crossplane_function_pythonic-0.7.0/src}/crossplane/pythonic/__main__.py +1 -0
- {crossplane_function_pythonic-0.6.2 → crossplane_function_pythonic-0.7.0/src}/crossplane/pythonic/auto_ready.py +118 -61
- crossplane_function_pythonic-0.7.0/src/crossplane/pythonic/command.py +136 -0
- crossplane_function_pythonic-0.7.0/src/crossplane/pythonic/composite.py +1314 -0
- {crossplane_function_pythonic-0.6.2 → crossplane_function_pythonic-0.7.0/src}/crossplane/pythonic/function.py +233 -130
- crossplane_function_pythonic-0.7.0/src/crossplane/pythonic/grpc.py +174 -0
- crossplane_function_pythonic-0.7.0/src/crossplane/pythonic/main.py +41 -0
- {crossplane_function_pythonic-0.6.2 → crossplane_function_pythonic-0.7.0/src}/crossplane/pythonic/packages.py +82 -51
- {crossplane_function_pythonic-0.6.2 → crossplane_function_pythonic-0.7.0/src}/crossplane/pythonic/proto/v1/run_function_pb2.py +3 -3
- crossplane_function_pythonic-0.7.0/src/crossplane/pythonic/proto/v1/run_function_pb2.pyi +1172 -0
- {crossplane_function_pythonic-0.6.2 → crossplane_function_pythonic-0.7.0/src}/crossplane/pythonic/proto/v1/run_function_pb2_grpc.py +4 -4
- crossplane_function_pythonic-0.7.0/src/crossplane/pythonic/proto/v1/run_function_pb2_grpc.pyi +64 -0
- {crossplane_function_pythonic-0.6.2 → crossplane_function_pythonic-0.7.0/src}/crossplane/pythonic/protobuf.py +589 -372
- crossplane_function_pythonic-0.7.0/src/crossplane/pythonic/render.py +1064 -0
- {crossplane_function_pythonic-0.6.2 → crossplane_function_pythonic-0.7.0/src}/crossplane/pythonic/version.py +3 -4
- crossplane_function_pythonic-0.6.2/.gitignore +0 -219
- crossplane_function_pythonic-0.6.2/LICENSE +0 -201
- crossplane_function_pythonic-0.6.2/crossplane/pythonic/__about__.py +0 -2
- crossplane_function_pythonic-0.6.2/crossplane/pythonic/__init__.py +0 -17
- crossplane_function_pythonic-0.6.2/crossplane/pythonic/command.py +0 -125
- crossplane_function_pythonic-0.6.2/crossplane/pythonic/composite.py +0 -1086
- crossplane_function_pythonic-0.6.2/crossplane/pythonic/grpc.py +0 -150
- crossplane_function_pythonic-0.6.2/crossplane/pythonic/main.py +0 -28
- crossplane_function_pythonic-0.6.2/crossplane/pythonic/proto/v1/run_function_pb2.pyi +0 -306
- crossplane_function_pythonic-0.6.2/crossplane/pythonic/render.py +0 -731
- crossplane_function_pythonic-0.6.2/examples/aks-cluster/README.md +0 -129
- crossplane_function_pythonic-0.6.2/examples/aks-cluster/aks/kubernetescluster.py +0 -29
- crossplane_function_pythonic-0.6.2/examples/aks-cluster/aks/resourcegroup.py +0 -12
- crossplane_function_pythonic-0.6.2/examples/aks-cluster/cluster-function-pythonic.yaml +0 -63
- crossplane_function_pythonic-0.6.2/examples/aks-cluster/composition.yaml +0 -24
- crossplane_function_pythonic-0.6.2/examples/aks-cluster/definition.yaml +0 -45
- crossplane_function_pythonic-0.6.2/examples/aks-cluster/install.sh +0 -13
- crossplane_function_pythonic-0.6.2/examples/aks-cluster/kustomization.yaml +0 -20
- crossplane_function_pythonic-0.6.2/examples/aks-cluster/providers.yaml +0 -14
- crossplane_function_pythonic-0.6.2/examples/aks-cluster/render.sh +0 -4
- crossplane_function_pythonic-0.6.2/examples/aks-cluster/xr.yaml +0 -16
- crossplane_function_pythonic-0.6.2/examples/connection-details-composition/composition.yaml +0 -30
- crossplane_function_pythonic-0.6.2/examples/connection-details-composition/definition.yaml +0 -26
- crossplane_function_pythonic-0.6.2/examples/connection-details-composition/observed.yaml +0 -49
- crossplane_function_pythonic-0.6.2/examples/connection-details-composition/render.sh +0 -5
- crossplane_function_pythonic-0.6.2/examples/connection-details-composition/resources.yaml +0 -18
- crossplane_function_pythonic-0.6.2/examples/connection-details-composition/xr.yaml +0 -8
- crossplane_function_pythonic-0.6.2/examples/connections/composition.yaml +0 -39
- crossplane_function_pythonic-0.6.2/examples/connections/observed.yaml +0 -21
- crossplane_function_pythonic-0.6.2/examples/connections/render.sh +0 -7
- crossplane_function_pythonic-0.6.2/examples/connections/resources.yaml +0 -27
- crossplane_function_pythonic-0.6.2/examples/connections/xr.yaml +0 -8
- crossplane_function_pythonic-0.6.2/examples/eks-cluster/composition-v2.yaml +0 -138
- crossplane_function_pythonic-0.6.2/examples/eks-cluster/composition.yaml +0 -126
- crossplane_function_pythonic-0.6.2/examples/eks-cluster/definition.yaml +0 -47
- crossplane_function_pythonic-0.6.2/examples/eks-cluster/render-v2.sh +0 -3
- crossplane_function_pythonic-0.6.2/examples/eks-cluster/render.sh +0 -3
- crossplane_function_pythonic-0.6.2/examples/eks-cluster/xr.yaml +0 -13
- crossplane_function_pythonic-0.6.2/examples/filing-system/README.md +0 -13
- crossplane_function_pythonic-0.6.2/examples/filing-system/composition.yaml +0 -17
- crossplane_function_pythonic-0.6.2/examples/filing-system/definition.yaml +0 -21
- crossplane_function_pythonic-0.6.2/examples/filing-system/kustomization.yaml +0 -18
- crossplane_function_pythonic-0.6.2/examples/filing-system/runtime.yaml +0 -23
- crossplane_function_pythonic-0.6.2/examples/filing-system/vcluster.py +0 -38
- crossplane_function_pythonic-0.6.2/examples/filing-system/vcluster.yaml +0 -5
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/conditions/composition.yaml +0 -31
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/conditions/render.sh +0 -3
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/conditions/xr.yaml +0 -5
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/conditions/xrd.yaml +0 -25
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/context/composition.yaml +0 -31
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/context/render.sh +0 -3
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/context/resources.yaml +0 -10
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/context/xr.yaml +0 -5
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/context/xrd.yaml +0 -25
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/fromYaml/composition.yaml +0 -21
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/fromYaml/render.sh +0 -3
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/fromYaml/xr.yaml +0 -9
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/getComposedResource/composition.yaml +0 -28
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/getComposedResource/observed.yaml +0 -20
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/getComposedResource/render.sh +0 -3
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/getComposedResource/xr.yaml +0 -6
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/getCompositeResource/composition.yaml +0 -23
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/getCompositeResource/render.sh +0 -3
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/getCompositeResource/xr.yaml +0 -7
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/getCredentialData/composition.yaml +0 -27
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/getCredentialData/render.sh +0 -3
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/getCredentialData/resources.yaml +0 -9
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/getCredentialData/xr.yaml +0 -5
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/getResourceCondition/composition.yaml +0 -27
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/getResourceCondition/observed.yaml +0 -21
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/getResourceCondition/render.sh +0 -3
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/getResourceCondition/xr.yaml +0 -12
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/include/composition.yaml +0 -41
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/include/render.sh +0 -3
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/include/xr.yaml +0 -7
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/toYaml/composition.yaml +0 -21
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/toYaml/render.sh +0 -3
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/toYaml/xr.yaml +0 -12
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/inline/composition.yaml +0 -35
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/inline/render.sh +0 -3
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/inline/xr.yaml +0 -6
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/recursive/composition-real.yaml +0 -21
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/recursive/composition.yaml +0 -23
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/recursive/render.sh +0 -4
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/recursive/xr.yaml +0 -8
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/required-resources/composition.yaml +0 -29
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/required-resources/render.sh +0 -3
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/required-resources/resources.yaml +0 -14
- crossplane_function_pythonic-0.6.2/examples/function-go-templating/required-resources/xr.yaml +0 -6
- crossplane_function_pythonic-0.6.2/examples/function-sequencer/composition.yaml +0 -48
- crossplane_function_pythonic-0.6.2/examples/function-sequencer/observed.yaml +0 -27
- crossplane_function_pythonic-0.6.2/examples/function-sequencer/render.sh +0 -11
- crossplane_function_pythonic-0.6.2/examples/function-sequencer/sequencer.py +0 -12
- crossplane_function_pythonic-0.6.2/examples/function-sequencer/xr.yaml +0 -6
- crossplane_function_pythonic-0.6.2/examples/get-started-app/composition.yaml +0 -39
- crossplane_function_pythonic-0.6.2/examples/get-started-app/definition.yaml +0 -35
- crossplane_function_pythonic-0.6.2/examples/get-started-app/observed.yaml +0 -53
- crossplane_function_pythonic-0.6.2/examples/get-started-app/render.sh +0 -3
- crossplane_function_pythonic-0.6.2/examples/get-started-app/xr.yaml +0 -7
- crossplane_function_pythonic-0.6.2/examples/helm-copy-secret/composition.yaml +0 -17
- crossplane_function_pythonic-0.6.2/examples/helm-copy-secret/kustomization.yaml +0 -16
- crossplane_function_pythonic-0.6.2/examples/helm-copy-secret/render.sh +0 -3
- crossplane_function_pythonic-0.6.2/examples/helm-copy-secret/vcluster.py +0 -63
- crossplane_function_pythonic-0.6.2/examples/helm-copy-secret/xr.yaml +0 -5
- crossplane_function_pythonic-0.6.2/examples/helm-copy-secret/xrd.yaml +0 -21
- crossplane_function_pythonic-0.6.2/examples/import-existing-vpc/README.md +0 -35
- crossplane_function_pythonic-0.6.2/examples/import-existing-vpc/composition.yaml +0 -68
- crossplane_function_pythonic-0.6.2/examples/import-existing-vpc/definition.yaml +0 -39
- crossplane_function_pythonic-0.6.2/examples/import-existing-vpc/observed.yaml +0 -6
- crossplane_function_pythonic-0.6.2/examples/import-existing-vpc/provider-ec2.yaml +0 -20
- crossplane_function_pythonic-0.6.2/examples/import-existing-vpc/render.sh +0 -4
- crossplane_function_pythonic-0.6.2/examples/import-existing-vpc/xr.yaml +0 -10
- crossplane_function_pythonic-0.6.2/examples/inlined/composition.yaml +0 -17
- crossplane_function_pythonic-0.6.2/examples/inlined/definition.yaml +0 -32
- crossplane_function_pythonic-0.6.2/examples/inlined/render.sh +0 -3
- crossplane_function_pythonic-0.6.2/examples/inlined/xr.yaml +0 -10
- crossplane_function_pythonic-0.6.2/examples/run-all.sh +0 -3
- crossplane_function_pythonic-0.6.2/examples/usages-extra/composition.yaml +0 -26
- crossplane_function_pythonic-0.6.2/examples/usages-extra/observed.yaml +0 -6
- crossplane_function_pythonic-0.6.2/examples/usages-extra/render.sh +0 -3
- crossplane_function_pythonic-0.6.2/examples/usages-extra/resources.yaml +0 -7
- crossplane_function_pythonic-0.6.2/examples/usages-extra/xr.yaml +0 -6
- crossplane_function_pythonic-0.6.2/pyproject.toml +0 -153
- {crossplane_function_pythonic-0.6.2 → crossplane_function_pythonic-0.7.0/src}/crossplane/pythonic/proto/v1/run_function.proto +0 -0
|
@@ -1,28 +1,23 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: crossplane-function-pythonic
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: A Python centric Crossplane Function
|
|
5
|
-
|
|
6
|
-
Project-URL: Issues, https://github.com/crossplane-contrib/function-pythonic/issues
|
|
7
|
-
Project-URL: Source, https://github.com/crossplane-contrib/function-pythonic
|
|
5
|
+
Author: Patrick J McNerthney
|
|
8
6
|
Author-email: Patrick J McNerthney <pat@mcnerthney.com>
|
|
9
7
|
License-Expression: Apache-2.0
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
Classifier: Programming Language :: Python
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.14
|
|
16
|
-
Requires-Python: <3.15,>=3.12
|
|
17
|
-
Requires-Dist: grpcio==1.83.0
|
|
18
|
-
Requires-Dist: inflect==7.5.0
|
|
8
|
+
Requires-Dist: grpcio==1.85.1
|
|
9
|
+
Requires-Dist: inflect==7.6.0
|
|
19
10
|
Requires-Dist: kr8s==0.20.15
|
|
20
|
-
Requires-Dist: protobuf==7.
|
|
11
|
+
Requires-Dist: protobuf==7.36.2
|
|
21
12
|
Requires-Dist: pyyaml==6.0.3
|
|
13
|
+
Requires-Dist: kopf==1.45.2 ; extra == 'packages'
|
|
14
|
+
Requires-Dist: pip==26.2.2 ; extra == 'pip-install'
|
|
15
|
+
Requires-Python: >=3.15, <3.16
|
|
16
|
+
Project-URL: Documentation, https://github.com/crossplane-contrib/function-pythonic#readme
|
|
17
|
+
Project-URL: Issues, https://github.com/crossplane-contrib/function-pythonic/issues
|
|
18
|
+
Project-URL: Source, https://github.com/crossplane-contrib/function-pythonic
|
|
22
19
|
Provides-Extra: packages
|
|
23
|
-
Requires-Dist: kopf==1.44.6; extra == 'packages'
|
|
24
20
|
Provides-Extra: pip-install
|
|
25
|
-
Requires-Dist: pip==26.2.1; extra == 'pip-install'
|
|
26
21
|
Description-Content-Type: text/markdown
|
|
27
22
|
|
|
28
23
|
# function-pythonic
|
|
@@ -213,7 +208,7 @@ message contents. For example, the following examples obtain the same value
|
|
|
213
208
|
from the RunFunctionRequest message:
|
|
214
209
|
```python
|
|
215
210
|
region = request.observed.composite.resource.spec.region
|
|
216
|
-
region = request[
|
|
211
|
+
region = request["observed"]["composite"]["resource"]["spec"]["region"]
|
|
217
212
|
```
|
|
218
213
|
Getting values from free form map and list values will not throw
|
|
219
214
|
errors for keys that do not exist, but will return an unknown placeholder
|
|
@@ -232,13 +227,15 @@ When setting fields, all intermediary unknown placeholders will automatically
|
|
|
232
227
|
be created. For example, this will create all items needed to set the
|
|
233
228
|
region on the desired resource:
|
|
234
229
|
```python
|
|
235
|
-
response.desired.resources.vpc.resource.spec.forProvider.region =
|
|
230
|
+
response.desired.resources.vpc.resource.spec.forProvider.region = "us-east-1"
|
|
236
231
|
```
|
|
237
232
|
Calling a message or map will clear it and will set any provided key word
|
|
238
233
|
arguments. For example, this will either create or clear the resource
|
|
239
234
|
and then set its apiVersion and kind:
|
|
240
235
|
```python
|
|
241
|
-
response.desired.resources.vpc.resource(
|
|
236
|
+
response.desired.resources.vpc.resource(
|
|
237
|
+
kind="VPC", apiVersion="ec2.aws.crossplane.io/v1beta1"
|
|
238
|
+
)
|
|
242
239
|
```
|
|
243
240
|
The following functions are provided to create Protobuf structures:
|
|
244
241
|
| Function | Description |
|
|
@@ -257,12 +254,12 @@ The following items are supported in all the Protobuf Message wrapper classes: `
|
|
|
257
254
|
|
|
258
255
|
To convert a Protobuf message to a string value, use either `str` or `format`.
|
|
259
256
|
```python
|
|
260
|
-
yaml
|
|
261
|
-
yaml
|
|
262
|
-
yaml
|
|
263
|
-
json
|
|
264
|
-
json
|
|
265
|
-
proto = format(request,
|
|
257
|
+
yaml = str(request) # get the request as yaml
|
|
258
|
+
yaml = format(request) # also get the request as yaml
|
|
259
|
+
yaml = format(request, "yaml") # yet another get the request as yaml
|
|
260
|
+
json = format(request, "json") # get the request as json
|
|
261
|
+
json = format(request, "jsonc") # get the request as json compact
|
|
262
|
+
proto = format(request, "protobuf") # get the request as a protobuf string
|
|
266
263
|
```
|
|
267
264
|
## Composite Composition
|
|
268
265
|
|
|
@@ -186,7 +186,7 @@ message contents. For example, the following examples obtain the same value
|
|
|
186
186
|
from the RunFunctionRequest message:
|
|
187
187
|
```python
|
|
188
188
|
region = request.observed.composite.resource.spec.region
|
|
189
|
-
region = request[
|
|
189
|
+
region = request["observed"]["composite"]["resource"]["spec"]["region"]
|
|
190
190
|
```
|
|
191
191
|
Getting values from free form map and list values will not throw
|
|
192
192
|
errors for keys that do not exist, but will return an unknown placeholder
|
|
@@ -205,13 +205,15 @@ When setting fields, all intermediary unknown placeholders will automatically
|
|
|
205
205
|
be created. For example, this will create all items needed to set the
|
|
206
206
|
region on the desired resource:
|
|
207
207
|
```python
|
|
208
|
-
response.desired.resources.vpc.resource.spec.forProvider.region =
|
|
208
|
+
response.desired.resources.vpc.resource.spec.forProvider.region = "us-east-1"
|
|
209
209
|
```
|
|
210
210
|
Calling a message or map will clear it and will set any provided key word
|
|
211
211
|
arguments. For example, this will either create or clear the resource
|
|
212
212
|
and then set its apiVersion and kind:
|
|
213
213
|
```python
|
|
214
|
-
response.desired.resources.vpc.resource(
|
|
214
|
+
response.desired.resources.vpc.resource(
|
|
215
|
+
kind="VPC", apiVersion="ec2.aws.crossplane.io/v1beta1"
|
|
216
|
+
)
|
|
215
217
|
```
|
|
216
218
|
The following functions are provided to create Protobuf structures:
|
|
217
219
|
| Function | Description |
|
|
@@ -230,12 +232,12 @@ The following items are supported in all the Protobuf Message wrapper classes: `
|
|
|
230
232
|
|
|
231
233
|
To convert a Protobuf message to a string value, use either `str` or `format`.
|
|
232
234
|
```python
|
|
233
|
-
yaml
|
|
234
|
-
yaml
|
|
235
|
-
yaml
|
|
236
|
-
json
|
|
237
|
-
json
|
|
238
|
-
proto = format(request,
|
|
235
|
+
yaml = str(request) # get the request as yaml
|
|
236
|
+
yaml = format(request) # also get the request as yaml
|
|
237
|
+
yaml = format(request, "yaml") # yet another get the request as yaml
|
|
238
|
+
json = format(request, "json") # get the request as json
|
|
239
|
+
json = format(request, "jsonc") # get the request as json compact
|
|
240
|
+
proto = format(request, "protobuf") # get the request as a protobuf string
|
|
239
241
|
```
|
|
240
242
|
## Composite Composition
|
|
241
243
|
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "crossplane-function-pythonic"
|
|
3
|
+
version = "0.7.0"
|
|
4
|
+
description = "A Python centric Crossplane Function"
|
|
5
|
+
license = "Apache-2.0"
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
requires-python = ">=3.15,<3.16"
|
|
8
|
+
dependencies = [
|
|
9
|
+
"grpcio==1.85.1",
|
|
10
|
+
"inflect==7.6.0",
|
|
11
|
+
"kr8s==0.20.15",
|
|
12
|
+
"protobuf==7.36.2",
|
|
13
|
+
"pyyaml==6.0.3",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
[[project.authors]]
|
|
17
|
+
name = "Patrick J McNerthney"
|
|
18
|
+
email = "pat@mcnerthney.com"
|
|
19
|
+
|
|
20
|
+
[project.optional-dependencies]
|
|
21
|
+
packages = ["kopf==1.45.2"]
|
|
22
|
+
pip-install = ["pip==26.2.2"]
|
|
23
|
+
|
|
24
|
+
[project.urls]
|
|
25
|
+
Documentation = "https://github.com/crossplane-contrib/function-pythonic#readme"
|
|
26
|
+
Issues = "https://github.com/crossplane-contrib/function-pythonic/issues"
|
|
27
|
+
Source = "https://github.com/crossplane-contrib/function-pythonic"
|
|
28
|
+
|
|
29
|
+
[project.scripts]
|
|
30
|
+
function-pythonic = "crossplane.pythonic.main:main"
|
|
31
|
+
|
|
32
|
+
[build-system]
|
|
33
|
+
requires = ["uv_build>=0.12.23,<0.13.0"]
|
|
34
|
+
build-backend = "uv_build"
|
|
35
|
+
|
|
36
|
+
[dependency-groups]
|
|
37
|
+
dev = [
|
|
38
|
+
"grpcio-tools>=1.85.0",
|
|
39
|
+
"kopf==1.45.2",
|
|
40
|
+
"mypy-protobuf>=5.1.0",
|
|
41
|
+
"poethepoet>=0.48.0",
|
|
42
|
+
"pip==26.2.2",
|
|
43
|
+
"pytest>=9.1.1",
|
|
44
|
+
"pytest-asyncio>=1.4.0",
|
|
45
|
+
"pytest-cov>=7.1.0",
|
|
46
|
+
"ruff>=0.17.0",
|
|
47
|
+
"ty>=0.0.92",
|
|
48
|
+
"types-grpcio>=1.84.0.20261021",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
[tool.uv.build-backend]
|
|
52
|
+
module-name = "crossplane.pythonic"
|
|
53
|
+
|
|
54
|
+
[tool.ty.src]
|
|
55
|
+
exclude = ["tests/test_protobuf_messages.py"]
|
|
56
|
+
|
|
57
|
+
[tool.poe.tasks]
|
|
58
|
+
generate-protoc = "python -m grpc_tools.protoc --proto_path=src --python_out=src --mypy_out=src --grpc_python_out=src --mypy_grpc_out=src crossplane/pythonic/proto/v1/run_function.proto"
|
|
59
|
+
test = "pytest tests -x --verbose --verbose --cov --cov-report=term --cov-report=html:reports"
|
|
60
|
+
test-protobuf = "pytest tests/test_protobuf_*.py -x --verbose --verbose --cov --cov-report=term --cov-report=html:reports"
|
|
61
|
+
test-ci = "pytest tests --verbose --verbose --junitxml=reports/pytest-junit.xml --cov --cov-report=term --cov-report=xml:reports/pytest-coverage.xml"
|
|
62
|
+
|
|
63
|
+
[tool.pytest.ini_options]
|
|
64
|
+
pythonpath = ["."]
|
|
65
|
+
filterwarnings = ["ignore:'asyncio.iscoroutinefunction' is deprecated:DeprecationWarning:kr8s._vendored.asyncache"]
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "crossplane-function-pythonic"
|
|
3
|
+
version = "0.7.0"
|
|
4
|
+
description = "A Python centric Crossplane Function"
|
|
5
|
+
license = "Apache-2.0"
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
authors = [
|
|
8
|
+
{ name = "Patrick J McNerthney", email = "pat@mcnerthney.com" }
|
|
9
|
+
]
|
|
10
|
+
requires-python = ">=3.15,<3.16"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"grpcio==1.85.1",
|
|
13
|
+
"inflect==7.6.0",
|
|
14
|
+
"kr8s==0.20.15",
|
|
15
|
+
"protobuf==7.36.2",
|
|
16
|
+
"pyyaml==6.0.3",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[project.optional-dependencies]
|
|
20
|
+
packages = ["kopf==1.45.2"]
|
|
21
|
+
pip-install = ["pip==26.2.2"]
|
|
22
|
+
|
|
23
|
+
[project.urls]
|
|
24
|
+
Documentation = "https://github.com/crossplane-contrib/function-pythonic#readme"
|
|
25
|
+
Issues = "https://github.com/crossplane-contrib/function-pythonic/issues"
|
|
26
|
+
Source = "https://github.com/crossplane-contrib/function-pythonic"
|
|
27
|
+
|
|
28
|
+
[project.scripts]
|
|
29
|
+
function-pythonic = "crossplane.pythonic.main:main"
|
|
30
|
+
|
|
31
|
+
[build-system]
|
|
32
|
+
requires = ["uv_build>=0.12.23,<0.13.0"]
|
|
33
|
+
build-backend = "uv_build"
|
|
34
|
+
|
|
35
|
+
[dependency-groups]
|
|
36
|
+
dev = [
|
|
37
|
+
"grpcio-tools>=1.85.0",
|
|
38
|
+
"kopf==1.45.2",
|
|
39
|
+
"mypy-protobuf>=5.1.0",
|
|
40
|
+
"poethepoet>=0.48.0",
|
|
41
|
+
"pip==26.2.2",
|
|
42
|
+
"pytest>=9.1.1",
|
|
43
|
+
"pytest-asyncio>=1.4.0",
|
|
44
|
+
"pytest-cov>=7.1.0",
|
|
45
|
+
"ruff>=0.17.0",
|
|
46
|
+
"ty>=0.0.92",
|
|
47
|
+
"types-grpcio>=1.84.0.20261021",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
[tool.uv.build-backend]
|
|
51
|
+
module-name = "crossplane.pythonic"
|
|
52
|
+
|
|
53
|
+
[tool.ty.src]
|
|
54
|
+
exclude = ["tests/test_protobuf_messages.py"]
|
|
55
|
+
|
|
56
|
+
[tool.poe.tasks]
|
|
57
|
+
generate-protoc = "python -m grpc_tools.protoc --proto_path=src --python_out=src --mypy_out=src --grpc_python_out=src --mypy_grpc_out=src crossplane/pythonic/proto/v1/run_function.proto"
|
|
58
|
+
test = "pytest tests -x --verbose --verbose --cov --cov-report=term --cov-report=html:reports"
|
|
59
|
+
test-protobuf = "pytest tests/test_protobuf_*.py -x --verbose --verbose --cov --cov-report=term --cov-report=html:reports"
|
|
60
|
+
test-ci = "pytest tests --verbose --verbose --junitxml=reports/pytest-junit.xml --cov --cov-report=term --cov-report=xml:reports/pytest-coverage.xml"
|
|
61
|
+
|
|
62
|
+
[tool.pytest.ini_options]
|
|
63
|
+
pythonpath = ["."]
|
|
64
|
+
filterwarnings = [
|
|
65
|
+
"ignore:'asyncio.iscoroutinefunction' is deprecated:DeprecationWarning:kr8s._vendored.asyncache",
|
|
66
|
+
]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from .composite import BaseComposite
|
|
2
|
+
from .protobuf import (
|
|
3
|
+
B64Decode,
|
|
4
|
+
B64Encode,
|
|
5
|
+
Json,
|
|
6
|
+
List,
|
|
7
|
+
Map,
|
|
8
|
+
Unknown,
|
|
9
|
+
Yaml,
|
|
10
|
+
YamlAll,
|
|
11
|
+
append,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"B64Decode",
|
|
16
|
+
"B64Encode",
|
|
17
|
+
"BaseComposite",
|
|
18
|
+
"Json",
|
|
19
|
+
"List",
|
|
20
|
+
"Map",
|
|
21
|
+
"Unknown",
|
|
22
|
+
"Yaml",
|
|
23
|
+
"YamlAll",
|
|
24
|
+
"append",
|
|
25
|
+
]
|
|
@@ -1,14 +1,24 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
1
2
|
|
|
2
|
-
|
|
3
|
+
if TYPE_CHECKING:
|
|
4
|
+
from . import (
|
|
5
|
+
composite,
|
|
6
|
+
protobuf,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def resource_ready(resource: composite.Resource) -> protobuf.Value | None:
|
|
3
11
|
if not resource.observed:
|
|
4
12
|
return None
|
|
5
|
-
return _checks.get(
|
|
13
|
+
return _checks.get(
|
|
14
|
+
(resource.observed.apiVersion, resource.observed.kind), _check_default
|
|
15
|
+
).ready(resource)
|
|
6
16
|
|
|
7
17
|
|
|
8
18
|
class ReadyCondition:
|
|
9
|
-
def ready(self, resource):
|
|
19
|
+
def ready(self, resource: composite.Resource) -> protobuf.Value | None:
|
|
10
20
|
ready = resource.conditions.Ready
|
|
11
|
-
if not ready.
|
|
21
|
+
if not ready._get_condition():
|
|
12
22
|
return None
|
|
13
23
|
if ready.status:
|
|
14
24
|
return resource.observed.metadata.name
|
|
@@ -16,36 +26,43 @@ class ReadyCondition:
|
|
|
16
26
|
return resource.status.notReadyCondition[ready.reason]
|
|
17
27
|
return resource.status.notReadyCondition
|
|
18
28
|
|
|
19
|
-
|
|
29
|
+
|
|
30
|
+
_checks: dict[tuple, Check] = {}
|
|
20
31
|
_check_default = ReadyCondition()
|
|
21
32
|
|
|
33
|
+
|
|
22
34
|
class Check:
|
|
23
35
|
@classmethod
|
|
24
36
|
def __init_subclass__(cls, **kwargs):
|
|
25
37
|
super().__init_subclass__(**kwargs)
|
|
26
|
-
if hasattr(cls,
|
|
38
|
+
if hasattr(cls, "apiVersion"):
|
|
27
39
|
_checks[(cls.apiVersion, cls.__name__)] = cls()
|
|
28
40
|
|
|
29
|
-
def ready(self, resource):
|
|
41
|
+
def ready(self, resource: composite.Resource) -> protobuf.Value | None:
|
|
30
42
|
raise NotImplementedError()
|
|
31
43
|
|
|
44
|
+
|
|
32
45
|
class AlwaysReady(Check):
|
|
33
|
-
def ready(self, resource):
|
|
46
|
+
def ready(self, resource: composite.Resource) -> protobuf.Value:
|
|
34
47
|
return resource.observed.metadata.name
|
|
35
48
|
|
|
36
49
|
|
|
37
50
|
class ClusterRole(AlwaysReady):
|
|
38
|
-
apiVersion =
|
|
51
|
+
apiVersion = "rbac.authorization.k8s.io/v1"
|
|
52
|
+
|
|
39
53
|
|
|
40
54
|
class ClusterRoleBinding(AlwaysReady):
|
|
41
|
-
apiVersion =
|
|
55
|
+
apiVersion = "rbac.authorization.k8s.io/v1"
|
|
56
|
+
|
|
42
57
|
|
|
43
58
|
class ConfigMap(AlwaysReady):
|
|
44
|
-
apiVersion =
|
|
59
|
+
apiVersion = "v1"
|
|
60
|
+
|
|
45
61
|
|
|
46
62
|
class CronJob(Check):
|
|
47
|
-
apiVersion =
|
|
48
|
-
|
|
63
|
+
apiVersion = "batch/v1"
|
|
64
|
+
|
|
65
|
+
def ready(self, resource: composite.Resource) -> protobuf.Value:
|
|
49
66
|
if resource.observed.spec.suspend and len(resource.observed.spec.suspend):
|
|
50
67
|
return resource.observed.metadata.name
|
|
51
68
|
if not resource.status.lastScheduleTime:
|
|
@@ -54,17 +71,21 @@ class CronJob(Check):
|
|
|
54
71
|
return resource.observed.metadata.name
|
|
55
72
|
if not resource.status.lastSuccessfulTime:
|
|
56
73
|
return resource.status.lastSuccessfulTime
|
|
57
|
-
if str(resource.status.lastSuccessfulTime) < str(
|
|
74
|
+
if str(resource.status.lastSuccessfulTime) < str(
|
|
75
|
+
resource.status.lastScheduleTime
|
|
76
|
+
):
|
|
58
77
|
return resource.status.successfulBeforeSchedule
|
|
59
78
|
return resource.observed.metadata.name
|
|
60
79
|
|
|
80
|
+
|
|
61
81
|
class DaemonSet(Check):
|
|
62
|
-
apiVersion =
|
|
63
|
-
|
|
82
|
+
apiVersion = "apps/v1"
|
|
83
|
+
|
|
84
|
+
def ready(self, resource: composite.Resource) -> protobuf.Value:
|
|
64
85
|
scheduled = resource.status.desiredNumberScheduled
|
|
65
86
|
if not scheduled:
|
|
66
87
|
return scheduled
|
|
67
|
-
for field in (
|
|
88
|
+
for field in ("numberReady", "updatedNumberScheduled", "numberAvailable"):
|
|
68
89
|
value = resource.status[field]
|
|
69
90
|
if not value:
|
|
70
91
|
return value
|
|
@@ -72,16 +93,18 @@ class DaemonSet(Check):
|
|
|
72
93
|
return resource.status[f"{field}NotScheduled"]
|
|
73
94
|
return resource.observed.metadata.name
|
|
74
95
|
|
|
96
|
+
|
|
75
97
|
class Deployment(Check):
|
|
76
|
-
apiVersion =
|
|
77
|
-
|
|
98
|
+
apiVersion = "apps/v1"
|
|
99
|
+
|
|
100
|
+
def ready(self, resource: composite.Resource) -> protobuf.Value:
|
|
78
101
|
replicas = resource.observed.spec.replicas or 1
|
|
79
|
-
for field in (
|
|
102
|
+
for field in ("updatedReplicas", "availableReplicas"):
|
|
80
103
|
value = resource.status[field]
|
|
81
104
|
if not value:
|
|
82
105
|
return value
|
|
83
106
|
if replicas != value:
|
|
84
|
-
return resource.status[
|
|
107
|
+
return resource.status[f"{field}NotReplicas"]
|
|
85
108
|
available = resource.conditions.Available
|
|
86
109
|
if not available:
|
|
87
110
|
return resource.status.notAvailable
|
|
@@ -91,28 +114,39 @@ class Deployment(Check):
|
|
|
91
114
|
return resource.status.notAvailable
|
|
92
115
|
return resource.observed.metadata.name
|
|
93
116
|
|
|
117
|
+
|
|
94
118
|
class HorizontalPodAutoscaler(Check):
|
|
95
|
-
apiVersion =
|
|
96
|
-
|
|
97
|
-
|
|
119
|
+
apiVersion = "autoscaling/v2"
|
|
120
|
+
|
|
121
|
+
def ready(self, resource: composite.Resource) -> protobuf.Value:
|
|
122
|
+
for type in (
|
|
123
|
+
"FailedGetScale",
|
|
124
|
+
"FailedUpdateScale",
|
|
125
|
+
"FailedGetResourceMetric",
|
|
126
|
+
"InvalidSelector",
|
|
127
|
+
):
|
|
98
128
|
if resource.conditions[type].status:
|
|
99
129
|
return resource.status[f"is{type}"]
|
|
100
|
-
for type in (
|
|
130
|
+
for type in ("ScalingActive", "ScalingLimited"):
|
|
101
131
|
if resource.conditions[type].status:
|
|
102
132
|
return resource.observed.metadata.name
|
|
103
|
-
return resource.status.
|
|
133
|
+
return resource.status.notScalingActiveOrLimited
|
|
134
|
+
|
|
104
135
|
|
|
105
136
|
class Ingress(Check):
|
|
106
|
-
apiVersion =
|
|
107
|
-
|
|
137
|
+
apiVersion = "networking.k8s.io/v1"
|
|
138
|
+
|
|
139
|
+
def ready(self, resource: composite.Resource) -> protobuf.Value:
|
|
108
140
|
if not len(resource.status.loadBalancer.ingress):
|
|
109
|
-
return resource.status.
|
|
141
|
+
return resource.status.noLoadBalancerIngresses
|
|
110
142
|
return resource.observed.metadata.name
|
|
111
143
|
|
|
144
|
+
|
|
112
145
|
class Job(Check):
|
|
113
|
-
apiVersion =
|
|
114
|
-
|
|
115
|
-
|
|
146
|
+
apiVersion = "batch/v1"
|
|
147
|
+
|
|
148
|
+
def ready(self, resource: composite.Resource) -> protobuf.Value:
|
|
149
|
+
for type in ("Failed", "Suspended"):
|
|
116
150
|
if resource.conditions[type].status:
|
|
117
151
|
return resource.status[f"is{type}"]
|
|
118
152
|
complete = resource.conditions.Complete
|
|
@@ -124,71 +158,94 @@ class Job(Check):
|
|
|
124
158
|
return resource.status.notComplete
|
|
125
159
|
return resource.observed.metadata.name
|
|
126
160
|
|
|
161
|
+
|
|
127
162
|
class Namespace(AlwaysReady):
|
|
128
|
-
apiVersion =
|
|
163
|
+
apiVersion = "v1"
|
|
164
|
+
|
|
129
165
|
|
|
130
166
|
class PersistentVolumeClaim(Check):
|
|
131
|
-
apiVersion =
|
|
132
|
-
|
|
133
|
-
|
|
167
|
+
apiVersion = "v1"
|
|
168
|
+
|
|
169
|
+
def ready(self, resource: composite.Resource) -> protobuf.Value:
|
|
170
|
+
if resource.status.phase != "Bound":
|
|
134
171
|
return resource.status.phaseNotBound
|
|
135
172
|
return resource.observed.metadata.name
|
|
136
173
|
|
|
174
|
+
|
|
137
175
|
class Pod(Check):
|
|
138
|
-
apiVersion =
|
|
139
|
-
|
|
140
|
-
|
|
176
|
+
apiVersion = "v1"
|
|
177
|
+
|
|
178
|
+
def ready(self, resource: composite.Resource) -> protobuf.Value:
|
|
179
|
+
if resource.status.phase == "Succeeded":
|
|
180
|
+
return resource.observed.metadata.name
|
|
181
|
+
if (
|
|
182
|
+
resource.status.phase == "Running"
|
|
183
|
+
and resource.observed.spec.restartPolicy == "Always"
|
|
184
|
+
and resource.conditions.Ready.status
|
|
185
|
+
):
|
|
141
186
|
return resource.observed.metadata.name
|
|
142
|
-
if resource.status.phase == 'Running':
|
|
143
|
-
if resource.observed.spec.restartPolicy == 'Always':
|
|
144
|
-
if resource.conditions.Ready.status:
|
|
145
|
-
return resource.observed.metadata.name
|
|
146
187
|
return resource.status.notSucceededOrRunning
|
|
147
188
|
|
|
189
|
+
|
|
148
190
|
class ReplicaSet(Check):
|
|
149
|
-
apiVersion =
|
|
150
|
-
|
|
151
|
-
|
|
191
|
+
apiVersion = "apps/v1"
|
|
192
|
+
|
|
193
|
+
def ready(self, resource: composite.Resource) -> protobuf.Value:
|
|
194
|
+
if int(resource.status.observedGeneration) < int(
|
|
195
|
+
resource.observed.metadata.generation
|
|
196
|
+
):
|
|
152
197
|
return resource.status.priorObservedGeneration
|
|
153
198
|
if resource.conditions.ReplicaFailure.status:
|
|
154
199
|
if resource.conditions.ReplicaFailure.reason:
|
|
155
|
-
return resource.status.isReplicaFailure[
|
|
200
|
+
return resource.status.isReplicaFailure[
|
|
201
|
+
resource.conditions.ReplicaFailure.reason
|
|
202
|
+
]
|
|
156
203
|
return resource.status.isReplicaFailure
|
|
157
|
-
if int(resource.status.availableReplicas) < int(
|
|
158
|
-
|
|
204
|
+
if int(resource.status.availableReplicas) < int(
|
|
205
|
+
resource.observed.spec.replicas or 1
|
|
206
|
+
):
|
|
207
|
+
return resource.status.tooFewAvailableReplicas
|
|
159
208
|
return resource.observed.metadata.name
|
|
160
209
|
|
|
210
|
+
|
|
161
211
|
class Role(AlwaysReady):
|
|
162
|
-
apiVersion =
|
|
212
|
+
apiVersion = "rbac.authorization.k8s.io/v1"
|
|
213
|
+
|
|
163
214
|
|
|
164
215
|
class RoleBinding(AlwaysReady):
|
|
165
|
-
apiVersion =
|
|
216
|
+
apiVersion = "rbac.authorization.k8s.io/v1"
|
|
217
|
+
|
|
166
218
|
|
|
167
219
|
class Secret(AlwaysReady):
|
|
168
|
-
apiVersion =
|
|
220
|
+
apiVersion = "v1"
|
|
221
|
+
|
|
169
222
|
|
|
170
223
|
class Service(Check):
|
|
171
|
-
apiVersion =
|
|
172
|
-
|
|
173
|
-
|
|
224
|
+
apiVersion = "v1"
|
|
225
|
+
|
|
226
|
+
def ready(self, resource: composite.Resource) -> protobuf.Value:
|
|
227
|
+
if resource.observed.spec.type != "LoadBalancer":
|
|
174
228
|
return resource.observed.metadata.name
|
|
175
229
|
if not len(resource.status.loadBalancer.ingress):
|
|
176
230
|
return resource.status.noLoadBalancerIngresses
|
|
177
231
|
return resource.observed.metadata.name
|
|
178
232
|
|
|
233
|
+
|
|
179
234
|
class ServiceAccount(AlwaysReady):
|
|
180
|
-
apiVersion =
|
|
235
|
+
apiVersion = "v1"
|
|
236
|
+
|
|
181
237
|
|
|
182
238
|
class StatefulSet(Check):
|
|
183
|
-
apiVersion =
|
|
184
|
-
|
|
239
|
+
apiVersion = "apps/v1"
|
|
240
|
+
|
|
241
|
+
def ready(self, resource: composite.Resource) -> protobuf.Value:
|
|
185
242
|
replicas = resource.observed.spec.replicas or 1
|
|
186
|
-
for field in (
|
|
243
|
+
for field in ("readyReplicas", "currentReplicas"):
|
|
187
244
|
value = resource.status[field]
|
|
188
245
|
if not value:
|
|
189
246
|
return value
|
|
190
247
|
if replicas != value:
|
|
191
|
-
return resource.status[
|
|
248
|
+
return resource.status[f"{field}NotReplicas"]
|
|
192
249
|
if resource.status.currentRevision != resource.status.updateRevision:
|
|
193
|
-
return resource.status.
|
|
250
|
+
return resource.status.currentRevisionNotUpdateRevision
|
|
194
251
|
return resource.observed.metadata.name
|