Skip to content

Commit 020cc89

Browse files
authored
Merge pull request #5814 from opsmill/jbr-resolve-conflits-merge-02212025
Resolve conflits merge 02212025
2 parents ee5b5ed + ae99e6f commit 020cc89

38 files changed

+370
-2719
lines changed

backend/tests/functional/branch/conftest.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,26 @@ def car_person_branch_agnostic_schema() -> dict[str, Any]:
5151
}
5252
],
5353
},
54+
{
55+
"name": "Roofrack",
56+
"namespace": "Test",
57+
"branch": BranchSupportType.AWARE.value,
58+
"attributes": [
59+
{"name": "size", "kind": "Text", "unique": True},
60+
],
61+
"relationships": [
62+
{
63+
"name": "car",
64+
"label": "Commander of Car",
65+
"peer": "TestCar",
66+
"optional": False,
67+
"kind": "Parent",
68+
"cardinality": "one",
69+
"direction": "outbound",
70+
"branch": BranchSupportType.AGNOSTIC.value,
71+
},
72+
],
73+
},
5474
],
5575
}
5676
return schema

backend/tests/functional/branch/test_delete_agnostic_rel.py

Lines changed: 75 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,58 @@
22

33
from typing import TYPE_CHECKING, Any
44

5+
import pytest
6+
from infrahub_sdk.exceptions import GraphQLError
7+
58
from tests.helpers.test_app import TestInfrahubApp
69

710
if TYPE_CHECKING:
811
from infrahub_sdk import InfrahubClient
12+
from infrahub_sdk.node import InfrahubNode
913

1014

1115
class TestDeleteAgnosticRel(TestInfrahubApp):
16+
@pytest.fixture(scope="class")
17+
async def load_schema(self, client: InfrahubClient, car_person_branch_agnostic_schema: dict[str, Any]) -> None:
18+
await client.schema.load([car_person_branch_agnostic_schema])
19+
20+
@pytest.fixture(scope="class")
21+
async def owner_1(self, client: InfrahubClient, load_schema) -> InfrahubNode:
22+
owner_1 = await client.create(kind="TestPerson", name="owner_1")
23+
await owner_1.save()
24+
return owner_1
25+
26+
@pytest.fixture(scope="class")
27+
async def owner_2(self, client: InfrahubClient, load_schema) -> InfrahubNode:
28+
owner_2 = await client.create(kind="TestPerson", name="owner_2")
29+
await owner_2.save()
30+
return owner_2
31+
32+
@pytest.fixture(scope="class")
33+
async def car(self, client: InfrahubClient, load_schema, owner_1: InfrahubNode) -> InfrahubNode:
34+
car = await client.create(kind="TestCar", name="car_name", owner=owner_1)
35+
await car.save()
36+
return car
37+
38+
@pytest.fixture(scope="class")
39+
async def car_2(self, client: InfrahubClient, load_schema, owner_2: InfrahubNode) -> InfrahubNode:
40+
car = await client.create(kind="TestCar", name="car_name_2", owner=owner_2)
41+
await car.save()
42+
return car
43+
44+
@pytest.fixture(scope="class")
45+
async def roofrack_2(self, client: InfrahubClient, load_schema, car_2: InfrahubNode) -> InfrahubNode:
46+
roofrack = await client.create(kind="TestRoofrack", size="big", car=car_2)
47+
await roofrack.save()
48+
return roofrack
49+
1250
async def test_delete_agnostic_rel(
13-
self, client: InfrahubClient, car_person_branch_agnostic_schema: dict[str, Any]
51+
self,
52+
client: InfrahubClient,
53+
load_schema,
54+
owner_1: InfrahubNode,
55+
owner_2: InfrahubNode,
56+
car: InfrahubNode,
1457
) -> None:
1558
"""
1659
Loads a car-person agnostic schema, then :
@@ -20,22 +63,42 @@ async def test_delete_agnostic_rel(
2063
This test makes sure changing owner, involving deleting relationship with first owner, works correctly.
2164
See https://github.com/opsmill/infrahub/issues/5559.
2265
"""
66+
car = await client.get(kind="TestCar", name__value="car_name", prefetch_relationships=True)
67+
car.owner = owner_2
68+
await car.save()
2369

