Skip to content

Commit feb5b1e

Browse files
committed
Add infrahubctl object command
1 parent 0510d52 commit feb5b1e

35 files changed

+1398
-188
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ jobs:
166166
python-version: "3.12"
167167
- name: "Setup Python environment"
168168
run: |
169-
pipx install poetry==1.8.5
169+
pipx install poetry==2.1
170170
poetry config virtualenvs.create true --local
171171
poetry env use 3.12
172172
- name: "Install dependencies"
@@ -230,7 +230,7 @@ jobs:
230230
python-version: ${{ matrix.python-version }}
231231
- name: "Setup environment"
232232
run: |
233-
pipx install poetry==1.8.5 --python python${{ matrix.python-version }}
233+
pipx install poetry==2.1 --python python${{ matrix.python-version }}
234234
poetry config virtualenvs.create true --local
235235
pip install invoke toml codecov
236236
- name: "Install Package"
@@ -283,7 +283,7 @@ jobs:
283283
echo "PYTEST_DEBUG_TEMPROOT=/var/lib/github/${RUNNER_NAME}/_temp" >> $GITHUB_ENV
284284
- name: "Setup environment"
285285
run: |
286-
pipx install poetry==1.8.5
286+
pipx install poetry==2.1
287287
poetry config virtualenvs.create true --local
288288
pip install invoke toml codecov
289289
- name: "Install Package"
@@ -359,7 +359,7 @@ jobs:
359359

360360
# - name: "Setup environment"
361361
# run: |
362-
# pipx install poetry==1.8.5
362+
# pipx install poetry==2.1
363363
# poetry config virtualenvs.create true --local
364364
# pip install invoke toml codecov
365365

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,5 @@ dist/*
2727
**/*.csv
2828

2929
# Generated files
30-
generated/
30+
generated/
31+
sandbox/

.yamllint.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ ignore: |
55
/.venv
66
/examples
77
tests/unit/sdk/test_data/schema_encoding_error.yml
8+
tests/unit/sdk/test_data/multiple_files_valid_not_valid.yml
89
910
rules:
1011
new-lines: disable

docs/docs/infrahubctl/infrahubctl-object.mdx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ $ infrahubctl object [OPTIONS] COMMAND [ARGS]...
1717
**Commands**:
1818

1919
* `load`: Load one or multiple objects files into...
20+
* `validate`: Validate one or multiple objects files.
2021

2122
## `infrahubctl object load`
2223

