infrahub-server 1.2.0b1__py3-none-any.whl → 1.2.1__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 (297) hide show
  1. infrahub/api/dependencies.py +6 -6
  2. infrahub/api/diff/validation_models.py +7 -7
  3. infrahub/api/schema.py +1 -1
  4. infrahub/artifacts/models.py +1 -3
  5. infrahub/artifacts/tasks.py +1 -3
  6. infrahub/cli/__init__.py +13 -9
  7. infrahub/cli/constants.py +3 -0
  8. infrahub/cli/db.py +165 -183
  9. infrahub/cli/upgrade.py +146 -0
  10. infrahub/computed_attribute/gather.py +185 -0
  11. infrahub/computed_attribute/models.py +239 -11
  12. infrahub/computed_attribute/tasks.py +77 -442
  13. infrahub/computed_attribute/triggers.py +11 -45
  14. infrahub/config.py +43 -32
  15. infrahub/context.py +14 -0
  16. infrahub/core/account.py +4 -4
  17. infrahub/core/attribute.py +57 -57
  18. infrahub/core/branch/tasks.py +12 -9
  19. infrahub/core/changelog/diff.py +16 -8
  20. infrahub/core/changelog/models.py +189 -26
  21. infrahub/core/constants/__init__.py +5 -1
  22. infrahub/core/constants/infrahubkind.py +2 -0
  23. infrahub/core/constraint/node/runner.py +9 -8
  24. infrahub/core/diff/branch_differ.py +10 -10
  25. infrahub/core/diff/ipam_diff_parser.py +4 -5
  26. infrahub/core/diff/model/diff.py +27 -27
  27. infrahub/core/diff/model/path.py +3 -3
  28. infrahub/core/diff/query/merge.py +20 -17
  29. infrahub/core/diff/query_parser.py +4 -4
  30. infrahub/core/graph/__init__.py +1 -1
  31. infrahub/core/initialization.py +1 -10
  32. infrahub/core/ipam/constants.py +3 -4
  33. infrahub/core/ipam/reconciler.py +12 -12
  34. infrahub/core/ipam/utilization.py +10 -13
  35. infrahub/core/manager.py +34 -34
  36. infrahub/core/merge.py +7 -7
  37. infrahub/core/migrations/__init__.py +2 -3
  38. infrahub/core/migrations/graph/__init__.py +9 -4
  39. infrahub/core/migrations/graph/m017_add_core_profile.py +1 -5
  40. infrahub/core/migrations/graph/m018_uniqueness_nulls.py +4 -4
  41. infrahub/core/migrations/graph/m020_duplicate_edges.py +160 -0
  42. infrahub/core/migrations/graph/m021_missing_hierarchy_merge.py +51 -0
  43. infrahub/core/migrations/graph/{m020_add_generate_template_attr.py → m022_add_generate_template_attr.py} +3 -3
  44. infrahub/core/migrations/graph/m023_deduplicate_cardinality_one_relationships.py +96 -0
  45. infrahub/core/migrations/query/attribute_add.py +2 -2
  46. infrahub/core/migrations/query/node_duplicate.py +18 -21
  47. infrahub/core/migrations/query/schema_attribute_update.py +2 -2
  48. infrahub/core/migrations/schema/models.py +19 -4
  49. infrahub/core/migrations/schema/tasks.py +2 -2
  50. infrahub/core/migrations/shared.py +16 -16
  51. infrahub/core/models.py +15 -6
  52. infrahub/core/node/__init__.py +29 -28
  53. infrahub/core/node/base.py +2 -4
  54. infrahub/core/node/constraints/attribute_uniqueness.py +2 -2
  55. infrahub/core/node/constraints/grouped_uniqueness.py +99 -47
  56. infrahub/core/node/constraints/interface.py +1 -2
  57. infrahub/core/node/delete_validator.py +3 -5
  58. infrahub/core/node/ipam.py +4 -4
  59. infrahub/core/node/permissions.py +7 -7
  60. infrahub/core/node/resource_manager/ip_address_pool.py +6 -6
  61. infrahub/core/node/resource_manager/ip_prefix_pool.py +6 -6
  62. infrahub/core/node/resource_manager/number_pool.py +3 -3
  63. infrahub/core/path.py +12 -12
  64. infrahub/core/property.py +11 -11
  65. infrahub/core/protocols.py +5 -0
  66. infrahub/core/protocols_base.py +21 -21
  67. infrahub/core/query/__init__.py +33 -33
  68. infrahub/core/query/attribute.py +6 -4
  69. infrahub/core/query/diff.py +3 -3
  70. infrahub/core/query/node.py +82 -32
  71. infrahub/core/query/relationship.py +24 -24
  72. infrahub/core/query/resource_manager.py +2 -0
  73. infrahub/core/query/standard_node.py +3 -3
  74. infrahub/core/query/subquery.py +9 -9
  75. infrahub/core/registry.py +13 -15
  76. infrahub/core/relationship/constraints/count.py +3 -4
  77. infrahub/core/relationship/constraints/peer_kind.py +3 -4
  78. infrahub/core/relationship/constraints/profiles_kind.py +2 -2
  79. infrahub/core/relationship/model.py +40 -46
  80. infrahub/core/schema/attribute_schema.py +9 -9
  81. infrahub/core/schema/basenode_schema.py +93 -44
  82. infrahub/core/schema/computed_attribute.py +3 -3
  83. infrahub/core/schema/definitions/core/__init__.py +13 -19
  84. infrahub/core/schema/definitions/core/account.py +151 -148
  85. infrahub/core/schema/definitions/core/artifact.py +122 -113
  86. infrahub/core/schema/definitions/core/builtin.py +19 -16
  87. infrahub/core/schema/definitions/core/check.py +61 -53
  88. infrahub/core/schema/definitions/core/core.py +17 -0
  89. infrahub/core/schema/definitions/core/generator.py +89 -85
  90. infrahub/core/schema/definitions/core/graphql_query.py +72 -70
  91. infrahub/core/schema/definitions/core/group.py +96 -93
  92. infrahub/core/schema/definitions/core/ipam.py +176 -235
  93. infrahub/core/schema/definitions/core/lineage.py +18 -16
  94. infrahub/core/schema/definitions/core/menu.py +42 -40
  95. infrahub/core/schema/definitions/core/permission.py +144 -142
  96. infrahub/core/schema/definitions/core/profile.py +16 -27
  97. infrahub/core/schema/definitions/core/propose_change.py +88 -79
  98. infrahub/core/schema/definitions/core/propose_change_comment.py +170 -165
  99. infrahub/core/schema/definitions/core/propose_change_validator.py +290 -288
  100. infrahub/core/schema/definitions/core/repository.py +231 -225
  101. infrahub/core/schema/definitions/core/resource_pool.py +156 -166
  102. infrahub/core/schema/definitions/core/template.py +27 -12
  103. infrahub/core/schema/definitions/core/transform.py +85 -76
  104. infrahub/core/schema/definitions/core/webhook.py +127 -101
  105. infrahub/core/schema/definitions/internal.py +16 -16
  106. infrahub/core/schema/dropdown.py +3 -4
  107. infrahub/core/schema/generated/attribute_schema.py +15 -18
  108. infrahub/core/schema/generated/base_node_schema.py +12 -14
  109. infrahub/core/schema/generated/node_schema.py +3 -5
  110. infrahub/core/schema/generated/relationship_schema.py +9 -11
  111. infrahub/core/schema/generic_schema.py +2 -2
  112. infrahub/core/schema/manager.py +20 -9
  113. infrahub/core/schema/node_schema.py +4 -2
  114. infrahub/core/schema/relationship_schema.py +7 -7
  115. infrahub/core/schema/schema_branch.py +276 -138
  116. infrahub/core/schema/schema_branch_computed.py +41 -4
  117. infrahub/core/task/task.py +3 -3
  118. infrahub/core/task/user_task.py +15 -15
  119. infrahub/core/utils.py +20 -18
  120. infrahub/core/validators/__init__.py +1 -3
  121. infrahub/core/validators/aggregated_checker.py +2 -2
  122. infrahub/core/validators/attribute/choices.py +2 -2
  123. infrahub/core/validators/attribute/enum.py +2 -2
  124. infrahub/core/validators/attribute/kind.py +2 -2
  125. infrahub/core/validators/attribute/length.py +2 -2
  126. infrahub/core/validators/attribute/optional.py +2 -2
  127. infrahub/core/validators/attribute/regex.py +2 -2
  128. infrahub/core/validators/attribute/unique.py +2 -2
  129. infrahub/core/validators/checks_runner.py +25 -2
  130. infrahub/core/validators/determiner.py +1 -3
  131. infrahub/core/validators/interface.py +6 -2
  132. infrahub/core/validators/model.py +22 -3
  133. infrahub/core/validators/models/validate_migration.py +17 -4
  134. infrahub/core/validators/node/attribute.py +2 -2
  135. infrahub/core/validators/node/generate_profile.py +2 -2
  136. infrahub/core/validators/node/hierarchy.py +3 -5
  137. infrahub/core/validators/node/inherit_from.py +27 -5
  138. infrahub/core/validators/node/relationship.py +2 -2
  139. infrahub/core/validators/relationship/count.py +4 -4
  140. infrahub/core/validators/relationship/optional.py +2 -2
  141. infrahub/core/validators/relationship/peer.py +2 -2
  142. infrahub/core/validators/shared.py +2 -2
  143. infrahub/core/validators/tasks.py +8 -0
  144. infrahub/core/validators/uniqueness/checker.py +22 -21
  145. infrahub/core/validators/uniqueness/index.py +2 -2
  146. infrahub/core/validators/uniqueness/model.py +11 -11
  147. infrahub/database/__init__.py +26 -22
  148. infrahub/database/metrics.py +7 -1
  149. infrahub/dependencies/builder/constraint/grouped/node_runner.py +1 -3
  150. infrahub/dependencies/component/registry.py +2 -2
  151. infrahub/events/__init__.py +25 -2
  152. infrahub/events/artifact_action.py +13 -25
  153. infrahub/events/branch_action.py +26 -18
  154. infrahub/events/generator.py +71 -0
  155. infrahub/events/group_action.py +10 -24
  156. infrahub/events/models.py +10 -16
  157. infrahub/events/node_action.py +87 -32
  158. infrahub/events/repository_action.py +5 -18
  159. infrahub/events/schema_action.py +4 -9
  160. infrahub/events/utils.py +16 -0
  161. infrahub/events/validator_action.py +55 -0
  162. infrahub/exceptions.py +23 -24
  163. infrahub/generators/models.py +1 -3
  164. infrahub/git/base.py +7 -7
  165. infrahub/git/integrator.py +26 -25
  166. infrahub/git/models.py +22 -9
  167. infrahub/git/repository.py +3 -3
  168. infrahub/git/tasks.py +67 -49
  169. infrahub/git/utils.py +48 -0
  170. infrahub/git/worktree.py +1 -2
  171. infrahub/git_credential/askpass.py +1 -2
  172. infrahub/graphql/analyzer.py +12 -0
  173. infrahub/graphql/app.py +13 -15
  174. infrahub/graphql/context.py +6 -0
  175. infrahub/graphql/initialization.py +3 -0
  176. infrahub/graphql/loaders/node.py +2 -12
  177. infrahub/graphql/loaders/peers.py +77 -0
  178. infrahub/graphql/loaders/shared.py +13 -0
  179. infrahub/graphql/manager.py +13 -10
  180. infrahub/graphql/mutations/artifact_definition.py +5 -5
  181. infrahub/graphql/mutations/computed_attribute.py +4 -5
  182. infrahub/graphql/mutations/graphql_query.py +5 -5
  183. infrahub/graphql/mutations/ipam.py +50 -70
  184. infrahub/graphql/mutations/main.py +164 -141
  185. infrahub/graphql/mutations/menu.py +5 -5
  186. infrahub/graphql/mutations/models.py +2 -4
  187. infrahub/graphql/mutations/node_getter/by_default_filter.py +10 -10
  188. infrahub/graphql/mutations/node_getter/by_hfid.py +1 -3
  189. infrahub/graphql/mutations/node_getter/by_id.py +1 -3
  190. infrahub/graphql/mutations/node_getter/interface.py +1 -2
  191. infrahub/graphql/mutations/proposed_change.py +7 -7
  192. infrahub/graphql/mutations/relationship.py +67 -35
  193. infrahub/graphql/mutations/repository.py +8 -8
  194. infrahub/graphql/mutations/resource_manager.py +3 -3
  195. infrahub/graphql/mutations/schema.py +4 -4
  196. infrahub/graphql/mutations/webhook.py +137 -0
  197. infrahub/graphql/parser.py +4 -4
  198. infrahub/graphql/queries/diff/tree.py +4 -4
  199. infrahub/graphql/queries/ipam.py +2 -2
  200. infrahub/graphql/queries/relationship.py +2 -2
  201. infrahub/graphql/queries/search.py +2 -2
  202. infrahub/graphql/resolvers/many_relationship.py +264 -0
  203. infrahub/graphql/resolvers/resolver.py +13 -110
  204. infrahub/graphql/subscription/graphql_query.py +2 -0
  205. infrahub/graphql/types/event.py +20 -11
  206. infrahub/graphql/types/node.py +2 -2
  207. infrahub/graphql/utils.py +2 -2
  208. infrahub/groups/ancestors.py +29 -0
  209. infrahub/groups/parsers.py +107 -0
  210. infrahub/menu/generator.py +7 -7
  211. infrahub/menu/menu.py +0 -10
  212. infrahub/menu/models.py +117 -16
  213. infrahub/menu/repository.py +111 -0
  214. infrahub/menu/utils.py +5 -8
  215. infrahub/message_bus/messages/__init__.py +1 -11
  216. infrahub/message_bus/messages/check_generator_run.py +2 -0
  217. infrahub/message_bus/messages/finalize_validator_execution.py +3 -0
  218. infrahub/message_bus/messages/request_generatordefinition_check.py +2 -0
  219. infrahub/message_bus/operations/__init__.py +0 -2
  220. infrahub/message_bus/operations/check/generator.py +1 -0
  221. infrahub/message_bus/operations/event/__init__.py +2 -2
  222. infrahub/message_bus/operations/finalize/validator.py +51 -1
  223. infrahub/message_bus/operations/requests/generator_definition.py +19 -19
  224. infrahub/message_bus/operations/requests/proposed_change.py +3 -1
  225. infrahub/pools/number.py +2 -4
  226. infrahub/proposed_change/tasks.py +37 -28
  227. infrahub/pytest_plugin.py +13 -10
  228. infrahub/server.py +1 -2
  229. infrahub/services/adapters/event/__init__.py +1 -1
  230. infrahub/task_manager/event.py +23 -9
  231. infrahub/tasks/artifact.py +2 -4
  232. infrahub/telemetry/__init__.py +0 -0
  233. infrahub/telemetry/constants.py +9 -0
  234. infrahub/telemetry/database.py +86 -0
  235. infrahub/telemetry/models.py +65 -0
  236. infrahub/telemetry/task_manager.py +77 -0
  237. infrahub/{tasks/telemetry.py → telemetry/tasks.py} +49 -56
  238. infrahub/telemetry/utils.py +11 -0
  239. infrahub/trace.py +4 -4
  240. infrahub/transformations/tasks.py +2 -2
  241. infrahub/trigger/catalogue.py +2 -5
  242. infrahub/trigger/constants.py +0 -8
  243. infrahub/trigger/models.py +14 -1
  244. infrahub/trigger/setup.py +90 -0
  245. infrahub/trigger/tasks.py +35 -90
  246. infrahub/utils.py +11 -1
  247. infrahub/validators/__init__.py +0 -0
  248. infrahub/validators/events.py +42 -0
  249. infrahub/validators/tasks.py +41 -0
  250. infrahub/webhook/gather.py +17 -0
  251. infrahub/webhook/models.py +22 -5
  252. infrahub/webhook/tasks.py +44 -19
  253. infrahub/webhook/triggers.py +22 -5
  254. infrahub/workers/infrahub_async.py +2 -2
  255. infrahub/workers/utils.py +2 -2
  256. infrahub/workflows/catalogue.py +28 -20
  257. infrahub/workflows/initialization.py +1 -3
  258. infrahub/workflows/models.py +1 -1
  259. infrahub/workflows/utils.py +10 -1
  260. infrahub_sdk/client.py +27 -8
  261. infrahub_sdk/config.py +3 -0
  262. infrahub_sdk/context.py +13 -0
  263. infrahub_sdk/exceptions.py +6 -0
  264. infrahub_sdk/generator.py +4 -1
  265. infrahub_sdk/graphql.py +45 -13
  266. infrahub_sdk/node.py +69 -20
  267. infrahub_sdk/protocols_base.py +32 -11
  268. infrahub_sdk/query_groups.py +6 -35
  269. infrahub_sdk/schema/__init__.py +55 -26
  270. infrahub_sdk/schema/main.py +8 -0
  271. infrahub_sdk/task/__init__.py +10 -0
  272. infrahub_sdk/task/manager.py +12 -6
  273. infrahub_sdk/testing/schemas/animal.py +9 -0
  274. infrahub_sdk/timestamp.py +12 -4
  275. {infrahub_server-1.2.0b1.dist-info → infrahub_server-1.2.1.dist-info}/METADATA +3 -2
  276. {infrahub_server-1.2.0b1.dist-info → infrahub_server-1.2.1.dist-info}/RECORD +289 -260
  277. {infrahub_server-1.2.0b1.dist-info → infrahub_server-1.2.1.dist-info}/entry_points.txt +1 -0
  278. infrahub_testcontainers/constants.py +2 -0
  279. infrahub_testcontainers/container.py +157 -12
  280. infrahub_testcontainers/docker-compose.test.yml +31 -6
  281. infrahub_testcontainers/helpers.py +18 -73
  282. infrahub_testcontainers/host.py +41 -0
  283. infrahub_testcontainers/measurements.py +93 -0
  284. infrahub_testcontainers/models.py +38 -0
  285. infrahub_testcontainers/performance_test.py +166 -0
  286. infrahub_testcontainers/plugin.py +136 -0
  287. infrahub_testcontainers/prometheus.yml +30 -0
  288. infrahub/message_bus/messages/event_branch_create.py +0 -11
  289. infrahub/message_bus/messages/event_branch_delete.py +0 -11
  290. infrahub/message_bus/messages/event_branch_rebased.py +0 -9
  291. infrahub/message_bus/messages/event_node_mutated.py +0 -15
  292. infrahub/message_bus/messages/event_schema_update.py +0 -9
  293. infrahub/message_bus/operations/event/node.py +0 -20
  294. infrahub/message_bus/operations/event/schema.py +0 -17
  295. infrahub/webhook/constants.py +0 -1
  296. {infrahub_server-1.2.0b1.dist-info → infrahub_server-1.2.1.dist-info}/LICENSE.txt +0 -0
  297. {infrahub_server-1.2.0b1.dist-info → infrahub_server-1.2.1.dist-info}/WHEEL +0 -0
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Any, Optional
3
+ from typing import TYPE_CHECKING, Any
4
4
 