24-
await client.schema.load([car_person_branch_agnostic_schema])
70+
car = await client.get(kind="TestCar", name__value="car_name", prefetch_relationships=True)
71+
assert car.owner.peer.name.value == "owner_2"
2572

26-
owner_1 = await client.create(kind="TestPerson", name="owner_1")
27-
await owner_1.save()
73+
async def test_delete_aware_mandatory_node_blocked(
74+
self, client: InfrahubClient, owner_2: InfrahubNode, car: InfrahubNode
75+
) -> None:
76+
owner_2 = await client.get(kind="TestPerson", name__value="owner_2")
2877

29-
car = await client.create(kind="TestCar", name="car_name", owner=owner_1)
30-
await car.save()
78+
with pytest.raises(GraphQLError) as exc:
79+
await owner_2.delete()
80+
81+
assert (
82+
f"Cannot delete TestPerson '{owner_2.id}'. It is linked to mandatory relationship owner on node TestCar '{car.id}'"
83+
in exc.value.message
84+
)
3185

86+
async def test_delete_agnostic_node(self, client: InfrahubClient, owner_2: InfrahubNode, car: InfrahubNode) -> None:
3287
car = await client.get(kind="TestCar", name__value="car_name", prefetch_relationships=True)
88+
await car.delete()
3389

34-
owner_2 = await client.create(kind="TestPerson", name="owner_2")
35-
await owner_2.save()
90+
owner_2 = await client.get(kind="TestPerson", name__value=owner_2.name.value, prefetch_relationships=True)
91+
assert len(owner_2.cars.peers) == 0
3692

37-
car.owner = owner_2
38-
await car.save()
93+
async def test_delete_aware_node_with_agnostic_parent_blocked(
94+
self, client: InfrahubClient, car_2: InfrahubNode, roofrack_2: InfrahubNode
95+
) -> None:
96+
car_2 = await client.get(kind="TestCar", name__value=car_2.name.value, prefetch_relationships=True)
3997

40-
car = await client.get(kind="TestCar", name__value="car_name", prefetch_relationships=True)
41-
assert car.owner.peer.name.value == "owner_2"
98+
with pytest.raises(GraphQLError) as exc:
99+
await car_2.delete()
100+
101+
assert (
102+
f"Cannot delete TestCar '{car_2.id}'. It is linked to mandatory relationship car on node TestRoofrack '{roofrack_2.id}'"
103+
in exc.value.message
104+
)

docker-compose.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ services:
207207
retries: 5
208208

209209
infrahub-server:
210-
image: "${INFRAHUB_DOCKER_IMAGE:-registry.opsmill.io/opsmill/infrahub}:${VERSION:-1.1.7}"
210+
image: "${INFRAHUB_DOCKER_IMAGE:-registry.opsmill.io/opsmill/infrahub}:${VERSION:-1.1.8a1}"
211211
restart: unless-stopped
212212
command: >
213213
gunicorn --config backend/infrahub/serve/gunicorn_config.py
@@ -253,7 +253,7 @@ services:
253253
deploy:
254254
mode: replicated
255255
replicas: 2
256-
image: "${INFRAHUB_DOCKER_IMAGE:-registry.opsmill.io/opsmill/infrahub}:${VERSION:-1.1.7}"
256+
image: "${INFRAHUB_DOCKER_IMAGE:-registry.opsmill.io/opsmill/infrahub}:${VERSION:-1.1.8a1}"
257257
command: prefect worker start --type infrahubasync --pool infrahub-worker --with-healthcheck
258258
restart: unless-stopped
259259
depends_on:

docs/docs/guides/artifact.mdx

