infrahub-server 1.3.4__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 +59 -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 +30 -3
- infrahub/core/query/diff.py +17 -3
- infrahub/core/query/node.py +13 -0
- infrahub/core/query/relationship.py +8 -11
- infrahub/core/relationship/model.py +13 -13
- infrahub/core/schema/schema_branch.py +22 -0
- infrahub/generators/tasks.py +1 -1
- infrahub/git/integrator.py +1 -1
- infrahub/git/tasks.py +2 -2
- infrahub/graphql/mutations/main.py +24 -5
- infrahub/graphql/mutations/relationship.py +1 -1
- infrahub/proposed_change/tasks.py +2 -2
- infrahub_sdk/node/node.py +81 -9
- infrahub_sdk/protocols_base.py +0 -2
- infrahub_sdk/protocols_generator/constants.py +1 -0
- infrahub_sdk/utils.py +0 -17
- {infrahub_server-1.3.4.dist-info → infrahub_server-1.3.6.dist-info}/METADATA +1 -1
- {infrahub_server-1.3.4.dist-info → infrahub_server-1.3.6.dist-info}/RECORD +25 -23
- {infrahub_server-1.3.4.dist-info → infrahub_server-1.3.6.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.3.4.dist-info → infrahub_server-1.3.6.dist-info}/WHEEL +0 -0
- {infrahub_server-1.3.4.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,
|
|
@@ -916,5 +941,39 @@ async def run_database_checks(db: InfrahubDatabase, output_dir: Path) -> None:
|
|
|
916
941
|
else:
|
|
917
942
|
rprint(f"{SUCCESS_BADGE} No duplicated edges found")
|
|
918
943
|
|
|
944
|
+
# Check 4: Orphaned Relationships
|
|
945
|
+
rprint("\n[bold cyan]Check 4: Orphaned Relationships[/bold cyan]")
|
|
946
|
+
orphaned_rels_query = """
|
|
947
|
+
MATCH (r:Relationship)-[:IS_RELATED]-(peer:Node)
|
|
948
|
+
WITH DISTINCT r, peer
|
|
949
|
+
WITH r, count(*) AS num_peers
|
|
950
|
+
WHERE num_peers < 2
|
|
951
|
+
MATCH (r)-[e:IS_RELATED]-(peer:Node)
|
|
952
|
+
RETURN DISTINCT
|
|
953
|
+
r.name AS r_name,
|
|
954
|
+
e.branch AS branch,
|
|
955
|
+
e.status AS status,
|
|
956
|
+
e.from AS from_time,
|
|
957
|
+
e.to AS to_time,
|
|
958
|
+
peer.uuid AS peer_uuid,
|
|
959
|
+
peer.kind AS peer_kind
|
|
960
|
+
"""
|
|
961
|
+
results = await db.execute_query(query=orphaned_rels_query)
|
|
962
|
+
if results:
|
|
963
|
+
rprint(f"[red]Found {len(results)} orphaned Relationships[/red]")
|
|
964
|
+
# Write detailed results to file
|
|
965
|
+
output_file = output_dir / "orphaned_relationships.csv"
|
|
966
|
+
with output_file.open(mode="w", newline="") as f:
|
|
967
|
+
writer = DictWriter(
|
|
968
|
+
f,
|
|
969
|
+
fieldnames=["r_name", "branch", "status", "from_time", "to_time", "peer_uuid", "peer_kind"],
|
|
970
|
+
)
|
|
971
|
+
writer.writeheader()
|
|
972
|
+
for result in results:
|
|
973
|
+
writer.writerow(dict(result))
|
|
974
|
+
rprint(f" Detailed results written to: {output_file}")
|
|
975
|
+
else:
|
|
976
|
+
rprint(f"{SUCCESS_BADGE} No orphaned relationships found")
|
|
977
|
+
|
|
919
978
|
rprint(f"\n{SUCCESS_BADGE} Database health checks completed")
|
|
920
979
|
rprint(f"Detailed results saved to: {output_dir.absolute()}")
|
|
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
|
|
@@ -37,10 +44,27 @@ class NodeDuplicateQuery(Query):
|
|
|
37
44
|
super().__init__(**kwargs)
|
|
38
45
|
|
|
39
46
|
def render_match(self) -> str:
|
|
40
|
-
|
|
47
|
+
labels_str = ":".join(self.previous_node.labels)
|
|
48
|
+
query = """
|
|
41
49
|
// Find all the active nodes
|
|
42
|
-
MATCH (node
|
|
43
|
-
|
|
50
|
+
MATCH (node:%(labels_str)s)
|
|
51
|
+
WITH DISTINCT node
|
|
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}
|
|
44
68
|
|
|
45
69
|
return query
|
|
46
70
|
|
|
@@ -109,6 +133,7 @@ class NodeDuplicateQuery(Query):
|
|
|
109
133
|
|
|
110
134
|
self.params["new_node"] = self.new_node.model_dump()
|
|
111
135
|
self.params["previous_node"] = self.previous_node.model_dump()
|
|
136
|
+
self.params["new_sorted_labels"] = sorted(self.new_node.labels + ["Node"])
|
|
112
137
|
|
|
113
138
|
self.params["current_time"] = self.at.to_string()
|
|
114
139
|
self.params["branch"] = self.branch.name
|
|
@@ -145,6 +170,7 @@ class NodeDuplicateQuery(Query):
|
|
|
145
170
|
WITH active_node, new_node
|
|
146
171
|
// Process Outbound Relationship
|
|
147
172
|
MATCH (active_node)-[]->(peer)
|
|
173
|
+
WITH DISTINCT active_node, new_node, peer
|
|
148
174
|
CALL (active_node, peer) {
|
|
149
175
|
MATCH (active_node)-[r]->(peer)
|
|
150
176
|
WHERE %(branch_filter)s
|
|
@@ -164,6 +190,7 @@ class NodeDuplicateQuery(Query):
|
|
|
164
190
|
WITH DISTINCT active_node, new_node
|
|
165
191
|
// Process Inbound Relationship
|
|
166
192
|
MATCH (active_node)<-[]-(peer)
|
|
193
|
+
WITH DISTINCT active_node, new_node, peer
|
|
167
194
|
CALL (active_node, peer) {
|
|
168
195
|
MATCH (active_node)<-[r]-(peer)
|
|
169
196
|
WHERE %(branch_filter)s
|
infrahub/core/query/diff.py
CHANGED
|
@@ -148,7 +148,14 @@ CALL (diff_path) {
|
|
|
148
148
|
OR type(base_r_node) <> "IS_RELATED" OR type(base_r_prop) <> "IS_RELATED"
|
|
149
149
|
)
|
|
150
150
|
WITH latest_base_path, base_r_root, base_r_node, base_r_prop
|
|
151
|
-
|
|
151
|
+
// status="active" ordering is for tie-breaking edges added and deleted at the same time, we want the active one
|
|
152
|
+
ORDER BY
|
|
153
|
+
base_r_prop.from DESC,
|
|
154
|
+
base_r_prop.status = "active" DESC,
|
|
155
|
+
base_r_node.from DESC,
|
|
156
|
+
base_r_node.status = "active" DESC,
|
|
157
|
+
base_r_root.from DESC,
|
|
158
|
+
base_r_root.status = "active" DESC
|
|
152
159
|
LIMIT 1
|
|
153
160
|
RETURN latest_base_path
|
|
154
161
|
}
|
|
@@ -172,8 +179,6 @@ CALL (penultimate_path) {
|
|
|
172
179
|
AND %(id_func)s(peer_r_node) = %(id_func)s(r_node)
|
|
173
180
|
AND [%(id_func)s(n), type(peer_r_node)] <> [%(id_func)s(peer), type(r_peer)]
|
|
174
181
|
AND r_peer.from < $to_time
|
|
175
|
-
// filter out paths where an earlier from time follows a later from time
|
|
176
|
-
AND peer_r_node.from <= r_peer.from
|
|
177
182
|
// filter out paths where a base branch edge follows a branch edge
|
|
178
183
|
AND (peer_r_node.branch = $base_branch_name OR r_peer.branch = $branch_name)
|
|
179
184
|
// filter out paths where an active edge follows a deleted edge
|
|
@@ -663,6 +668,15 @@ AND ALL(
|
|
|
663
668
|
AND ((r_pair[0]).status = "active" OR (r_pair[1]).status = "deleted")
|
|
664
669
|
// filter out paths where an earlier from time follows a later from time
|
|
665
670
|
AND (r_pair[0]).from <= (r_pair[1]).from
|
|
671
|
+
// if both are deleted, then the deeper edge must have been deleted first
|
|
672
|
+
AND ((r_pair[0]).status = "active" OR (r_pair[1]).status = "active" OR (r_pair[0]).from >= (r_pair[1].from))
|
|
673
|
+
AND (
|
|
674
|
+
(r_pair[0]).status = (r_pair[1]).status
|
|
675
|
+
OR (
|
|
676
|
+
(r_pair[0]).from <= (r_pair[1]).from
|
|
677
|
+
AND ((r_pair[0]).to IS NULL OR (r_pair[0]).to >= (r_pair[1]).from)
|
|
678
|
+
)
|
|
679
|
+
)
|
|
666
680
|
// require adjacent edge pairs to have overlapping times, but only if on the same branch
|
|
667
681
|
AND (
|
|
668
682
|
(r_pair[0]).branch <> (r_pair[1]).branch
|
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
|
|
@@ -643,12 +643,9 @@ class RelationshipGetPeerQuery(Query):
|
|
|
643
643
|
|
|
644
644
|
arrows = self.schema.get_query_arrows()
|
|
645
645
|
|
|
646
|
-
path_str = (
|
|
647
|
-
f"{arrows.left.start}[:IS_RELATED]{arrows.left.end}(rl){arrows.right.start}[:IS_RELATED]{arrows.right.end}"
|
|
648
|
-
)
|
|
646
|
+
path_str = f"{arrows.left.start}[r1:IS_RELATED]{arrows.left.end}(rl){arrows.right.start}[r2:IS_RELATED]{arrows.right.end}"
|
|
649
647
|
|
|
650
648
|
branch_level_str = "reduce(br_lvl = 0, r in relationships(path) | br_lvl + r.branch_level)"
|
|
651
|
-
froms_str = db.render_list_comprehension(items="relationships(path)", item_name="from")
|
|
652
649
|
query = """
|
|
653
650
|
MATCH (source_node:Node)%(arrow_left_start)s[:IS_RELATED]%(arrow_left_end)s(rl:Relationship { name: $rel_identifier })
|
|
654
651
|
WHERE source_node.uuid IN $source_ids
|
|
@@ -659,24 +656,24 @@ class RelationshipGetPeerQuery(Query):
|
|
|
659
656
|
$source_kind IN LABELS(source_node) AND
|
|
660
657
|
peer.uuid <> source_node.uuid AND
|
|
661
658
|
$peer_kind IN LABELS(peer) AND
|
|
662
|
-
all(r IN
|
|
663
|
-
WITH source_node, peer, rl,
|
|
664
|
-
RETURN peer as peer,
|
|
665
|
-
|
|
659
|
+
all(r IN [r1, r2] WHERE (%(branch_filter)s))
|
|
660
|
+
WITH source_node, peer, rl, r1, r2, %(branch_level)s AS branch_level
|
|
661
|
+
RETURN peer as peer, r1.status = "active" AND r2.status = "active" AS is_active, [r1, r2] AS rels
|
|
662
|
+
// status is required as a tiebreaker for migrated-kind nodes
|
|
663
|
+
ORDER BY branch_level DESC, r2.from DESC, r2.status ASC, r1.from DESC, r1.status ASC
|
|
666
664
|
LIMIT 1
|
|
667
665
|
}
|
|
668
|
-
WITH peer,
|
|
666
|
+
WITH peer, rl, is_active, rels, source_node
|
|
669
667
|
""" % {
|
|
670
668
|
"path": path_str,
|
|
671
669
|
"branch_filter": branch_filter,
|
|
672
670
|
"branch_level": branch_level_str,
|
|
673
|
-
"froms": froms_str,
|
|
674
671
|
"arrow_left_start": arrows.left.start,
|
|
675
672
|
"arrow_left_end": arrows.left.end,
|
|
676
673
|
}
|
|
677
674
|
|
|
678
675
|
self.add_to_query(query)
|
|
679
|
-
where_clause = [
|
|
676
|
+
where_clause = ["is_active = TRUE"]
|
|
680
677
|
clean_filters = extract_field_filters(field_name=self.schema.name, filters=self.filters)
|
|
681
678
|
|
|
682
679
|
if (clean_filters and "id" in clean_filters) or "ids" in clean_filters:
|
|
@@ -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)
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import copy
|
|
4
4
|
import hashlib
|
|
5
|
+
import keyword
|
|
5
6
|
from collections import defaultdict
|
|
6
7
|
from itertools import chain, combinations
|
|
7
8
|
from typing import Any
|
|
@@ -518,6 +519,7 @@ class SchemaBranch:
|
|
|
518
519
|
|
|
519
520
|
def process_validate(self) -> None:
|
|
520
521
|
self.validate_names()
|
|
522
|
+
self.validate_python_keywords()
|
|
521
523
|
self.validate_kinds()
|
|
522
524
|
self.validate_computed_attributes()
|
|
523
525
|
self.validate_attribute_parameters()
|
|
@@ -987,6 +989,26 @@ class SchemaBranch:
|
|
|
987
989
|
):
|
|
988
990
|
raise ValueError(f"{node.kind}: {rel.name} isn't allowed as a relationship name.")
|
|
989
991
|
|
|
992
|
+
def validate_python_keywords(self) -> None:
|
|
993
|
+
"""Validate that attribute and relationship names don't use Python keywords."""
|
|
994
|
+
for name in self.all_names:
|
|
995
|
+
node = self.get(name=name, duplicate=False)
|
|
996
|
+
|
|
997
|
+
# Check for Python keywords in attribute names
|
|
998
|
+
for attribute in node.attributes:
|
|
999
|
+
if keyword.iskeyword(attribute.name):
|
|
1000
|
+
raise ValueError(
|
|
1001
|
+
f"Python keyword '{attribute.name}' cannot be used as an attribute name on '{node.kind}'"
|
|
1002
|
+
)
|
|
1003
|
+
|
|
1004
|
+
# Check for Python keywords in relationship names
|
|
1005
|
+
if config.SETTINGS.main.schema_strict_mode:
|
|
1006
|
+
for relationship in node.relationships:
|
|
1007
|
+
if keyword.iskeyword(relationship.name):
|
|
1008
|
+
raise ValueError(
|
|
1009
|
+
f"Python keyword '{relationship.name}' cannot be used as a relationship name on '{node.kind}' when using strict mode"
|
|
1010
|
+
)
|
|
1011
|
+
|
|
990
1012
|
def _validate_common_parent(self, node: NodeSchema, rel: RelationshipSchema) -> None:
|
|
991
1013
|
if not rel.common_parent:
|
|
992
1014
|
return
|
infrahub/generators/tasks.py
CHANGED
|
@@ -214,7 +214,7 @@ async def request_generator_definition_run(
|
|
|
214
214
|
repository_kind=repository.typename,
|
|
215
215
|
branch_name=model.branch,
|
|
216
216
|
query=model.generator_definition.query_name,
|
|
217
|
-
variables=member.extract(params=model.generator_definition.parameters),
|
|
217
|
+
variables=await member.extract(params=model.generator_definition.parameters),
|
|
218
218
|
target_id=member.id,
|
|
219
219
|
target_name=member.display_label,
|
|
220
220
|
)
|
infrahub/git/integrator.py
CHANGED
|
@@ -1294,7 +1294,7 @@ class InfrahubRepositoryIntegrator(InfrahubRepositoryBase):
|
|
|
1294
1294
|
query: CoreGraphQLQuery,
|
|
1295
1295
|
) -> ArtifactGenerateResult:
|
|
1296
1296
|
"""It doesn't look like this is used anywhere today ... we should either remove it or refactor render_artifact below to use this."""
|
|
1297
|
-
variables = target.extract(params=definition.parameters.value)
|
|
1297
|
+
variables = await target.extract(params=definition.parameters.value)
|
|
1298
1298
|
response = await self.sdk.query_gql_query(
|
|
1299
1299
|
name=query.name.value,
|
|
1300
1300
|
variables=variables,
|
infrahub/git/tasks.py
CHANGED
|
@@ -365,7 +365,7 @@ async def generate_request_artifact_definition(
|
|
|
365
365
|
repository_kind=repository.get_kind(),
|
|
366
366
|
branch_name=model.branch,
|
|
367
367
|
query=query.name.value,
|
|
368
|
-
variables=member.extract(params=artifact_definition.parameters.value),
|
|
368
|
+
variables=await member.extract(params=artifact_definition.parameters.value),
|
|
369
369
|
target_id=member.id,
|
|
370
370
|
target_name=member.display_label,
|
|
371
371
|
target_kind=member.get_kind(),
|
|
@@ -583,7 +583,7 @@ async def trigger_repository_user_checks_definitions(
|
|
|
583
583
|
branch_name=model.branch_name,
|
|
584
584
|
check_definition_id=model.check_definition_id,
|
|
585
585
|
proposed_change=model.proposed_change,
|
|
586
|
-
variables=member.extract(params=definition.parameters.value),
|
|
586
|
+
variables=await member.extract(params=definition.parameters.value),
|
|
587
587
|
branch_diff=model.branch_diff,
|
|
588
588
|
timeout=definition.timeout.value,
|
|
589
589
|
)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import hashlib
|
|
3
4
|
from dataclasses import dataclass, field
|
|
4
5
|
from typing import TYPE_CHECKING, Any
|
|
5
6
|
|
|
@@ -152,7 +153,7 @@ class InfrahubMutationMixin:
|
|
|
152
153
|
"""
|
|
153
154
|
schema_branch = db.schema.get_schema_branch(name=branch.name)
|
|
154
155
|
lock_names = _get_kind_lock_names_on_object_mutation(
|
|
155
|
-
kind=cls._meta.active_schema.kind, branch=branch, schema_branch=schema_branch
|
|
156
|
+
kind=cls._meta.active_schema.kind, branch=branch, schema_branch=schema_branch, data=data
|
|
156
157
|
)
|
|
157
158
|
if lock_names:
|
|
158
159
|
async with InfrahubMultiLock(lock_registry=lock.registry, locks=lock_names):
|
|
@@ -216,7 +217,7 @@ class InfrahubMutationMixin:
|
|
|
216
217
|
|
|
217
218
|
schema_branch = db.schema.get_schema_branch(name=branch.name)
|
|
218
219
|
lock_names = _get_kind_lock_names_on_object_mutation(
|
|
219
|
-
kind=cls._meta.active_schema.kind, branch=branch, schema_branch=schema_branch
|
|
220
|
+
kind=cls._meta.active_schema.kind, branch=branch, schema_branch=schema_branch, data=data
|
|
220
221
|
)
|
|
221
222
|
|
|
222
223
|
if db.is_transaction:
|
|
@@ -266,7 +267,6 @@ class InfrahubMutationMixin:
|
|
|
266
267
|
obj = node or await NodeManager.find_object(
|
|
267
268
|
db=db, kind=cls._meta.active_schema.kind, id=data.get("id"), hfid=data.get("hfid"), branch=branch
|
|
268
269
|
)
|
|
269
|
-
|
|
270
270
|
obj, result = await cls._call_mutate_update(info=info, data=data, db=db, branch=branch, obj=obj)
|
|
271
271
|
|
|
272
272
|
return obj, result
|
|
@@ -517,15 +517,34 @@ def _should_kind_be_locked_on_any_branch(kind: str, schema_branch: SchemaBranch)
|
|
|
517
517
|
return False
|
|
518
518
|
|
|
519
519
|
|
|
520
|
-
def
|
|
520
|
+
def _hash(value: str) -> str:
|
|
521
|
+
# Do not use builtin `hash` for lock names as due to randomization results would differ between
|
|
522
|
+
# different processes.
|
|
523
|
+
return hashlib.sha256(value.encode()).hexdigest()
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
def _get_kind_lock_names_on_object_mutation(
|
|
527
|
+
kind: str, branch: Branch, schema_branch: SchemaBranch, data: InputObjectType
|
|
528
|
+
) -> list[str]:
|
|
521
529
|
"""
|
|
522
530
|
Return objects kind for which we want to avoid concurrent mutation (create/update). Except for some specific kinds,
|
|
523
531
|
concurrent mutations are only allowed on non-main branch as objects validations will be performed at least when merging in main branch.
|
|
524
532
|
"""
|
|
525
533
|
|
|
526
|
-
if not branch.is_default and not _should_kind_be_locked_on_any_branch(kind, schema_branch):
|
|
534
|
+
if not branch.is_default and not _should_kind_be_locked_on_any_branch(kind=kind, schema_branch=schema_branch):
|
|
527
535
|
return []
|
|
528
536
|
|
|
537
|
+
if kind == InfrahubKind.GRAPHQLQUERYGROUP:
|
|
538
|
+
# Lock on name as well to improve performances
|
|
539
|
+
try:
|
|
540
|
+
name = data.name.value
|
|
541
|
+
return [build_object_lock_name(kind + "." + _hash(name))]
|
|
542
|
+
except AttributeError:
|
|
543
|
+
# We might reach here if we are updating a CoreGraphQLQueryGroup without updating the name,
|
|
544
|
+
# in which case we would not need to lock. This is not supposed to happen as current `update`
|
|
545
|
+
# logic first fetches the node with its name.
|
|
546
|
+
return []
|
|
547
|
+
|
|
529
548
|
lock_kinds = _get_kinds_to_lock_on_object_mutation(kind, schema_branch)
|
|
530
549
|
lock_names = [build_object_lock_name(kind) for kind in lock_kinds]
|
|
531
550
|
return lock_names
|
|
@@ -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)
|
|
@@ -652,7 +652,7 @@ async def validate_artifacts_generation(
|
|
|
652
652
|
repository_kind=repository.kind,
|
|
653
653
|
branch_name=model.source_branch,
|
|
654
654
|
query=model.artifact_definition.query_name,
|
|
655
|
-
variables=member.extract(params=artifact_definition.parameters.value),
|
|
655
|
+
variables=await member.extract(params=artifact_definition.parameters.value),
|
|
656
656
|
target_id=member.id,
|
|
657
657
|
target_kind=member.get_kind(),
|
|
658
658
|
target_name=member.display_label,
|
|
@@ -913,7 +913,7 @@ async def request_generator_definition_check(
|
|
|
913
913
|
repository_kind=repository.kind,
|
|
914
914
|
branch_name=model.source_branch,
|
|
915
915
|
query=model.generator_definition.query_name,
|
|
916
|
-
variables=member.extract(params=model.generator_definition.parameters),
|
|
916
|
+
variables=await member.extract(params=model.generator_definition.parameters),
|
|
917
917
|
target_id=member.id,
|
|
918
918
|
target_name=member.display_label,
|
|
919
919
|
validator_id=validator.id,
|
infrahub_sdk/node/node.py
CHANGED
|
@@ -8,7 +8,7 @@ from ..constants import InfrahubClientMode
|
|
|
8
8
|
from ..exceptions import FeatureNotSupportedError, NodeNotFoundError, ResourceNotDefinedError, SchemaNotFoundError
|
|
9
9
|
from ..graphql import Mutation, Query
|
|
10
10
|
from ..schema import GenericSchemaAPI, RelationshipCardinality, RelationshipKind
|
|
11
|
-
from ..utils import compare_lists, generate_short_id
|
|
11
|
+
from ..utils import compare_lists, generate_short_id
|
|
12
12
|
from .attribute import Attribute
|
|
13
13
|
from .constants import (
|
|
14
14
|
ARTIFACT_DEFINITION_GENERATE_FEATURE_NOT_SUPPORTED_MESSAGE,
|
|
@@ -418,14 +418,6 @@ class InfrahubNodeBase:
|
|
|
418
418
|
|
|
419
419
|
return data
|
|
420
420
|
|
|
421
|
-
def extract(self, params: dict[str, str]) -> dict[str, Any]:
|
|
422
|
-
"""Extract some datapoints defined in a flat notation."""
|
|
423
|
-
result: dict[str, Any] = {}
|
|
424
|
-
for key, value in params.items():
|
|
425
|
-
result[key] = get_flat_value(self, key=value)
|
|
426
|
-
|
|
427
|
-
return result
|
|
428
|
-
|
|
429
421
|
def __hash__(self) -> int:
|
|
430
422
|
return hash(self.id)
|
|
431
423
|
|
|
@@ -1036,6 +1028,46 @@ class InfrahubNode(InfrahubNodeBase):
|
|
|
1036
1028
|
|
|
1037
1029
|
raise ResourceNotDefinedError(message=f"The node doesn't have a cardinality=one relationship for {name}")
|
|
1038
1030
|
|
|
1031
|
+
async def get_flat_value(self, key: str, separator: str = "__") -> Any:
|
|
1032
|
+
"""Query recursively a value defined in a flat notation (string), on a hierarchy of objects
|
|
1033
|
+
|
|
1034
|
+
Examples:
|
|
1035
|
+
name__value
|
|
1036
|
+
module.object.value
|
|
1037
|
+
"""
|
|
1038
|
+
if separator not in key:
|
|
1039
|
+
return getattr(self, key)
|
|
1040
|
+
|
|
1041
|
+
first, remaining = key.split(separator, maxsplit=1)
|
|
1042
|
+
|
|
1043
|
+
if first in self._schema.attribute_names:
|
|
1044
|
+
attr = getattr(self, first)
|
|
1045
|
+
for part in remaining.split(separator):
|
|
1046
|
+
attr = getattr(attr, part)
|
|
1047
|
+
return attr
|
|
1048
|
+
|
|
1049
|
+
try:
|
|
1050
|
+
rel = self._schema.get_relationship(name=first)
|
|
1051
|
+
except ValueError as exc:
|
|
1052
|
+
raise ValueError(f"No attribute or relationship named '{first}' for '{self._schema.kind}'") from exc
|
|
1053
|
+
|
|
1054
|
+
if rel.cardinality != RelationshipCardinality.ONE:
|
|
1055
|
+
raise ValueError(
|
|
1056
|
+
f"Can only look up flat value for relationships of cardinality {RelationshipCardinality.ONE.value}"
|
|
1057
|
+
)
|
|
1058
|
+
|
|
1059
|
+
related_node: RelatedNode = getattr(self, first)
|
|
1060
|
+
await related_node.fetch()
|
|
1061
|
+
return await related_node.peer.get_flat_value(key=remaining, separator=separator)
|
|
1062
|
+
|
|
1063
|
+
async def extract(self, params: dict[str, str]) -> dict[str, Any]:
|
|
1064
|
+
"""Extract some datapoints defined in a flat notation."""
|
|
1065
|
+
result: dict[str, Any] = {}
|
|
1066
|
+
for key, value in params.items():
|
|
1067
|
+
result[key] = await self.get_flat_value(key=value)
|
|
1068
|
+
|
|
1069
|
+
return result
|
|
1070
|
+
|
|
1039
1071
|
def __dir__(self) -> Iterable[str]:
|
|
1040
1072
|
base = list(super().__dir__())
|
|
1041
1073
|
return sorted(
|
|
@@ -1622,6 +1654,46 @@ class InfrahubNodeSync(InfrahubNodeBase):
|
|
|
1622
1654
|
|
|
1623
1655
|
raise ResourceNotDefinedError(message=f"The node doesn't have a cardinality=one relationship for {name}")
|
|
1624
1656
|
|
|
1657
|
+
def get_flat_value(self, key: str, separator: str = "__") -> Any:
|
|
1658
|
+
"""Query recursively a value defined in a flat notation (string), on a hierarchy of objects
|
|
1659
|
+
|
|
1660
|
+
Examples:
|
|
1661
|
+
name__value
|
|
1662
|
+
module.object.value
|
|
1663
|
+
"""
|
|
1664
|
+
if separator not in key:
|
|
1665
|
+
return getattr(self, key)
|
|
1666
|
+
|
|
1667
|
+
first, remaining = key.split(separator, maxsplit=1)
|
|
1668
|
+
|
|
1669
|
+
if first in self._schema.attribute_names:
|
|
1670
|
+
attr = getattr(self, first)
|
|
1671
|
+
for part in remaining.split(separator):
|
|
1672
|
+
attr = getattr(attr, part)
|
|
1673
|
+
return attr
|
|
1674
|
+
|
|
1675
|
+
try:
|
|
1676
|
+
rel = self._schema.get_relationship(name=first)
|
|
1677
|
+
except ValueError as exc:
|
|
1678
|
+
raise ValueError(f"No attribute or relationship named '{first}' for '{self._schema.kind}'") from exc
|
|
1679
|
+
|
|
1680
|
+
if rel.cardinality != RelationshipCardinality.ONE:
|
|
1681
|
+
raise ValueError(
|
|
1682
|
+
f"Can only look up flat value for relationships of cardinality {RelationshipCardinality.ONE.value}"
|
|
1683
|
+
)
|
|
1684
|
+
|
|
1685
|
+
related_node: RelatedNodeSync = getattr(self, first)
|
|
1686
|
+
related_node.fetch()
|
|
1687
|
+
return related_node.peer.get_flat_value(key=remaining, separator=separator)
|
|
1688
|
+
|
|
1689
|
+
def extract(self, params: dict[str, str]) -> dict[str, Any]:
|
|
1690
|
+
"""Extract some datapoints defined in a flat notation."""
|
|
1691
|
+
result: dict[str, Any] = {}
|
|
1692
|
+
for key, value in params.items():
|
|
1693
|
+
result[key] = self.get_flat_value(key=value)
|
|
1694
|
+
|
|
1695
|
+
return result
|
|
1696
|
+
|
|
1625
1697
|
def __dir__(self) -> Iterable[str]:
|
|
1626
1698
|
base = list(super().__dir__())
|
|
1627
1699
|
return sorted(
|
infrahub_sdk/protocols_base.py
CHANGED
infrahub_sdk/utils.py
CHANGED
|
@@ -190,23 +190,6 @@ def str_to_bool(value: str) -> bool:
|
|
|
190
190
|
raise ValueError(f"{value} can not be converted into a boolean") from exc
|
|
191
191
|
|
|
192
192
|
|
|
193
|
-
def get_flat_value(obj: Any, key: str, separator: str = "__") -> Any:
|
|
194
|
-
"""Query recursively an value defined in a flat notation (string), on a hierarchy of objects
|
|
195
|
-
|
|
196
|
-
Examples:
|
|
197
|
-
name__value
|
|
198
|
-
module.object.value
|
|
199
|
-
"""
|
|
200
|
-
if separator not in key:
|
|
201
|
-
return getattr(obj, key)
|
|
202
|
-
|
|
203
|
-
first_part, remaining_part = key.split(separator, maxsplit=1)
|
|
204
|
-
sub_obj = getattr(obj, first_part)
|
|
205
|
-
if not sub_obj:
|
|
206
|
-
return None
|
|
207
|
-
return get_flat_value(obj=sub_obj, key=remaining_part, separator=separator)
|
|
208
|
-
|
|
209
|
-
|
|
210
193
|
def generate_request_filename(request: httpx.Request) -> str:
|
|
211
194
|
"""Return a filename for a request sent to the Infrahub API
|
|
212
195
|
|
|
@@ -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
|
|
@@ -230,10 +232,10 @@ infrahub/core/query/__init__.py,sha256=2qIMaODLwJ6pK6BUd5vODTlA15Aecf5I8_-J44UlC
|
|
|
230
232
|
infrahub/core/query/attribute.py,sha256=DzwbElgTaZs6-nBYGmnDpBr9n0lmUPK3p7eyI30Snh8,11783
|
|
231
233
|
infrahub/core/query/branch.py,sha256=B3QEqpwbJrs_8juWQPaHrdwLNJR-1tSkvMuixCFFdt4,3680
|
|
232
234
|
infrahub/core/query/delete.py,sha256=7tPP1qtNV6QGYtmgE1RKsuQ9oxENnMTVkttLvJ2PiKg,1927
|
|
233
|
-
infrahub/core/query/diff.py,sha256=
|
|
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=
|
|
236
|
-
infrahub/core/query/relationship.py,sha256=
|
|
237
|
+
infrahub/core/query/node.py,sha256=HXOeT14vCsgpKHp76k-V_VMw7uvVJzFuWY2qBCFZGZk,68317
|
|
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
|
|
239
241
|
infrahub/core/query/subquery.py,sha256=UE071w3wccdU_dtKLV-7mdeQ53DKXjPmNxDV0zd5Tpg,7588
|
|
@@ -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
|
|
@@ -294,7 +296,7 @@ infrahub/core/schema/manager.py,sha256=Vz6EJo8pDq9u5apRU7wgFMtcsCHDEt9BHwS0VRlct
|
|
|
294
296
|
infrahub/core/schema/node_schema.py,sha256=ld_Wrqf-RsoEUVz_lKE0tcSf5n_oYZYtRI0lTqtd63o,6150
|
|
295
297
|
infrahub/core/schema/profile_schema.py,sha256=cOPSOt5KLgQ0nbqrAN_o33hY_pUtrKmiwSbY_YpVolI,1092
|
|
296
298
|
infrahub/core/schema/relationship_schema.py,sha256=R-1iC1d70bBW0vWhgJhDB0_J3tRpOqcJmmLzh39NuYs,8501
|
|
297
|
-
infrahub/core/schema/schema_branch.py,sha256=
|
|
299
|
+
infrahub/core/schema/schema_branch.py,sha256=Yms2QdNZxqWjtK2sEAgxfRMQmeLEXA16VyqyHErwXgE,106138
|
|
298
300
|
infrahub/core/schema/schema_branch_computed.py,sha256=14UUsQJDLMHkYhg7QMqeLiTF3PO8c8rGa90ul3F2ZZo,10629
|
|
299
301
|
infrahub/core/schema/template_schema.py,sha256=O-PBS9IRM4JX6PxeoyZKwqZ0u0SdQ2zxWMc01PJ2_EA,1084
|
|
300
302
|
infrahub/core/task/__init__.py,sha256=Ied1NvKGJUDmff27z_-yWW8ArenHxGvSvQTaQyx1iHs,128
|
|
@@ -423,15 +425,15 @@ infrahub/events/validator_action.py,sha256=nQJH-RWcgr3-tzmIldvPmu5O7dUAmv1qQnuxx
|
|
|
423
425
|
infrahub/exceptions.py,sha256=cbM-f_2U-5ZFVZ_MaaXgs64k4M7uJ7fqDU2iCRoWlYY,11861
|
|
424
426
|
infrahub/generators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
425
427
|
infrahub/generators/models.py,sha256=9qhSfsoG-uYux35HClAxSq7TRfkosqN3i_eQkeTokLs,1916
|
|
426
|
-
infrahub/generators/tasks.py,sha256=
|
|
428
|
+
infrahub/generators/tasks.py,sha256=9K7r9yt2s9kSUfaENeTBEU8Sf-omVuOxLyz_N1DjzxE,9475
|
|
427
429
|
infrahub/git/__init__.py,sha256=KeQ9U8UI5jDj6KB6j00Oal7MZmtOD9vKqVgiezG_EQA,281
|
|
428
430
|
infrahub/git/base.py,sha256=pMkavzZn34j50exrOYBzvlRrW5aGoP5XdWPCg6lMmx4,38802
|
|
429
431
|
infrahub/git/constants.py,sha256=XpzcAkXbsgXZgrXey74id1sXV8Q6EHb_4FNw7BndxyY,106
|
|
430
432
|
infrahub/git/directory.py,sha256=fozxLXXJPweHG95yQwQkR5yy3sfTdmHiczCAJnsUX54,861
|
|
431
|
-
infrahub/git/integrator.py,sha256=
|
|
433
|
+
infrahub/git/integrator.py,sha256=Oh181VNRn-YkllULu8J_J6Vh8VD5BfGQYCriOgZdd_w,62403
|
|
432
434
|
infrahub/git/models.py,sha256=ozk9alxQ8Ops1lw1g8iR3O7INuw1VPsEUr5Wceh9HQY,12152
|
|
433
435
|
infrahub/git/repository.py,sha256=mjYeH3pKWRM3UuvcwRCWeE793FuPbSdY8VF1IYK-BxA,11603
|
|
434
|
-
infrahub/git/tasks.py,sha256=
|
|
436
|
+
infrahub/git/tasks.py,sha256=7NPJxmzOcDworKT-veRcSoL3nM_Lj27VGgdQp3TsYEg,37345
|
|
435
437
|
infrahub/git/utils.py,sha256=xhWxlu_FbMqbrwanpPkex4hKRS_d2AFzlxI_6kVQllw,1741
|
|
436
438
|
infrahub/git/worktree.py,sha256=8IYJWOBytKUWwhMmMVehR4ceeO9e13nV-mvn3iVEgZY,1727
|
|
437
439
|
infrahub/git_credential/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -477,7 +479,7 @@ infrahub/graphql/mutations/diff_conflict.py,sha256=JngQfyKXCVlmtlqQ_VyabmrOEDOEK
|
|
|
477
479
|
infrahub/graphql/mutations/generator.py,sha256=Ulw4whZm8Gc8lJjwfUFoFSsR0cOUliFKl87Oca4B9O0,3579
|
|
478
480
|
infrahub/graphql/mutations/graphql_query.py,sha256=mp_O2byChneCihUrEAFEiIAgJ1gW9WrgtwPetUQmkJw,3562
|
|
479
481
|
infrahub/graphql/mutations/ipam.py,sha256=wIN8OcTNCHVy32YgatWZi2Of-snFYBd4wlxOAJvE-AY,15961
|
|
480
|
-
infrahub/graphql/mutations/main.py,sha256=
|
|
482
|
+
infrahub/graphql/mutations/main.py,sha256=eqUSbjcnDvigmIeLrPA0nyiVHCHdzqDocfbSx-q63lA,21626
|
|
481
483
|
infrahub/graphql/mutations/menu.py,sha256=u2UbOA-TFDRcZRGFkgYTmpGxN2IAUgOvJXd7SnsufyI,3708
|
|
482
484
|
infrahub/graphql/mutations/models.py,sha256=ilkSLr8OxVO9v3Ra_uDyUTJT9qPOmdPMqQbuWIydJMo,264
|
|
483
485
|
infrahub/graphql/mutations/node_getter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -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
|
|
@@ -604,7 +606,7 @@ infrahub/proposed_change/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
|
|
|
604
606
|
infrahub/proposed_change/branch_diff.py,sha256=Oerw3cHo51XPKwBsAmpO0T470Fg9mkpWViHVY51hToY,2303
|
|
605
607
|
infrahub/proposed_change/constants.py,sha256=w8fPxKWJM1DzeClRd7Vr53hxkzl2Bq-rnXWfE2y3Bz0,1296
|
|
606
608
|
infrahub/proposed_change/models.py,sha256=ivWJmEAihprKmwgaBGDJ4Koq4ETciE5GfDp86KHDnns,5892
|
|
607
|
-
infrahub/proposed_change/tasks.py,sha256=
|
|
609
|
+
infrahub/proposed_change/tasks.py,sha256=WJuXXJ4zR3MNSpr9ko2hF8HvLU92g3cYWwxSS9h0P6o,61877
|
|
608
610
|
infrahub/pytest_plugin.py,sha256=u3t0WgLMo9XmuQYeb28mccQ3xbnyv2Fv173YWl1zBiM,6678
|
|
609
611
|
infrahub/schema/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
610
612
|
infrahub/schema/tasks.py,sha256=vEaWWksqrfwwCAIgPxscVU81W9PIxODX552Rtkte1Cs,947
|
|
@@ -725,7 +727,7 @@ infrahub_sdk/jinja2.py,sha256=lTfV9E_P5gApaX6RW9M8U8oixQi-0H3U8wcs8fdGVaU,1150
|
|
|
725
727
|
infrahub_sdk/node/__init__.py,sha256=clAUZ9lNVPFguelR5Sg9PzklAZruTKEm2xk-BaO68l8,1262
|
|
726
728
|
infrahub_sdk/node/attribute.py,sha256=oEY1qxip8ETEx9Q33NhSQo013zmzrmpVIFzSkEMUY8M,4547
|
|
727
729
|
infrahub_sdk/node/constants.py,sha256=TJO4uxvv7sc3FjoLdQdV7Ccymqz8AqxDenARst8awb4,775
|
|
728
|
-
infrahub_sdk/node/node.py,sha256=
|
|
730
|
+
infrahub_sdk/node/node.py,sha256=0EuD7N3KyjCK6RkZ48EY0t5LZ68wHrH_wa8PG0tUtZw,73473
|
|
729
731
|
infrahub_sdk/node/parsers.py,sha256=sLDdT6neoYSZIjOCmq8Bgd0LK8FFoasjvJLuSz0whSU,543
|
|
730
732
|
infrahub_sdk/node/property.py,sha256=8Mjkc8bp3kLlHyllwxDJlpJTuOA1ciMgY8mtH3dFVLM,728
|
|
731
733
|
infrahub_sdk/node/related_node.py,sha256=fPMnZ83OZnnbimaPC14MdE3lR-kumAA6hbOhRlo1gms,10093
|
|
@@ -734,9 +736,9 @@ infrahub_sdk/object_store.py,sha256=d-EDnxPpw_7BsbjbGbH50rjt-1-Ojj2zNrhFansP5hA,
|
|
|
734
736
|
infrahub_sdk/operation.py,sha256=hsbZSjLbLsqvjZg5R5x_bOxxlteXJAk0fQy3mLrZhn4,2730
|
|
735
737
|
infrahub_sdk/playback.py,sha256=ubkY1LiW_wFwm4auerdQ0zFJcFJZ1SYQT6-d4bxzaLg,1906
|
|
736
738
|
infrahub_sdk/protocols.py,sha256=lmjBSrmpc-7j9H7mn0DaLpJa8eD2gQk2oy0u2HvGLOk,24209
|
|
737
|
-
infrahub_sdk/protocols_base.py,sha256=
|
|
739
|
+
infrahub_sdk/protocols_base.py,sha256=rw5gP9IEuV2e-DeFHMUoL43nVfxEeGmQKjoE3YZCV78,5616
|
|
738
740
|
infrahub_sdk/protocols_generator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
739
|
-
infrahub_sdk/protocols_generator/constants.py,sha256=
|
|
741
|
+
infrahub_sdk/protocols_generator/constants.py,sha256=8y5L7aqxhez6_10Cl2zUfNlHJCCosIVnxrOPKrwNVEw,820
|
|
740
742
|
infrahub_sdk/protocols_generator/generator.py,sha256=hhBxxMw0Pgbih6GiC81xjb9R9TBT8I4y-O2cIdcbChs,5264
|
|
741
743
|
infrahub_sdk/protocols_generator/template.j2,sha256=cm2wsKXHXOiW7N_byxnE2vLnn8gHQ99rs56qVquTTDw,3722
|
|
742
744
|
infrahub_sdk/pytest_plugin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -791,7 +793,7 @@ infrahub_sdk/transfer/importer/json.py,sha256=-Tlmg22TiBrEqXOSLMnUzlCFOZ2M0Q8lWy
|
|
|
791
793
|
infrahub_sdk/transfer/schema_sorter.py,sha256=ZoBjJGFT-6jQoKOLaoOPMAWzs7vGOeo7x6zOOP4LNv0,1244
|
|
792
794
|
infrahub_sdk/transforms.py,sha256=RLiB_CkM-JQSfyifChxxQVl2FrHKOGEf_YynSMKeFZU,2340
|
|
793
795
|
infrahub_sdk/types.py,sha256=UeZ1rDp4eyH12ApTcUD9a1OOtCp3IL1YZUeeZ06qF-I,1726
|
|
794
|
-
infrahub_sdk/utils.py,sha256=
|
|
796
|
+
infrahub_sdk/utils.py,sha256=dkNqnMEzyPORQ6mw90mH3Qge1fkIclcuQ5kmxND1JAg,11748
|
|
795
797
|
infrahub_sdk/uuidt.py,sha256=Tz-4nHkJwbi39UT3gaIe2wJeZNAoBqf6tm3sw7LZbXc,2155
|
|
796
798
|
infrahub_sdk/yaml.py,sha256=PRsS7BEM-Xn5wRLAAG-YLTGRBEJy5Dnyim2YskFfe8I,5539
|
|
797
799
|
infrahub_testcontainers/__init__.py,sha256=oPpmesGgYBSdKTg1L37FGwYBeao1EHury5SJGul-CT8,216
|
|
@@ -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
|