infrahub-server 1.2.10__py3-none-any.whl → 1.2.12__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.
Files changed (53) hide show
  1. infrahub/config.py +9 -0
  2. infrahub/core/constants/database.py +1 -0
  3. infrahub/core/constants/infrahubkind.py +1 -0
  4. infrahub/core/constraint/node/runner.py +1 -1
  5. infrahub/core/diff/query/save.py +75 -45
  6. infrahub/core/diff/query_parser.py +5 -1
  7. infrahub/core/diff/tasks.py +3 -3
  8. infrahub/core/graph/__init__.py +1 -1
  9. infrahub/core/migrations/graph/__init__.py +6 -0
  10. infrahub/core/migrations/graph/m029_duplicates_cleanup.py +680 -0
  11. infrahub/core/migrations/graph/m030_illegal_edges.py +83 -0
  12. infrahub/core/migrations/query/attribute_add.py +13 -9
  13. infrahub/core/node/resource_manager/ip_address_pool.py +6 -2
  14. infrahub/core/node/resource_manager/ip_prefix_pool.py +6 -2
  15. infrahub/core/protocols.py +4 -0
  16. infrahub/core/query/diff.py +7 -0
  17. infrahub/core/schema/definitions/core/__init__.py +8 -1
  18. infrahub/core/schema/definitions/core/resource_pool.py +20 -0
  19. infrahub/core/schema/schema_branch.py +5 -3
  20. infrahub/core/validators/tasks.py +1 -1
  21. infrahub/database/__init__.py +5 -4
  22. infrahub/database/validation.py +101 -0
  23. infrahub/graphql/app.py +1 -1
  24. infrahub/graphql/loaders/node.py +1 -1
  25. infrahub/graphql/loaders/peers.py +1 -1
  26. infrahub/graphql/mutations/main.py +1 -1
  27. infrahub/graphql/mutations/proposed_change.py +1 -1
  28. infrahub/graphql/queries/relationship.py +1 -1
  29. infrahub/graphql/queries/task.py +10 -0
  30. infrahub/graphql/resolvers/many_relationship.py +4 -4
  31. infrahub/graphql/resolvers/resolver.py +4 -4
  32. infrahub/graphql/resolvers/single_relationship.py +2 -2
  33. infrahub/graphql/subscription/graphql_query.py +2 -2
  34. infrahub/graphql/types/branch.py +1 -1
  35. infrahub/graphql/types/task_log.py +3 -2
  36. infrahub/message_bus/operations/refresh/registry.py +4 -4
  37. infrahub/message_bus/operations/requests/proposed_change.py +4 -4
  38. infrahub/patch/queries/delete_duplicated_edges.py +40 -29
  39. infrahub/task_manager/task.py +44 -4
  40. infrahub/telemetry/database.py +1 -1
  41. infrahub/telemetry/tasks.py +1 -1
  42. infrahub/webhook/tasks.py +2 -1
  43. {infrahub_server-1.2.10.dist-info → infrahub_server-1.2.12.dist-info}/METADATA +3 -3
  44. {infrahub_server-1.2.10.dist-info → infrahub_server-1.2.12.dist-info}/RECORD +52 -49
  45. {infrahub_server-1.2.10.dist-info → infrahub_server-1.2.12.dist-info}/WHEEL +1 -1
  46. infrahub_testcontainers/container.py +239 -64
  47. infrahub_testcontainers/docker-compose-cluster.test.yml +321 -0
  48. infrahub_testcontainers/docker-compose.test.yml +1 -0
  49. infrahub_testcontainers/helpers.py +15 -1
  50. infrahub_testcontainers/plugin.py +9 -0
  51. infrahub/patch/queries/consolidate_duplicated_nodes.py +0 -109
  52. {infrahub_server-1.2.10.dist-info → infrahub_server-1.2.12.dist-info}/LICENSE.txt +0 -0
  53. {infrahub_server-1.2.10.dist-info → infrahub_server-1.2.12.dist-info}/entry_points.txt +0 -0
infrahub/config.py CHANGED
@@ -269,6 +269,7 @@ class DatabaseSettings(BaseSettings):
269
269
  address: str = "localhost"
270
270
  port: int = 7687
271
271
  database: str | None = Field(default=None, pattern=VALID_DATABASE_NAME_REGEX, description="Name of the database")
272
+ policy: str | None = Field(default=None, description="Routing policy for database connections")
272
273
  tls_enabled: bool = Field(default=False, description="Indicates if TLS is enabled for the connection")
