Skip to content

Commit 407c626

Browse files
committed
fix issue 264
1 parent 6059e7e commit 407c626

13 files changed

Lines changed: 1111 additions & 63 deletions

File tree

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
Export/Import Guide
2+
3+
## Overview
4+
5+
Exports and imports allow elements to share resources. An element declares an
6+
**export** to publish a resource for other elements, and another element
7+
declares an **import** to consume that resource. The element engine resolves
8+
import links to exported resources at manifest install/upgrade time.
9+
10+
## Data Models
11+
12+
### Export (`exordos_core/elements/dm/models.py:925`)
13+
14+
| Field | Type | Description |
15+
|-----------|----------------|----------------------------------------|
16+
| `element` | `Element` (FK) | The element that owns the export |
17+
| `name` | `String` | Export name, unique per element |
18+
| `kind` | `Enum` | Export kind; currently only `resource` |
19+
| `link` | `String` | Dotted path to the exported resource |
20+
21+
The `full_link` property returns `{element.link}.{link}` — the canonical
22+
identifier used for matching imports.
23+
24+
### Import (`exordos_core/elements/dm/models.py:958`)
25+
26+
| Field | Type | Description |
27+
|------------------|------------------|-----------------------------------------------------|
28+
| `element` | `Element` (FK) | The element that imports |
29+
| `name` | `String` | Import name, unique per element |
30+
| `from_element` | `Element` (FK) | The element that exports the resource |
31+
| `from_resource` | `Resource` (FK) | The specific exported resource |
32+
| `kind` | `Enum` | Import kind; currently only `resource` |
33+
34+
The `link` property returns `{element.link}.imports.${name}` — how the
35+
imported resource is addressed inside the importing element.
36+
37+
### ImportedResource (`exordos_core/elements/dm/models.py:996`)
38+
39+
A lightweight proxy that delegates attribute access to the underlying
40+
`Resource`. Used by the element engine to represent imported resources in
41+
the namespace without duplicating the resource record.
42+
43+
## YAML Manifest Examples
44+
45+
### Exporting a resource
46+
47+
An element publishes a resource by declaring it under `exports`:
48+
49+
```yaml
50+
# manifest-export.yaml
51+
name: "test_export_node_1"
52+
description: "Exports a compute node"
53+
schema_version: 1
54+
version: "0.0.1"
55+
api_version: "v1"
56+
57+
requirements:
58+
core:
59+
from_version: "0.0.0"
60+
61+
resources:
62+
$core.compute.nodes:
63+
test_node:
64+
name: "shared-node-name"
65+
description: "Test node from manifest 1"
66+
cores: 1
67+
ram: 1024
68+
project_id: "12345678-c625-4fee-81d5-f691897b8142"
69+
disk_spec:
70+
kind: "root_disk"
71+
size: 10
72+
image: "https://repo.exordos.com/exordos-base/1.1.2/exordos-base.raw.zst"
73+
74+
exports:
75+
shared_node:
76+
link: "$core.compute.nodes.$test_node"
77+
```
78+
79+
The export name `shared_node` is local to this element. The `link` field
80+
points to a resource declared in the `resources` section.
81+
82+
### Importing a resource
83+
84+
Another element consumes the exported resource via `imports`:
85+
86+
```yaml
87+
# manifest-import.yaml
88+
name: "test_import_node"
89+
description: "Imports a node from test_export_node_2"
90+
schema_version: 1
91+
version: "0.0.1"
92+
api_version: "v1"
93+
94+
requirements:
95+
core:
96+
from_version: "0.0.0"
97+
98+
resources:
99+
$core.compute.nodes:
100+
test_node:
101+
name: "shared-node-name"
102+
description: $test_import_node.imports.$test_node:description
103+
cores: 1
104+
ram: 1024
105+
project_id: "12345678-c625-4fee-81d5-f691897b8142"
106+
disk_spec:
107+
kind: "root_disk"
108+
size: 10
109+
image: "https://repo.exordos.com/exordos-base/1.1.2/exordos-base.raw.zst"
110+
111+
imports:
112+
test_node:
113+
element: "$test_export_node_2"
114+
kind: "resource"
115+
link: "$core.compute.nodes.$test_node"
116+
```
117+
118+
- `element`: which element to import from (by its `$name` link)
119+
- `kind`: must be `"resource"` (the only kind supported)
120+
- `link`: the resource path *within* the exporting element's resources
121+
122+
The importing element can then reference the imported resource's fields with
123+
the `$element.link.imports.$name:field` syntax (see `description` above).
124+
125+
### The core element's exports
126+
127+
The `core` element itself exports several resources for other elements to use:
128+
129+
```yaml
130+
# manifests/examples/core.element.yaml (excerpt)
131+
exports:
132+
local_domain:
133+
link: "$core.dns.domains.$local_domain"
134+
var_core_ip_address:
135+
link: "$core.vs.variables.$core_ip_address"
136+
var_default_cores:
137+
link: "$core.vs.variables.$default_cores"
138+
var_default_ram:
139+
link: "$core.vs.variables.$default_ram"
140+
var_default_replicas:
141+
link: "$core.vs.variables.$default_replicas"
142+
profile_develop:
143+
link: "$core.vs.profiles.$develop"
144+
profile_small:
145+
link: "$core.vs.profiles.$small"
146+
profile_medium:
147+
link: "$core.vs.profiles.$medium"
148+
profile_large:
149+
link: "$core.vs.profiles.$large"
150+
profile_legacy:
151+
link: "$core.vs.profiles.$legacy"
152+
```
153+
154+
## Link Resolution
155+
156+
### Export link format
157+
158+
```text
159+
${element.name}.${link}
160+
```
161+
162+
Example: `$dbaas.types.postgres.instances.$cluster_pg`
163+
164+
### Import link resolution
165+
166+
When `Manifest.apply_imports()` processes an import:
167+
168+
1. Look up the exporting element by its `$name` link in the element engine
169+
2. Resolve the export link within that element via `get_resource_by_export_link()`
170+
3. Match the full import link against the `_resource_exports` dictionary
171+
4. Create an `Import` record and an `ImportedResource` proxy in the element engine
172+
173+
### Matching algorithm
174+
175+
The full import link is constructed as:
176+
177+
```text
178+
{from_element.link}.{import.link}
179+
```
180+
181+
This is compared against registered export full links. The match is exact —
182+
the full link strings must be identical.
183+
184+
**Example with matching element prefixes:**
185+
186+
| Export full link | Import full link | Match |
187+
|-----------------------------------------------|-----------------------------------------------------|-------|
188+
| `$dbaas.types.postgres.versions.$pg18` | `$dbaas.$dbaas.types.postgres.versions.$pg18` | Yes |
189+
| `$dbaas.types.postgres.instances.$cluster_pg` | `$dbaas.$dbaas.types.postgres.versions.$cluster_pg` | No |
190+
| `$foo.types.postgres.versions.$cluster` | `$bar.$bar.types.postgres.versions.$cluster` | No |
191+
192+
### Invalid examples
193+
194+
**Missing `link` in export** (uses `no_link` instead):
195+
196+
```yaml
197+
exports:
198+
example_node:
199+
no_link: "$core.compute.nodes.example_node"
200+
```
201+
202+
This produces a schema validation error because `link` is required.
203+
204+
**Missing `element` in import:**
205+
206+
```yaml
207+
imports:
208+
var_default_cores_invalid:
209+
kind: "resource"
210+
link: "$core.vs.variables.$default_cores"
211+
```
212+
213+
This produces a schema validation error because `element` is required.
214+
215+
**Importing from an unknown element:**
216+
217+
```yaml
218+
imports:
219+
test_node:
220+
element: "$test_export_node_unknown"
221+
kind: "resource"
222+
link: "$core.compute.nodes.$test_node"
223+
```
224+
225+
Raises `exceptions.ValidateException` at install time because the element
226+
does not exist in the engine.
227+
228+
**Importing a resource not in exports:**
229+
230+
```yaml
231+
imports:
232+
test_node:
233+
element: "$test_export_node_2"
234+
kind: "resource"
235+
link: "$core.compute.nodes.$test_node_unknown"
236+
```
237+
238+
Raises `exceptions.ValidateException("is not in export list")` at install
239+
time.
240+
241+
## Lifecycle
242+
243+
### Install
244+
245+
`Manifest.install()` → `Manifest.apply_element()` → `apply_imports()` then
246+
`apply_exports()`. Imports are processed before resources so that imported
247+
values are available during resource value rendering.
248+
249+
### Upgrade
250+
251+
`Manifest.upgrade()` follows the same flow. Existing imports/exports are
252+
updated in place; removed ones are deleted along with their
253+
`ImportedResource` proxies.
254+
255+
### Uninstall
256+
257+
`Manifest.uninstall()` checks for dependent elements via
258+
`_check_no_dependents()` before removing the element and all its
259+
imports/exports.
260+
261+
## See also
262+
263+
- [Core Developer Guide](index.md) — architecture overview
264+
- [Manifest reference](../em/manifest.md) — manifest YAML specification

docs/core-developer-guide/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,5 @@ and so on). See [Troubleshooting](../usage/troubleshooting.md) for the symptoms
7878

7979
- [Manifest reference](../em/manifest.md)
8080
- [Service as a Service API](../em/service.md)
81+
- [Exports reference](./exports.md)
8182
- [Troubleshooting](../usage/troubleshooting.md)

0 commit comments

Comments
 (0)