5
5
  from graphene import InputObjectType, Mutation
6
6
  from typing_extensions import Self
@@ -28,7 +28,7 @@ class InfrahubArtifactDefinitionMutation(InfrahubMutationMixin, Mutation):
28
28
  def __init_subclass_with_meta__(
29
29
  cls,
30
30
  schema: NodeSchema,
31
- _meta: Optional[Any] = None,
31
+ _meta: Any | None = None,
32
32
  **options: dict[str, Any],
33
33
  ) -> None:
34
34
  # Make sure schema is a valid NodeSchema Node Class
@@ -48,7 +48,7 @@ class InfrahubArtifactDefinitionMutation(InfrahubMutationMixin, Mutation):
48
48
  info: GraphQLResolveInfo,
49
49
  data: InputObjectType,
50
50
  branch: Branch,
51
- database: Optional[InfrahubDatabase] = None, # noqa: ARG003
51
+ database: InfrahubDatabase | None = None, # noqa: ARG003
52
52
  ) -> tuple[Node, Self]:
53
53
  graphql_context: GraphqlContext = info.context
54
54
 
@@ -74,8 +74,8 @@ class InfrahubArtifactDefinitionMutation(InfrahubMutationMixin, Mutation):
74
74
  info: GraphQLResolveInfo,