273
274
  tls_insecure: bool = Field(default=False, description="Indicates if TLS certificates are verified")
274
275
  tls_ca_file: str | None = Field(default=None, description="File path to CA cert or bundle in PEM format")
@@ -293,6 +294,14 @@ class DatabaseSettings(BaseSettings):
293
294
  default=0.01, ge=0, description="Delay to add when max_concurrent_queries is reached."
294
295
  )
295
296
 
297
+ @property
298
+ def database_uri(self) -> str:
299
+ """Constructs the database URI based on the configuration settings."""
300
+ base_uri = f"{self.protocol}://{self.address}:{self.port}"
301
+ if self.policy is not None:
302
+ return f"{base_uri}?policy={self.policy}"
303
+ return base_uri
304
+
296
305
  @property
297
306
  def database_name(self) -> str:
298
307
  return self.database or self.db_type.value
@@ -12,3 +12,4 @@ class DatabaseEdgeType(Enum):
12
12
  IS_PROTECTED = "IS_PROTECTED"
13
13
  HAS_OWNER = "HAS_OWNER"
14
14
  HAS_SOURCE = "HAS_SOURCE"
15
+ IS_RESERVED = "IS_RESERVED"
@@ -70,3 +70,4 @@ TRANSFORMPYTHON = "CoreTransformPython"
70
70
  USERVALIDATOR = "CoreUserValidator"
71
71
  VALIDATOR = "CoreValidator"
72
72
  WEBHOOK = "CoreWebhook"
73
+ WEIGHTED_POOL_RESOURCE = "CoreWeightedPoolResource"
@@ -26,7 +26,7 @@ class NodeConstraintRunner:
26
26
  async def check(
27
27
  self, node: Node, field_filters: list[str] | None = None, skip_uniqueness_check: bool = False
28
28
  ) -> None:
29
- async with self.db.start_session() as db:
29
+ async with self.db.start_session(read_only=False) as db:
30
30
  await node.resolve_relationships(db=db)
31
31
 
32
32
  if not skip_uniqueness_check:
@@ -80,6 +80,7 @@ class EnrichedNodeBatchCreateQuery(Query):
80
80
 
81
81
  async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
82
82
  self.params = self._build_node_batch_params()