@@ -38,3 +39,24 @@ $ infrahubctl object load [OPTIONS] PATHS...
3839
* `--branch TEXT`: Branch on which to load the objects.
3940
* `--config-file TEXT`: [env var: INFRAHUBCTL_CONFIG; default: infrahubctl.toml]
4041
* `--help`: Show this message and exit.
42+
43+
## `infrahubctl object validate`
44+
45+
Validate one or multiple objects files.
46+
47+
**Usage**:
48+
49+
```console
50+
$ infrahubctl object validate [OPTIONS] PATHS...
51+
```
52+
53+
**Arguments**:
54+
55+
* `PATHS...`: [required]
56+
57+
**Options**:
58+
59+
* `--debug / --no-debug`: [default: no-debug]
60+
* `--branch TEXT`: Branch on which to validate the objects.
61+
* `--config-file TEXT`: [env var: INFRAHUBCTL_CONFIG; default: infrahubctl.toml]
62+
* `--help`: Show this message and exit.
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
---
2+
title: Manage data with Object Files
3+
---
4+
5+
# Manage data with Object files
6+
7+
## Introduction
8+
9+
An Object file is a YAML file that allows you to manage data to be loaded in Infrahub based on your own custom schema. It provides a declarative way to define and manage resources in your Infrahub instance.
10+
11+
Object files work well for models that don't change too often and/or that need to be tracked in Git. Examples include: Groups, tags, Users, etc.
12+
Below is an example of an Object file that defines tags (`BuiltinTag`).
13+
14+
```yaml
15+
---
16+
apiVersion: infrahub.app/v1
17+
kind: Object
18+
spec:
19+
kind: BuiltinTag
20+
data:
21+
- name: Blue
22+
- name: Yellow
23+
- name: Red
24+
```
25+
26+
Object files are meant to be used in an idempotent way and as such they work better for models with a Human Friendly ID (HFID) defined. An HFID is a unique identifier that makes it easier to reference objects across different files and operations.
27+
28+
## Load Object files into Infrahub
29+
30+
Object files can be loaded into Infrahub using the `infrahub object load` command.
31+
32+
```bash
33+
infrahub object load <path_to_object_file>
34+
```
35+
36+
Multiple object files can be loaded at once by specifying the path to multiple files or by specifying a directory.
37+
38+
The `object load` command will create/update the objects using an `Upsert` operation. All objects previously loaded will NOT be deleted in the Infrahub instance.
39+
Also, if some objects present in different files are identical and dependent on each other, the `object load` command will NOT calculate the dependencies between the objects and as such it's the responsibility of the users to execute the command in the right order.
40+
41+
### Validate the format of object files
42+
43+
The object file can be validated using the `infrahub object validate` command.
44+
45+
```bash
46+
infrahub object validate <path_to_object_file>
47+
```
48+
49+
## Object file format
50+
51+
All object files must start with the following format, all other formats will be automatically ignored.
52+
Each file is intended for one specific top level kind, but one file can include multiple nested objects of any kind.
53+
The kind of the top level object must be defined in spec/kind.
54+
55+
```yaml
56+
---
57+
apiVersion: infrahub.app/v1
58+
kind: Object
59+
spec:
60+
kind: <NamespaceName>
61+
data:
62+
- [...]
63+
```
64+
65+
> Multiple documents in a single YAML file are also supported, each document will be loaded separately. Documents are separated by `---`
66+
67+
### Relationship of cardinality One
68+
69+
A relationship of cardinality one can either reference an existing node via its HFID or create a new node if it doesn't exist.
70+
In the example below, both `site` and `primary_ip` are relationships of cardinality one.
71+
72+
```yaml
73+
---
74+
apiVersion: infrahub.app/v1
75+
kind: Object
76+
spec:
77+
kind: InfraDevice
78+
data:
79+
- name: edge01
80+
site: "Paris" # Reference existing node via its HFID
81+
primary_ip: # Nested object, will be created if it doesn't exist
82+
data:
83+
address: "192.168.1.1"
84+
```
85+
86+
### Relationship of cardinality Many
87+
88+
A relationship of cardinality many can reference existing nodes via their HFID or define nested objects.
89+
90+
#### Existing nodes referenced by their HFID
91+
92+
Existing nodes can be referenced by their HFID in string format or in list format.
93+
In the example below, both `best_friends` and `tags` are relationships of cardinality many.
94+
95+
> An HFID is composed of a single value, it's possible to use a string instead of a list
96+
97+
```yaml
98+
---
99+
apiVersion: infrahub.app/v1
100+
kind: Object
101+
spec:
102+
kind: TestingPerson
103+
data:
104+
- name: Mike Johnson
105+
height: 175
106+
best_friends: # Relationship of cardinality many that references existing nodes based on their HFID
107+
- [Jane Smith, Max]
108+
- [Sarah Williams, Charlie]
109+
tags:
110+
- Veterinarian # Existing Node referenced by its HFID in string format
111+
- [Breeder] # Existing Node referenced by its HFID in list format
112+
```
113+
114+
#### Nested Objects
115+
116+
When defining nested objects, the node will be automatically created if it doesn't exist and if the relationship between the parent object and the nested object exists, it will be automatically inserted.
117+
For example, in the example below, the `owner` of a `TestingDog` doesn't need to be specified because it will be automatically inserted.
118+
119+
Two different syntax are supported:
120+
121+
- A dictionary with multiple values under data
122+
- A list of objects
123+
124+
##### Nested objects as a dictionary
125+
126+
In the example below, `tags` is a relationship of cardinality many that is defined as a dictionary with multiple values under data.
127+
128+
> The kind is optional here because there is only one option possible (not a generic)
129+
130+
```yaml
131+
---
132+
apiVersion: infrahub.app/v1
133+
kind: Object
134+
spec:
135+
kind: TestingPerson
136+
data:
137+
- name: Alex Thompson
138+
tags:
139+
data:
140+
- name: dog-lover
141+
description: "Dog Lover"
142+
- name: veterinarian
143+
description: "Veterinarian"
144+
```
145+
146+
This format works well when all objects are of the same kind and when all objects are using the same properties.
147+
For more complex cases, the list of objects format is more flexible.
148+
149+
##### Nested objects as a list of objects
150+
151+
In the example below, `animals` is a relationship of cardinality many that is defined as a list of objects.
152+
Each object must contain a `data` key and each object can also define a specific `kind`.
153+
154+
> If the kind is not specified, it will be inferred from schema
155+
156+
```yaml
157+
---
158+
apiVersion: infrahub.app/v1
159+
kind: Object
160+
spec:
161+
kind: TestingPerson
162+
data:
163+
- name: Alex Thompson
164+
height: 180
165+
animals:
166+
- kind: TestingDog
167+
data:
168+
name: Max
169+
weight: 25
170+
breed: Golden Retriever
171+
color: "#FFD700"
172+
- kind: TestingCat
173+
data:
174+
name: Mimi
175+
breed: Persian
176+
```
177+
178+
### Support for Metadata
179+
180+
Metadata support is planned for future releases. Currently, the Object file does not support metadata on attributes or relationships.
181+
182+
## Troubleshooting
183+
184+
### Common Issues
185+
186+
1. **Objects not being created**: Ensure that the YAML syntax is correct and that the file follows the required format.
187+
2. **Dependency errors**: When objects depend on each other, load them in the correct order (dependencies first).
188+
3. **Validation errors**: Use the `infrahub object validate` command to check for syntax errors before loading.
189+
190+
### Best Practices
191+
192+
1. Use Human Friendly IDs (HFIDs) for all objects to ensure consistent referencing.
193+
2. Keep object files organized by model type or purpose.
194+
3. Validate object files before loading them into production environments.
195+
4. Use comments in your YAML files to document complex relationships or dependencies.