75
75
  data: InputObjectType,
76
76
  branch: Branch,
77
- database: Optional[InfrahubDatabase] = None, # noqa: ARG003
78
- node: Optional[Node] = None, # noqa: ARG003
77
+ database: InfrahubDatabase | None = None, # noqa: ARG003
78
+ node: Node | None = None, # noqa: ARG003
79
79
  ) -> tuple[Node, Self]:
80
80
  graphql_context: GraphqlContext = info.context
81
81
 
@@ -5,12 +5,12 @@ from typing import TYPE_CHECKING, Any
5
5
  from graphene import Boolean, InputObjectType, Mutation, String
6
6
 
7
7
  from infrahub.core.account import ObjectPermission
8
- from infrahub.core.constants import ComputedAttributeKind, MutationAction, PermissionAction, PermissionDecision
8
+ from infrahub.core.constants import ComputedAttributeKind, PermissionAction, PermissionDecision
9
9
  from infrahub.core.manager import NodeManager
10
10
  from infrahub.core.registry import registry
11
11
  from infrahub.database import retry_db_transaction
12
12
  from infrahub.events import EventMeta
13
- from infrahub.events.node_action import NodeMutatedEvent
13
+ from infrahub.events.node_action import NodeUpdatedEvent
14
14
  from infrahub.exceptions import NodeNotFoundError, ValidationError
