infrahub-server 1.3.8__py3-none-any.whl → 1.4.0__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 (172) hide show
  1. infrahub/api/internal.py +5 -0
  2. infrahub/artifacts/tasks.py +17 -22
  3. infrahub/branch/merge_mutation_checker.py +38 -0
  4. infrahub/cli/__init__.py +2 -2
  5. infrahub/cli/context.py +7 -3
  6. infrahub/cli/db.py +5 -16
  7. infrahub/cli/upgrade.py +10 -29
  8. infrahub/computed_attribute/tasks.py +36 -46
  9. infrahub/config.py +57 -6
  10. infrahub/constants/environment.py +1 -0
  11. infrahub/core/attribute.py +15 -7
  12. infrahub/core/branch/tasks.py +43 -41
  13. infrahub/core/constants/__init__.py +21 -6
  14. infrahub/core/constants/infrahubkind.py +2 -0
  15. infrahub/core/diff/coordinator.py +3 -1
  16. infrahub/core/diff/repository/repository.py +0 -8
  17. infrahub/core/diff/tasks.py +11 -8
  18. infrahub/core/graph/__init__.py +1 -1
  19. infrahub/core/graph/index.py +1 -2
  20. infrahub/core/graph/schema.py +50 -29
  21. infrahub/core/initialization.py +81 -47
  22. infrahub/core/ipam/tasks.py +4 -3
  23. infrahub/core/merge.py +8 -10
  24. infrahub/core/migrations/__init__.py +2 -0
  25. infrahub/core/migrations/graph/__init__.py +4 -0
  26. infrahub/core/migrations/graph/m036_drop_attr_value_index.py +45 -0
  27. infrahub/core/migrations/graph/m037_index_attr_vals.py +577 -0
  28. infrahub/core/migrations/query/attribute_add.py +27 -2
  29. infrahub/core/migrations/schema/attribute_kind_update.py +156 -0
  30. infrahub/core/migrations/schema/tasks.py +6 -5
  31. infrahub/core/models.py +5 -1
  32. infrahub/core/node/proposed_change.py +43 -0
  33. infrahub/core/protocols.py +12 -0
  34. infrahub/core/query/attribute.py +32 -14
  35. infrahub/core/query/diff.py +11 -0
  36. infrahub/core/query/ipam.py +13 -7
  37. infrahub/core/query/node.py +51 -10
  38. infrahub/core/query/resource_manager.py +3 -3
  39. infrahub/core/schema/basenode_schema.py +8 -0
  40. infrahub/core/schema/definitions/core/__init__.py +10 -1
  41. infrahub/core/schema/definitions/core/ipam.py +28 -2
  42. infrahub/core/schema/definitions/core/propose_change.py +15 -0
  43. infrahub/core/schema/definitions/core/webhook.py +3 -0
  44. infrahub/core/schema/definitions/internal.py +1 -1
  45. infrahub/core/schema/generated/attribute_schema.py +1 -1
  46. infrahub/core/schema/generic_schema.py +10 -0
  47. infrahub/core/schema/manager.py +10 -1
  48. infrahub/core/schema/node_schema.py +22 -17
  49. infrahub/core/schema/profile_schema.py +8 -0
  50. infrahub/core/schema/schema_branch.py +9 -5
  51. infrahub/core/schema/template_schema.py +8 -0
  52. infrahub/core/validators/attribute/kind.py +5 -1
  53. infrahub/core/validators/checks_runner.py +5 -5
  54. infrahub/core/validators/determiner.py +22 -2
  55. infrahub/core/validators/tasks.py +6 -7
  56. infrahub/core/validators/uniqueness/checker.py +4 -2
  57. infrahub/core/validators/uniqueness/model.py +1 -0
  58. infrahub/core/validators/uniqueness/query.py +57 -7
  59. infrahub/database/__init__.py +2 -1
  60. infrahub/events/__init__.py +20 -0
  61. infrahub/events/constants.py +7 -0
  62. infrahub/events/generator.py +29 -2
  63. infrahub/events/proposed_change_action.py +203 -0
  64. infrahub/generators/tasks.py +24 -20
  65. infrahub/git/base.py +4 -7
  66. infrahub/git/integrator.py +21 -12
  67. infrahub/git/repository.py +15 -30
  68. infrahub/git/tasks.py +121 -106
  69. infrahub/graphql/field_extractor.py +69 -0
  70. infrahub/graphql/manager.py +15 -11
  71. infrahub/graphql/mutations/account.py +2 -2
  72. infrahub/graphql/mutations/action.py +8 -2
  73. infrahub/graphql/mutations/artifact_definition.py +4 -1
  74. infrahub/graphql/mutations/branch.py +10 -5
  75. infrahub/graphql/mutations/graphql_query.py +2 -1
  76. infrahub/graphql/mutations/main.py +14 -8
  77. infrahub/graphql/mutations/menu.py +2 -1
  78. infrahub/graphql/mutations/proposed_change.py +230 -8
  79. infrahub/graphql/mutations/relationship.py +5 -0
  80. infrahub/graphql/mutations/repository.py +2 -1
  81. infrahub/graphql/mutations/tasks.py +7 -9
  82. infrahub/graphql/mutations/webhook.py +4 -1
  83. infrahub/graphql/parser.py +15 -6
  84. infrahub/graphql/queries/__init__.py +10 -1
  85. infrahub/graphql/queries/account.py +3 -3
  86. infrahub/graphql/queries/branch.py +2 -2
  87. infrahub/graphql/queries/diff/tree.py +3 -3
  88. infrahub/graphql/queries/event.py +13 -3
  89. infrahub/graphql/queries/ipam.py +23 -1
  90. infrahub/graphql/queries/proposed_change.py +84 -0
  91. infrahub/graphql/queries/relationship.py +2 -2
  92. infrahub/graphql/queries/resource_manager.py +3 -3
  93. infrahub/graphql/queries/search.py +3 -2
  94. infrahub/graphql/queries/status.py +3 -2
  95. infrahub/graphql/queries/task.py +2 -2
  96. infrahub/graphql/resolvers/ipam.py +440 -0
  97. infrahub/graphql/resolvers/many_relationship.py +4 -3
  98. infrahub/graphql/resolvers/resolver.py +5 -5
  99. infrahub/graphql/resolvers/single_relationship.py +3 -2
  100. infrahub/graphql/schema.py +25 -5
  101. infrahub/graphql/types/__init__.py +2 -2
  102. infrahub/graphql/types/attribute.py +3 -3
  103. infrahub/graphql/types/event.py +68 -0
  104. infrahub/groups/tasks.py +6 -6
  105. infrahub/lock.py +3 -2
  106. infrahub/menu/generator.py +8 -0
  107. infrahub/message_bus/operations/__init__.py +9 -12
  108. infrahub/message_bus/operations/git/file.py +6 -5
  109. infrahub/message_bus/operations/git/repository.py +12 -20
  110. infrahub/message_bus/operations/refresh/registry.py +15 -9
  111. infrahub/message_bus/operations/send/echo.py +7 -4
  112. infrahub/message_bus/types.py +1 -0
  113. infrahub/permissions/__init__.py +2 -1
  114. infrahub/permissions/constants.py +13 -0
  115. infrahub/permissions/globals.py +31 -2
  116. infrahub/permissions/manager.py +8 -5
  117. infrahub/pools/prefix.py +7 -5
  118. infrahub/prefect_server/app.py +31 -0
  119. infrahub/prefect_server/bootstrap.py +18 -0
  120. infrahub/proposed_change/action_checker.py +206 -0
  121. infrahub/proposed_change/approval_revoker.py +40 -0
  122. infrahub/proposed_change/branch_diff.py +3 -1
  123. infrahub/proposed_change/checker.py +45 -0
  124. infrahub/proposed_change/constants.py +32 -2
  125. infrahub/proposed_change/tasks.py +182 -150
  126. infrahub/py.typed +0 -0
  127. infrahub/server.py +29 -17
  128. infrahub/services/__init__.py +13 -28
  129. infrahub/services/adapters/cache/__init__.py +4 -0
  130. infrahub/services/adapters/cache/nats.py +2 -0
  131. infrahub/services/adapters/cache/redis.py +3 -0
  132. infrahub/services/adapters/message_bus/__init__.py +0 -2
  133. infrahub/services/adapters/message_bus/local.py +1 -2
  134. infrahub/services/adapters/message_bus/nats.py +6 -8
  135. infrahub/services/adapters/message_bus/rabbitmq.py +7 -9
  136. infrahub/services/adapters/workflow/__init__.py +1 -0
  137. infrahub/services/adapters/workflow/local.py +1 -8
  138. infrahub/services/component.py +2 -1
  139. infrahub/task_manager/event.py +56 -0
  140. infrahub/task_manager/models.py +9 -0
  141. infrahub/tasks/artifact.py +6 -7
  142. infrahub/tasks/check.py +4 -7
  143. infrahub/telemetry/tasks.py +15 -18
  144. infrahub/transformations/tasks.py +10 -6
  145. infrahub/trigger/tasks.py +4 -3
  146. infrahub/types.py +4 -0
  147. infrahub/validators/events.py +7 -7
  148. infrahub/validators/tasks.py +6 -7
  149. infrahub/webhook/models.py +45 -45
  150. infrahub/webhook/tasks.py +25 -24
  151. infrahub/workers/dependencies.py +143 -0
  152. infrahub/workers/infrahub_async.py +19 -43
  153. infrahub/workflows/catalogue.py +16 -2
  154. infrahub/workflows/initialization.py +5 -4
  155. infrahub/workflows/models.py +2 -0
  156. infrahub_sdk/client.py +2 -2
  157. infrahub_sdk/ctl/repository.py +51 -0
  158. infrahub_sdk/ctl/schema.py +9 -9
  159. infrahub_sdk/node/node.py +2 -2
  160. infrahub_sdk/pytest_plugin/items/graphql_query.py +1 -1
  161. infrahub_sdk/schema/repository.py +1 -1
  162. infrahub_sdk/testing/docker.py +1 -1
  163. infrahub_sdk/utils.py +2 -2
  164. {infrahub_server-1.3.8.dist-info → infrahub_server-1.4.0.dist-info}/METADATA +7 -5
  165. {infrahub_server-1.3.8.dist-info → infrahub_server-1.4.0.dist-info}/RECORD +172 -156
  166. infrahub_testcontainers/container.py +17 -0
  167. infrahub_testcontainers/docker-compose-cluster.test.yml +56 -1
  168. infrahub_testcontainers/docker-compose.test.yml +56 -1
  169. infrahub_testcontainers/helpers.py +4 -1
  170. {infrahub_server-1.3.8.dist-info → infrahub_server-1.4.0.dist-info}/LICENSE.txt +0 -0
  171. {infrahub_server-1.3.8.dist-info → infrahub_server-1.4.0.dist-info}/WHEEL +0 -0
  172. {infrahub_server-1.3.8.dist-info → infrahub_server-1.4.0.dist-info}/entry_points.txt +0 -0
