infrahub-server 1.3.6__py3-none-any.whl → 1.4.0b1__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/api/internal.py +5 -0
- infrahub/artifacts/tasks.py +17 -22
- infrahub/branch/merge_mutation_checker.py +38 -0
- infrahub/cli/__init__.py +2 -2
- infrahub/cli/context.py +7 -3
- infrahub/cli/db.py +5 -41
- infrahub/cli/upgrade.py +7 -29
- infrahub/computed_attribute/tasks.py +36 -46
- infrahub/config.py +53 -2
- infrahub/constants/environment.py +1 -0
- infrahub/core/attribute.py +9 -7
- infrahub/core/branch/tasks.py +43 -41
- infrahub/core/constants/__init__.py +20 -6
- infrahub/core/constants/infrahubkind.py +2 -0
- infrahub/core/diff/coordinator.py +3 -1
- infrahub/core/diff/repository/repository.py +0 -8
- infrahub/core/diff/tasks.py +11 -8
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/graph/index.py +1 -2
- infrahub/core/graph/schema.py +50 -29
- infrahub/core/initialization.py +62 -33
- infrahub/core/ipam/tasks.py +4 -3
- infrahub/core/manager.py +2 -2
- infrahub/core/merge.py +8 -10
- infrahub/core/migrations/graph/__init__.py +4 -0
- infrahub/core/migrations/graph/m035_drop_attr_value_index.py +45 -0
- infrahub/core/migrations/graph/m036_index_attr_vals.py +577 -0
- infrahub/core/migrations/query/attribute_add.py +27 -2
- infrahub/core/migrations/query/node_duplicate.py +3 -26
- infrahub/core/migrations/schema/tasks.py +6 -5
- infrahub/core/node/proposed_change.py +43 -0
- infrahub/core/protocols.py +12 -0
- infrahub/core/query/attribute.py +32 -14
- infrahub/core/query/diff.py +11 -0
- infrahub/core/query/ipam.py +13 -7
- infrahub/core/query/node.py +51 -23
- infrahub/core/query/resource_manager.py +3 -3
- infrahub/core/relationship/model.py +13 -13
- infrahub/core/schema/basenode_schema.py +8 -0
- infrahub/core/schema/definitions/core/__init__.py +10 -1
- infrahub/core/schema/definitions/core/ipam.py +28 -2
- infrahub/core/schema/definitions/core/propose_change.py +15 -0
- infrahub/core/schema/definitions/core/webhook.py +3 -0
- infrahub/core/schema/generic_schema.py +10 -0
- infrahub/core/schema/manager.py +10 -1
- infrahub/core/schema/node_schema.py +22 -17
- infrahub/core/schema/profile_schema.py +8 -0
- infrahub/core/schema/schema_branch.py +9 -5
- infrahub/core/schema/template_schema.py +8 -0
- infrahub/core/validators/checks_runner.py +5 -5
- infrahub/core/validators/tasks.py +6 -7
- infrahub/core/validators/uniqueness/checker.py +4 -2
- infrahub/core/validators/uniqueness/model.py +1 -0
- infrahub/core/validators/uniqueness/query.py +57 -7
- infrahub/database/__init__.py +2 -1
- infrahub/events/__init__.py +18 -0
- infrahub/events/constants.py +7 -0
- infrahub/events/generator.py +29 -2
- infrahub/events/proposed_change_action.py +181 -0
- infrahub/generators/tasks.py +24 -20
- infrahub/git/base.py +4 -7
- infrahub/git/integrator.py +21 -12
- infrahub/git/repository.py +15 -30
- infrahub/git/tasks.py +121 -106
- infrahub/graphql/field_extractor.py +69 -0
- infrahub/graphql/manager.py +15 -11
- infrahub/graphql/mutations/account.py +2 -2
- infrahub/graphql/mutations/action.py +8 -2
- infrahub/graphql/mutations/artifact_definition.py +4 -1
- infrahub/graphql/mutations/branch.py +10 -5
- infrahub/graphql/mutations/graphql_query.py +2 -1
- infrahub/graphql/mutations/main.py +14 -8
- infrahub/graphql/mutations/menu.py +2 -1
- infrahub/graphql/mutations/proposed_change.py +225 -8
- infrahub/graphql/mutations/relationship.py +6 -1
- infrahub/graphql/mutations/repository.py +2 -1
- infrahub/graphql/mutations/tasks.py +7 -9
- infrahub/graphql/mutations/webhook.py +4 -1
- infrahub/graphql/parser.py +15 -6
- infrahub/graphql/queries/__init__.py +10 -1
- infrahub/graphql/queries/account.py +3 -3
- infrahub/graphql/queries/branch.py +2 -2
- infrahub/graphql/queries/diff/tree.py +3 -3
- infrahub/graphql/queries/event.py +13 -3
- infrahub/graphql/queries/ipam.py +23 -1
- infrahub/graphql/queries/proposed_change.py +84 -0
- infrahub/graphql/queries/relationship.py +2 -2
- infrahub/graphql/queries/resource_manager.py +3 -3
- infrahub/graphql/queries/search.py +3 -2
- infrahub/graphql/queries/status.py +3 -2
- infrahub/graphql/queries/task.py +2 -2
- infrahub/graphql/resolvers/ipam.py +440 -0
- infrahub/graphql/resolvers/many_relationship.py +4 -3
- infrahub/graphql/resolvers/resolver.py +5 -5
- infrahub/graphql/resolvers/single_relationship.py +3 -2
- infrahub/graphql/schema.py +25 -5
- infrahub/graphql/types/__init__.py +2 -2
- infrahub/graphql/types/attribute.py +3 -3
- infrahub/graphql/types/event.py +60 -0
- infrahub/groups/tasks.py +6 -6
- infrahub/lock.py +3 -2
- infrahub/menu/generator.py +8 -0
- infrahub/message_bus/operations/__init__.py +9 -12
- infrahub/message_bus/operations/git/file.py +6 -5
- infrahub/message_bus/operations/git/repository.py +12 -20
- infrahub/message_bus/operations/refresh/registry.py +15 -9
- infrahub/message_bus/operations/send/echo.py +7 -4
- infrahub/message_bus/types.py +1 -0
- infrahub/permissions/globals.py +1 -4
- infrahub/permissions/manager.py +8 -5
- infrahub/pools/prefix.py +7 -5
- infrahub/prefect_server/app.py +31 -0
- infrahub/prefect_server/bootstrap.py +18 -0
- infrahub/proposed_change/action_checker.py +206 -0
- infrahub/proposed_change/approval_revoker.py +40 -0
- infrahub/proposed_change/branch_diff.py +3 -1
- infrahub/proposed_change/checker.py +45 -0
- infrahub/proposed_change/constants.py +32 -2
- infrahub/proposed_change/tasks.py +182 -150
- infrahub/server.py +29 -17
- infrahub/services/__init__.py +13 -28
- infrahub/services/adapters/cache/__init__.py +4 -0
- infrahub/services/adapters/cache/nats.py +2 -0
- infrahub/services/adapters/cache/redis.py +3 -0
- infrahub/services/adapters/message_bus/__init__.py +0 -2
- infrahub/services/adapters/message_bus/local.py +1 -2
- infrahub/services/adapters/message_bus/nats.py +6 -8
- infrahub/services/adapters/message_bus/rabbitmq.py +7 -9
- infrahub/services/adapters/workflow/__init__.py +1 -0
- infrahub/services/adapters/workflow/local.py +1 -8
- infrahub/services/component.py +2 -1
- infrahub/task_manager/event.py +52 -0
- infrahub/task_manager/models.py +9 -0
- infrahub/tasks/artifact.py +6 -7
- infrahub/tasks/check.py +4 -7
- infrahub/telemetry/tasks.py +15 -18
- infrahub/transformations/tasks.py +10 -6
- infrahub/trigger/tasks.py +4 -3
- infrahub/types.py +4 -0
- infrahub/validators/events.py +7 -7
- infrahub/validators/tasks.py +6 -7
- infrahub/webhook/models.py +45 -45
- infrahub/webhook/tasks.py +25 -24
- infrahub/workers/dependencies.py +143 -0
- infrahub/workers/infrahub_async.py +19 -43
- infrahub/workflows/catalogue.py +16 -2
- infrahub/workflows/initialization.py +5 -4
- infrahub/workflows/models.py +2 -0
- infrahub_sdk/client.py +6 -6
- infrahub_sdk/ctl/repository.py +51 -0
- infrahub_sdk/ctl/schema.py +9 -9
- infrahub_sdk/protocols.py +40 -6
- {infrahub_server-1.3.6.dist-info → infrahub_server-1.4.0b1.dist-info}/METADATA +6 -4
- {infrahub_server-1.3.6.dist-info → infrahub_server-1.4.0b1.dist-info}/RECORD +162 -149
- infrahub_testcontainers/container.py +17 -0
- infrahub_testcontainers/docker-compose-cluster.test.yml +56 -1
- infrahub_testcontainers/docker-compose.test.yml +56 -1
- infrahub_testcontainers/helpers.py +4 -1
- infrahub/cli/db_commands/check_inheritance.py +0 -284
- /infrahub/{cli/db_commands/__init__.py → py.typed} +0 -0
- {infrahub_server-1.3.6.dist-info → infrahub_server-1.4.0b1.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.3.6.dist-info → infrahub_server-1.4.0b1.dist-info}/WHEEL +0 -0
- {infrahub_server-1.3.6.dist-info → infrahub_server-1.4.0b1.dist-info}/entry_points.txt +0 -0
|
@@ -59,7 +59,7 @@ builtin_ip_prefix = GenericSchema(
|
|
|
59
59
|
name="IPPrefix",
|
|
60
60
|
label="IP Prefix",
|
|
61
61
|
namespace="Builtin",
|
|
62
|
-
description="
|
|
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="
|
|
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",
|
|
@@ -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
|
infrahub/core/schema/manager.py
CHANGED
|
@@ -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,
|
|
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
|
|
|
@@ -60,14 +70,17 @@ class NodeSchema(GeneratedNodeSchema):
|
|
|
60
70
|
)
|
|
61
71
|
|
|
62
72
|
for relationship in self.relationships:
|
|
63
|
-
if
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
73
|
+
if relationship.name in interface.relationship_names and not relationship.inherited:
|
|
74
|
+
interface_relationship = interface.get_relationship(relationship.name)
|
|
75
|
+
if interface_relationship.allow_override == AllowOverrideType.NONE:
|
|
76
|
+
raise ValueError(
|
|
77
|
+
f"{self.kind}'s relationship {relationship.name} inherited from {interface.kind} cannot be overriden"
|
|
78
|
+
)
|
|
79
|
+
if relationship.kind != RelationshipKind.HIERARCHY and relationship.peer != interface_relationship.peer:
|
|
80
|
+
raise ValueError(
|
|
81
|
+
f"{self.kind}'s relationship {relationship.name} inherited from {interface.kind} must have the same peer "
|
|
82
|
+
f"({interface_relationship.peer} != {relationship.peer})"
|
|
83
|
+
)
|
|
71
84
|
|
|
72
85
|
def inherit_from_interface(self, interface: GenericSchema) -> None:
|
|
73
86
|
existing_inherited_attributes: dict[str, int] = {
|
|
@@ -137,11 +150,3 @@ class NodeSchema(GeneratedNodeSchema):
|
|
|
137
150
|
if self.namespace not in ["Schema", "Internal"] and InfrahubKind.GENERICGROUP not in self.inherit_from:
|
|
138
151
|
labels.append(InfrahubKind.NODE)
|
|
139
152
|
return labels
|
|
140
|
-
|
|
141
|
-
def is_ip_prefix(self) -> bool:
|
|
142
|
-
"""Return whether a node is a derivative of built-in IP prefixes."""
|
|
143
|
-
return InfrahubKind.IPPREFIX in self.inherit_from
|
|
144
|
-
|
|
145
|
-
def is_ip_address(self) -> bool:
|
|
146
|
-
"""Return whether a node is a derivative of built-in IP addreses."""
|
|
147
|
-
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[
|
|
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
|
|
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
|
-
|
|
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
|
|
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
59
|
+
event_service=event_service, validator=validator, proposed_change_id=proposed_change_id, context=context
|
|
60
60
|
)
|
|
@@ -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.
|
|
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
|
-
|
|
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
|
-
|
|
69
|
+
database: InfrahubDatabase,
|
|
71
70
|
) -> SchemaValidatorPathResponseData:
|
|
72
|
-
async with
|
|
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(
|
|
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(
|
|
@@ -3,7 +3,9 @@ from __future__ import annotations
|
|
|
3
3
|
from typing import TYPE_CHECKING, Any
|
|
4
4
|
|
|
5
5
|
from infrahub.core.constants.relationship_label import RELATIONSHIP_TO_VALUE_LABEL
|
|
6
|
+
from infrahub.core.graph.schema import GraphAttributeValueIndexedNode, GraphAttributeValueNode
|
|
6
7
|
from infrahub.core.query import Query, QueryType
|
|
8
|
+
from infrahub.types import is_large_attribute_type
|
|
7
9
|
|
|
8
10
|
from .model import QueryAttributePathValued, QueryRelationshipPathValued
|
|
9
11
|
|
|
@@ -40,6 +42,7 @@ class NodeUniqueAttributeConstraintQuery(Query):
|
|
|
40
42
|
items="relationships(active_path)", item_names=["branch", "branch_level"]
|
|
41
43
|
)
|
|
42
44
|
|
|
45
|
+
attrs_include_large_type = False
|
|
43
46
|
attribute_names = set()
|
|
44
47
|
attr_paths, attr_paths_with_value, attr_values = [], [], []
|
|
45
48
|
for attr_path in self.query_request.unique_attribute_paths:
|
|
@@ -49,12 +52,19 @@ class NodeUniqueAttributeConstraintQuery(Query):
|
|
|
49
52
|
raise ValueError(
|
|
50
53
|
f"{attr_path.property_name} is not a valid property for a uniqueness constraint"
|
|
51
54
|
) from exc
|
|
55
|
+
if is_large_attribute_type(attr_path.attribute_kind):
|
|
56
|
+
attrs_include_large_type = True
|
|
52
57
|
attribute_names.add(attr_path.attribute_name)
|
|
53
58
|
if attr_path.value:
|
|
54
59
|
attr_paths_with_value.append((attr_path.attribute_name, property_rel_name, attr_path.value))
|
|
55
60
|
attr_values.append(attr_path.value)
|
|
56
61
|
else:
|
|
57
62
|
attr_paths.append((attr_path.attribute_name, property_rel_name))
|
|
63
|
+
attr_value_label = (
|
|
64
|
+
GraphAttributeValueNode.get_default_label()
|
|
65
|
+
if attrs_include_large_type
|
|
66
|
+
else GraphAttributeValueIndexedNode.get_default_label()
|
|
67
|
+
)
|
|
58
68
|
|
|
59
69
|
relationship_names = set()
|
|
60
70
|
relationship_attr_paths = []
|
|
@@ -112,11 +122,11 @@ class NodeUniqueAttributeConstraintQuery(Query):
|
|
|
112
122
|
""" % {"node_kind": self.query_request.kind}
|
|
113
123
|
|
|
114
124
|
attr_paths_with_value_subquery = """
|
|
115
|
-
MATCH attr_path = (start_node:%(node_kind)s)-[:HAS_ATTRIBUTE]->(attr:Attribute)-[r:HAS_VALUE]->(attr_value
|
|
125
|
+
MATCH attr_path = (start_node:%(node_kind)s)-[:HAS_ATTRIBUTE]->(attr:Attribute)-[r:HAS_VALUE]->(attr_value:%(attr_value_label)s)
|
|
116
126
|
WHERE attr.name in $attribute_names AND attr_value.value in $attr_values
|
|
117
127
|
AND [attr.name, type(r), attr_value.value] in $attr_paths_with_value
|
|
118
128
|
RETURN start_node, attr_path as potential_path, NULL as rel_identifier, attr.name as potential_attr, attr_value.value as potential_attr_value
|
|
119
|
-
""" % {"node_kind": self.query_request.kind}
|
|
129
|
+
""" % {"node_kind": self.query_request.kind, "attr_value_label": attr_value_label}
|
|
120
130
|
|
|
121
131
|
relationship_attr_paths_subquery = """
|
|
122
132
|
MATCH rel_path = (start_node:%(node_kind)s)-[:IS_RELATED]-(relationship_node:Relationship)-[:IS_RELATED]-(related_n:Node)-[:HAS_ATTRIBUTE]->(rel_attr:Attribute)-[:HAS_VALUE]->(rel_attr_value:AttributeValue)
|
|
@@ -262,8 +272,20 @@ class UniquenessValidationQuery(Query):
|
|
|
262
272
|
self.node_ids_to_exclude = node_ids_to_exclude
|
|
263
273
|
super().__init__(**kwargs)
|
|
264
274
|
|
|
275
|
+
def _is_attribute_large_type(self, db: InfrahubDatabase, node_kind: str, attribute_name: str) -> bool:
|
|
276
|
+
"""Determine if an attribute is a large type that should use AttributeValue instead of AttributeValueIndexed."""
|
|
277
|
+
node_schema = db.schema.get(node_kind, branch=self.branch, duplicate=False)
|
|
278
|
+
attr_schema = node_schema.get_attribute(attribute_name)
|
|
279
|
+
return is_large_attribute_type(attr_schema.kind)
|
|
280
|
+
|
|
265
281
|
def _build_attr_subquery(
|
|
266
|
-
self,
|
|
282
|
+
self,
|
|
283
|
+
node_kind: str,
|
|
284
|
+
attr_path: QueryAttributePathValued,
|
|
285
|
+
index: int,
|
|
286
|
+
branch_filter: str,
|
|
287
|
+
is_first_query: bool,
|
|
288
|
+
is_large_type: bool,
|
|
267
289
|
) -> tuple[str, dict[str, str | int | float | bool]]:
|
|
268
290
|
attr_name_var = f"attr_name_{index}"
|
|
269
291
|
attr_value_var = f"attr_value_{index}"
|
|
@@ -271,8 +293,16 @@ class UniquenessValidationQuery(Query):
|
|
|
271
293
|
first_query_filter = "WHERE $node_ids_to_exclude IS NULL OR NOT node.uuid IN $node_ids_to_exclude"
|
|
272
294
|
else:
|
|
273
295
|
first_query_filter = ""
|
|
296
|
+
|
|
297
|
+
# Determine the appropriate label based on attribute type
|
|
298
|
+
attr_value_label = (
|
|
299
|
+
GraphAttributeValueNode.get_default_label()
|
|
300
|
+
if is_large_type
|
|
301
|
+
else GraphAttributeValueIndexedNode.get_default_label()
|
|
302
|
+
)
|
|
303
|
+
|
|
274
304
|
attribute_query = """
|
|
275
|
-
MATCH (node:%(node_kind)s)-[:HAS_ATTRIBUTE]->(attr:Attribute {name: $%(attr_name_var)s})-[:HAS_VALUE]->(
|
|
305
|
+
MATCH (node:%(node_kind)s)-[:HAS_ATTRIBUTE]->(attr:Attribute {name: $%(attr_name_var)s})-[:HAS_VALUE]->(:%(attr_value_label)s {value: $%(attr_value_var)s})
|
|
276
306
|
%(first_query_filter)s
|
|
277
307
|
WITH DISTINCT node
|
|
278
308
|
CALL (node) {
|
|
@@ -300,6 +330,7 @@ CALL (node) {
|
|
|
300
330
|
"attr_value_var": attr_value_var,
|
|
301
331
|
"branch_filter": branch_filter,
|
|
302
332
|
"index": index,
|
|
333
|
+
"attr_value_label": attr_value_label,
|
|
303
334
|
}
|
|
304
335
|
params: dict[str, str | int | float | bool] = {
|
|
305
336
|
attr_name_var: attr_path.attribute_name,
|
|
@@ -314,6 +345,7 @@ CALL (node) {
|
|
|
314
345
|
index: int,
|
|
315
346
|
branch_filter: str,
|
|
316
347
|
is_first_query: bool,
|
|
348
|
+
is_large_type: bool = False,
|
|
317
349
|
) -> tuple[str, dict[str, str | int | float | bool]]:
|
|
318
350
|
params: dict[str, str | int | float | bool] = {}
|
|
319
351
|
rel_attr_query = ""
|
|
@@ -321,6 +353,14 @@ CALL (node) {
|
|
|
321
353
|
if rel_path.attribute_name and rel_path.attribute_value:
|
|
322
354
|
attr_name_var = f"attr_name_{index}"
|
|
323
355
|
attr_value_var = f"attr_value_{index}"
|
|
356
|
+
|
|
357
|
+
# Determine the appropriate label based on relationship attribute type
|
|
358
|
+
rel_attr_value_label = (
|
|
359
|
+
GraphAttributeValueNode.get_default_label()
|
|
360
|
+
if is_large_type
|
|
361
|
+
else GraphAttributeValueIndexedNode.get_default_label()
|
|
362
|
+
)
|
|
363
|
+
|
|
324
364
|
rel_attr_query = """
|
|
325
365
|
MATCH (peer)-[r:HAS_ATTRIBUTE]->(attr:Attribute {name: $%(attr_name_var)s})
|
|
326
366
|
WHERE %(branch_filter)s
|
|
@@ -330,19 +370,25 @@ CALL (node) {
|
|
|
330
370
|
LIMIT 1
|
|
331
371
|
WITH attr, is_active
|
|
332
372
|
WHERE is_active = TRUE
|
|
333
|
-
MATCH (attr)-[r:HAS_VALUE]->(
|
|
373
|
+
MATCH (attr)-[r:HAS_VALUE]->(:%(rel_attr_value_label)s {value: $%(attr_value_var)s})
|
|
334
374
|
WHERE %(branch_filter)s
|
|
335
375
|
WITH r
|
|
336
376
|
ORDER BY r.branch_level DESC, r.from DESC, r.status ASC
|
|
337
377
|
LIMIT 1
|
|
338
378
|
WITH r
|
|
339
379
|
WHERE r.status = "active"
|
|
340
|
-
""" % {
|
|
380
|
+
""" % {
|
|
381
|
+
"attr_name_var": attr_name_var,
|
|
382
|
+
"attr_value_var": attr_value_var,
|
|
383
|
+
"branch_filter": branch_filter,
|
|
384
|
+
"rel_attr_value_label": rel_attr_value_label,
|
|
385
|
+
}
|
|
341
386
|
rel_attr_match = (
|
|
342
|
-
"-[r:HAS_ATTRIBUTE]->(attr:Attribute {name: $%(attr_name_var)s})-[:HAS_VALUE]->(
|
|
387
|
+
"-[r:HAS_ATTRIBUTE]->(attr:Attribute {name: $%(attr_name_var)s})-[:HAS_VALUE]->(:%(rel_attr_value_label)s {value: $%(attr_value_var)s})"
|
|
343
388
|
% {
|
|
344
389
|
"attr_name_var": attr_name_var,
|
|
345
390
|
"attr_value_var": attr_value_var,
|
|
391
|
+
"rel_attr_value_label": rel_attr_value_label,
|
|
346
392
|
}
|
|
347
393
|
)
|
|
348
394
|
params[attr_name_var] = rel_path.attribute_name
|
|
@@ -426,12 +472,16 @@ CALL (node) {
|
|
|
426
472
|
for index, schema_path in enumerate(self.query_request.unique_valued_paths):
|
|
427
473
|
is_first_query = index == 0
|
|
428
474
|
if isinstance(schema_path, QueryAttributePathValued):
|
|
475
|
+
is_large_type = self._is_attribute_large_type(
|
|
476
|
+
db=db, node_kind=self.query_request.kind, attribute_name=schema_path.attribute_name
|
|
477
|
+
)
|
|
429
478
|
subquery, params = self._build_attr_subquery(
|
|
430
479
|
node_kind=self.query_request.kind,
|
|
431
480
|
attr_path=schema_path,
|
|
432
481
|
index=index,
|
|
433
482
|
branch_filter=branch_filter,
|
|
434
483
|
is_first_query=is_first_query,
|
|
484
|
+
is_large_type=is_large_type,
|
|
435
485
|
)
|
|
436
486
|
else:
|
|
437
487
|
subquery, params = self._build_rel_subquery(
|
infrahub/database/__init__.py
CHANGED
|
@@ -295,7 +295,8 @@ class InfrahubDatabase:
|
|
|
295
295
|
traceback: TracebackType | None,
|
|
296
296
|
) -> None:
|
|
297
297
|
if self._mode == InfrahubDatabaseMode.SESSION:
|
|
298
|
-
|
|
298
|
+
await self._session.close()
|
|
299
|
+
return
|
|
299
300
|
|
|
300
301
|
if self._mode == InfrahubDatabaseMode.TRANSACTION:
|
|
301
302
|
if exc_type is not None:
|
infrahub/events/__init__.py
CHANGED
|
@@ -3,6 +3,16 @@ from .branch_action import BranchCreatedEvent, BranchDeletedEvent, BranchMergedE
|
|
|
3
3
|
from .group_action import GroupMemberAddedEvent, GroupMemberRemovedEvent
|
|
4
4
|
from .models import EventMeta, InfrahubEvent
|
|
5
5
|
from .node_action import NodeCreatedEvent, NodeDeletedEvent, NodeUpdatedEvent
|
|
6
|
+
from .proposed_change_action import (
|
|
7
|
+
ProposedChangeApprovalRevokedEvent,
|
|
8
|
+
ProposedChangeApprovedEvent,
|
|
9
|
+
ProposedChangeMergedEvent,
|
|
10
|
+
ProposedChangeRejectedEvent,
|
|
11
|
+
ProposedChangeRejectionRevokedEvent,
|
|
12
|
+
ProposedChangeReviewRequestedEvent,
|
|
13
|
+
ProposedChangeThreadCreatedEvent,
|
|
14
|
+
ProposedChangeThreadUpdatedEvent,
|
|
15
|
+
)
|
|
6
16
|
from .repository_action import CommitUpdatedEvent
|
|
7
17
|
from .validator_action import ValidatorFailedEvent, ValidatorPassedEvent, ValidatorStartedEvent
|
|
8
18
|
|
|
@@ -21,6 +31,14 @@ __all__ = [
|
|
|
21
31
|
"NodeCreatedEvent",
|
|
22
32
|
"NodeDeletedEvent",
|
|
23
33
|
"NodeUpdatedEvent",
|
|
34
|
+
"ProposedChangeApprovalRevokedEvent",
|
|
35
|
+
"ProposedChangeApprovedEvent",
|
|
36
|
+
"ProposedChangeMergedEvent",
|
|
37
|
+
"ProposedChangeRejectedEvent",
|
|
38
|
+
"ProposedChangeRejectionRevokedEvent",
|
|
39
|
+
"ProposedChangeReviewRequestedEvent",
|
|
40
|
+
"ProposedChangeThreadCreatedEvent",
|
|
41
|
+
"ProposedChangeThreadUpdatedEvent",
|
|
24
42
|
"ValidatorFailedEvent",
|
|
25
43
|
"ValidatorPassedEvent",
|
|
26
44
|
"ValidatorStartedEvent",
|