15
15
  from infrahub.graphql.context import apply_external_context
16
16
  from infrahub.graphql.types.context import ContextInput
@@ -95,12 +95,11 @@ class UpdateComputedAttribute(Mutation):
95
95
  log_data = get_log_data()
96
96
  request_id = log_data.get("request_id", "")
97
97
 
98
- event = NodeMutatedEvent(
98
+ event = NodeUpdatedEvent(
99
99
  kind=node_schema.kind,
100
100
  node_id=target_node.get_id(),
101
- data=target_node.node_changelog.model_dump(),
101
+ changelog=target_node.node_changelog.model_dump(),
102
102
  fields=[str(data.attribute)],
103
- action=MutationAction.UPDATED,
104
103
  meta=EventMeta(
105
104
  context=graphql_context.get_context(),
106
105
  initiator_id=WORKER_IDENTITY,
@@ -1,4 +1,4 @@
1
- from typing import TYPE_CHECKING, Any, Optional
1
+ from typing import TYPE_CHECKING, Any
2
2
 
3
3
  from graphene import InputObjectType, Mutation
4
4
  from graphql import GraphQLResolveInfo
@@ -20,7 +20,7 @@ if TYPE_CHECKING:
20
20
  class InfrahubGraphQLQueryMutation(InfrahubMutationMixin, Mutation):
21
21
  @classmethod
22
22
  def __init_subclass_with_meta__(
23
- cls, schema: NodeSchema, _meta: Optional[Any] = None, **options: dict[str, Any]
23
+ cls, schema: NodeSchema, _meta: Any | None = None, **options: dict[str, Any]
24
24
  ) -> None:
25
25
  # Make sure schema is a valid NodeSchema Node Class
26
26
  if not isinstance(schema, NodeSchema):
@@ -67,7 +67,7 @@ class InfrahubGraphQLQueryMutation(InfrahubMutationMixin, Mutation):
67
67
  info: GraphQLResolveInfo,
68
68
  data: InputObjectType,
69
69
  branch: Branch,
70
- database: Optional[InfrahubDatabase] = None, # noqa: ARG003
70
+ database: InfrahubDatabase | None = None, # noqa: ARG003
71
71
  ) -> tuple[Node, Self]:
72
72
  graphql_context: GraphqlContext = info.context
73
73
 
@@ -85,8 +85,8 @@ class InfrahubGraphQLQueryMutation(InfrahubMutationMixin, Mutation):
85
85
  info: GraphQLResolveInfo,
86
86
  data: InputObjectType,
87
87
  branch: Branch,
88
- database: Optional[InfrahubDatabase] = None, # noqa: ARG003
89
- node: Optional[Node] = None, # noqa: ARG003
88
+ database: InfrahubDatabase | None = None, # noqa: ARG003
89
+ node: Node | None = None, # noqa: ARG003
90
90
  ) -> tuple[Node, Self]:
91
91
  graphql_context: GraphqlContext = info.context
92
92
 
@@ -1,6 +1,6 @@
1
1
  import ipaddress
2
2
  from ipaddress import IPv4Interface
3
- from typing import TYPE_CHECKING, Any, Optional
3
+ from typing import TYPE_CHECKING, Any
4
4
 
5
5
  from graphene import InputObjectType, Mutation
6
6
  from graphql import GraphQLResolveInfo
@@ -16,11 +16,11 @@ from infrahub.core.node import Node
16
16
  from infrahub.core.schema import NodeSchema
17
17
  from infrahub.database import InfrahubDatabase, retry_db_transaction
18
18
  from infrahub.exceptions import NodeNotFoundError, ValidationError
19
- from infrahub.graphql.mutations.node_getter.interface import MutationNodeGetterInterface
20
19
  from infrahub.lock import InfrahubMultiLock, build_object_lock_name
21
20
  from infrahub.log import get_logger
22
21
 
23
22
  from .main import DeleteResult, InfrahubMutationMixin, InfrahubMutationOptions
23
+ from .node_getter.by_default_filter import MutationNodeGetterByDefaultFilter
24
24
 
25
25
  if TYPE_CHECKING:
26
26
  from infrahub.graphql.initialization import GraphqlContext
@@ -32,10 +32,10 @@ async def validate_namespace(
32
32
  db: InfrahubDatabase,
33
33
  branch: Branch | str | None,
34
34
  data: InputObjectType,
35
- existing_namespace_id: Optional[str] = None,
35
+ existing_namespace_id: str | None = None,
36
36
  ) -> str:
37
37
  """Validate or set (if not present) the namespace to pass to the mutation and return its ID."""
38
- namespace_id: Optional[str] = None
38
+ namespace_id: str | None = None
39
39
  if "ip_namespace" not in data or not data["ip_namespace"]:
40
40
  namespace_id = existing_namespace_id or registry.default_ipnamespace
41
41
  data["ip_namespace"] = {"id": namespace_id}
@@ -56,7 +56,7 @@ class InfrahubIPNamespaceMutation(InfrahubMutationMixin, Mutation):
56
56
  def __init_subclass_with_meta__(
57
57
  cls,
58
58
  schema: NodeSchema,
59
- _meta: Optional[Any] = None,
59
+ _meta: Any | None = None,
60
60
  **options: dict[str, Any],
61
61
  ) -> None:
62
62
  # Make sure schema is a valid NodeSchema Node Class
@@ -75,7 +75,7 @@ class InfrahubIPNamespaceMutation(InfrahubMutationMixin, Mutation):
75
75
  info: GraphQLResolveInfo,
76
76
  data: InputObjectType,
77
77
  branch: Branch,
78
- ):
78
+ ) -> DeleteResult:
79
79
  if data["id"] == registry.default_ipnamespace:
80
80
  raise ValueError("Cannot delete default IPAM namespace")
81
81
 
@@ -87,7 +87,7 @@ class InfrahubIPAddressMutation(InfrahubMutationMixin, Mutation):
87
87
  def __init_subclass_with_meta__(
88
88
  cls,
89
89
  schema: NodeSchema,
90
- _meta: Optional[Any] = None,
90
+ _meta: Any | None = None,
91
91
  **options: dict[str, Any],
92
92
  ) -> None:
93
93
  # Make sure schema is a valid NodeSchema Node Class
@@ -130,7 +130,7 @@ class InfrahubIPAddressMutation(InfrahubMutationMixin, Mutation):
130
130
  info: GraphQLResolveInfo,
131
131
  data: InputObjectType,
132
132
  branch: Branch,
133
- database: Optional[InfrahubDatabase] = None,
133
+ database: InfrahubDatabase | None = None,
134
134
  ) -> tuple[Node, Self]:
135
135
  graphql_context: GraphqlContext = info.context
136
136
  db = database or graphql_context.db
@@ -176,8 +176,8 @@ class InfrahubIPAddressMutation(InfrahubMutationMixin, Mutation):
176
176
  info: GraphQLResolveInfo,
177
177
  data: InputObjectType,
178
178
  branch: Branch,
