Skip to content

Commit 4c55a31

Browse files
committed
Add some Codex generated documentation
1 parent d9a2e1b commit 4c55a31

3 files changed

Lines changed: 471 additions & 0 deletions

File tree

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,11 @@ subnet.spec.forProvider.cidrBlock = '10.0.0.0/20'
173173
```
174174
Will generate the appropriate Crossplane Usage resource.
175175

176+
## API Documentation
177+
178+
- [Composite API (`composite.py`)](https://github.com/crossplane-contrib/function-pythonic/blob/main/docs/composite.md)
179+
- [Protobuf Wrapper API (`protobuf.py`)](https://github.com/crossplane-contrib/function-pythonic/blob/main/docs/protobuf.md)
180+
176181
## Pythonic access of Protobuf Messages
177182

178183
All Protobuf messages are wrapped by a set of python classes which enable using

docs/composite.md

Lines changed: 312 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,312 @@
1+
# composite.py Reference
2+
3+
`composite.py` defines the primary authoring model for function-pythonic. It provides:
4+
5+
- `BaseComposite`: the base class your composition classes subclass.
6+
- Resource wrappers for composed and required resources.
7+
- Condition and result helpers.
8+
- Connection secret and connection-detail handling.
9+
- Convenience descriptors for TTL and ready state.
10+
11+
## High-Level Model
12+
13+
At runtime, function-pythonic instantiates your `BaseComposite` subclass with a
14+
`RunFunctionRequest`, then your `compose()` implementation mutates desired state via
15+
wrapper objects.
16+
17+
Key object graph:
18+
- `self.request`: wrapped incoming request (read-oriented).
19+
- `self.response`: wrapped outgoing response (write-oriented).
20+
- `self.resources`: composed resource collection (`desired.resources`).
21+
- `self.requireds`: required/extra resource selectors and resolved items.
22+
- `self.conditions`: composite condition helpers.
23+
- `self.results`: function result helpers.
24+
25+
## Core Descriptors
26+
27+
## `TTL`
28+
29+
Exposed as `BaseComposite.ttl`.
30+
31+
- Getter reads `response.meta.ttl` and returns `int` or fractional `float`.
32+
- Setter accepts `int` or `float` and maps into `seconds`/`nanos`.
33+
- Invalid type raises `ValueError`.
34+
35+
## `Ready`
36+
37+
Exposed as `BaseComposite.ready`.
38+
39+
- Getter maps protobuf ready enum to `True`, `False`, or `None`.
40+
- Setter writes protobuf ready enum:
41+
- `True` -> `READY_TRUE`
42+
- `False` -> `READY_FALSE`
43+
- `None` -> `READY_UNSPECIFIED`
44+
45+
## `ConnectionSecret`
46+
47+
Exposed as `BaseComposite.connectionSecret`.
48+
49+
- Crossplane v1:
50+
- Reads from `spec.writeConnectionSecretToRef`.
51+
- Setting to a different value raises `NotImplementedError`.
52+
- Crossplane v2:
53+
- Reads/writes `input.writeConnectionSecretToRef` as a protobuf `Map` wrapper.
54+
55+
## `Connection`
56+
57+
Exposed as `BaseComposite.connection`; returns `_Connection`.
58+
59+
- Manages `desired.composite.connection_details`.
60+
- In Crossplane v2, mirrors connection details into a composed `v1/Secret`.
61+
- Ignores non-string/unknown/`None` values when appropriate.
62+
63+
## `BaseComposite`
64+
65+
Subclass this class and implement:
66+
67+
```python
68+
class MyComposite(BaseComposite):
69+
def compose(self):
70+
...
71+
```
72+
73+
or async:
74+
75+
```python
76+
class MyComposite(BaseComposite):
77+
async def compose(self):
78+
...
79+
```
80+
81+
Initialization behavior:
82+
- Creates `self.request` (`protobuf.Message`) from input request.
83+
- Creates `self.response` with:
84+
- copied `meta.tag`
85+
- default TTL of 60s
86+
- cloned `desired` and `context` from request
87+
- Selects `self.parameters` from:
88+
- `observed.composite.resource.spec.parameters` for single-use composites
89+
- otherwise `input.parameters`
90+
- Initializes:
91+
- `self.credentials`
92+
- `self.context`
93+
- `self.environment` (`apiextensions.crossplane.io/environment`)
94+
- `self.requireds`
95+
- `self.resources`
96+
- defaults: `autoReady=True`, `usages=False`, `unknownsFatal=False`
97+
- Binds composite-focused shortcuts:
98+
- `self.observed`, `self.desired`, `self.apiVersion`, `self.kind`,
99+
`self.metadata`, `self.spec`, `self.status`
100+
- `self.conditions`, `self.results`, `self.events` (`events` is deprecated alias)
101+
102+
`compose()` in `BaseComposite` is abstract and raises `NotImplementedError`.
103+
104+
## Credentials API
105+
106+
## `Credentials`
107+
108+
Collection wrapper around `request.credentials`.
109+
110+
- Access: `self.credentials['name']` or `self.credentials.name`
111+
- Supports `bool`, `len`, `contains`, and iteration of `(name, Credential)`
112+
113+
## `Credential`
114+
115+
Wrapper around a single credential’s `credential_data.data`.
116+
117+
- Access: `credential['key']` or `credential.key`
118+
- Supports `bool`, `len`, `contains`, and iteration of `(key, value)`
119+
120+
## Composed Resource API
121+
122+
## `Resources`
123+
124+
Collection wrapper for `response.desired.resources`.
125+
126+
- Access/create by name: `self.resources.vpc` or `self.resources['vpc']`
127+
- Assign raw resource object: `self.resources['x'] = resource`
128+
- Delete by name.
129+
- Iteration yields `(name, Resource)`
130+
131+
Instances are cached by composition resource name.
132+
133+
## `Resource`
134+
135+
Represents one composed resource entry.
136+
137+
Construction binds:
138+
- `observed` = `request.observed.resources[name].resource`
139+
- `desired` = `response.desired.resources[name].resource`
140+
- `conditions`, `connection`
141+
- toggles: `autoReady`, `usages`, `unknownsFatal` (default `None`, inherit from composite)
142+
143+
### `Resource(...)` call semantics
144+
145+
`resource(kind?, apiVersion?, namespace?, name?)`:
146+
- Clears desired resource (`self.desired()`).
147+
- Supports swapped first two args when first arg looks like `apiVersion`.
148+
- Optionally sets `metadata.namespace` and `metadata.name`.
149+
- Returns `self`.
150+
151+
### Key properties
152+
153+
- `apiVersion`, `kind`, `metadata`, `spec`, `type`, `data` map to desired resource.
154+
- `status` is read from observed resource status.
155+
- `externalName` reads desired annotation, falls back to observed annotation, and
156+
writes back into desired.
157+
158+
### Ready behavior
159+
160+
- `ready` getter:
161+
- returns cached explicit value if set
162+
- otherwise maps desired ready enum
163+
- if unspecified and auto-ready is enabled, computes via `auto_ready.resource_ready()`
164+
and sets desired ready true when determined.
165+
- `ready` setter writes ready enum similarly to composite-level setter.
166+
167+
### Methods
168+
169+
- `setReadyCondition(type='Ready')`:
170+
- reads `conditions[type]`
171+
- if condition true -> `ready` set to observed name
172+
- else sets `ready` to a `status.not<type>Condition...` path
173+
- `addDependency(resource, field=_notset)`:
174+
- Adds explicit dependency annotation:
175+
`metadata.annotations["pythonic.dependency/<resource.name>"] = <field>`
176+
- If field omitted, derives from dependent resource readiness.
177+
- Converts non-wrapper truthy/falsey values into observed-name or status-based tokens.
178+
179+
## Required Resource API
180+
181+
## `Requireds`
182+
183+
Collection wrapper for required resources.
184+
185+
- Crossplane v1 uses `extra_resources`.
186+
- Crossplane v2 uses `required_resources`.
187+
- Supports `bool`, `len`, `contains`, iteration of `(name, RequiredResources)`.
188+
189+
## `RequiredResources`
190+
191+
Selector + resolved-items wrapper for a named required resource request.
192+
193+
- Callable reset/update:
194+
- `required(kind?, apiVersion?, namespace?, name?, labels?)`
195+
- Selector properties:
196+
- `apiVersion`, `kind`, `namespace`, `matchName`, `matchLabels`
197+
- Resolved results:
198+
- index access `required[ix] -> RequiredResource`
199+
- `bool`, `len`, iteration
200+
201+
`matchLabels` setter accepts mapping-like iteration or `(key, value)` pairs.
202+
203+
## `RequiredResource`
204+
205+
Read-only wrapper for one returned required resource item.
206+
207+
Fields:
208+
- `name`, `ix`
209+
- `observed`, `apiVersion`, `kind`, `metadata`, `spec`, `type`, `data`, `status`
210+
- `conditions`, `connection`
211+
212+
Truthiness reflects existence of `observed`.
213+
214+
## Conditions API
215+
216+
## `Conditions`
217+
218+
Wrapper for condition access on composite/resource/required resource.
219+
220+
- Access by type: `conditions['Ready']` or `conditions.Ready`
221+
- Supports `bool`, `len`, iteration
222+
- Merges condition types from observed and response (when response exists)
223+
224+
## `Condition`
225+
226+
Represents one condition type. Subclasses `protobuf.ProtobufValue`.
227+
228+
`_protobuf_value` serialization includes:
229+
- `type`, `status`, `reason`, `message`, optional RFC3339 `lastTransitionTime`.
230+
231+
Mutation:
232+
- `condition(reason=?, message=?, status=?, claim=?)` updates in one call.
233+
- `status` maps between bool/unknown and protobuf status enums.
234+
- `reason`, `message` read/write strings.
235+
- `lastTransitionTime` reads from observed condition timestamp (read-only).
236+
- `claim` toggles target:
237+
- composite only
238+
- composite + claim
239+
- unspecified
240+
241+
Creation rules:
242+
- If response exists, setting fields creates condition entry when missing.
243+
- Without response, creation is disallowed (`ValueError: Condition is read only`).
244+
245+
## Results API
246+
247+
## `Results`
248+
249+
Wrapper for `response.results`.
250+
251+
Factory methods:
252+
- `info(reason?, message?, claim?)`
253+
- `warning(reason?, message?, claim?)`
254+
- `fatal(reason?, message?, claim?)`
255+
256+
Also supports `bool`, `len`, index, and iteration.
257+
258+
## `Result`
259+
260+
Wrapper around one result entry.
261+
262+
Properties:
263+
- severity flags: `info`, `warning`, `fatal`
264+
- `reason`
265+
- `message`
266+
- `claim` target (composite vs composite+claim vs unspecified)
267+
268+
A `Result()` without backing entry is falsey and ignores setters.
269+
270+
## `_Connection`
271+
272+
Internal helper returned by `BaseComposite.connection`.
273+
274+
Behavior:
275+
- Reads/writes composite connection details map.
276+
- `observed` exposes observed connection details.
277+
- `__call__(**kwargs)` clears and resets connection details.
278+
- On v2, keeps a composed secret resource synchronized:
279+
- secret name defaults to `connection-secret` or `connectionSecret.resourceName`
280+
- sets type `connection.crossplane.io/v1alpha1`
281+
- base64-encodes values into `secret.data`
282+
- removes secret when last key removed
283+
- For cluster-scoped XR without secret namespace, emits fatal result:
284+
- reason: `ConnectionNoNamespace`
285+
- message: `Cluster scoped XR must specify connection secret namespace`
286+
287+
## Minimal Example
288+
289+
```python
290+
from crossplane.pythonic import BaseComposite
291+
292+
class ExampleComposite(BaseComposite):
293+
def compose(self):
294+
bucket = self.resources.bucket("Bucket", "s3.aws.crossplane.io/v1beta1")
295+
bucket.spec.forProvider.region = self.spec.region
296+
297+
# propagate observed id when available
298+
self.status.bucketId = bucket.status.atProvider.id
299+
300+
# optional condition/result
301+
self.conditions.Ready(status=True, reason="Available", message="Composed")
302+
self.results.info("Composed", "Bucket desired state generated")
303+
```
304+
305+
## Crossplane v1 vs v2 Differences in This Module
306+
307+
- Required resources:
308+
- v1: `extra_resources`
309+
- v2: `required_resources`
310+
- Connection secret handling:
311+
- v1: relies on XR `writeConnectionSecretToRef`, no write-through override
312+
- v2: allows `connectionSecret` overrides and manages composed secret mirror

0 commit comments

Comments
 (0)