83
+
83
84
  query = """
84
85
  UNWIND $node_details_list AS node_details
85
86
  WITH
@@ -136,65 +137,82 @@ CALL {
136
137
  OPTIONAL MATCH (diff_node)-[:DIFF_HAS_CONFLICT]->(node_conflict:DiffConflict)
137
138
  SET node_conflict = node_conflict_params
138
139
  }
140
+ // -------------------------
141
+ // resetting the UNWIND and starting over here reduces memory usage
142
+ // -------------------------
143
+ WITH root_uuid LIMIT 1
144
+ UNWIND $node_details_list AS node_details
145
+ WITH
146
+ node_details.root_uuid AS root_uuid,
147
+ node_details.node_map AS node_map,
148
+ toString(node_details.node_map.node_properties.uuid) AS node_uuid,
149
+ node_details.node_map.node_properties.db_id AS node_db_id
150
+ MATCH (:DiffRoot {uuid: root_uuid})-[:DIFF_HAS_NODE]->(diff_node:DiffNode {uuid: node_uuid, db_id: node_db_id})
151
+ WITH diff_node, node_map, %(attr_name_list_comp)s AS attr_names
152
+ OPTIONAL MATCH (diff_node)-[:DIFF_HAS_ATTRIBUTE]->(attr_to_delete:DiffAttribute)
153
+ WHERE NOT (attr_to_delete.name IN attr_names)
154
+ OPTIONAL MATCH (attr_to_delete)-[*..6]->(next_to_delete)
155
+ DETACH DELETE next_to_delete
156
+ DETACH DELETE attr_to_delete
157
+ // -------------------------
158
+ // add attributes for this node
159
+ // -------------------------
160
+ WITH DISTINCT diff_node, node_map
139
161
  CALL {
140
- // -------------------------
141
- // remove stale attributes for this node
142
- // -------------------------
143
162
  WITH diff_node, node_map
144
- CALL {
145
- WITH diff_node, node_map
146
- WITH diff_node, %(attr_name_list_comp)s AS attr_names
147
- OPTIONAL MATCH (diff_node)-[:DIFF_HAS_ATTRIBUTE]->(attr_to_delete:DiffAttribute)
148
- WHERE NOT (attr_to_delete.name IN attr_names)
149
- OPTIONAL MATCH (attr_to_delete)-[*..6]->(next_to_delete)
150
- DETACH DELETE next_to_delete
151
- DETACH DELETE attr_to_delete
152
- }
153
- // -------------------------
154
- // add attributes for this node
155
- // -------------------------
156
163
  UNWIND node_map.attributes AS node_attribute
157
164
  MERGE (diff_node)-[:DIFF_HAS_ATTRIBUTE]->(diff_attribute:DiffAttribute {name: node_attribute.node_properties.name})
158
165
  SET diff_attribute = node_attribute.node_properties
159
166
  // -------------------------
160
- // add properties for this attribute
167
+ // remove stale properties for this attribute
161
168
  // -------------------------
162
- WITH diff_attribute, node_attribute
169
+ WITH diff_attribute, node_attribute, %(attr_props_list_comp)s AS prop_types
170
+ OPTIONAL MATCH (diff_attribute)-[:DIFF_HAS_PROPERTY]->(prop_to_delete:DiffProperty)
171
+ WHERE NOT (prop_to_delete.property_type IN prop_types)
172
+ OPTIONAL MATCH (prop_to_delete)-[*..4]->(next_to_delete)
173
+ DETACH DELETE next_to_delete
174
+ DETACH DELETE prop_to_delete
163
175
  // -------------------------
164
- // remove stale properties for this attribute
176
+ // set attribute property values
165
177
  // -------------------------
166
- CALL {
167
- WITH diff_attribute, node_attribute
168
- WITH diff_attribute, %(attr_props_list_comp)s AS prop_types
169
- OPTIONAL MATCH (diff_attribute)-[:DIFF_HAS_PROPERTY]->(prop_to_delete:DiffProperty)
170
- WHERE NOT (prop_to_delete.property_type IN prop_types)
171
- OPTIONAL MATCH (prop_to_delete)-[*..4]->(next_to_delete)
172
- DETACH DELETE next_to_delete
173
- DETACH DELETE prop_to_delete
174
- }
178
+ WITH DISTINCT diff_attribute, node_attribute
175
179
  UNWIND node_attribute.properties AS attr_property
176
180
  MERGE (diff_attribute)-[:DIFF_HAS_PROPERTY]->(diff_attr_prop:DiffProperty {property_type: attr_property.node_properties.property_type})
177
181
  SET diff_attr_prop = attr_property.node_properties
178
- // -------------------------
179
- // add/remove conflict for this property
180
- // -------------------------
181
182
  WITH diff_attr_prop, attr_property
182
183
  OPTIONAL MATCH (diff_attr_prop)-[:DIFF_HAS_CONFLICT]->(current_attr_prop_conflict:DiffConflict)
183
184
  WITH diff_attr_prop, attr_property, current_attr_prop_conflict, (attr_property.conflict_params IS NOT NULL) AS has_prop_conflict
184
- FOREACH (i in CASE WHEN has_prop_conflict = FALSE THEN [1] ELSE [] END |
185
+ CALL {
186
+ WITH has_prop_conflict, current_attr_prop_conflict
187
+ WITH has_prop_conflict, current_attr_prop_conflict
188
+ WHERE has_prop_conflict = FALSE AND current_attr_prop_conflict IS NOT NULL
185
189
  DETACH DELETE current_attr_prop_conflict
186
- )
187
- FOREACH (i in CASE WHEN has_prop_conflict = TRUE THEN [1] ELSE [] END |
190
+ }
191
+ CALL {
192
+ WITH has_prop_conflict, diff_attr_prop, attr_property
193
+ WITH has_prop_conflict, diff_attr_prop, attr_property
194
+ WHERE has_prop_conflict = TRUE
188
195
  MERGE (diff_attr_prop)-[:DIFF_HAS_CONFLICT]->(diff_attr_prop_conflict:DiffConflict)
189
196
  SET diff_attr_prop_conflict = attr_property.conflict_params
190
- )
197
+ }
191
198
  }
192
199
  // -------------------------
200
+ // resetting the UNWIND and starting over here reduces memory usage
201
+ // -------------------------
202
+ WITH 1 AS resetting LIMIT 1
203
+ UNWIND $node_details_list AS node_details
204
+ WITH
205
+ node_details.root_uuid AS root_uuid,
206
+ node_details.node_map AS node_map,
207
+ toString(node_details.node_map.node_properties.uuid) AS node_uuid,
208
+ node_details.node_map.node_properties.db_id AS node_db_id
209
+ MATCH (:DiffRoot {uuid: root_uuid})-[:DIFF_HAS_NODE]->(diff_node:DiffNode {uuid: node_uuid, db_id: node_db_id})
210
+ // -------------------------
193
211
  // remove stale relationships for this node
194
212
  // -------------------------
195
213
  CALL {
196
214
  WITH diff_node, node_map
197
- WITH diff_node, %(rel_name_list_comp)s AS rel_names
215
+ WITH diff_node, node_map, %(rel_name_list_comp)s AS rel_names
198
216
  OPTIONAL MATCH (diff_node)-[:DIFF_HAS_RELATIONSHIP]->(rel_to_delete:DiffRelationship)
199
217
  WHERE NOT (rel_to_delete.name IN rel_names)
200
218
  OPTIONAL MATCH (rel_to_delete)-[*..8]->(next_to_delete)
@@ -214,7 +232,7 @@ SET diff_relationship = node_relationship.node_properties
214
232
  WITH diff_relationship, node_relationship
215
233
  CALL {
216
234
  WITH diff_relationship, node_relationship
217
- WITH diff_relationship, %(rel_peers_list_comp)s AS rel_peers
235
+ WITH diff_relationship, node_relationship, %(rel_peers_list_comp)s AS rel_peers
218
236
  OPTIONAL MATCH (diff_relationship)-[:DIFF_HAS_ELEMENT]->(element_to_delete:DiffRelationshipElement)
219
237
  WHERE NOT (element_to_delete.peer_id IN rel_peers)
220
238
  OPTIONAL MATCH (element_to_delete)-[*..6]->(next_to_delete)
@@ -236,20 +254,26 @@ WITH diff_relationship_element, node_single_relationship
236
254
  OPTIONAL MATCH (diff_relationship_element)-[:DIFF_HAS_CONFLICT]->(current_element_conflict:DiffConflict)
237
255
  WITH diff_relationship_element, node_single_relationship, current_element_conflict,
238
256
  (node_single_relationship.conflict_params IS NOT NULL) AS has_element_conflict
239
- FOREACH (i in CASE WHEN has_element_conflict = FALSE THEN [1] ELSE [] END |
257
+ CALL {
258
+ WITH has_element_conflict, current_element_conflict
259
+ WITH has_element_conflict, current_element_conflict
260
+ WHERE has_element_conflict = FALSE
240
261
  DETACH DELETE current_element_conflict
241
- )
242
- FOREACH (i in CASE WHEN has_element_conflict = TRUE THEN [1] ELSE [] END |
262
+ }
263
+ CALL {
264
+ WITH has_element_conflict, diff_relationship_element, node_single_relationship
265
+ WITH has_element_conflict, diff_relationship_element, node_single_relationship
266
+ WHERE has_element_conflict = TRUE
243
267
  MERGE (diff_relationship_element)-[:DIFF_HAS_CONFLICT]->(element_conflict:DiffConflict)
244
268
  SET element_conflict = node_single_relationship.conflict_params
245
- )
269
+ }
246
270
  // -------------------------
247
271
  // remove stale properties for this relationship element
248
272
  // -------------------------
249
273
  WITH diff_relationship_element, node_single_relationship
250
274
  CALL {
251
275
  WITH diff_relationship_element, node_single_relationship
252
- WITH diff_relationship_element, %(element_props_list_comp)s AS element_props
276
+ WITH diff_relationship_element, node_single_relationship, %(element_props_list_comp)s AS element_props
253
277
  OPTIONAL MATCH (diff_relationship_element)-[:DIFF_HAS_PROPERTY]->(property_to_delete:DiffProperty)
254
278
  WHERE NOT (property_to_delete.property_type IN element_props)
255
279
  OPTIONAL MATCH (property_to_delete)-[*..4]->(next_to_delete)
@@ -271,13 +295,19 @@ WITH diff_relationship_property, node_relationship_property
271
295
  OPTIONAL MATCH (diff_relationship_property)-[:DIFF_HAS_CONFLICT]->(diff_relationship_property_conflict:DiffConflict)
272
296
  WITH diff_relationship_property, node_relationship_property, diff_relationship_property_conflict,
273
297
  (node_relationship_property.conflict_params IS NOT NULL) AS has_property_conflict
274
- FOREACH (i in CASE WHEN has_property_conflict = FALSE THEN [1] ELSE [] END |
298
+ CALL {
299
+ WITH has_property_conflict, diff_relationship_property_conflict
300
+ WITH has_property_conflict, diff_relationship_property_conflict
301
+ WHERE has_property_conflict = FALSE
275
302
  DETACH DELETE diff_relationship_property_conflict
276
- )
277
- FOREACH (i in CASE WHEN has_property_conflict = TRUE THEN [1] ELSE [] END |
303
+ }
304
+ CALL {
305
+ WITH has_property_conflict, diff_relationship_property, node_relationship_property
306
+ WITH has_property_conflict, diff_relationship_property, node_relationship_property
307
+ WHERE has_property_conflict = TRUE
278
308
  MERGE (diff_relationship_property)-[:DIFF_HAS_CONFLICT]->(property_conflict:DiffConflict)
279
309
  SET property_conflict = node_relationship_property.conflict_params
280
- )
310
+ }
281
311
  """ % {
282
312
  "attr_name_list_comp": db.render_list_comprehension(
283
313
  items="node_map.attributes", item_name="node_properties.name"
@@ -526,7 +526,11 @@ class DiffQueryParser:
526
526
  return self._current_node_field_specifiers
527
527
 
528
528
  def read_result(self, query_result: QueryResult) -> None:
529
- path = query_result.get_path(label="diff_path")
529
+ try:
530
+ path = query_result.get_path(label="diff_path")
531
+ except ValueError:
532
+ # the path was null, so nothing to read
533
+ return
530
534
  database_path = DatabasePath.from_cypher_path(cypher_path=path)
531
535
  self._parse_path(database_path=database_path)
532
536
  self._current_node_field_specifiers = None
@@ -20,7 +20,7 @@ log = get_logger()
20
20
  async def update_diff(model: RequestDiffUpdate, service: InfrahubServices) -> None:
21
21
  await add_tags(branches=[model.branch_name])
22
22
 
23
- async with service.database.start_session() as db:
23
+ async with service.database.start_session(read_only=False) as db:
24
24
  component_registry = get_component_registry()
25
25
  base_branch = await registry.get_branch(db=db, branch=registry.default_branch)
26
26
  diff_branch = await registry.get_branch(db=db, branch=model.branch_name)
@@ -40,7 +40,7 @@ async def update_diff(model: RequestDiffUpdate, service: InfrahubServices) -> No
40
40
  async def refresh_diff(branch_name: str, diff_id: str, service: InfrahubServices) -> None:
41
41
  await add_tags(branches=[branch_name])
42
42
 
43
- async with service.database.start_session() as db:
43
+ async with service.database.start_session(read_only=False) as db:
44
44
  component_registry = get_component_registry()
45
45
  base_branch = await registry.get_branch(db=db, branch=registry.default_branch)
46
46
  diff_branch = await registry.get_branch(db=db, branch=branch_name)
@@ -53,7 +53,7 @@ async def refresh_diff(branch_name: str, diff_id: str, service: InfrahubServices
53
53
  async def refresh_diff_all(branch_name: str, context: InfrahubContext, service: InfrahubServices) -> None:
54
54
  await add_tags(branches=[branch_name])
55
55
 
56
- async with service.database.start_session() as db:
56
+ async with service.database.start_session(read_only=True) as db:
57
57
  component_registry = get_component_registry()
58
58
  default_branch = registry.get_branch_from_registry()
59
59
  diff_repository = await component_registry.get_component(DiffRepository, db=db, branch=default_branch)
@@ -1 +1 @@
1
- GRAPH_VERSION = 28
1
+ GRAPH_VERSION = 30
@@ -29,6 +29,9 @@ from .m024_missing_hierarchy_backfill import Migration024
29
29
  from .m025_uniqueness_nulls import Migration025
30
30
  from .m026_0000_prefix_fix import Migration026
31
31
  from .m027_delete_isolated_nodes import Migration027
32
+ from .m028_delete_diffs import Migration028
33
+ from .m029_duplicates_cleanup import Migration029
34
+ from .m030_illegal_edges import Migration030
32
35
 
33
36
  if TYPE_CHECKING:
34
37
  from infrahub.core.root import Root
@@ -63,6 +66,9 @@ MIGRATIONS: list[type[GraphMigration | InternalSchemaMigration | ArbitraryMigrat
63
66
  Migration025,
64
67
  Migration026,
65
68
  Migration027,
69
+ Migration028,
70
+ Migration029,
71
+ Migration030,
66
72
  ]
67
73
 
68
74