179
- database: Optional[InfrahubDatabase] = None,
180
- node: Optional[Node] = None,
179
+ database: InfrahubDatabase | None = None,
180
+ node: Node | None = None,
181
181
  ) -> tuple[Node, Self]:
182
182
  graphql_context: GraphqlContext = info.context
183
183
  db = database or graphql_context.db
@@ -192,21 +192,19 @@ class InfrahubIPAddressMutation(InfrahubMutationMixin, Mutation):
192
192
  )
193
193
  namespace = await address.ip_namespace.get_peer(db)
194
194
  namespace_id = await validate_namespace(db=db, branch=branch, data=data, existing_namespace_id=namespace.id)
195
- try:
196
- async with db.start_transaction() as dbt:
197
- if lock_name := cls._get_lock_name(namespace_id, branch):
198
- async with InfrahubMultiLock(lock_registry=lock.registry, locks=[lock_name]):
199
- reconciled_address = await cls._mutate_update_object_and_reconcile(
200
- info=info, data=data, branch=branch, address=address, namespace_id=namespace_id, db=dbt
201
- )
202
- else:
195
+
196
+ async with db.start_transaction() as dbt:
197
+ if lock_name := cls._get_lock_name(namespace_id, branch):
198
+ async with InfrahubMultiLock(lock_registry=lock.registry, locks=[lock_name]):
203
199
  reconciled_address = await cls._mutate_update_object_and_reconcile(
204
200
  info=info, data=data, branch=branch, address=address, namespace_id=namespace_id, db=dbt
205
201
  )