Lines changed: 58 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,56 @@ import ReactPlayer from 'react-player/youtube'
88

99
:::note
1010

11-
This guide has the assumption that you followed the [Creating a Jinja Rendered File (Transform)](./jinja2-transform.mdx) or [Creating a Python transform](./python-transform.mdx) guide as well as [Creating a Group](./groups.mdx) guide
12-
This is a prerequisite.
11+
This guide has the assumption that you followed the [Creating a Jinja Rendered File (Transform)](./jinja2-transform.mdx) or [Creating a Python transform](./python-transform.mdx) guide. This is a prerequisite.
1312

1413
:::
1514

1615
The goal of this guide is to define an artifact for a Jinja rendered Transform or Python transform in Infrahub. We will be using the following steps.
1716

18-
1. Creating an artifact definition
19-
2. Accessing the artifacts
17+
1. Modifying the schema
18+
2. Creating a group and adding member nodes
19+
2. Creating an artifact definition
20+
3. Accessing the artifacts
21+
22+
## Modifying the node schema
23+
24+
Before we can generate an artifact for a node, we need to modify the schema. The node will need to inherit from the `CoreArtifactTarget` generic.
25+
26+
```yaml
27+
---
28+
version: "1.0"
29+
nodes:
30+
- name: Device
31+
namespace: Network
32+
display_labels:
33+
- name__value
34+
inherit_from:
35+
- CoreArtifactTarget
36+
attributes:
37+
- name: name
38+
kind: Text
39+
label: Name
40+
optional: false
41+
unique: true
42+
- name: description
43+
kind: Text
44+
label: Description
45+
optional: true
46+
```
47+
48+
We then have to load the modified schema definition into Infrahub.
49+
50+
```bash
51+
infrahubctl schema load /tmp/schema.yml
52+
```
53+
54+
## Creating a group and adding members
55+
56+
We need to create a group for every Artifact Definition that we will define. An artifact will be generated for every member that is part of that group.
57+
58+
Create a Standard Group with the name `DeviceGroup` and add `NetworkDevice` `switch1`, `switch2` and `switch3` as a member.
59+
60+
More information can be found in the [Creating a group guide](./groups.mdx).
2061

2162
<center>
2263
<ReactPlayer url='https://www.youtube.com/watch?v=ASGMKZVLCbY' light />
@@ -26,23 +67,23 @@ The goal of this guide is to define an artifact for a Jinja rendered Transform o
2667

2768
In the last step we need to define an Artifact definition, which groups together a [transformation](../topics/transformation.mdx) with a target group and forms the definition of the artifact. Artifact definitions can be created via the frontend, via GraphQL or via a [Git repository](../topics/repository.mdx). In this guide we will be using the Git repository.
2869

29-
Add the following contents to the end of the `.infrahub.yml` file at the root of the `tags_render` repository.
70+
Add the following contents to the end of the `.infrahub.yml` file at the root of the `device_config_render` repository.
3071

3172
```yaml
3273
artifact_definitions:
33-
- name: "tags_config_file"
34-
artifact_name: "Tags configuration file"
74+
- name: "device_configuration"
75+
artifact_name: "Device configuration file"
3576
parameters:
36-
tag: "name__value"
77+
name: "name__value"
3778
content_type: "text/plain"
38-
targets: "TagConfigGroup"
39-
transformation: "my-transform"
79+
targets: "DeviceGroup"
80+
transformation: "device_config_transform"
4081
```
4182
4283
This defines an artifact with the following properties:
4384
4485
- **name**: a unique name for the artifact
45-
- **parameters**: the parameter to pass to the transformation GraphQL query, in this case this we will pass the name of the object (tag) as the tag parameter
86+
- **parameters**: the parameter to pass to the transformation GraphQL query, in this case this we will pass the name of the object (device) as the name parameter
4687
- **content type**: the content type for the resulting artifact
4788
- **targets**: the name of a group of which the members will be a target for this artifact
4889
- **transformation**: the Jinja2 or Python transformation that should be used
@@ -53,11 +94,11 @@ Commit the changes to the repository and push them to the Git server
5394

