infrahub-server 1.3.5__py3-none-any.whl → 1.3.6__py3-none-any.whl
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.
- infrahub/cli/db.py +25 -0
- infrahub/cli/db_commands/__init__.py +0 -0
- infrahub/cli/db_commands/check_inheritance.py +284 -0
- infrahub/core/manager.py +2 -2
- infrahub/core/migrations/query/node_duplicate.py +26 -3
- infrahub/core/query/node.py +13 -0
- infrahub/core/relationship/model.py +13 -13
- infrahub/graphql/mutations/relationship.py +1 -1
- {infrahub_server-1.3.5.dist-info → infrahub_server-1.3.6.dist-info}/METADATA +1 -1
- {infrahub_server-1.3.5.dist-info → infrahub_server-1.3.6.dist-info}/RECORD +13 -11
- {infrahub_server-1.3.5.dist-info → infrahub_server-1.3.6.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.3.5.dist-info → infrahub_server-1.3.6.dist-info}/WHEEL +0 -0
- {infrahub_server-1.3.5.dist-info → infrahub_server-1.3.6.dist-info}/entry_points.txt +0 -0
infrahub/cli/db.py
CHANGED
|
@@ -56,6 +56,7 @@ from infrahub.services.adapters.message_bus.local import BusSimulator
|
|
|
56
56
|
from infrahub.services.adapters.workflow.local import WorkflowLocalExecution
|
|
57
57
|
|
|
58
58
|
from .constants import ERROR_BADGE, FAILED_BADGE, SUCCESS_BADGE
|
|
59
|
+
from .db_commands.check_inheritance import check_inheritance
|
|
59
60
|
from .patch import patch_app
|
|
60
61
|
|
|
61
62
|
|
|
@@ -178,6 +179,30 @@ async def migrate_cmd(
|
|
|
178
179
|
await dbdriver.close()
|
|
179
180
|
|
|
180
181
|
|
|
182
|
+
@app.command(name="check-inheritance")
|
|
183
|
+
async def check_inheritance_cmd(
|
|
184
|
+
ctx: typer.Context,
|
|
185
|
+
fix: bool = typer.Option(False, help="Fix the inheritance of any invalid nodes."),
|
|
186
|
+
config_file: str = typer.Argument("infrahub.toml", envvar="INFRAHUB_CONFIG"),
|
|
187
|
+
) -> None:
|
|
188
|
+
"""Check the database for any vertices with incorrect inheritance"""
|
|
189
|
+
logging.getLogger("infrahub").setLevel(logging.WARNING)
|
|
190
|
+
logging.getLogger("neo4j").setLevel(logging.ERROR)
|
|
191
|
+
logging.getLogger("prefect").setLevel(logging.ERROR)
|
|
192
|
+
|
|
193
|
+
config.load_and_exit(config_file_name=config_file)
|
|
194
|
+
|
|
195
|
+
context: CliContext = ctx.obj
|
|
196
|
+
dbdriver = await context.init_db(retry=1)
|
|
197
|
+
await initialize_registry(db=dbdriver)
|
|
198
|
+
|
|
199
|
+
success = await check_inheritance(db=dbdriver, fix=fix)
|
|
200
|
+
if not success:
|
|
201
|
+
raise typer.Exit(code=1)
|
|
202
|
+
|
|
203
|
+
await dbdriver.close()
|
|
204
|
+
|
|
205
|
+
|
|
181
206
|
@app.command(name="update-core-schema")
|
|
182
207
|
async def update_core_schema_cmd(
|
|
183
208
|
ctx: typer.Context,
|
|
File without changes
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections import defaultdict
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
|
|
7
|
+
from rich import print as rprint
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.table import Table
|
|
10
|
+
|
|
11
|
+
from infrahub.core import registry
|
|
12
|
+
from infrahub.core.branch.models import Branch
|
|
13
|
+
from infrahub.core.constants import InfrahubKind
|
|
14
|
+
from infrahub.core.migrations.query.node_duplicate import NodeDuplicateQuery, SchemaNodeInfo
|
|
15
|
+
from infrahub.core.query import Query, QueryType
|
|
16
|
+
from infrahub.core.schema import SchemaRoot, internal_schema
|
|
17
|
+
from infrahub.core.schema.manager import SchemaManager
|
|
18
|
+
from infrahub.log import get_logger
|
|
19
|
+
|
|
20
|
+
from ..constants import FAILED_BADGE, SUCCESS_BADGE
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from infrahub.core.schema.node_schema import NodeSchema
|
|
24
|
+
from infrahub.database import InfrahubDatabase
|
|
25
|
+
|
|
26
|
+
log = get_logger()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class GetSchemaWithUpdatedInheritance(Query):
|
|
30
|
+
"""
|
|
31
|
+
Get the name, namespace, and branch of any SchemaNodes with _updated_ inheritance
|
|
32
|
+
This query will only return schemas that have had `inherit_from` updated after they were created
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
name = "get_schema_with_updated_inheritance"
|
|
36
|
+
type = QueryType.READ
|
|
37
|
+
insert_return = False
|
|
38
|
+
|
|
39
|
+
async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
|
|
40
|
+
query = """
|
|
41
|
+
// find inherit_from attributes that have been updated
|
|
42
|
+
MATCH p = (schema_node:SchemaNode)-[has_attr_e:HAS_ATTRIBUTE {status: "active"}]->(a:Attribute {name: "inherit_from"})
|
|
43
|
+
WHERE has_attr_e.to IS NULL
|
|
44
|
+
CALL (a) {
|
|
45
|
+
// only get branches on which the value was updated, we can ignore the initial create
|
|
46
|
+
MATCH (a)-[e:HAS_VALUE]->(:AttributeValue)
|
|
47
|
+
ORDER BY e.from ASC
|
|
48
|
+
// tail leaves out the earliest one, which is the initial create
|
|
49
|
+
RETURN tail(collect(e.branch)) AS branches
|
|
50
|
+
}
|
|
51
|
+
WITH schema_node, a, branches
|
|
52
|
+
WHERE size(branches) > 0
|
|
53
|
+
UNWIND branches AS branch
|
|
54
|
+
WITH DISTINCT schema_node, a, branch
|
|
55
|
+
|
|
56
|
+
//get branch details
|
|
57
|
+
CALL (branch) {
|
|
58
|
+
MATCH (b:Branch {name: branch})
|
|
59
|
+
RETURN b.branched_from AS branched_from, b.hierarchy_level AS branch_level
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// get the namespace for the schema
|
|
63
|
+
CALL (schema_node, a, branch, branched_from, branch_level) {
|
|
64
|
+
MATCH (schema_node)-[e1:HAS_ATTRIBUTE]-(:Attribute {name: "namespace"})-[e2:HAS_VALUE]->(av)
|
|
65
|
+
WHERE (
|
|
66
|
+
e1.branch = branch OR
|
|
67
|
+
(e1.branch_level < branch_level AND e1.from <= branched_from)
|
|
68
|
+
) AND e1.to IS NULL
|
|
69
|
+
AND e1.status = "active"
|
|
70
|
+
AND (
|
|
71
|
+
e2.branch = branch OR
|
|
72
|
+
(e2.branch_level < branch_level AND e2.from <= branched_from)
|
|
73
|
+
) AND e2.to IS NULL
|
|
74
|
+
AND e2.status = "active"
|
|
75
|
+
ORDER BY e2.branch_level DESC, e1.branch_level DESC, e2.from DESC, e1.from DESC
|
|
76
|
+
RETURN av.value AS namespace
|
|
77
|
+
LIMIT 1
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// get the name for the schema
|
|
81
|
+
CALL (schema_node, a, branch, branched_from, branch_level) {
|
|
82
|
+
MATCH (schema_node)-[e1:HAS_ATTRIBUTE]-(:Attribute {name: "name"})-[e2:HAS_VALUE]->(av)
|
|
83
|
+
WHERE (
|
|
84
|
+
e1.branch = branch OR
|
|
85
|
+
(e1.branch_level < branch_level AND e1.from <= branched_from)
|
|
86
|
+
) AND e1.to IS NULL
|
|
87
|
+
AND e1.status = "active"
|
|
88
|
+
AND (
|
|
89
|
+
e2.branch = branch OR
|
|
90
|
+
(e2.branch_level < branch_level AND e2.from <= branched_from)
|
|
91
|
+
) AND e2.to IS NULL
|
|
92
|
+
AND e2.status = "active"
|
|
93
|
+
ORDER BY e2.branch_level DESC, e1.branch_level DESC, e2.from DESC, e1.from DESC
|
|
94
|
+
RETURN av.value AS name
|
|
95
|
+
LIMIT 1
|
|
96
|
+
}
|
|
97
|
+
RETURN name, namespace, branch
|
|
98
|
+
"""
|
|
99
|
+
self.return_labels = ["name", "namespace", "branch"]
|
|
100
|
+
self.add_to_query(query)
|
|
101
|
+
|
|
102
|
+
def get_updated_inheritance_kinds_by_branch(self) -> dict[str, list[str]]:
|
|
103
|
+
kinds_by_branch: dict[str, list[str]] = defaultdict(list)
|
|
104
|
+
for result in self.results:
|
|
105
|
+
name = result.get_as_type(label="name", return_type=str)
|
|
106
|
+
namespace = result.get_as_type(label="namespace", return_type=str)
|
|
107
|
+
branch = result.get_as_type(label="branch", return_type=str)
|
|
108
|
+
kinds_by_branch[branch].append(f"{namespace}{name}")
|
|
109
|
+
return kinds_by_branch
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@dataclass
|
|
113
|
+
class KindLabelCount:
|
|
114
|
+
kind: str
|
|
115
|
+
labels: frozenset[str]
|
|
116
|
+
num_nodes: int
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@dataclass
|
|
120
|
+
class KindLabelCountCorrected(KindLabelCount):
|
|
121
|
+
node_schema: NodeSchema
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class GetAllKindsAndLabels(Query):
|
|
125
|
+
"""
|
|
126
|
+
Get the kind, labels, and number of nodes for the given kinds and branch
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
name = "get_all_kinds_and_labels"
|
|
130
|
+
type = QueryType.READ
|
|
131
|
+
insert_return = False
|
|
132
|
+
|
|
133
|
+
def __init__(self, kinds: list[str] | None = None, **kwargs: Any) -> None:
|
|
134
|
+
super().__init__(**kwargs)
|
|
135
|
+
self.kinds = kinds
|
|
136
|
+
|
|
137
|
+
async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
|
|
138
|
+
self.params["branch_name"] = self.branch.name
|
|
139
|
+
self.params["branched_from"] = self.branch.get_branched_from()
|
|
140
|
+
self.params["branch_level"] = self.branch.hierarchy_level
|
|
141
|
+
kinds_str = "Node"
|
|
142
|
+
if self.kinds:
|
|
143
|
+
kinds_str = "|".join(self.kinds)
|
|
144
|
+
query = """
|
|
145
|
+
MATCH (n:%(kinds_str)s)-[r:IS_PART_OF]->(:Root)
|
|
146
|
+
WHERE (
|
|
147
|
+
r.branch = $branch_name OR
|
|
148
|
+
(r.branch_level < $branch_level AND r.from <= $branched_from)
|
|
149
|
+
)
|
|
150
|
+
AND r.to IS NULL
|
|
151
|
+
AND r.status = "active"
|
|
152
|
+
RETURN DISTINCT n.kind AS kind, labels(n) AS labels, count(*) AS num_nodes
|
|
153
|
+
ORDER BY kind ASC
|
|
154
|
+
""" % {"kinds_str": kinds_str}
|
|
155
|
+
self.return_labels = ["kind", "labels", "num_nodes"]
|
|
156
|
+
self.add_to_query(query)
|
|
157
|
+
|
|
158
|
+
def get_kind_label_counts(self) -> list[KindLabelCount]:
|
|
159
|
+
kind_label_counts: list[KindLabelCount] = []
|
|
160
|
+
for result in self.results:
|
|
161
|
+
kind = result.get_as_type(label="kind", return_type=str)
|
|
162
|
+
num_nodes = result.get_as_type(label="num_nodes", return_type=int)
|
|
163
|
+
labels: list[str] = result.get_as_type(label="labels", return_type=list)
|
|
164
|
+
# we can ignore the Node label and the label that matches the kind
|
|
165
|
+
cleaned_labels = frozenset(str(lbl) for lbl in labels if lbl not in ["Node", "CoreNode", kind])
|
|
166
|
+
kind_label_counts.append(KindLabelCount(kind=kind, labels=cleaned_labels, num_nodes=num_nodes))
|
|
167
|
+
return kind_label_counts
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def display_kind_label_counts(kind_label_counts_by_branch: dict[str, list[KindLabelCountCorrected]]) -> None:
|
|
171
|
+
console = Console()
|
|
172
|
+
|
|
173
|
+
table = Table(title="Incorrect Inheritance Nodes")
|
|
174
|
+
|
|
175
|
+
table.add_column("Branch")
|
|
176
|
+
table.add_column("Kind")
|
|
177
|
+
table.add_column("Incorrect Labels")
|
|
178
|
+
table.add_column("Num Nodes")
|
|
179
|
+
|
|
180
|
+
for branch_name, kind_label_counts in kind_label_counts_by_branch.items():
|
|
181
|
+
for kind_label_count in kind_label_counts:
|
|
182
|
+
table.add_row(
|
|
183
|
+
branch_name, kind_label_count.kind, str(list(kind_label_count.labels)), str(kind_label_count.num_nodes)
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
console.print(table)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
async def check_inheritance(db: InfrahubDatabase, fix: bool = False) -> bool:
|
|
190
|
+
"""
|
|
191
|
+
Run migrations to update the inheritance of any nodes with incorrect inheritance from a failed migration
|
|
192
|
+
1. Identifies node schemas that have had their inheritance updated after they were created
|
|
193
|
+
a. includes the kind and branch of the inheritance update
|
|
194
|
+
2. Checks nodes of the given kinds on the given branch to verify their inheritance is correct
|
|
195
|
+
3. Displays counts of any kinds with incorrect inheritance on the given branch
|
|
196
|
+
4. If fix is True, runs migrations to update the inheritance of any nodes with incorrect inheritance
|
|
197
|
+
on the correct branch
|
|
198
|
+
"""
|
|
199
|
+
|
|
200
|
+
updated_inheritance_query = await GetSchemaWithUpdatedInheritance.init(db=db)
|
|
201
|
+
await updated_inheritance_query.execute(db=db)
|
|
202
|
+
updated_inheritance_kinds_by_branch = updated_inheritance_query.get_updated_inheritance_kinds_by_branch()
|
|
203
|
+
|
|
204
|
+
if not updated_inheritance_kinds_by_branch:
|
|
205
|
+
rprint(f"{SUCCESS_BADGE} No schemas have had their inheritance updated")
|
|
206
|
+
return True
|
|
207
|
+
|
|
208
|
+
schema_manager = SchemaManager()
|
|
209
|
+
registry.schema = schema_manager
|
|
210
|
+
schema = SchemaRoot(**internal_schema)
|
|
211
|
+
schema_manager.register_schema(schema=schema)
|
|
212
|
+
branches_by_name = {b.name: b for b in await Branch.get_list(db=db)}
|
|
213
|
+
|
|
214
|
+
kind_label_counts_by_branch: dict[str, list[KindLabelCountCorrected]] = defaultdict(list)
|
|
215
|
+
for branch_name, kinds in updated_inheritance_kinds_by_branch.items():
|
|
216
|
+
rprint(f"Checking branch: {branch_name}", end="...")
|
|
217
|
+
branch = branches_by_name[branch_name]
|
|
218
|
+
schema_branch = await schema_manager.load_schema_from_db(db=db, branch=branch)
|
|
219
|
+
kind_label_query = await GetAllKindsAndLabels.init(db=db, branch=branch, kinds=kinds)
|
|
220
|
+
await kind_label_query.execute(db=db)
|
|
221
|
+
kind_label_counts = kind_label_query.get_kind_label_counts()
|
|
222
|
+
|
|
223
|
+
for kind_label_count in kind_label_counts:
|
|
224
|
+
node_schema = schema_branch.get_node(name=kind_label_count.kind, duplicate=False)
|
|
225
|
+
correct_labels = frozenset(node_schema.inherit_from)
|
|
226
|
+
if kind_label_count.labels == correct_labels:
|
|
227
|
+
continue
|
|
228
|
+
|
|
229
|
+
kind_label_counts_by_branch[branch_name].append(
|
|
230
|
+
KindLabelCountCorrected(
|
|
231
|
+
kind=kind_label_count.kind,
|
|
232
|
+
labels=kind_label_count.labels,
|
|
233
|
+
num_nodes=kind_label_count.num_nodes,
|
|
234
|
+
node_schema=node_schema,
|
|
235
|
+
)
|
|
236
|
+
)
|
|
237
|
+
rprint("done")
|
|
238
|
+
|
|
239
|
+
if not kind_label_counts_by_branch:
|
|
240
|
+
rprint(f"{SUCCESS_BADGE} All nodes have the correct inheritance")
|
|
241
|
+
return True
|
|
242
|
+
|
|
243
|
+
display_kind_label_counts(kind_label_counts_by_branch)
|
|
244
|
+
|
|
245
|
+
if not fix:
|
|
246
|
+
rprint(f"{FAILED_BADGE} Use the --fix flag to fix the inheritance of any invalid nodes")
|
|
247
|
+
return False
|
|
248
|
+
|
|
249
|
+
for branch_name, kind_label_counts_corrected in kind_label_counts_by_branch.items():
|
|
250
|
+
for kind_label_count in kind_label_counts_corrected:
|
|
251
|
+
rprint(f"Fixing kind {kind_label_count.kind} on branch {branch_name}", end="...")
|
|
252
|
+
node_schema = kind_label_count.node_schema
|
|
253
|
+
migration_query = await NodeDuplicateQuery.init(
|
|
254
|
+
db=db,
|
|
255
|
+
branch=branches_by_name[branch_name],
|
|
256
|
+
previous_node=SchemaNodeInfo(
|
|
257
|
+
name=node_schema.name,
|
|
258
|
+
namespace=node_schema.namespace,
|
|
259
|
+
branch_support=node_schema.branch.value,
|
|
260
|
+
labels=list(kind_label_count.labels) + [kind_label_count.kind, InfrahubKind.NODE],
|
|
261
|
+
kind=kind_label_count.kind,
|
|
262
|
+
),
|
|
263
|
+
new_node=SchemaNodeInfo(
|
|
264
|
+
name=node_schema.name,
|
|
265
|
+
namespace=node_schema.namespace,
|
|
266
|
+
branch_support=node_schema.branch.value,
|
|
267
|
+
labels=list(node_schema.inherit_from) + [kind_label_count.kind, InfrahubKind.NODE],
|
|
268
|
+
kind=kind_label_count.kind,
|
|
269
|
+
),
|
|
270
|
+
)
|
|
271
|
+
await migration_query.execute(db=db)
|
|
272
|
+
rprint("done")
|
|
273
|
+
|
|
274
|
+
rprint(f"{SUCCESS_BADGE} All nodes have the correct inheritance")
|
|
275
|
+
|
|
276
|
+
if registry.default_branch in kind_label_counts_by_branch:
|
|
277
|
+
kinds = [kind_label_count.kind for kind_label_count in kind_label_counts_by_branch[registry.default_branch]]
|
|
278
|
+
rprint(
|
|
279
|
+
"[bold cyan]Note that migrations were run on the default branch for the following schema kinds: "
|
|
280
|
+
f"{', '.join(kinds)}. You should rebase any branches that include/will include changes using "
|
|
281
|
+
"the migrated schemas[/bold cyan]"
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
return True
|
infrahub/core/manager.py
CHANGED
|
@@ -400,7 +400,7 @@ class NodeManager:
|
|
|
400
400
|
|
|
401
401
|
results = []
|
|
402
402
|
for peer in peers_info:
|
|
403
|
-
result =
|
|
403
|
+
result = Relationship(schema=schema, branch=branch, at=at, node_id=peer.source_id).load(
|
|
404
404
|
db=db,
|
|
405
405
|
id=peer.rel_node_id,
|
|
406
406
|
db_id=peer.rel_node_db_id,
|
|
@@ -408,7 +408,7 @@ class NodeManager:
|
|
|
408
408
|
data=peer,
|
|
409
409
|
)
|
|
410
410
|
if fetch_peers:
|
|
411
|
-
|
|
411
|
+
result.set_peer(value=peer_nodes[peer.peer_id])
|
|
412
412
|
results.append(result)
|
|
413
413
|
|
|
414
414
|
return results
|
|
@@ -21,6 +21,13 @@ class SchemaNodeInfo(BaseModel):
|
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class NodeDuplicateQuery(Query):
|
|
24
|
+
"""
|
|
25
|
+
Duplicates a Node to use a new kind or inheritance.
|
|
26
|
+
Creates a copy of each affected Node and sets the new kind/inheritance.
|
|
27
|
+
Adds duplicate edges to the new Node that match all the active edges on the old Node.
|
|
28
|
+
Sets all the edges on the old Node to deleted.
|
|
29
|
+
"""
|
|
30
|
+
|
|
24
31
|
name = "node_duplicate"
|
|
25
32
|
type = QueryType.WRITE
|
|
26
33
|
insert_return: bool = False
|
|
@@ -38,11 +45,26 @@ class NodeDuplicateQuery(Query):
|
|
|
38
45
|
|
|
39
46
|
def render_match(self) -> str:
|
|
40
47
|
labels_str = ":".join(self.previous_node.labels)
|
|
41
|
-
query =
|
|
48
|
+
query = """
|
|
42
49
|
// Find all the active nodes
|
|
43
|
-
MATCH (node
|
|
50
|
+
MATCH (node:%(labels_str)s)
|
|
44
51
|
WITH DISTINCT node
|
|
45
|
-
|
|
52
|
+
// ----------------
|
|
53
|
+
// Filter out nodes that have already been migrated
|
|
54
|
+
// ----------------
|
|
55
|
+
CALL (node) {
|
|
56
|
+
WITH labels(node) AS node_labels
|
|
57
|
+
UNWIND node_labels AS n_label
|
|
58
|
+
ORDER BY n_label ASC
|
|
59
|
+
WITH collect(n_label) AS sorted_labels
|
|
60
|
+
|
|
61
|
+
RETURN (
|
|
62
|
+
node.kind = $new_node.kind AND
|
|
63
|
+
sorted_labels = $new_sorted_labels
|
|
64
|
+
) AS already_migrated
|
|
65
|
+
}
|
|
66
|
+
WITH node WHERE already_migrated = FALSE
|
|
67
|
+
""" % {"labels_str": labels_str}
|
|
46
68
|
|
|
47
69
|
return query
|
|
48
70
|
|
|
@@ -111,6 +133,7 @@ class NodeDuplicateQuery(Query):
|
|
|
111
133
|
|
|
112
134
|
self.params["new_node"] = self.new_node.model_dump()
|
|
113
135
|
self.params["previous_node"] = self.previous_node.model_dump()
|
|
136
|
+
self.params["new_sorted_labels"] = sorted(self.new_node.labels + ["Node"])
|
|
114
137
|
|
|
115
138
|
self.params["current_time"] = self.at.to_string()
|
|
116
139
|
self.params["branch"] = self.branch.name
|
infrahub/core/query/node.py
CHANGED
|
@@ -157,7 +157,20 @@ class NodeCreateAllQuery(NodeQuery):
|
|
|
157
157
|
relationships: list[RelationshipCreateData] = []
|
|
158
158
|
for rel_name in self.node._relationships:
|
|
159
159
|
rel_manager: RelationshipManager = getattr(self.node, rel_name)
|
|
160
|
+
# Fetch all relationship peers through a single database call for performances.
|
|
161
|
+
peers = await rel_manager.get_peers(db=db, branch_agnostic=self.branch_agnostic)
|
|
162
|
+
|
|
160
163
|
for rel in rel_manager._relationships:
|
|
164
|
+
try:
|
|
165
|
+
rel.set_peer(value=peers[rel.get_peer_id()])
|
|
166
|
+
except KeyError:
|
|
167
|
+
pass
|
|
168
|
+
except ValueError:
|
|
169
|
+
# Relationship has not been initialized yet, it means the peer does not exist in db yet
|
|
170
|
+
# typically because it will be allocated from a ressource pool. In that case, the peer
|
|
171
|
+
# will be fetched using `rel.resolve` later.
|
|
172
|
+
pass
|
|
173
|
+
|
|
161
174
|
rel_create_data = await rel.get_create_data(db=db, at=at)
|
|
162
175
|
if rel_create_data.peer_branch_level > deepest_branch_level or (
|
|
163
176
|
deepest_branch_name == GLOBAL_BRANCH_NAME and rel_create_data.peer_branch == registry.default_branch
|
|
@@ -166,11 +166,11 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
|
|
|
166
166
|
return registry.get_global_branch()
|
|
167
167
|
return self.branch
|
|
168
168
|
|
|
169
|
-
|
|
169
|
+
def _process_data(self, data: dict | RelationshipPeerData | str) -> None:
|
|
170
170
|
self.data = data
|
|
171
171
|
|
|
172
172
|
if isinstance(data, RelationshipPeerData):
|
|
173
|
-
|
|
173
|
+
self.set_peer(value=str(data.peer_id))
|
|
174
174
|
|
|
175
175
|
if not self.id and data.rel_node_id:
|
|
176
176
|
self.id = data.rel_node_id
|
|
@@ -187,7 +187,7 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
|
|
|
187
187
|
elif isinstance(data, dict):
|
|
188
188
|
for key, value in data.items():
|
|
189
189
|
if key in ["peer", "id"]:
|
|
190
|
-
|
|
190
|
+
self.set_peer(value=data.get(key, None))
|
|
191
191
|
elif key == "hfid" and self.peer_id is None:
|
|
192
192
|
self.peer_hfid = value
|
|
193
193
|
elif key.startswith(PREFIX_PROPERTY) and key.replace(PREFIX_PROPERTY, "") in self._flag_properties:
|
|
@@ -198,7 +198,7 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
|
|
|
198
198
|
self.from_pool = value
|
|
199
199
|
|
|
200
200
|
else:
|
|
201
|
-
|
|
201
|
+
self.set_peer(value=data)
|
|
202
202
|
|
|
203
203
|
async def new(
|
|
204
204
|
self,
|
|
@@ -206,11 +206,11 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
|
|
|
206
206
|
data: dict | RelationshipPeerData | Any = None,
|
|
207
207
|
**kwargs: Any, # noqa: ARG002
|
|
208
208
|
) -> Relationship:
|
|
209
|
-
|
|
209
|
+
self._process_data(data=data)
|
|
210
210
|
|
|
211
211
|
return self
|
|
212
212
|
|
|
213
|
-
|
|
213
|
+
def load(
|
|
214
214
|
self,
|
|
215
215
|
db: InfrahubDatabase, # noqa: ARG002
|
|
216
216
|
id: UUID | None = None,
|
|
@@ -223,7 +223,7 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
|
|
|
223
223
|
self.id = id or self.id
|
|
224
224
|
self.db_id = db_id or self.db_id
|
|
225
225
|
|
|
226
|
-
|
|
226
|
+
self._process_data(data=data)
|
|
227
227
|
|
|
228
228
|
if updated_at and hash(self) != hash_before:
|
|
229
229
|
self.updated_at = Timestamp(updated_at)
|
|
@@ -252,7 +252,7 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
|
|
|
252
252
|
self._node_id = self._node.id
|
|
253
253
|
return node
|
|
254
254
|
|
|
255
|
-
|
|
255
|
+
def set_peer(self, value: str | Node) -> None:
|
|
256
256
|
if isinstance(value, str):
|
|
257
257
|
self.peer_id = value
|
|
258
258
|
else:
|
|
@@ -433,7 +433,7 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
|
|
|
433
433
|
db=db, id=self.peer_id, branch=self.branch, kind=self.schema.peer, fields={"display_label": None}
|
|
434
434
|
)
|
|
435
435
|
if peer:
|
|
436
|
-
|
|
436
|
+
self.set_peer(value=peer)
|
|
437
437
|
|
|
438
438
|
if not self.peer_id and self.peer_hfid:
|
|
439
439
|
peer_schema = db.schema.get(name=self.schema.peer, branch=self.branch)
|
|
@@ -450,7 +450,7 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
|
|
|
450
450
|
fields={"display_label": None},
|
|
451
451
|
raise_on_error=True,
|
|
452
452
|
)
|
|
453
|
-
|
|
453
|
+
self.set_peer(value=peer)
|
|
454
454
|
|
|
455
455
|
if not self.peer_id and self.from_pool and "id" in self.from_pool:
|
|
456
456
|
pool_id = str(self.from_pool.get("id"))
|
|
@@ -473,7 +473,7 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
|
|
|
473
473
|
data_from_pool["identifier"] = f"hfid={hfid_str} rel={self.name}"
|
|
474
474
|
|
|
475
475
|
assigned_peer: Node = await pool.get_resource(db=db, branch=self.branch, at=at, **data_from_pool) # type: ignore[attr-defined]
|
|
476
|
-
|
|
476
|
+
self.set_peer(value=assigned_peer)
|
|
477
477
|
self.set_source(value=pool.id)
|
|
478
478
|
|
|
479
479
|
async def save(self, db: InfrahubDatabase, at: Timestamp | None = None) -> Self:
|
|
@@ -962,7 +962,7 @@ class RelationshipManager:
|
|
|
962
962
|
|
|
963
963
|
for peer_id in details.peer_ids_present_database_only:
|
|
964
964
|
self._relationships.append(
|
|
965
|
-
|
|
965
|
+
Relationship(
|
|
966
966
|
schema=self.schema,
|
|
967
967
|
branch=self.branch,
|
|
968
968
|
at=at or self.at,
|
|
@@ -1050,7 +1050,7 @@ class RelationshipManager:
|
|
|
1050
1050
|
if isinstance(item, dict) and item.get("id", None) in previous_relationships:
|
|
1051
1051
|
rel = previous_relationships[item["id"]]
|
|
1052
1052
|
hash_before = hash(rel)
|
|
1053
|
-
|
|
1053
|
+
rel.load(data=item, db=db)
|
|
1054
1054
|
if hash(rel) != hash_before:
|
|
1055
1055
|
changed = True
|
|
1056
1056
|
self._relationships.append(rel)
|
|
@@ -233,7 +233,7 @@ class RelationshipRemove(Mutation):
|
|
|
233
233
|
# we should use RelationshipDataDeleteQuery to delete the relationship
|
|
234
234
|
# it would be more query efficient
|
|
235
235
|
rel = Relationship(schema=rel_schema, branch=graphql_context.branch, node=source)
|
|
236
|
-
|
|
236
|
+
rel.load(db=db, data=existing_peers[node_data.get("id")])
|
|
237
237
|
if group_event_type != GroupUpdateType.NONE:
|
|
238
238
|
peers.append(EventNode(id=rel.get_peer_id(), kind=nodes[rel.get_peer_id()].get_kind()))
|
|
239
239
|
node_changelog.delete_relationship(relationship=rel)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: infrahub-server
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.6
|
|
4
4
|
Summary: Infrahub is taking a new approach to Infrastructure Management by providing a new generation of datastore to organize and control all the data that defines how an infrastructure should run.
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Author: OpsMill
|
|
@@ -38,7 +38,9 @@ infrahub/branch/triggers.py,sha256=4sywoEX79fY2NkaGe6tTHnmytf4k6gXDm2FJHkkRJOw,7
|
|
|
38
38
|
infrahub/cli/__init__.py,sha256=zQjE9zMrwAmk_4qb5mbUgNi06g3HKvrPwQvJLQmv9JY,1814
|
|
39
39
|
infrahub/cli/constants.py,sha256=CoCeTMnfsA3j7ArdLKLZK4VPxOM7ls17qpxGJmND0m8,129
|
|
40
40
|
infrahub/cli/context.py,sha256=20CJj_D1VhigR9uhTDPHiVHnV7vzsgK8v-uLKs06kzA,398
|
|
41
|
-
infrahub/cli/db.py,sha256=
|
|
41
|
+
infrahub/cli/db.py,sha256=OhKiKr-UrQhYxxsInO7PuC6YB0hnDzl4tRTLKi_IBbk,38268
|
|
42
|
+
infrahub/cli/db_commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
43
|
+
infrahub/cli/db_commands/check_inheritance.py,sha256=a9aRg6yW0K5364Crqp_U9VDZjT9Shqu3on5nkuoZaYo,11389
|
|
42
44
|
infrahub/cli/events.py,sha256=nJmowQgTxRs6qaT41A71Ei9jm6qtYaL2amAT5TA1H_k,1726
|
|
43
45
|
infrahub/cli/git_agent.py,sha256=ajT9-kdd3xLIysOPe8GqZyCDMkpNyhqfWjBg9HPWVcg,5240
|
|
44
46
|
infrahub/cli/patch.py,sha256=ztOkWyo0l_Wo0WX10bvSqGZibKzowrwx82oi69cjwkY,6018
|
|
@@ -151,7 +153,7 @@ infrahub/core/ipam/reconciler.py,sha256=48do6rx12G25gaKuOguSrVdUDXVpMr3t6ogU1hdP
|
|
|
151
153
|
infrahub/core/ipam/size.py,sha256=Iu7cVvN9MkilyG_AGvYm3g3dSDesKRVdDh_AKH7yAqk,614
|
|
152
154
|
infrahub/core/ipam/tasks.py,sha256=TUoP6WZjQkd7DdGLxKnBVVH4SxTHkH2xmJCU8nRWqH8,1483
|
|
153
155
|
infrahub/core/ipam/utilization.py,sha256=d-zpXCaWsHgJxBLopCDd7y4sJYvHcIzzpYhbTMIgH74,6733
|
|
154
|
-
infrahub/core/manager.py,sha256=
|
|
156
|
+
infrahub/core/manager.py,sha256=xMXPwlaGNnghkRUW0ILwJAUlBQJZqo9cGp9GVyqkqYk,47564
|
|
155
157
|
infrahub/core/merge.py,sha256=2TiPC3fAHkhZCl8RARPzLj_Us47OBGHAp6txgCbWopU,11238
|
|
156
158
|
infrahub/core/migrations/__init__.py,sha256=syPb3-Irf11dXCHgbT0UdmTnEBbpf4wXJ3m8ADYXDpk,1175
|
|
157
159
|
infrahub/core/migrations/graph/__init__.py,sha256=e1lazHQy_gJHb1LdwALeUSTn8-ErZjEIcgeFut3LWtU,3885
|
|
@@ -193,7 +195,7 @@ infrahub/core/migrations/query/__init__.py,sha256=JoWOUWlV6IzwxWxObsfCnAAKUOHJkE
|
|
|
193
195
|
infrahub/core/migrations/query/attribute_add.py,sha256=LlhkIfVOR3TFSUJEV_4kU5JBKXsWwTsRiX1ySUPe4TU,3655
|
|
194
196
|
infrahub/core/migrations/query/attribute_rename.py,sha256=onb9Nanht1Tz47JgneAcFsuhqqvPS6dvI2nNjRupLLo,6892
|
|
195
197
|
infrahub/core/migrations/query/delete_element_in_schema.py,sha256=QYw2LIpJGQXBPOTm6w9gFdCltZRd-V_YUh5l9HmGthg,7402
|
|
196
|
-
infrahub/core/migrations/query/node_duplicate.py,sha256=
|
|
198
|
+
infrahub/core/migrations/query/node_duplicate.py,sha256=yaxeZBYd52jgEB5XY1PZxksTLpM9x1ZEFbwetFeRKTQ,9623
|
|
197
199
|
infrahub/core/migrations/query/relationship_duplicate.py,sha256=hjUFjNqhaFc2tEg79BXR2xDr_4Wdmu9fVF02cTEICTk,7319
|
|
198
200
|
infrahub/core/migrations/query/schema_attribute_update.py,sha256=fLclNEgoikO_ddlFEo1ts-dZwTXITA85kdJ00fXFxqo,3382
|
|
199
201
|
infrahub/core/migrations/schema/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -232,7 +234,7 @@ infrahub/core/query/branch.py,sha256=B3QEqpwbJrs_8juWQPaHrdwLNJR-1tSkvMuixCFFdt4
|
|
|
232
234
|
infrahub/core/query/delete.py,sha256=7tPP1qtNV6QGYtmgE1RKsuQ9oxENnMTVkttLvJ2PiKg,1927
|
|
233
235
|
infrahub/core/query/diff.py,sha256=Dc70L5u1wokt106g84QNFJdKhnTTCxmCgAGsBilCgEo,36514
|
|
234
236
|
infrahub/core/query/ipam.py,sha256=0glfVQmcKqMvNyK4GU_zRl2O9pjl7JBeavyE8VC-De4,28234
|
|
235
|
-
infrahub/core/query/node.py,sha256=
|
|
237
|
+
infrahub/core/query/node.py,sha256=HXOeT14vCsgpKHp76k-V_VMw7uvVJzFuWY2qBCFZGZk,68317
|
|
236
238
|
infrahub/core/query/relationship.py,sha256=KmS9zrcr-RViXxiITXOjq1t0s-AfsICHk3wyyirZBfA,47817
|
|
237
239
|
infrahub/core/query/resource_manager.py,sha256=NpxHVayh-HleoRz-bk54z2_PuBHBdU7ng3pCqKWObbo,16584
|
|
238
240
|
infrahub/core/query/standard_node.py,sha256=mPBXyqk4RzoWRUX4NoojoVi8zk-sJ03GmzmUaWqOgSI,4825
|
|
@@ -249,7 +251,7 @@ infrahub/core/relationship/constraints/peer_kind.py,sha256=Bropiav4y6r0iU2KfWJ_k
|
|
|
249
251
|
infrahub/core/relationship/constraints/peer_parent.py,sha256=z7elpC8xS_ovAF28Haq-RNpFtTEiUehzowiDgYGT68U,2343
|
|
250
252
|
infrahub/core/relationship/constraints/peer_relatives.py,sha256=Ye79l7njaWxZkU2chTOaptIjvKBIawsNCl0IQxCTDtM,2737
|
|
251
253
|
infrahub/core/relationship/constraints/profiles_kind.py,sha256=nEZPGtGcmelZ1Nb8EPcQ-7_zCLCNIYwwWbU6C9fLj5E,2464
|
|
252
|
-
infrahub/core/relationship/model.py,sha256=
|
|
254
|
+
infrahub/core/relationship/model.py,sha256=mVptkvzEQJXORagSPK-FUjIWito1Sx6BlIMskp_EgxY,47173
|
|
253
255
|
infrahub/core/root.py,sha256=8ZLSOtnmjQcrjqX2vxNO-AGopEUArmBPo_X5NeZBdP0,416
|
|
254
256
|
infrahub/core/schema/__init__.py,sha256=Tif-BUwYWVQ0PJGZHFog6lFgnwZevXk3iBcr3zK__BU,4192
|
|
255
257
|
infrahub/core/schema/attribute_parameters.py,sha256=ABL1GEsOl4_CcDvK9_NucGMaF6LUeOjAxbDQVm_G7eg,6516
|
|
@@ -486,7 +488,7 @@ infrahub/graphql/mutations/node_getter/by_hfid.py,sha256=txpj4xPeL3eYOB9lRiHEFxx
|
|
|
486
488
|
infrahub/graphql/mutations/node_getter/by_id.py,sha256=azERy5XBUe4fYf4t1rhKEn64MlGfm_zH4k-tJU6W69s,856
|
|
487
489
|
infrahub/graphql/mutations/node_getter/interface.py,sha256=3MVTz_3EQnI7REp-ytQvgJuEgWUmrmnRIqKpP8WHCyY,419
|
|
488
490
|
infrahub/graphql/mutations/proposed_change.py,sha256=4y9YTE6f9Rqk_TC2K_uued1bECthE8DmrRm1wfxXzmM,10374
|
|
489
|
-
infrahub/graphql/mutations/relationship.py,sha256=
|
|
491
|
+
infrahub/graphql/mutations/relationship.py,sha256=9LqEPrziruXGBY3Ywquj8yNbA_HB_v2PmGELI6NWBFM,21596
|
|
490
492
|
infrahub/graphql/mutations/repository.py,sha256=Whrt1uYWt7Ro6omJYN8zc3D-poZ6bOBrpBHIG4odAmo,11316
|
|
491
493
|
infrahub/graphql/mutations/resource_manager.py,sha256=DvnmfXmS9bNYXjtgedGTKPdJmtdaCbM5qxl0OJ-t1yQ,11342
|
|
492
494
|
infrahub/graphql/mutations/schema.py,sha256=vOwP8SIcQxamhP_JwbeXPG5iOEwxHhHawgqU6bD-4us,12897
|
|
@@ -807,8 +809,8 @@ infrahub_testcontainers/models.py,sha256=ASYyvl7d_WQz_i7y8-3iab9hwwmCl3OCJavqVbe
|
|
|
807
809
|
infrahub_testcontainers/performance_test.py,sha256=hvwiy6tc_lWniYqGkqfOXVGAmA_IV15VOZqbiD9ezno,6149
|
|
808
810
|
infrahub_testcontainers/plugin.py,sha256=I3RuZQ0dARyKHuqCf0y1Yj731P2Mwf3BJUehRJKeWrs,5645
|
|
809
811
|
infrahub_testcontainers/prometheus.yml,sha256=610xQEyj3xuVJMzPkC4m1fRnCrjGpiRBrXA2ytCLa54,599
|
|
810
|
-
infrahub_server-1.3.
|
|
811
|
-
infrahub_server-1.3.
|
|
812
|
-
infrahub_server-1.3.
|
|
813
|
-
infrahub_server-1.3.
|
|
814
|
-
infrahub_server-1.3.
|
|
812
|
+
infrahub_server-1.3.6.dist-info/LICENSE.txt,sha256=7GQO7kxVoQYnZtFrjZBKLRXbrGwwwimHPPOJtqXsozQ,11340
|
|
813
|
+
infrahub_server-1.3.6.dist-info/METADATA,sha256=9at1iBSv1D91S_nPEQJJ29jVjUyibVPCxUGzze34eAs,8189
|
|
814
|
+
infrahub_server-1.3.6.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
815
|
+
infrahub_server-1.3.6.dist-info/entry_points.txt,sha256=UXIeFWDsrV-4IllNvUEd6KieYGzQfn9paga2YyABOQI,393
|
|
816
|
+
infrahub_server-1.3.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|