202
+ else:
203
+ reconciled_address = await cls._mutate_update_object_and_reconcile(
204
+ info=info, data=data, branch=branch, address=address, namespace_id=namespace_id, db=dbt
205
+ )
206
206
 
207
- result = await cls.mutate_update_to_graphql(db=dbt, info=info, obj=reconciled_address)
208
- except ValidationError as exc:
209
- raise ValueError(str(exc)) from exc
207
+ result = await cls.mutate_update_to_graphql(db=dbt, info=info, obj=reconciled_address)
210
208
 
211
209
  return address, result
212
210
 
@@ -216,15 +214,15 @@ class InfrahubIPAddressMutation(InfrahubMutationMixin, Mutation):
216
214
  info: GraphQLResolveInfo,
217
215
  data: InputObjectType,
218
216
  branch: Branch,
219
- node_getters: list[MutationNodeGetterInterface],
220
- database: Optional[InfrahubDatabase] = None,
217
+ node_getter_default_filter: MutationNodeGetterByDefaultFilter,
218
+ database: InfrahubDatabase | None = None,
221
219
  ) -> tuple[Node, Self, bool]:
222
220
  graphql_context: GraphqlContext = info.context
223
221
  db = database or graphql_context.db
224
222
 
225
223
  await validate_namespace(db=db, branch=branch, data=data)
226
224
  prefix, result, created = await super().mutate_upsert(
227
- info=info, data=data, branch=branch, node_getters=node_getters, database=db
225
+ info=info, data=data, branch=branch, node_getter_default_filter=node_getter_default_filter, database=db
228
226
  )
229
227
 
230
228
  return prefix, result, created
@@ -244,7 +242,7 @@ class InfrahubIPPrefixMutation(InfrahubMutationMixin, Mutation):
244
242
  def __init_subclass_with_meta__(
245
243
  cls,
246
244
  schema: NodeSchema,
247
- _meta: Optional[Any] = None,
245
+ _meta: Any | None = None,
248
246
  **options: dict[str, Any],
249
247
  ) -> None:
250
248
  # Make sure schema is a valid NodeSchema Node Class
@@ -258,10 +256,10 @@ class InfrahubIPPrefixMutation(InfrahubMutationMixin, Mutation):
258
256
  super().__init_subclass_with_meta__(_meta=_meta, **options)
259
257
 
260
258
  @staticmethod
261
- def _get_lock_name(namespace_id: str, branch: Branch) -> str | None:
262
- if not branch.is_default:
263
- # Do not lock on other branches as reconciliation will be performed at least when merging in main branch.
264
- return None
259
+ def _get_lock_name(namespace_id: str) -> str | None:
260
+ # IPPrefix has some cardinality-one relationships involved (parent/child/ip_address),
261
+ # so we need to lock on any branch to avoid creating multiple peers for these relationships
262
+ # during concurrent ipam reconciliations.
265
263
  return build_object_lock_name(InfrahubKind.IPPREFIX + "_" + namespace_id)
266
264
 
267
265
  @classmethod
@@ -284,19 +282,15 @@ class InfrahubIPPrefixMutation(InfrahubMutationMixin, Mutation):
284
282
  info: GraphQLResolveInfo,
285
283
  data: InputObjectType,
286
284
  branch: Branch,
287
- database: Optional[InfrahubDatabase] = None,
285
+ database: InfrahubDatabase | None = None,
288
286
  ) -> tuple[Node, Self]:
289
287
  graphql_context: GraphqlContext = info.context
290
288
  db = database or graphql_context.db
291
289
  namespace_id = await validate_namespace(db=db, branch=branch, data=data)
292
290
 
293
291
  async with db.start_transaction() as dbt:
294
- if lock_name := cls._get_lock_name(namespace_id, branch):
295
- async with InfrahubMultiLock(lock_registry=lock.registry, locks=[lock_name]):
296
- reconciled_prefix = await cls._mutate_create_object_and_reconcile(
297
- data=data, branch=branch, db=dbt, namespace_id=namespace_id
298
- )
299
- else:
292
+ lock_name = cls._get_lock_name(namespace_id)
293
+ async with InfrahubMultiLock(lock_registry=lock.registry, locks=[lock_name]):
300
294
  reconciled_prefix = await cls._mutate_create_object_and_reconcile(
301
295
  data=data, branch=branch, db=dbt, namespace_id=namespace_id
302
296
  )
