crossplane-function-pythonic 0.0.11__tar.gz → 0.1.1__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.
Files changed (120) hide show
  1. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/PKG-INFO +26 -4
  2. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/README.md +25 -3
  3. crossplane_function_pythonic-0.1.1/crossplane/pythonic/__init__.py +16 -0
  4. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/crossplane/pythonic/composite.py +16 -13
  5. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/crossplane/pythonic/function.py +121 -38
  6. crossplane_function_pythonic-0.1.1/crossplane/pythonic/protobuf.py +1278 -0
  7. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/.dev/functions.yaml +1 -1
  8. crossplane_function_pythonic-0.1.1/examples/aks-cluster/README.md +129 -0
  9. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/aks-cluster/aks/kubernetescluster.py +1 -1
  10. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/aks-cluster/cluster-function-pythonic.yaml +1 -1
  11. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/aks-cluster/composition.yaml +1 -1
  12. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/aks-cluster/definition.yaml +2 -0
  13. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/aks-cluster/functions.yaml +1 -1
  14. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/aks-cluster/xr.yaml +1 -0
  15. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/eks-cluster/composition.yaml +2 -0
  16. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/eks-cluster/functions.yaml +1 -1
  17. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/eks-cluster/xr.yaml +1 -0
  18. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/filing-system/function.yaml +1 -1
  19. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/conditions/composition.yaml +6 -6
  20. {crossplane_function_pythonic-0.0.11/examples/function-go-templating/context → crossplane_function_pythonic-0.1.1/examples/function-go-templating/conditions}/functions.yaml +1 -1
  21. {crossplane_function_pythonic-0.0.11/examples/function-go-templating/conditions → crossplane_function_pythonic-0.1.1/examples/function-go-templating/context}/functions.yaml +1 -1
  22. {crossplane_function_pythonic-0.0.11/examples/function-go-templating/functions/getComposedResource → crossplane_function_pythonic-0.1.1/examples/function-go-templating/extra-resources}/functions.yaml +1 -1
  23. {crossplane_function_pythonic-0.0.11/examples/function-go-templating/functions/getCompositeResource → crossplane_function_pythonic-0.1.1/examples/function-go-templating/functions/fromYaml}/functions.yaml +1 -1
  24. {crossplane_function_pythonic-0.0.11/examples/function-go-templating/functions/fromYaml → crossplane_function_pythonic-0.1.1/examples/function-go-templating/functions/getComposedResource}/functions.yaml +1 -1
  25. crossplane_function_pythonic-0.1.1/examples/function-go-templating/functions/getCompositeResource/functions.yaml +9 -0
  26. crossplane_function_pythonic-0.1.1/examples/function-go-templating/functions/getResourceCondition/functions.yaml +9 -0
  27. crossplane_function_pythonic-0.1.1/examples/function-go-templating/functions/include/functions.yaml +9 -0
  28. crossplane_function_pythonic-0.1.1/examples/function-go-templating/functions/toYaml/functions.yaml +9 -0
  29. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/inline/composition.yaml +1 -1
  30. crossplane_function_pythonic-0.1.1/examples/function-go-templating/inline/functions.yaml +9 -0
  31. {crossplane_function_pythonic-0.0.11/examples/helm-copy-secret → crossplane_function_pythonic-0.1.1/examples/get-started-app}/functions.yaml +1 -1
  32. {crossplane_function_pythonic-0.0.11/examples/single-purpose → crossplane_function_pythonic-0.1.1/examples/helm-copy-secret}/functions.yaml +1 -1
  33. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/helm-copy-secret/vcluster.py +1 -1
  34. {crossplane_function_pythonic-0.0.11/examples/get-started-app → crossplane_function_pythonic-0.1.1/examples/single-purpose}/functions.yaml +1 -1
  35. crossplane_function_pythonic-0.1.1/examples/usages-extra/composition.yaml +26 -0
  36. crossplane_function_pythonic-0.1.1/examples/usages-extra/extraResources.yaml +7 -0
  37. crossplane_function_pythonic-0.1.1/examples/usages-extra/functions.yaml +9 -0
  38. crossplane_function_pythonic-0.1.1/examples/usages-extra/observedResources.yaml +6 -0
  39. crossplane_function_pythonic-0.1.1/examples/usages-extra/render.sh +3 -0
  40. crossplane_function_pythonic-0.1.1/examples/usages-extra/xr.yaml +6 -0
  41. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/pyproject.toml +3 -2
  42. crossplane_function_pythonic-0.0.11/crossplane/pythonic/__init__.py +0 -19
  43. crossplane_function_pythonic-0.0.11/crossplane/pythonic/protobuf.py +0 -955
  44. crossplane_function_pythonic-0.0.11/examples/aks-cluster/README.md +0 -32
  45. crossplane_function_pythonic-0.0.11/examples/function-go-templating/extra-resources/functions.yaml +0 -9
  46. crossplane_function_pythonic-0.0.11/examples/function-go-templating/functions/getResourceCondition/functions.yaml +0 -9
  47. crossplane_function_pythonic-0.0.11/examples/function-go-templating/functions/include/functions.yaml +0 -9
  48. crossplane_function_pythonic-0.0.11/examples/function-go-templating/functions/toYaml/functions.yaml +0 -9
  49. crossplane_function_pythonic-0.0.11/examples/function-go-templating/inline/functions.yaml +0 -9
  50. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/.gitignore +0 -0
  51. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/LICENSE +0 -0
  52. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/crossplane/pythonic/main.py +0 -0
  53. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/crossplane/pythonic/packages.py +0 -0
  54. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/aks-cluster/aks/resourcegroup.py +0 -0
  55. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/aks-cluster/install.sh +0 -0
  56. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/aks-cluster/kustomization.yaml +0 -0
  57. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/aks-cluster/providers.yaml +0 -0
  58. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/aks-cluster/render.sh +0 -0
  59. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/eks-cluster/composition-v2.yaml +0 -0
  60. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/eks-cluster/definition.yaml +0 -0
  61. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/eks-cluster/render-v2.sh +0 -0
  62. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/eks-cluster/render.sh +0 -0
  63. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/filing-system/README.md +0 -0
  64. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/filing-system/composition.yaml +0 -0
  65. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/filing-system/definition.yaml +0 -0
  66. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/filing-system/kustomization.yaml +0 -0
  67. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/filing-system/runtime.yaml +0 -0
  68. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/filing-system/vcluster.py +0 -0
  69. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/filing-system/vcluster.yaml +0 -0
  70. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/conditions/render.sh +0 -0
  71. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/conditions/xr.yaml +0 -0
  72. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/conditions/xrd.yaml +0 -0
  73. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/context/composition.yaml +0 -0
  74. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/context/environmentConfigs.yaml +0 -0
  75. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/context/render.sh +0 -0
  76. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/context/xr.yaml +0 -0
  77. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/context/xrd.yaml +0 -0
  78. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/extra-resources/composition.yaml +0 -0
  79. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/extra-resources/extraResources.yaml +0 -0
  80. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/extra-resources/render.sh +0 -0
  81. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/extra-resources/xr.yaml +0 -0
  82. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/functions/fromYaml/composition.yaml +0 -0
  83. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/functions/fromYaml/render.sh +0 -0
  84. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/functions/fromYaml/xr.yaml +0 -0
  85. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/functions/getComposedResource/composition.yaml +0 -0
  86. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/functions/getComposedResource/observed.yaml +0 -0
  87. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/functions/getComposedResource/render.sh +0 -0
  88. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/functions/getComposedResource/xr.yaml +0 -0
  89. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/functions/getCompositeResource/composition.yaml +0 -0
  90. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/functions/getCompositeResource/render.sh +0 -0
  91. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/functions/getCompositeResource/xr.yaml +0 -0
  92. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/functions/getResourceCondition/composition.yaml +0 -0
  93. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/functions/getResourceCondition/observed.yaml +0 -0
  94. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/functions/getResourceCondition/render.sh +0 -0
  95. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/functions/getResourceCondition/xr.yaml +0 -0
  96. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/functions/include/composition.yaml +0 -0
  97. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/functions/include/render.sh +0 -0
  98. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/functions/include/xr.yaml +0 -0
  99. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/functions/toYaml/composition.yaml +0 -0
  100. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/functions/toYaml/render.sh +0 -0
  101. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/functions/toYaml/xr.yaml +0 -0
  102. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/inline/render.sh +0 -0
  103. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/inline/xr.yaml +0 -0
  104. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/recursive/composition-real.yaml +0 -0
  105. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/recursive/composition-wrapper.yaml +0 -0
  106. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/recursive/functions.yaml +0 -0
  107. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/recursive/render.sh +0 -0
  108. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/function-go-templating/recursive/xr.yaml +0 -0
  109. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/get-started-app/composition.yaml +0 -0
  110. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/get-started-app/definition.yaml +0 -0
  111. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/get-started-app/render.sh +0 -0
  112. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/get-started-app/xr.yaml +0 -0
  113. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/helm-copy-secret/composition.yaml +0 -0
  114. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/helm-copy-secret/kustomization.yaml +0 -0
  115. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/helm-copy-secret/render.sh +0 -0
  116. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/helm-copy-secret/run-function.sh +0 -0
  117. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/helm-copy-secret/xr.yaml +0 -0
  118. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/helm-copy-secret/xrd.yaml +0 -0
  119. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/single-purpose/render.sh +0 -0
  120. {crossplane_function_pythonic-0.0.11 → crossplane_function_pythonic-0.1.1}/examples/single-purpose/xr.yaml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: crossplane-function-pythonic