5495
```shell
5596
git add .
56-
git commit -m "add tags_config_file artifact definition"
97+
git commit -m "add device_configuration artifact definition"
5798
git push origin main
5899
```
59100

60-
The artifact definition will be created in the database, when the Task worker(s) notice the change in the Git repository. The `tags_config_file` should now be visible in the Artifact Definition view in the web interface.
101+
The artifact definition will be created in the database, when the Task worker(s) notice the change in the Git repository. The `device_configuration` should now be visible in the Artifact Definition view in the web interface.
61102
![Artifact Definition](../media/guides/artifact/artifact_definition.png)
62103

63104
## Accessing the artifacts
@@ -69,6 +110,8 @@ The artifacts are generated by the git-agents. You can find the resulting artifa
69110
Open an artifact to get the result.
70111

71112
![Artifact detail](../media/guides/artifact/artifact_detail.png)
72-
You can download the artifact by clicking on the `Storage Id` or alternatively through the rest API endpoint `http://<INFRAHUB_HOST:INFRAHUB_PORT>/api/storage/object/<storage_id>`
113+
You can download the artifact by clicking on the download button, or by using the REST API endpoint `http://<INFRAHUB_HOST:INFRAHUB_PORT>/api/storage/object/<storage_id>`. The `Storage Id` can be copied from the menu next to the artifact name.
114+
115+
![Artifact menu](../media/guides/artifact/artifact_menu.png)
73116

74-
Optionally, Infrahub can create a relation between a node and an artifact, by inheriting from the CoreArtifactTarget generic in the schema of the node. This is outside the scope of this guide, since the BuiltinTag node does not inherit from CoreArtifactTarget. More information can be found in the [artifact](../topics/artifact.mdx) topic.
117+
Alternatively we can access the artifact of an object, by navigating to the object detail view. Navigate to device `switch1`'s detail page, from the *Artifacts* tab you can access all the artifacts that were generated for this object.

docs/docs/guides/check.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ Then save the `TagsQuery` as a text file named `tags_check.gql`.
110110

111111
### 2. Create the Python check file
112112

113-
The next step is to create the Check Python business logic. The check is a Python class that inherits from `InfrahubCheck` from the [Python SDK](../python-sdk). Create a file called `tags_check.py`:
113+
The next step is to create the Check Python business logic. The check is a Python class that inherits from `InfrahubCheck` from the [Python SDK]($(local_base_url_1)python-sdk). Create a file called `tags_check.py`:
114114

115115
```python
116116
import re

docs/docs/guides/generator.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ We provide `convert_query_response` option to be toggled to be able to access ob
104104

105105
This allows you to manage the returned data with helper methods on the SDK objects such as `save`, `fetch`, etc. on the returned data rather than having to build a payload to send back to Infrahub to manage the objects.
106106

107-
Read more on the [Infrahub Python SDK](../python-sdk).
107+
Read more on the [Infrahub Python SDK]($(local_base_url_1)python-sdk).
108108
:::
109109

110110
<Tabs groupId="convertResponse">
@@ -291,7 +291,7 @@ widget_generator (widget_generator.py::Generator) Target: widgets
291291

292292
:::warning
293293

294-
When running a generator with `infrahubctl` the [SDK tracking](../python-sdk/topics/tracking) feature isn't used. The reason for this is that internally Infrahub uses the ID of the generator_definition to control the tracking, this isn't available from the outside. For this reason it is recommended to create test branches when developing generators and validating the results.
294+
When running a generator with `infrahubctl` the [SDK tracking]($(local_base_url_1)python-sdk/topics/tracking) feature isn't used. The reason for this is that internally Infrahub uses the ID of the generator_definition to control the tracking, this isn't available from the outside. For this reason it is recommended to create test branches when developing generators and validating the results.
295295

296296
:::
297297

0 commit comments

Comments
 (0)