@@ -35,7 +35,14 @@ from .group import (
35
35
  core_repository_group,
36
36
  core_standard_group,
37
37
  )
38
- from .ipam import builtin_ip_address, builtin_ip_prefix, builtin_ipam, core_ipam_namespace
38
+ from .ipam import (
39
+ builtin_ip_address,
40
+ builtin_ip_prefix,
41
+ builtin_ipam,
42
+ core_ipam_namespace,
43
+ internal_ipam_ip_prefix_available,
44
+ internal_ipam_ip_range_available,
45
+ )
39
46
  from .lineage import lineage_owner, lineage_source
40
47
  from .menu import generic_menu_item, menu_item
41
48
  from .permission import (
@@ -172,6 +179,8 @@ core_models_mixed: dict[str, list] = {
172
179
  core_object_permission,
173
180
  core_account_role,
174
181
  core_account_group,
182
+ internal_ipam_ip_prefix_available,
183
+ internal_ipam_ip_range_available,
175
184
  ],
176
185
  }
177
186
 
@@ -59,7 +59,7 @@ builtin_ip_prefix = GenericSchema(
59
59
  name="IPPrefix",
60
60
  label="IP Prefix",
61
61
  namespace="Builtin",
62
- description="IPv6 or IPv4 prefix also referred as network",
62
+ description="IPv4 or IPv6 prefix also referred as network",
63
63
  include_in_menu=False,
64
64
  default_filter="prefix__value",
65
65
  order_by=["prefix__version", "prefix__binary_address", "prefix__prefixlen"],
@@ -142,7 +142,7 @@ builtin_ip_address = GenericSchema(
142
142
  name="IPAddress",
143
143
  label="IP Address",
144
144
  namespace="Builtin",
145
- description="IPv6 or IPv4 address",
145
+ description="IPv4 or IPv6 address",
146
146
  include_in_menu=False,
147
147
  default_filter="address__value",
148
148
  order_by=["address__version", "address__binary_address"],
@@ -176,6 +176,32 @@ builtin_ip_address = GenericSchema(
176
176
  ],
177
177
  )
178
178
 
179
+ internal_ipam_ip_range_available = NodeSchema(
180
+ name="IPRangeAvailable",
181
+ label="Available IP Range",
182
+ namespace="Internal",
183
+ description="Range of IPv4 or IPv6 addresses which has not been allocated yet",
184
+ include_in_menu=False,
185
+ display_labels=["address__value", "last_address__value"],
186
+ branch=BranchSupportType.AWARE,
187
+ inherit_from=[InfrahubKind.IPADDRESS],
188
+ generate_profile=False,
189
+ attributes=[Attr(name="last_address", kind="IPHost", branch=BranchSupportType.AWARE, order_weight=2000)],
190
+ )
191
+
192
+ internal_ipam_ip_prefix_available = NodeSchema(
193
+ name="IPPrefixAvailable",
194
+ label="Available IP Prefix",
195
+ namespace="Internal",
196
+ description="IPv4 or IPv6 prefix also referred as network which has not been allocated yet",
197
+ include_in_menu=False,
198
+ display_labels=["prefix__value"],
199
+ branch=BranchSupportType.AWARE,
200
+ inherit_from=[InfrahubKind.IPPREFIX],
201
+ generate_profile=False,
202
+ )
203
+
204
+
179
205
  core_ipam_namespace = NodeSchema(
180
206
  name="Namespace",
181
207
  namespace="Ipam",
@@ -38,6 +38,9 @@ core_proposed_change = NodeSchema(
38
38
  default_value=ProposedChangeState.OPEN.value,
39
39
  optional=True,
40
40
  ),
41
+ Attr(name="is_draft", kind="Boolean", optional=False, default_value=False),
42
+ # Ideally we should support some "runtime-attribute" that could not even be stored in the database.
43
+ Attr(name="total_comments", kind="Number", optional=True, read_only=True),
41
44
  ],
42
45
  relationships=[
43
46
  Rel(
@@ -48,6 +51,17 @@ core_proposed_change = NodeSchema(
48
51
  kind=RelKind.ATTRIBUTE,
49
52
  branch=BranchSupportType.AGNOSTIC,
50
53
  identifier="coreaccount__proposedchange_approved_by",
54
+ read_only=True,
55
+ ),
56
+ Rel(
57
+ name="rejected_by",
58
+ peer=InfrahubKind.GENERICACCOUNT,
59
+ optional=True,
60
+ cardinality=Cardinality.MANY,
61
+ kind=RelKind.ATTRIBUTE,
62
+ branch=BranchSupportType.AGNOSTIC,
63
+ identifier="coreaccount__proposedchange_rejected_by",
64
+ read_only=True,
51
65
  ),
52
66
  Rel(
53
67
  name="reviewers",
@@ -66,6 +80,7 @@ core_proposed_change = NodeSchema(
66
80
  kind=RelKind.ATTRIBUTE,
67
81
  branch=BranchSupportType.AGNOSTIC,
68
82
  identifier="coreaccount__proposedchange_created_by",
83
+ read_only=True,
69
84
  ),
70
85
  Rel(
71
86
  name="comments",
@@ -120,6 +120,9 @@ core_custom_webhook = NodeSchema(
120
120
  branch=BranchSupportType.AGNOSTIC,
121
121
  generate_profile=False,
122
122
  inherit_from=[InfrahubKind.WEBHOOK, InfrahubKind.TASKTARGET],
123
+ attributes=[
124
+ Attr(name="shared_key", kind="Password", unique=False, optional=True, order_weight=4000),
125
+ ],
123
126
  relationships=[
124
127
  Rel(
125
128
  name="transformation",
@@ -487,7 +487,7 @@ attribute_schema = SchemaNode(
487
487
  kind="Text",
488
488
  description="Defines the type of the attribute.",
489
489
  enum=ATTRIBUTE_KIND_LABELS,
490
- extra={"update": UpdateSupport.VALIDATE_CONSTRAINT},
490
+ extra={"update": UpdateSupport.MIGRATION_REQUIRED},
491
491
  ),
492
492
  SchemaAttribute(
493
493
  name="enum",
@@ -31,7 +31,7 @@ class GeneratedAttributeSchema(HashableModel):
31
31
  json_schema_extra={"update": "migration_required"},
32
32
  )
33
33
  kind: str = Field(
34
- ..., description="Defines the type of the attribute.", json_schema_extra={"update": "validate_constraint"}
34
+ ..., description="Defines the type of the attribute.", json_schema_extra={"update": "migration_required"}
35
35
  )
36
36
  enum: list | None = Field(
37
37
  default=None,
@@ -2,6 +2,8 @@ from __future__ import annotations
2
2
 
3
3
  from typing import TYPE_CHECKING
4
4
 
5
+ from infrahub.core.constants import InfrahubKind
6
+
5
7
  from .generated.genericnode_schema import GeneratedGenericSchema
6
8
 
7
9
  if TYPE_CHECKING:
@@ -28,6 +30,14 @@ class GenericSchema(GeneratedGenericSchema):
28
30
  def is_template_schema(self) -> bool:
29
31
  return False
30
32
 
33
+ @property
34
+ def is_ip_prefix(self) -> bool:
35
+ return self.kind == InfrahubKind.IPPREFIX
36
+
37
+ @property
38
+ def is_ip_address(self) -> bool:
39
+ return self.kind == InfrahubKind.IPADDRESS
40
+
31
41
  def get_hierarchy_schema(self, db: InfrahubDatabase, branch: Branch | str | None = None) -> GenericSchema: # noqa: ARG002
32
42
  if self.hierarchical:
33
43
  return self
@@ -93,6 +93,15 @@ class SchemaManager(NodeManager):
93
93
 
94
94
  raise ValueError("The selected node is not of type NodeSchema")
95
95
 
96
+ def get_generic_schema(
97
+ self, name: str, branch: Branch | str | None = None, duplicate: bool = True
98
+ ) -> GenericSchema:
99
+ schema = self.get(name=name, branch=branch, duplicate=duplicate)
100
+ if isinstance(schema, GenericSchema):
101
+ return schema
102
+
103
+ raise ValueError("The selected node is not of type GenericSchema")
104
+
96
105
  def get_profile_schema(
97
106
  self, name: str, branch: Branch | str | None = None, duplicate: bool = True
98
107
  ) -> ProfileSchema:
@@ -122,7 +131,7 @@ class SchemaManager(NodeManager):
122
131
 
123
132
  return self._branches[branch_name].get_all(duplicate=duplicate)
124
133
 
125
- async def get_full_safe(self, branch: Branch | str | None = None) -> dict[str, NodeSchema | GenericSchema]:
134
+ async def get_full_safe(self, branch: Branch | str | None = None) -> dict[str, MainSchemaTypes]:
126
135
  await lock.registry.local_schema_wait()
127
136
 
128
137
  return self.get_full(branch=branch)
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from typing import TYPE_CHECKING
4
4
 
5
- from infrahub.core.constants import AllowOverrideType, InfrahubKind
5
+ from infrahub.core.constants import AllowOverrideType, InfrahubKind, RelationshipKind
6
6
 
7
7
  from .generated.node_schema import GeneratedNodeSchema
8
8
  from .generic_schema import GenericSchema
@@ -29,6 +29,16 @@ class NodeSchema(GeneratedNodeSchema):
29
29
  def is_template_schema(self) -> bool:
30
30
  return False
31
31
 
32
+ @property
33
+ def is_ip_prefix(self) -> bool:
34
+ """Return whether a node is a derivative of built-in IP prefixes."""
35
+ return InfrahubKind.IPPREFIX in self.inherit_from
36
+
37
+ @property
38
+ def is_ip_address(self) -> bool:
39
+ """Return whether a node is a derivative of built-in IP addreses."""
40
+ return InfrahubKind.IPADDRESS in self.inherit_from
41
+
32
42
  def validate_inheritance(self, interface: GenericSchema) -> None:
33
43
  """Perform checks specific to inheritance from Generics.
34
44
 
@@ -55,14 +65,17 @@ class NodeSchema(GeneratedNodeSchema):
55
65
  )
56
66
 
57
67
  for relationship in self.relationships:
58
- if (
59
- relationship.name in interface.relationship_names
60
- and not relationship.inherited
61
- and interface.get_relationship(relationship.name).allow_override == AllowOverrideType.NONE
62
- ):
63
- raise ValueError(
64
- f"{self.kind}'s relationship {relationship.name} inherited from {interface.kind} cannot be overriden"
65
- )
68
+ if relationship.name in interface.relationship_names and not relationship.inherited:
69
+ interface_relationship = interface.get_relationship(relationship.name)
70
+ if interface_relationship.allow_override == AllowOverrideType.NONE:
71
+ raise ValueError(
72
+ f"{self.kind}'s relationship {relationship.name} inherited from {interface.kind} cannot be overriden"
73
+ )
74
+ if relationship.kind != RelationshipKind.HIERARCHY and relationship.peer != interface_relationship.peer:
75
+ raise ValueError(
76
+ f"{self.kind}'s relationship {relationship.name} inherited from {interface.kind} must have the same peer "
77
+ f"({interface_relationship.peer} != {relationship.peer})"
78
+ )
66
79
 
67
80
  def inherit_from_interface(self, interface: GenericSchema) -> None:
68
81
  existing_inherited_attributes: dict[str, int] = {
@@ -132,11 +145,3 @@ class NodeSchema(GeneratedNodeSchema):
132
145
  if self.namespace not in ["Schema", "Internal"] and InfrahubKind.GENERICGROUP not in self.inherit_from:
133
146
  labels.append(InfrahubKind.NODE)
134
147
  return labels
135
-
136
- def is_ip_prefix(self) -> bool:
137
- """Return whether a node is a derivative of built-in IP prefixes."""
138
- return InfrahubKind.IPPREFIX in self.inherit_from
139
-
140
- def is_ip_address(self) -> bool:
141
- """Return whether a node is a derivative of built-in IP addreses."""
142
- return InfrahubKind.IPADDRESS in self.inherit_from
@@ -28,6 +28,14 @@ class ProfileSchema(BaseNodeSchema):
28
28
  def is_template_schema(self) -> bool:
29
29
  return False
30
30
 
31
+ @property
32
+ def is_ip_prefix(self) -> bool:
33
+ return False
34
+
35
+ @property
36
+ def is_ip_address(self) -> bool:
37
+ return False
38
+
31
39
  def get_labels(self) -> list[str]:
32
40
  """Return the labels for this object, composed of the kind
33
41
  and the list of Generic this object is inheriting from."""
@@ -119,7 +119,7 @@ class SchemaBranch:
119
119
  def template_names(self) -> list[str]:
120
120
  return list(self.templates.keys())
121
121
 
122
- def get_all_kind_id_map(self, nodes_and_generics_only: bool = False) -> dict[str, str]:
122
+ def get_all_kind_id_map(self, nodes_and_generics_only: bool = False) -> dict[str, str | None]:
123
123
  kind_id_map = {}
124
124
  if nodes_and_generics_only:
125
125
  names = self.node_names + self.generic_names_without_templates
@@ -441,7 +441,7 @@ class SchemaBranch:
441
441
  return list(all_schemas.values())
442
442
 
443
443
  def get_schemas_by_rel_identifier(self, identifier: str) -> list[MainSchemaTypes]:
444
- nodes: list[RelationshipSchema] = []
444
+ nodes: list[MainSchemaTypes] = []
445
445
  for node_name in list(self.nodes.keys()) + list(self.generics.keys()):
446
446
  node = self.get(name=node_name, duplicate=False)
447
447
  rel = node.get_relationship_by_identifier(id=identifier, raise_on_error=False)
@@ -660,7 +660,7 @@ class SchemaBranch:
660
660
  and not (
661
661
  schema_attribute_path.relationship_schema.name == "ip_namespace"
662
662
  and isinstance(node_schema, NodeSchema)
663
- and (node_schema.is_ip_address() or node_schema.is_ip_prefix())
663
+ and (node_schema.is_ip_address or node_schema.is_ip_prefix)
664
664
  )
665
665
  ):
666
666
  raise ValueError(
@@ -1447,7 +1447,8 @@ class SchemaBranch:
1447
1447
  node.validate_inheritance(interface=generic_kind_schema)
1448
1448
 
1449
1449
  # Store the list of node referencing a specific generics
1450
- generics_used_by[generic_kind].append(node.kind)
1450
+ if node.namespace != "Internal":
1451
+ generics_used_by[generic_kind].append(node.kind)
1451
1452
  node.inherit_from_interface(interface=generic_kind_schema)
1452
1453
 
1453
1454
  if len(generic_with_hierarchical_support) > 1:
@@ -1925,7 +1926,10 @@ class SchemaBranch:
1925
1926
  for node_name in self.node_names + self.generic_names:
1926
1927
  node = self.get(name=node_name, duplicate=False)
1927
1928
 
1928
- if node.namespace in RESTRICTED_NAMESPACES:
1929
+ if node.namespace in RESTRICTED_NAMESPACES and node.kind not in (
1930
+ InfrahubKind.IPRANGEAVAILABLE,
1931
+ InfrahubKind.IPPREFIXAVAILABLE,
1932
+ ):
1929
1933
  continue
1930
1934
 
1931
1935
  profiles_rel_settings: dict[str, Any] = {
@@ -27,6 +27,14 @@ class TemplateSchema(BaseNodeSchema):
27
27
  def is_template_schema(self) -> bool:
28
28
  return True
29
29
 
30
+ @property
31
+ def is_ip_prefix(self) -> bool:
32
+ return False
33
+
34
+ @property
35
+ def is_ip_address(self) -> bool:
36
+ return False
37
+
30
38
  def get_labels(self) -> list[str]:
31
39
  """Return the labels for this object, composed of the kind and the list of Generic this object is inheriting from."""
32
40
 
@@ -65,8 +65,12 @@ class AttributeKindUpdateValidatorQuery(AttributeSchemaValidatorQuery):
65
65
  if value in (None, NULL_VALUE):
66
66
  continue
67
67
  try:
68
+ attr_value = result.get("attribute_value")
68
69
  infrahub_attribute_class.validate_format(
69
- value=result.get("attribute_value"), name=self.attribute_schema.name, schema=self.attribute_schema
70
+ value=attr_value, name=self.attribute_schema.name, schema=self.attribute_schema
71
+ )
72
+ infrahub_attribute_class.validate_content(
73
+ value=attr_value, name=self.attribute_schema.name, schema=self.attribute_schema
70
74
  )
71
75
  except ValidationError:
72
76
  grouped_data_paths.add_data_path(
@@ -6,7 +6,7 @@ from infrahub_sdk.protocols import CoreValidator
6
6
  from infrahub.context import InfrahubContext
7
7
  from infrahub.core.constants import ValidatorConclusion, ValidatorState
8
8
  from infrahub.core.timestamp import Timestamp
9
- from infrahub.services import InfrahubServices
9
+ from infrahub.services.adapters.event import InfrahubEventService
10
10
  from infrahub.validators.events import send_failed_validator, send_passed_validator
11
11
 
12
12
 
@@ -14,7 +14,7 @@ async def run_checks_and_update_validator(
14
14
  checks: list[Coroutine[Any, None, ValidatorConclusion]],
15
15
  validator: CoreValidator,
16
16
  context: InfrahubContext,
17
- service: InfrahubServices,
17
+ event_service: InfrahubEventService,
18
18
  proposed_change_id: str,
19
19
  ) -> None:
20
20
  """
@@ -38,7 +38,7 @@ async def run_checks_and_update_validator(
38
38
  failed_early = True
39
39
  await validator.save()
40
40
  await send_failed_validator(
41
- service=service, validator=validator, proposed_change_id=proposed_change_id, context=context
41
+ event_service=event_service, validator=validator, proposed_change_id=proposed_change_id, context=context
42
42
  )
43
43
  # Continue to iterate to wait for the end of all checks
44
44
 
@@ -52,9 +52,9 @@ async def run_checks_and_update_validator(
52
52
  if not failed_early:
53
53
  if validator.conclusion.value == ValidatorConclusion.SUCCESS.value:
54
54
  await send_passed_validator(
55
- service=service, validator=validator, proposed_change_id=proposed_change_id, context=context
55
+ event_service=event_service, validator=validator, proposed_change_id=proposed_change_id, context=context
56
56
  )
57
57
  else:
58
58
  await send_failed_validator(
59
- service=service, validator=validator, proposed_change_id=proposed_change_id, context=context
59
+ event_service=event_service, validator=validator, proposed_change_id=proposed_change_id, context=context
60
60
  )
@@ -98,7 +98,10 @@ class ConstraintValidatorDeterminer:
98
98
  continue
99
99
 
100
100
  prop_field_update = prop_field_info.json_schema_extra.get("update")
101
- if prop_field_update != UpdateSupport.VALIDATE_CONSTRAINT.value:
101
+ if prop_field_update not in (
102
+ UpdateSupport.VALIDATE_CONSTRAINT.value,
103
+ UpdateSupport.MIGRATION_REQUIRED.value,
104
+ ):
102
105
  continue
103
106
 
104
107
  if getattr(schema, prop_name) is None:
@@ -112,6 +115,13 @@ class ConstraintValidatorDeterminer:
112
115
  )
113
116
  constraint_name = f"node.{prop_name}.update"
114
117
 
118
+ do_constraint_validation = prop_field_update == UpdateSupport.VALIDATE_CONSTRAINT.value or (
119
+ prop_field_update == UpdateSupport.MIGRATION_REQUIRED.value
120
+ and CONSTRAINT_VALIDATOR_MAP.get(constraint_name)
121
+ )
122
+ if not do_constraint_validation:
123
+ continue
124
+
115
125
  constraints.append(SchemaUpdateConstraintInfo(constraint_name=constraint_name, path=schema_path))
116
126
  return constraints
117
127
 
@@ -154,7 +164,10 @@ class ConstraintValidatorDeterminer:
154
164
  continue
155
165
 
156
166
  prop_field_update = prop_field_info.json_schema_extra.get("update")
157
- if prop_field_update != UpdateSupport.VALIDATE_CONSTRAINT.value:
167
+ if prop_field_update not in (
168
+ UpdateSupport.VALIDATE_CONSTRAINT.value,
169
+ UpdateSupport.MIGRATION_REQUIRED.value,
170
+ ):
158
171
  continue
159
172
 
160
173
  if prop_value is None:
@@ -168,6 +181,13 @@ class ConstraintValidatorDeterminer:
168
181
  path_type = SchemaPathType.RELATIONSHIP
169
182
  constraint_name = f"relationship.{prop_name}.update"
170
183
 
184
+ do_constraint_validation = prop_field_update == UpdateSupport.VALIDATE_CONSTRAINT.value or (
185
+ prop_field_update == UpdateSupport.MIGRATION_REQUIRED.value
186
+ and CONSTRAINT_VALIDATOR_MAP.get(constraint_name)
187
+ )
188
+ if not do_constraint_validation:
189
+ continue
190
+
171
191
  schema_path = SchemaPath(
172
192
  schema_kind=schema.kind,
173
193
  path_type=path_type,
@@ -13,19 +13,18 @@ from infrahub.core.schema import GenericSchema, NodeSchema
13
13
  from infrahub.core.validators.aggregated_checker import AggregatedConstraintChecker
14
14
  from infrahub.core.validators.model import SchemaConstraintValidatorRequest, SchemaViolation
15
15
  from infrahub.dependencies.registry import get_component_registry
16
- from infrahub.services import InfrahubServices # noqa: TC001 needed for prefect flow
16
+ from infrahub.workers.dependencies import get_database
17
17
  from infrahub.workflows.utils import add_tags
18
18
 
19
19
  from .models.validate_migration import SchemaValidateMigrationData, SchemaValidatorPathResponseData
20
20
 
21
21
  if TYPE_CHECKING:
22
22
  from infrahub.core.schema.schema_branch import SchemaBranch
23
+ from infrahub.database import InfrahubDatabase
23
24
 
24
25
 
25
26
  @flow(name="schema_validate_migrations", flow_run_name="Validate schema migrations", persist_result=True)
26
- async def schema_validate_migrations(
27
- message: SchemaValidateMigrationData, service: InfrahubServices
28
- ) -> list[SchemaValidatorPathResponseData]:
27
+ async def schema_validate_migrations(message: SchemaValidateMigrationData) -> list[SchemaValidatorPathResponseData]:
29
28
  batch = InfrahubBatch(return_exceptions=True)
30
29
  log = get_run_logger()
31
30
  await add_tags(branches=[message.branch.name])
@@ -47,7 +46,7 @@ async def schema_validate_migrations(
47
46
  node_schema=schema,
48
47
  schema_path=constraint.path,
49
48
  schema_branch=message.schema_branch,
50
- service=service,
49
+ database=await get_database(),
51
50
  )
52
51
 
53
52
  results = [result async for _, result in batch.execute()]
@@ -67,9 +66,9 @@ async def schema_path_validate(
67
66
  node_schema: NodeSchema | GenericSchema,
68
67
  schema_path: SchemaPath,
69
68
  schema_branch: SchemaBranch,
70
- service: InfrahubServices,
69
+ database: InfrahubDatabase,
71
70
  ) -> SchemaValidatorPathResponseData:
72
- async with service.database.start_session(read_only=True) as db:
71
+ async with database.start_session(read_only=True) as db:
73
72
  constraint_request = SchemaConstraintValidatorRequest(
74
73
  branch=branch,
75
74
  constraint_name=constraint_name,
@@ -75,7 +75,7 @@ class UniquenessChecker(ConstraintCheckerInterface):
75
75
 
76
76
  async def build_query_request(self, schema: MainSchemaTypes) -> NodeUniquenessQueryRequest:
77
77
  unique_attr_paths = {
78
- QueryAttributePath(attribute_name=attr_schema.name, property_name="value")
78
+ QueryAttributePath(attribute_name=attr_schema.name, attribute_kind=attr_schema.kind, property_name="value")
79
79
  for attr_schema in schema.unique_attributes
80
80
  }
81
81
  relationship_attr_paths = set()
@@ -92,7 +92,9 @@ class UniquenessChecker(ConstraintCheckerInterface):
92
92
  sub_schema, property_name = get_attribute_path_from_string(path, schema)
93
93
  if isinstance(sub_schema, AttributeSchema):
94
94
  unique_attr_paths.add(
95
- QueryAttributePath(attribute_name=sub_schema.name, property_name=property_name)
95
+ QueryAttributePath(
96
+ attribute_name=sub_schema.name, attribute_kind=sub_schema.kind, property_name=property_name
97
+ )
96
98
  )
97
99
  elif isinstance(sub_schema, RelationshipSchema):
98
100
  relationship_attr_paths.add(
@@ -22,6 +22,7 @@ class QueryRelationshipAttributePath(BaseModel):
22
22
 
23
23
  class QueryAttributePath(BaseModel):
24
24
  attribute_name: str
25
+ attribute_kind: str
25
26
  property_name: str | None = Field(default=None)
26
27
  value: Any | None = Field(default=None)
27
28