docs/sidebars-python-sdk.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const sidebars: SidebarsConfig = {
3131
label: 'Topics',
3232
items: [
3333
'topics/tracking',
34+
'topics/object_file',
3435
],
3536
},
3637
{

infrahub_sdk/ctl/cli_commands.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
app.add_typer(validate_app, name="validate")
6262
app.add_typer(repository_app, name="repository")
6363
app.add_typer(menu_app, name="menu")
64-
app.add_typer(object_app, name="object", hidden=True)
64+
app.add_typer(object_app, name="object")
6565

6666
app.command(name="dump")(dump)
6767
app.command(name="load")(load)

infrahub_sdk/ctl/menu.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from ..async_typer import AsyncTyper
88
from ..ctl.client import initialize_client
99
from ..ctl.utils import catch_exception, init_logging
10+
from ..exceptions import ObjectValidationError
1011
from ..spec.menu import MenuFile
1112
from .parameters import CONFIG_PARAM
1213
from .utils import load_yamlfile_from_disk_and_exit
@@ -44,11 +45,16 @@ async def load(
4445
schema = await client.schema.get(kind=file.spec.kind, branch=branch)
4546

4647
for idx, item in enumerate(file.spec.data):
47-
await file.spec.create_node(
48-
client=client,
49-
schema=schema,
50-
data=item,
51-
branch=branch,
52-
default_schema_kind=file.spec.kind,
53-
context={"list_index": idx},
54-
)
48+
try:
49+
await file.spec.create_node(
50+
client=client,
51+
schema=schema,
52+
position=[idx + 1],
53+
data=item,
54+
branch=branch,
55+
default_schema_kind=file.spec.kind,
56+
context={"list_index": idx},
57+
)
58+
except ObjectValidationError as exc:
59+
console.print(f"[red] {exc!s}")
60+
raise typer.Exit(1)

0 commit comments

Comments
 (0)