3
- Version: 0.0.11
3
+ Version: 0.1.1
4
4
  Summary: A Python centric Crossplane Function
5
5
  Project-URL: Documentation, https://github.com/fortra/function-pythonic#readme
6
6
  Project-URL: Issues, https://github.com/fortra/function-pythonic/issues
@@ -81,7 +81,7 @@ kind: Function
81
81
  metadata:
82
82
  name: function-pythonic
83
83
  spec:
84
- package: ghcr.io/fortra/function-pythonic:v0.0.10
84
+ package: ghcr.io/fortra/function-pythonic:v0.1.1
85
85
  ```
86
86
  ## Composed Resource Dependencies
87
87
 
@@ -119,6 +119,26 @@ overridden for all composed resource by setting the Composite `self.unknownsFata
119
119
  to False, or at the individual composed resource level by setting the
120
120
  `Resource.unknownsFatal` field to False.
121
121
 
122
+ ## Usage Dependencies
123
+
124
+ function-pythonic can be configured to automatically create
125
+ [Crossplane Usages](https://docs.crossplane.io/latest/managed-resources/usages/)
126
+ dependencies between resources. Modifying the above VPC example with:
127
+ ```yaml
128
+ self.usages = True
129
+
130
+ vpc = self.resources.VPC('ec2.aws.crossplane.io/v1beta1', 'VPC')
131
+ vpc.spec.forProvider.region = 'us-east-1
132
+ vpc.spec.forProvider.cidrBlock = '10.0.0.0/16'
133
+
134
+ subnet = self.resources.SubnetA('ec2.aws.crossplane.io/v1beta1', 'Subnet')
135
+ subnet.spec.forProvider.region = 'us-east-1'
136
+ subnet.spec.forProvider.vpcId = vpc.status.atProvider.vpcId
137
+ subnet.spec.forProvider.availabilityZone = 'us-east-1a'
138
+ subnet.spec.forProvider.cidrBlock = '10.0.0.0/20'
139
+ ```
140
+ Will generate the appropriate Crossplane Usage resource.
141
+
122
142
  ## Pythonic access of Protobuf Messages
123
143
 
124
144
  All Protobuf messages are wrapped by a set of python classes which enable using
@@ -225,6 +245,7 @@ The BaseComposite also provides access to the following Crossplane Function leve
225
245
  | self.requireds | Requireds | Request and read additional local Kubernetes resources |
226
246
  | self.resources | Resources | Define and process composed resources |
227
247
  | self.unknownsFatal | Boolean | Terminate the composition if already created resources are assigned unknown values, default True |
248
+ | self.usages| Boolean | Generate Crossplane Usages for resource dependencies, default False |
228
249
  | self.autoReady | Boolean | Perform auto ready processing on all composed resources, default True |
229
250
 
230
251
  ### Composed Resources
@@ -251,6 +272,7 @@ Resource class:
251
272
  | Resource.connection | Connection | The resource connection details |
252
273
  | Resource.ready | Boolean | The resource ready state |
253
274
  | Resource.unknownsFatal | Boolean | Terminate the composition if this resource has been created and is assigned unknown values, default is Composite.unknownsFatal |
275
+ | Resource.usages | Boolean | Generate Crossplane Usages for this resource, default is Composite.autoReady |
254
276
  | Resource.autoReady | Boolean | Perform auto ready processing on this resource, default is Composite.autoReady |
255
277
 
256
278
  ### Required Resources (AKA Extra Resources)
@@ -386,7 +408,7 @@ metadata:
386
408
  annotations:
387
409
  render.crossplane.io/runtime: Development
388
410
  spec:
389
- package: ghcr.io/fortra/function-pythonic:v0.0.10
411
+ package: ghcr.io/fortra/function-pythonic:v0.1.1
390
412
  ```
391
413
  In one terminal session, run function-pythonic:
392
414
  ```shell
@@ -488,7 +510,7 @@ kind: Function
488
510
  metadata:
489
511
  name: function-pythonic
490
512
  spec:
491
- package: ghcr.io/fortra/function-pythonic:v0.0.10
513
+ package: ghcr.io/fortra/function-pythonic:v0.1.1
492
514
  runtimeConfigRef:
493
515
  name: function-pythonic
494
516
  ---
@@ -57,7 +57,7 @@ kind: Function
57
57
  metadata:
58
58
  name: function-pythonic
59
59
  spec:
60
- package: ghcr.io/fortra/function-pythonic:v0.0.10
60
+ package: ghcr.io/fortra/function-pythonic:v0.1.1
61
61
  ```
62
62
  ## Composed Resource Dependencies
63
63
 
@@ -95,6 +95,26 @@ overridden for all composed resource by setting the Composite `self.unknownsFata
95
95
  to False, or at the individual composed resource level by setting the
96
96
  `Resource.unknownsFatal` field to False.
97
97
 
98
+ ## Usage Dependencies
99
+
100
+ function-pythonic can be configured to automatically create
101
+ [Crossplane Usages](https://docs.crossplane.io/latest/managed-resources/usages/)
102
+ dependencies between resources. Modifying the above VPC example with:
103
+ ```yaml
104
+ self.usages = True
105
+
106
+ vpc = self.resources.VPC('ec2.aws.crossplane.io/v1beta1', 'VPC')
107
+ vpc.spec.forProvider.region = 'us-east-1
108
+ vpc.spec.forProvider.cidrBlock = '10.0.0.0/16'
109
+
110
+ subnet = self.resources.SubnetA('ec2.aws.crossplane.io/v1beta1', 'Subnet')
111
+ subnet.spec.forProvider.region = 'us-east-1'
112
+ subnet.spec.forProvider.vpcId = vpc.status.atProvider.vpcId
113
+ subnet.spec.forProvider.availabilityZone = 'us-east-1a'
114
+ subnet.spec.forProvider.cidrBlock = '10.0.0.0/20'
115
+ ```
116
+ Will generate the appropriate Crossplane Usage resource.
117
+
98
118
  ## Pythonic access of Protobuf Messages
99
119
 
100
120
  All Protobuf messages are wrapped by a set of python classes which enable using
@@ -201,6 +221,7 @@ The BaseComposite also provides access to the following Crossplane Function leve
201
221
  | self.requireds | Requireds | Request and read additional local Kubernetes resources |
202
222
  | self.resources | Resources | Define and process composed resources |
203
223
  | self.unknownsFatal | Boolean | Terminate the composition if already created resources are assigned unknown values, default True |
224
+ | self.usages| Boolean | Generate Crossplane Usages for resource dependencies, default False |
204
225
  | self.autoReady | Boolean | Perform auto ready processing on all composed resources, default True |
205
226
 
206
227
  ### Composed Resources
@@ -227,6 +248,7 @@ Resource class:
227
248
  | Resource.connection | Connection | The resource connection details |
228
249
  | Resource.ready | Boolean | The resource ready state |
229
250
  | Resource.unknownsFatal | Boolean | Terminate the composition if this resource has been created and is assigned unknown values, default is Composite.unknownsFatal |
251
+ | Resource.usages | Boolean | Generate Crossplane Usages for this resource, default is Composite.autoReady |
230
252
  | Resource.autoReady | Boolean | Perform auto ready processing on this resource, default is Composite.autoReady |
231
253
 
232
254
  ### Required Resources (AKA Extra Resources)
@@ -362,7 +384,7 @@ metadata:
362
384
  annotations:
363
385
  render.crossplane.io/runtime: Development
364
386
  spec:
365
- package: ghcr.io/fortra/function-pythonic:v0.0.10
387
+ package: ghcr.io/fortra/function-pythonic:v0.1.1
366
388
  ```
367
389
  In one terminal session, run function-pythonic:
368
390
  ```shell
@@ -464,7 +486,7 @@ kind: Function
464
486
  metadata:
465
487
  name: function-pythonic
466
488
  spec:
467
- package: ghcr.io/fortra/function-pythonic:v0.0.10
489
+ package: ghcr.io/fortra/function-pythonic:v0.1.1
468
490
  runtimeConfigRef:
469
491
  name: function-pythonic
470
492
  ---
@@ -0,0 +1,16 @@
1
+
2
+
3
+ from .composite import BaseComposite
4
+ from .protobuf import append, Map, List, Unknown, Yaml, Json, B64Encode, B64Decode
5
+
6
+ __all__ = [
7
+ 'BaseComposite',
8
+ 'append',
9
+ 'Map',
10
+ 'List',
11
+ 'Unknown',
12
+ 'Yaml',
13
+ 'Json',
14
+ 'B64Encode',
15
+ 'B64Decode',
16
+ ]
@@ -31,6 +31,7 @@ class BaseComposite:
31
31
  self.resources = Resources(self)
32
32
  self.unknownsFatal = True
33
33
  self.autoReady = True
34
+ self.usages = False
34
35
 
35
36
  observed = self.request.observed.composite
36
37
  desired = self.response.desired.composite
@@ -49,7 +50,7 @@ class BaseComposite:
49
50
  def ttl(self):
50
51
  if self.response.meta.ttl.nanos:
51
52
  return float(self.response.meta.ttl.seconds) + (float(self.response.meta.ttl.nanos) / 1000000000.0)
52
- return self.response.meta.ttl.seconds
53
+ return int(self.response.meta.ttl.seconds)
53
54
 
54
55
  @ttl.setter
55
56
  def ttl(self, ttl):
@@ -61,7 +62,7 @@ class BaseComposite:
61
62
  if ttl.is_integer():
62
63
  self.response.meta.ttl.nanos = 0
63
64
  else:
64
- self.response.meta.ttl.nanos = int((ttl - self.response.meta.ttl.seconds) * 1000000000)
65
+ self.response.meta.ttl.nanos = int((ttl - int(self.response.meta.ttl.seconds)) * 1000000000)
65
66
  else:
66
67
  raise ValueError('ttl must be an int or float')
67
68
 
@@ -78,7 +79,7 @@ class BaseComposite:
78
79
  def ready(self, ready):
79
80
  if ready:
80
81
  ready = fnv1.Ready.READY_TRUE
81
- elif ready == None or (isinstance(ready, protobuf.Values) and ready._isUnknown):
82
+ elif ready == None or (isinstance(ready, protobuf.Value) and ready._isUnknown):
82
83
  ready = fnv1.Ready.READY_UNSPECIFIED
83
84
  else:
84
85
  ready = fnv1.Ready.READY_FALSE
@@ -184,6 +185,7 @@ class Resource:
184
185
  self.connection = Connection(observed)
185
186
  self.unknownsFatal = None
186
187
  self.autoReady = None
188
+ self.usages = None
187
189
 
188
190
  def __call__(self, apiVersion=_notset, kind=_notset, namespace=_notset, name=_notset):
189
191
  self.desired()
@@ -199,7 +201,7 @@ class Resource:
199
201
 
200
202
  @property
201
203
  def apiVersion(self):
202
- return self.observed.apiVersion
204
+ return self.desired.apiVersion
203
205
 
204
206
  @apiVersion.setter
205
207
  def apiVersion(self, apiVersion):
@@ -207,7 +209,7 @@ class Resource:
207
209
 
208
210
  @property
209
211
  def kind(self):
210
- return self.observed.kind
212
+ return self.desired.kind
211
213
 
212
214
  @kind.setter
213
215
  def kind(self, kind):
@@ -265,7 +267,7 @@ class Resource:
265
267
  def ready(self, ready):
266
268
  if ready:
267
269
  ready = fnv1.Ready.READY_TRUE
268
- elif ready == None or (isinstance(ready, protobuf.Values) and ready._isUnknown):
270
+ elif ready == None or (isinstance(ready, protobuf.Value) and ready._isUnknown):
269
271
  ready = fnv1.Ready.READY_UNSPECIFIED
270
272
  else:
271
273
  ready = fnv1.Ready.READY_FALSE
@@ -376,8 +378,8 @@ class RequiredResources:
376
378
  elif isinstance(entry, (list, tuple)):
377
379
  self._selector.match_labels.labels[entry[0]] = entry[1]
378
380
 
379
- def __getitem__(self, key):
380
- return RequiredResource(self.name, self._resources.items[key])
381
+ def __getitem__(self, ix):
382
+ return RequiredResource(self.name, ix, self._resources.items[ix])
381
383
 
382
384
  def __bool__(self):
383
385
  return bool(self._resources.items)
@@ -391,8 +393,9 @@ class RequiredResources:
391
393
 
392
394
 
393
395
  class RequiredResource:
394
- def __init__(self, name, resource):
396
+ def __init__(self, name, ix, resource):
395
397
  self.name = name
398
+ self.ix = ix
396
399
  self.observed = resource.resource
397
400
  self.apiVersion = self.observed.apiVersion
398
401
  self.kind = self.observed.kind
@@ -487,7 +490,7 @@ class Condition(protobuf.ProtobufValue):
487
490
  condition.status = fnv1.Status.STATUS_CONDITION_TRUE
488
491
  elif status == None:
489
492
  condition.status = fnv1.Status.STATUS_CONDITION_UNKNOWN
490
- elif isinstance(status, protobuf.Values) and status._isUnknown:
493
+ elif isinstance(status, protobuf.Value) and status._isUnknown:
491
494
  condition.status = fnv1.Status.STATUS_CONDITION_UNSPECIFIED
492
495
  else:
493
496
  condition.status = fnv1.Status.STATUS_CONDITION_FALSE
@@ -521,7 +524,7 @@ class Condition(protobuf.ProtobufValue):
521
524
  if observed.type == self.type:
522
525
  time = observed.lastTransitionTime
523
526
  if time:
524
- return datetime.datetime.fromisoformat(time)
527
+ return datetime.datetime.fromisoformat(str(time))
525
528
  return None
526
529
 
527
530
  @property
@@ -534,7 +537,7 @@ class Condition(protobuf.ProtobufValue):
534
537
  condition = self._find_condition(True)
535
538
  if claim:
536
539
  condition.target = fnv1.Target.TARGET_COMPOSITE_AND_CLAIM
537
- elif claim == None or (isinstance(claim, protobuf.Values) and claim._isUnknown):
540
+ elif claim == None or (isinstance(claim, protobuf.Value) and claim._isUnknown):
538
541
  condition.target = fnv1.Target.TARGET_UNSPECIFIED
539
542
  else:
540
543
  condition.target = fnv1.Target.TARGET_COMPOSITE
@@ -711,7 +714,7 @@ class Event:
711
714
  if bool(self):
712
715
  if claim:
713
716
  self._result.target = fnv1.Target.TARGET_COMPOSITE_AND_CLAIM
714
- elif claim == None or (isinstance(claim, protobuf.Values) and claim._isUnknown):
717
+ elif claim == None or (isinstance(claim, protobuf.Value) and claim._isUnknown):
715
718
  self._result.target = fnv1.Target.TARGET_UNSPECIFIED
716
719
  else:
717
720
  self._result.target = fnv1.Target.TARGET_COMPOSITE
@@ -99,7 +99,7 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
99
99
  return self.fatal(request, logger, 'Instantiate', e)
100
100
 
101
101
  step = composite.context._pythonic[step]
102
- iteration = (step.iteration or 0) + 1
102
+ iteration = int(step.iteration) + 1
103
103
  step.iteration = iteration
104
104
  composite.context.iteration = iteration
105
105
  logger.debug(f"Starting compose, {ordinal(len(composite.context._pythonic))} step, {ordinal(iteration)} pass")
@@ -111,7 +111,40 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
111
111
  except Exception as e:
112
112
  return self.fatal(request, logger, 'Compose', e)
113
113
 
114
- requested = []
114
+ if requireds := self.get_requireds(step, composite):
115
+ logger.info(f"Requireds requested: {','.join(requireds)}")
116
+ else:
117
+ self.process_usages(composite)
118
+ self.process_unknowns(composite)
119
+ self.process_auto_readies(composite)
120
+ logger.info('Completed compose')
121
+
122
+ return composite.response._message
123
+
124
+ def fatal(self, request, logger, message, exception=None):
125
+ if exception:
126
+ message += ' exceptiion'
127
+ logger.exception(message)
128
+ m = str(exception)
129
+ if not m:
130
+ m = exception.__class__.__name__
131
+ message += ': ' + m
132
+ else:
133
+ logger.error(message)
134
+ return fnv1.RunFunctionResponse(
135
+ meta=fnv1.ResponseMeta(
136
+ tag=request.meta.tag,
137
+ ),
138
+ results=[
139
+ fnv1.Result(
140
+ severity=fnv1.SEVERITY_FATAL,
141
+ message=message,
142
+ )
143
+ ]
144
+ )
145
+
146
+ def get_requireds(self, step, composite):
147
+ requireds = []
115
148
  for name, required in composite.requireds:
116
149
  if required.apiVersion and required.kind:
117
150
  r = pythonic.Map(apiVersion=required.apiVersion, kind=required.kind)
@@ -123,11 +156,84 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
123
156
  r.matchLabels[key] = value
124
157
  if r != step.requireds[name]:
125
158
  step.requireds[name] = r
126
- requested.append(name)
127
- if requested:
128
- logger.info(f"Requireds requested: {','.join(requested)}")
129
- return composite.response._message
159
+ requireds.append(name)
160
+ return requireds
161
+
162
+ def process_usages(self, composite):
163
+ for _, resource in sorted(entry for entry in composite.resources):
164
+ dependencies = resource.desired._getDependencies
165
+ if dependencies:
166
+ if self.debug:
167
+ for destination, source in sorted(dependencies.items()):
168
+ destination = self.trimFullName(destination)
169
+ source = self.trimFullName(source)
170
+ composite.logger.debug(f"Dependency: {destination} = {source}")
171
+ if resource.usages or (resource.usages is None and composite.usages):
172
+ resources = {}
173
+ requireds = {}
174
+ for destination, source in sorted(dependencies.items()):
175
+ name = source.split('.')
176
+ if (len(name) > 5 and
177
+ name[0] == 'request' and
178
+ name[1] == 'observed' and
179
+ name[2] == 'resources' and
180
+ name[4] == 'resource'
181
+ ):
182
+ if name[3] not in resources:
183
+ resources[name[3]] = []
184
+ resources[name[3]].append(f"{'.'.join(destination.split('.')[5:])} = {'.'.join(name[5:])}")
185
+ elif (len(name) > 5 and
186
+ name[0] == 'request' and
187
+ name[1] == 'extra_resources' and
188
+ name[3].startswith('items[') and name[3][-1] == ']' and
189
+ name[4] == 'resource'
190
+ ):
191
+ key = (name[2], int(name[3][6:-1]))
192
+ if key not in requireds:
193
+ requireds[key] = []
194
+ requireds[key].append(f"{'.'.join(destination.split('.')[5:])} = {'.'.join(name[5:])}")
195
+ for name, dependencies in resources.items():
196
+ source = composite.resources[name]
197
+ name = [resource.name, str(source.kind)]
198
+ if source.metadata.namespace:
199
+ name.append(str(source.metadata.namespace))
200
+ name.append(str(source.observed.metadata.name))
201
+ usage = composite.resources['_'.join(name)]('apiextensions.crossplane.io/v1beta1', 'Usage')
202
+ #usage = composite.resources['_'.join(name)]('protection.crossplane.io/v1beta1', 'Usage')
203
+ if resource.metadata.namespace:
204
+ usage.metadata.namespace = resource.metadata.namespace
205
+ usage.spec.reason = '\n'.join(dependencies)
206
+ usage.spec.replayDeletion = True
207
+ usage.spec.by.apiVersion = resource.apiVersion
208
+ usage.spec.by.kind = resource.kind
209
+ usage.spec.by.resourceRef.name = resource.observed.metadata.name
210
+ usage.spec.of.apiVersion = source.apiVersion
211
+ usage.spec.of.kind = source.kind
212
+ if source.metadata.namespace:
213
+ usage.spec.of.resourceRef.namespace = source.metadata.namespace
214
+ usage.spec.of.resourceRef.name = source.observed.metadata.name
215
+ for key, dependencies in requireds.items():
216
+ source = composite.requireds[key[0]][key[1]]
217
+ name = [resource.name, str(source.kind)]
218
+ if source.metadata.namespace:
219
+ name.append(str(source.metadata.namespace))
220
+ name.append(str(source.metadata.name))
221
+ usage = composite.resources['_'.join(name)]('apiextensions.crossplane.io/v1beta1', 'Usage')
222
+ #usage = composite.resources['_'.join(name)]('protection.crossplane.io/v1beta1', 'Usage')
223
+ if resource.metadata.namespace:
224
+ usage.metadata.namespace = resource.metadata.namespace
225
+ usage.spec.reason = '\n'.join(dependencies)
226
+ usage.spec.replayDeletion = True
227
+ usage.spec.by.apiVersion = resource.apiVersion
228
+ usage.spec.by.kind = resource.kind
229
+ usage.spec.by.resourceRef.name = resource.observed.metadata.name
230
+ usage.spec.of.apiVersion = source.apiVersion
231
+ usage.spec.of.kind = source.kind
232
+ if source.metadata.namespace:
233
+ usage.spec.of.resourceRef.namespace = source.metadata.namespace
234
+ usage.spec.of.resourceRef.name = source.observed.metadata.name
130
235
 
236
+ def process_unknowns(self, composite):
131
237
  unknownResources = []
132
238
  warningResources = []
133
239
  fatalResources = []
@@ -148,11 +254,11 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
148
254
  destination = self.trimFullName(destination)
149
255
  source = self.trimFullName(source)
150
256
  if fatal:
151
- logger.error(f'Observed unknown: {destination} = {source}')
257
+ composite.logger.error(f'Observed unknown: {destination} = {source}')
152
258
  elif warning:
153
- logger.warning(f'Observed unknown: {destination} = {source}')
259
+ composite.logger.warning(f'Observed unknown: {destination} = {source}')
154
260
  else:
155
- logger.debug(f'Desired unknown: {destination} = {source}')
261
+ composite.logger.debug(f'Desired unknown: {destination} = {source}')
156
262
  if resource.observed:
157
263
  resource.desired._patchUnknowns(resource.observed)
158
264
  elif self.renderUnknowns:
@@ -161,19 +267,19 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
161
267
  del composite.resources[name]
162
268
 
163
269
  if fatalResources:
164
- level = logger.error
270
+ level = composite.logger.error
165
271
  reason = 'FatalUnknowns'
166
272
  message = f"Observed resources with unknowns: {','.join(fatalResources)}"
167
273
  status = False
168
274
  event = composite.events.fatal
169
275
  elif warningResources:
170
- level = logger.warning
276
+ level = composite.logger.warning
171
277
  reason = 'ObservedUnknowns'
172
278
  message = f"Observed resources with unknowns: {','.join(warningResources)}"
173
279
  status = False
174
280
  event = composite.events.warning
175
281
  elif unknownResources:
176
- level = logger.info
282
+ level = composite.logger.info
177
283
  reason = 'DesiredUnknowns'
178
284
  message = f"Desired resources with unknowns: {','.join(unknownResources)}"
179
285
  status = False
@@ -190,42 +296,19 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
190
296
  if event:
191
297
  event(reason, message)
192
298
 
299
+ def process_auto_readies(self, composite):
193
300
  for name, resource in composite.resources:
194
301
  if resource.autoReady or (resource.autoReady is None and composite.autoReady):
195
302
  if resource.ready is None:
196
303
  if resource.conditions.Ready.status:
197
304
  resource.ready = True
198
305
 
199
- logger.info('Completed compose')
200
- return composite.response._message
201
-
202
- def fatal(self, request, logger, message, exception=None):
203
- if exception:
204
- message += ' exceptiion'
205
- logger.exception(message)
206
- m = str(exception)
207
- if not m:
208
- m = exception.__class__.__name__
209
- message += ': ' + m
210
- else:
211
- logger.error(message)
212
- return fnv1.RunFunctionResponse(
213
- meta=fnv1.ResponseMeta(
214
- tag=request.meta.tag,
215
- ),
216
- results=[
217
- fnv1.Result(
218
- severity=fnv1.SEVERITY_FATAL,
219
- message=message,
220
- )
221
- ]
222
- )
223
-
224
306
  def trimFullName(self, name):
225
307
  name = name.split('.')
226
308
  for values in (
309
+ ('request', 'observed', 'composite', 'resource'),
227
310
  ('request', 'observed', 'resources', None, 'resource'),
228
- ('request', 'extra_resources', None, 'items', 'resource'),
311
+ ('request', 'extra_resources', None, 'items', None, 'resource'),
229
312
  ('response', 'desired', 'resources', None, 'resource'),
230
313
  ):
231
314
  if len(values) < len(name):