@@ -327,8 +321,8 @@ class InfrahubIPPrefixMutation(InfrahubMutationMixin, Mutation):
327
321
  info: GraphQLResolveInfo,
328
322
  data: InputObjectType,
329
323
  branch: Branch,
330
- database: Optional[InfrahubDatabase] = None,
331
- node: Optional[Node] = None,
324
+ database: InfrahubDatabase | None = None,
325
+ node: Node | None = None,
332
326
  ) -> tuple[Node, Self]:
333
327
  graphql_context: GraphqlContext = info.context
334
328
  db = database or graphql_context.db
@@ -343,20 +337,14 @@ class InfrahubIPPrefixMutation(InfrahubMutationMixin, Mutation):
343
337
  )
344
338
  namespace = await prefix.ip_namespace.get_peer(db)
345
339
  namespace_id = await validate_namespace(db=db, branch=branch, data=data, existing_namespace_id=namespace.id)
346
- try:
347
- async with db.start_transaction() as dbt:
348
- if lock_name := cls._get_lock_name(namespace_id, branch):
349
- async with InfrahubMultiLock(lock_registry=lock.registry, locks=[lock_name]):
350
- reconciled_prefix = await cls._mutate_update_object_and_reconcile(
351
- info=info, data=data, prefix=prefix, db=dbt, namespace_id=namespace_id, branch=branch
352
- )
353
- else:
354
- reconciled_prefix = await cls._mutate_update_object_and_reconcile(
355
- info=info, data=data, prefix=prefix, db=dbt, namespace_id=namespace_id, branch=branch
356
- )
357
- result = await cls.mutate_update_to_graphql(db=dbt, info=info, obj=reconciled_prefix)
358
- except ValidationError as exc:
359
- raise ValueError(str(exc)) from exc
340
+
341
+ async with db.start_transaction() as dbt:
342
+ lock_name = cls._get_lock_name(namespace_id)
343
+ async with InfrahubMultiLock(lock_registry=lock.registry, locks=[lock_name]):
344
+ reconciled_prefix = await cls._mutate_update_object_and_reconcile(
345
+ info=info, data=data, prefix=prefix, db=dbt, namespace_id=namespace_id, branch=branch
346
+ )
347
+ result = await cls.mutate_update_to_graphql(db=dbt, info=info, obj=reconciled_prefix)
360
348
 
361
349
  return prefix, result
362
350
 
@@ -366,15 +354,15 @@ class InfrahubIPPrefixMutation(InfrahubMutationMixin, Mutation):
366
354
  info: GraphQLResolveInfo,
367
355
  data: InputObjectType,
368
356
  branch: Branch,
369
- node_getters: list[MutationNodeGetterInterface],
370
- database: Optional[InfrahubDatabase] = None,
371
- ):
357
+ node_getter_default_filter: MutationNodeGetterByDefaultFilter,
358
+ database: InfrahubDatabase | None = None,
359
+ ) -> tuple[Node, Self, bool]:
372
360
  graphql_context: GraphqlContext = info.context
373
361
  db = database or graphql_context.db
374
362
 
375
363
  await validate_namespace(db=db, branch=branch, data=data)
376
364
  prefix, result, created = await super().mutate_upsert(
377
- info=info, data=data, branch=branch, node_getters=node_getters, database=db
365
+ info=info, data=data, branch=branch, node_getter_default_filter=node_getter_default_filter, database=db
378
366
  )
379
367
 
380
368
  return prefix, result, created
@@ -414,21 +402,13 @@ class InfrahubIPPrefixMutation(InfrahubMutationMixin, Mutation):
414
402
 
415
403
  namespace_rels = await prefix.ip_namespace.get_relationships(db=db)
416
404
  namespace_id = namespace_rels[0].peer_id
417
- try:
418
- async with graphql_context.db.start_transaction() as dbt:
419
- if lock_name := cls._get_lock_name(namespace_id, branch):
420
- async with InfrahubMultiLock(lock_registry=lock.registry, locks=[lock_name]):
421
- reconciled_prefix = await cls._reconcile_prefix(
422
- branch=branch, db=dbt, prefix=prefix, namespace_id=namespace_id, is_delete=True
423
- )
424
- else:
425
- reconciled_prefix = await cls._reconcile_prefix(
426
- branch=branch, db=dbt, prefix=prefix, namespace_id=namespace_id, is_delete=True
427
- )
428
-
429
- except ValidationError as exc:
430
- raise ValueError(str(exc)) from exc
431
405
 
406
+ async with graphql_context.db.start_transaction() as dbt:
407
+ lock_name = cls._get_lock_name(namespace_id)
408
+ async with InfrahubMultiLock(lock_registry=lock.registry, locks=[lock_name]):
409
+ reconciled_prefix = await cls._reconcile_prefix(
410
+ branch=branch, db=dbt, prefix=prefix, namespace_id=namespace_id, is_delete=True
411
+ )
432
412
  ok = True
433
413
 
434
414
  return DeleteResult(node=reconciled_prefix, mutation=cls(ok=ok))