infrahub-server 1.5.5__py3-none-any.whl → 1.6.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.
- infrahub/api/artifact.py +5 -3
- infrahub/auth.py +5 -6
- infrahub/cli/db.py +3 -3
- infrahub/cli/db_commands/clean_duplicate_schema_fields.py +2 -2
- infrahub/cli/dev.py +30 -0
- infrahub/config.py +62 -14
- infrahub/constants/database.py +5 -5
- infrahub/core/branch/models.py +24 -6
- infrahub/core/constants/__init__.py +1 -0
- infrahub/core/diff/model/diff.py +2 -2
- infrahub/core/graph/constraints.py +2 -2
- infrahub/core/manager.py +191 -60
- infrahub/core/merge.py +29 -2
- infrahub/core/migrations/graph/m041_deleted_dup_edges.py +2 -3
- infrahub/core/migrations/shared.py +2 -2
- infrahub/core/models.py +5 -6
- infrahub/core/node/__init__.py +12 -6
- infrahub/core/node/create.py +36 -8
- infrahub/core/node/ipam.py +4 -4
- infrahub/core/node/node_property_attribute.py +2 -2
- infrahub/core/node/standard.py +1 -1
- infrahub/core/query/attribute.py +1 -1
- infrahub/core/query/branch.py +11 -0
- infrahub/core/query/node.py +9 -5
- infrahub/core/query/standard_node.py +3 -0
- infrahub/core/relationship/model.py +15 -10
- infrahub/core/schema/__init__.py +3 -3
- infrahub/core/schema/generic_schema.py +1 -1
- infrahub/core/schema/schema_branch.py +35 -16
- infrahub/core/task/user_task.py +2 -2
- infrahub/core/validators/determiner.py +3 -6
- infrahub/core/validators/enum.py +2 -2
- infrahub/database/__init__.py +1 -1
- infrahub/dependencies/interface.py +2 -2
- infrahub/events/constants.py +2 -2
- infrahub/git/base.py +42 -1
- infrahub/git/models.py +2 -1
- infrahub/git/repository.py +5 -1
- infrahub/git/tasks.py +28 -1
- infrahub/git/utils.py +9 -0
- infrahub/graphql/analyzer.py +4 -4
- infrahub/graphql/loaders/peers.py +6 -0
- infrahub/graphql/mutations/computed_attribute.py +1 -1
- infrahub/graphql/mutations/convert_object_type.py +1 -1
- infrahub/graphql/mutations/display_label.py +1 -1
- infrahub/graphql/mutations/hfid.py +1 -1
- infrahub/graphql/mutations/ipam.py +1 -1
- infrahub/graphql/mutations/profile.py +9 -1
- infrahub/graphql/mutations/relationship.py +2 -2
- infrahub/graphql/mutations/resource_manager.py +1 -1
- infrahub/graphql/queries/__init__.py +2 -1
- infrahub/graphql/queries/branch.py +58 -3
- infrahub/graphql/queries/ipam.py +9 -4
- infrahub/graphql/queries/resource_manager.py +7 -11
- infrahub/graphql/queries/search.py +5 -6
- infrahub/graphql/resolvers/ipam.py +20 -0
- infrahub/graphql/resolvers/many_relationship.py +12 -11
- infrahub/graphql/resolvers/resolver.py +6 -2
- infrahub/graphql/resolvers/single_relationship.py +1 -11
- infrahub/graphql/schema.py +2 -0
- infrahub/graphql/types/__init__.py +3 -1
- infrahub/graphql/types/branch.py +98 -2
- infrahub/lock.py +6 -6
- infrahub/log.py +1 -1
- infrahub/message_bus/messages/__init__.py +0 -12
- infrahub/patch/constants.py +2 -2
- infrahub/profiles/node_applier.py +9 -0
- infrahub/proposed_change/tasks.py +1 -1
- infrahub/task_manager/task.py +4 -4
- infrahub/telemetry/constants.py +2 -2
- infrahub/trigger/models.py +2 -2
- infrahub/trigger/setup.py +6 -9
- infrahub/utils.py +19 -1
- infrahub/validators/tasks.py +1 -1
- infrahub/workers/infrahub_async.py +39 -1
- infrahub_sdk/async_typer.py +2 -1
- infrahub_sdk/batch.py +2 -2
- infrahub_sdk/client.py +121 -10
- infrahub_sdk/config.py +2 -2
- infrahub_sdk/ctl/branch.py +176 -2
- infrahub_sdk/ctl/check.py +3 -3
- infrahub_sdk/ctl/cli.py +2 -2
- infrahub_sdk/ctl/cli_commands.py +10 -9
- infrahub_sdk/ctl/generator.py +2 -2
- infrahub_sdk/ctl/graphql.py +3 -4
- infrahub_sdk/ctl/importer.py +2 -3
- infrahub_sdk/ctl/repository.py +5 -6
- infrahub_sdk/ctl/task.py +2 -4
- infrahub_sdk/ctl/utils.py +4 -4
- infrahub_sdk/ctl/validate.py +1 -2
- infrahub_sdk/diff.py +80 -3
- infrahub_sdk/graphql/constants.py +14 -1
- infrahub_sdk/graphql/renderers.py +5 -1
- infrahub_sdk/node/attribute.py +10 -10
- infrahub_sdk/node/constants.py +2 -3
- infrahub_sdk/node/node.py +54 -11
- infrahub_sdk/node/related_node.py +1 -2
- infrahub_sdk/node/relationship.py +1 -2
- infrahub_sdk/object_store.py +4 -4
- infrahub_sdk/operation.py +2 -2
- infrahub_sdk/protocols_base.py +0 -1
- infrahub_sdk/protocols_generator/generator.py +1 -1
- infrahub_sdk/pytest_plugin/items/jinja2_transform.py +1 -1
- infrahub_sdk/pytest_plugin/models.py +1 -1
- infrahub_sdk/pytest_plugin/plugin.py +1 -1
- infrahub_sdk/query_groups.py +2 -2
- infrahub_sdk/schema/__init__.py +10 -14
- infrahub_sdk/schema/main.py +2 -2
- infrahub_sdk/schema/repository.py +2 -2
- infrahub_sdk/spec/object.py +2 -2
- infrahub_sdk/spec/range_expansion.py +1 -1
- infrahub_sdk/template/__init__.py +2 -1
- infrahub_sdk/transfer/importer/json.py +3 -3
- infrahub_sdk/types.py +2 -2
- infrahub_sdk/utils.py +2 -2
- {infrahub_server-1.5.5.dist-info → infrahub_server-1.6.0.dist-info}/METADATA +58 -59
- {infrahub_server-1.5.5.dist-info → infrahub_server-1.6.0.dist-info}/RECORD +240 -246
- {infrahub_server-1.5.5.dist-info → infrahub_server-1.6.0.dist-info}/WHEEL +1 -1
- infrahub_server-1.6.0.dist-info/entry_points.txt +12 -0
- infrahub_testcontainers/container.py +2 -2
- infrahub_testcontainers/docker-compose-cluster.test.yml +1 -1
- infrahub_testcontainers/docker-compose.test.yml +1 -1
- infrahub/core/schema/generated/__init__.py +0 -0
- infrahub/core/schema/generated/attribute_schema.py +0 -133
- infrahub/core/schema/generated/base_node_schema.py +0 -111
- infrahub/core/schema/generated/genericnode_schema.py +0 -30
- infrahub/core/schema/generated/node_schema.py +0 -40
- infrahub/core/schema/generated/relationship_schema.py +0 -141
- infrahub_server-1.5.5.dist-info/entry_points.txt +0 -13
- {infrahub_server-1.5.5.dist-info → infrahub_server-1.6.0.dist-info/licenses}/LICENSE.txt +0 -0
infrahub/core/node/ipam.py
CHANGED
|
@@ -40,8 +40,8 @@ class BuiltinIPPrefix(Node):
|
|
|
40
40
|
retrieved = await NodeManager.get_one(
|
|
41
41
|
db=db, branch=self._branch, id=self.id, fields={"member_type": None, "prefix": None}
|
|
42
42
|
)
|
|
43
|
-
self.member_type = retrieved.member_type # type: ignore[union-attr]
|
|
44
|
-
self.prefix = retrieved.prefix # type: ignore[union-attr]
|
|
43
|
+
self.member_type = retrieved.member_type # type: ignore[attr-defined,union-attr]
|
|
44
|
+
self.prefix = retrieved.prefix # type: ignore[attr-defined,union-attr]
|
|
45
45
|
utilization_getter = PrefixUtilizationGetter(db=db, ip_prefixes=[self])
|
|
46
46
|
utilization = await utilization_getter.get_use_percentage(
|
|
47
47
|
ip_prefixes=[self], branch_names=[self._branch.name]
|
|
@@ -57,6 +57,6 @@ class BuiltinIPPrefix(Node):
|
|
|
57
57
|
retrieved = await NodeManager.get_one(
|
|
58
58
|
db=db, branch=self._branch, id=self.id, fields={"member_type": None, "prefix": None}
|
|
59
59
|
)
|
|
60
|
-
self.member_type = retrieved.member_type # type: ignore[union-attr]
|
|
61
|
-
self.prefix = retrieved.prefix # type: ignore[union-attr]
|
|
60
|
+
self.member_type = retrieved.member_type # type: ignore[attr-defined,union-attr]
|
|
61
|
+
self.prefix = retrieved.prefix # type: ignore[attr-defined,union-attr]
|
|
62
62
|
return get_prefix_space(self)
|
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from abc import abstractmethod
|
|
4
4
|
from enum import Enum
|
|
5
|
-
from typing import TYPE_CHECKING, Any,
|
|
5
|
+
from typing import TYPE_CHECKING, Any, TypeVar
|
|
6
6
|
|
|
7
7
|
from infrahub_sdk.template import Jinja2Template
|
|
8
8
|
|
|
@@ -21,7 +21,7 @@ if TYPE_CHECKING:
|
|
|
21
21
|
T = TypeVar("T")
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
class NodePropertyAttribute
|
|
24
|
+
class NodePropertyAttribute[T]:
|
|
25
25
|
"""A node property attribute is a construct that seats between a property and an attribute.
|
|
26
26
|
|
|
27
27
|
View it as a property, set at the node level but stored in the database as an attribute. It usually is something computed from other components of
|
infrahub/core/node/standard.py
CHANGED
infrahub/core/query/attribute.py
CHANGED
|
@@ -373,7 +373,7 @@ async def default_attribute_query_filter(
|
|
|
373
373
|
if property_name not in [v.value for v in NodeProperty]:
|
|
374
374
|
raise ValueError(f"filter {filter_name}: {filter_value}, {property_name} is not a valid property")
|
|
375
375
|
|
|
376
|
-
if property_attr
|
|
376
|
+
if property_attr != "id":
|
|
377
377
|
raise ValueError(f"filter {filter_name}: {filter_value}, {property_attr} is supported")
|
|
378
378
|
|
|
379
379
|
clean_filter_name = f"{property_name}_{property_attr}"
|
infrahub/core/query/branch.py
CHANGED
|
@@ -3,8 +3,10 @@ from __future__ import annotations
|
|
|
3
3
|
from typing import TYPE_CHECKING, Any
|
|
4
4
|
|
|
5
5
|
from infrahub import config
|
|
6
|
+
from infrahub.core.branch.enums import BranchStatus
|
|
6
7
|
from infrahub.core.constants import GLOBAL_BRANCH_NAME
|
|
7
8
|
from infrahub.core.query import Query, QueryType
|
|
9
|
+
from infrahub.core.query.standard_node import StandardNodeGetListQuery
|
|
8
10
|
|
|
9
11
|
if TYPE_CHECKING:
|
|
10
12
|
from infrahub.database import InfrahubDatabase
|
|
@@ -146,3 +148,12 @@ class RebaseBranchDeleteRelationshipQuery(Query):
|
|
|
146
148
|
self.add_to_query(query=query)
|
|
147
149
|
|
|
148
150
|
self.params["ids"] = [db.to_database_id(id) for id in self.ids]
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class BranchNodeGetListQuery(StandardNodeGetListQuery):
|
|
154
|
+
def __init__(self, exclude_global: bool = False, **kwargs: Any) -> None:
|
|
155
|
+
self.raw_filter = f"n.status <> '{BranchStatus.DELETING.value}'"
|
|
156
|
+
if exclude_global:
|
|
157
|
+
self.raw_filter += f" AND n.name <> '{GLOBAL_BRANCH_NAME}'"
|
|
158
|
+
|
|
159
|
+
super().__init__(**kwargs)
|
infrahub/core/query/node.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import contextlib
|
|
3
4
|
from collections import defaultdict
|
|
4
5
|
from copy import copy
|
|
5
6
|
from dataclasses import dataclass
|
|
@@ -12,6 +13,7 @@ from infrahub.core import registry
|
|
|
12
13
|
from infrahub.core.constants import (
|
|
13
14
|
GLOBAL_BRANCH_NAME,
|
|
14
15
|
PROFILE_NODE_RELATIONSHIP_IDENTIFIER,
|
|
16
|
+
PROFILE_TEMPLATE_RELATIONSHIP_IDENTIFIER,
|
|
15
17
|
AttributeDBNodeType,
|
|
16
18
|
RelationshipDirection,
|
|
17
19
|
RelationshipHierarchyDirection,
|
|
@@ -623,7 +625,8 @@ class NodeListGetAttributeQuery(Query):
|
|
|
623
625
|
|
|
624
626
|
async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: # noqa: ARG002
|
|
625
627
|
self.params["ids"] = self.ids
|
|
626
|
-
self.params["
|
|
628
|
+
self.params["profile_node_relationship_name"] = PROFILE_NODE_RELATIONSHIP_IDENTIFIER
|
|
629
|
+
self.params["profile_template_relationship_name"] = PROFILE_TEMPLATE_RELATIONSHIP_IDENTIFIER
|
|
627
630
|
|
|
628
631
|
branch_filter, branch_params = self.branch.get_query_filter_path(
|
|
629
632
|
at=self.at, branch_agnostic=self.branch_agnostic
|
|
@@ -632,7 +635,10 @@ class NodeListGetAttributeQuery(Query):
|
|
|
632
635
|
|
|
633
636
|
query = """
|
|
634
637
|
MATCH (n:Node) WHERE n.uuid IN $ids
|
|
635
|
-
WITH n,
|
|
638
|
+
WITH n, (
|
|
639
|
+
exists((n)-[:IS_RELATED]-(:Relationship {name: $profile_node_relationship_name})) OR
|
|
640
|
+
exists((n)-[:IS_RELATED]-(:Relationship {name: $profile_template_relationship_name}))
|
|
641
|
+
) AS might_use_profile
|
|
636
642
|
MATCH (n)-[:HAS_ATTRIBUTE]-(a:Attribute)
|
|
637
643
|
"""
|
|
638
644
|
if self.fields:
|
|
@@ -1134,10 +1140,8 @@ class NodeGetListQuery(Query):
|
|
|
1134
1140
|
self._variables_to_track.append(variable)
|
|
1135
1141
|
|
|
1136
1142
|
def _untrack_variable(self, variable: str) -> None:
|
|
1137
|
-
|
|
1143
|
+
with contextlib.suppress(ValueError):
|
|
1138
1144
|
self._variables_to_track.remove(variable)
|
|
1139
|
-
except ValueError:
|
|
1140
|
-
...
|
|
1141
1145
|
|
|
1142
1146
|
def _get_tracked_variables(self) -> list[str]:
|
|
1143
1147
|
return self._variables_to_track
|
|
@@ -132,6 +132,7 @@ class StandardNodeGetItemQuery(Query):
|
|
|
132
132
|
class StandardNodeGetListQuery(Query):
|
|
133
133
|
name = "standard_node_list"
|
|
134
134
|
type = QueryType.READ
|
|
135
|
+
raw_filter: str | None = None
|
|
135
136
|
|
|
136
137
|
def __init__(
|
|
137
138
|
self, node_class: StandardNode, ids: list[str] | None = None, node_name: str | None = None, **kwargs: Any
|
|
@@ -150,6 +151,8 @@ class StandardNodeGetListQuery(Query):
|
|
|
150
151
|
if self.node_name:
|
|
151
152
|
filters.append("n.name = $name")
|
|
152
153
|
self.params["name"] = self.node_name
|
|
154
|
+
if self.raw_filter:
|
|
155
|
+
filters.append(self.raw_filter)
|
|
153
156
|
|
|
154
157
|
where = ""
|
|
155
158
|
if filters:
|
|
@@ -912,6 +912,8 @@ class RelationshipManager:
|
|
|
912
912
|
db: InfrahubDatabase,
|
|
913
913
|
peer_type: type[PeerType],
|
|
914
914
|
branch_agnostic: bool = ...,
|
|
915
|
+
include_source: bool = ...,
|
|
916
|
+
include_owner: bool = ...,
|
|
915
917
|
) -> Mapping[str, PeerType]: ...
|
|
916
918
|
|
|
917
919
|
@overload
|
|
@@ -920,6 +922,8 @@ class RelationshipManager:
|
|
|
920
922
|
db: InfrahubDatabase,
|
|
921
923
|
peer_type: None = None,
|
|
922
924
|
branch_agnostic: bool = ...,
|
|
925
|
+
include_source: bool = ...,
|
|
926
|
+
include_owner: bool = ...,
|
|
923
927
|
) -> Mapping[str, Node]: ...
|
|
924
928
|
|
|
925
929
|
async def get_peers(
|
|
@@ -927,11 +931,18 @@ class RelationshipManager:
|
|
|
927
931
|
db: InfrahubDatabase,
|
|
928
932
|
peer_type: type[PeerType] | None = None, # noqa: ARG002
|
|
929
933
|
branch_agnostic: bool = False,
|
|
934
|
+
include_source: bool = False,
|
|
935
|
+
include_owner: bool = False,
|
|
930
936
|
) -> Mapping[str, Node | PeerType]:
|
|
931
937
|
rels = await self.get_relationships(db=db, branch_agnostic=branch_agnostic)
|
|
932
938
|
peer_ids = [rel.peer_id for rel in rels if rel.peer_id]
|
|
933
939
|
nodes = await registry.manager.get_many(
|
|
934
|
-
db=db,
|
|
940
|
+
db=db,
|
|
941
|
+
ids=peer_ids,
|
|
942
|
+
branch=self.branch,
|
|
943
|
+
branch_agnostic=branch_agnostic,
|
|
944
|
+
include_source=include_source,
|
|
945
|
+
include_owner=include_owner,
|
|
935
946
|
)
|
|
936
947
|
return nodes
|
|
937
948
|
|
|
@@ -1061,12 +1072,7 @@ class RelationshipManager:
|
|
|
1061
1072
|
|
|
1062
1073
|
return self._relationships.as_list()
|
|
1063
1074
|
|
|
1064
|
-
async def update(
|
|
1065
|
-
self,
|
|
1066
|
-
data: list[str | Node] | dict[str, Any] | str | Node | None,
|
|
1067
|
-
db: InfrahubDatabase,
|
|
1068
|
-
process_delete: bool = True,
|
|
1069
|
-
) -> bool:
|
|
1075
|
+
async def update(self, data: list[str | Node] | dict[str, Any] | str | Node | None, db: InfrahubDatabase) -> bool:
|
|
1070
1076
|
"""Replace and Update the list of relationships with this one."""
|
|
1071
1077
|
if not isinstance(data, list):
|
|
1072
1078
|
list_data: Sequence[str | Node | dict[str, Any] | None] = [data]
|
|
@@ -1092,9 +1098,8 @@ class RelationshipManager:
|
|
|
1092
1098
|
|
|
1093
1099
|
if item is None:
|
|
1094
1100
|
if previous_relationships:
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
await rel.delete(db=db)
|
|
1101
|
+
for rel in previous_relationships.values():
|
|
1102
|
+
await rel.delete(db=db)
|
|
1098
1103
|
changed = True
|
|
1099
1104
|
continue
|
|
1100
1105
|
|
infrahub/core/schema/__init__.py
CHANGED
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import uuid
|
|
4
4
|
from enum import Enum
|
|
5
|
-
from typing import Any
|
|
5
|
+
from typing import Any
|
|
6
6
|
|
|
7
7
|
from infrahub_sdk.utils import deep_merge_dict
|
|
8
8
|
from pydantic import BaseModel, ConfigDict, Field
|
|
@@ -22,8 +22,8 @@ from .profile_schema import ProfileSchema
|
|
|
22
22
|
from .relationship_schema import RelationshipSchema
|
|
23
23
|
from .template_schema import TemplateSchema
|
|
24
24
|
|
|
25
|
-
NonGenericSchemaTypes
|
|
26
|
-
MainSchemaTypes
|
|
25
|
+
NonGenericSchemaTypes = NodeSchema | ProfileSchema | TemplateSchema
|
|
26
|
+
MainSchemaTypes = NonGenericSchemaTypes | GenericSchema
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
# -----------------------------------------------------
|
|
@@ -51,4 +51,4 @@ class GenericSchema(GeneratedGenericSchema):
|
|
|
51
51
|
def _get_field_names_for_diff(self) -> list[str]:
|
|
52
52
|
"""Exclude used_by from the diff for generic nodes"""
|
|
53
53
|
fields = super()._get_field_names_for_diff()
|
|
54
|
-
return [field for field in fields if field
|
|
54
|
+
return [field for field in fields if field != "used_by"]
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import contextlib
|
|
3
4
|
import copy
|
|
4
5
|
import hashlib
|
|
5
6
|
import keyword
|
|
@@ -19,6 +20,7 @@ from infrahub.core.constants import (
|
|
|
19
20
|
OBJECT_TEMPLATE_NAME_ATTR,
|
|
20
21
|
OBJECT_TEMPLATE_RELATIONSHIP_NAME,
|
|
21
22
|
PROFILE_NODE_RELATIONSHIP_IDENTIFIER,
|
|
23
|
+
PROFILE_TEMPLATE_RELATIONSHIP_IDENTIFIER,
|
|
22
24
|
RESERVED_ATTR_GEN_NAMES,
|
|
23
25
|
RESERVED_ATTR_REL_NAMES,
|
|
24
26
|
RESTRICTED_NAMESPACES,
|
|
@@ -72,6 +74,16 @@ from .schema_branch_hfid import HFIDs
|
|
|
72
74
|
log = get_logger()
|
|
73
75
|
|
|
74
76
|
|
|
77
|
+
profiles_rel_settings: dict[str, Any] = {
|
|
78
|
+
"name": "profiles",
|
|
79
|
+
"identifier": PROFILE_NODE_RELATIONSHIP_IDENTIFIER,
|
|
80
|
+
"peer": InfrahubKind.PROFILE,
|
|
81
|
+
"kind": RelationshipKind.PROFILE,
|
|
82
|
+
"cardinality": RelationshipCardinality.MANY,
|
|
83
|
+
"branch": BranchSupportType.AWARE,
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
75
87
|
class SchemaBranch:
|
|
76
88
|
def __init__(
|
|
77
89
|
self,
|
|
@@ -334,10 +346,8 @@ class SchemaBranch:
|
|
|
334
346
|
)
|
|
335
347
|
|
|
336
348
|
schema: MainSchemaTypes | None = None
|
|
337
|
-
|
|
349
|
+
with contextlib.suppress(KeyError):
|
|
338
350
|
schema = self._cache[key]
|
|
339
|
-
except KeyError:
|
|
340
|
-
pass
|
|
341
351
|
|
|
342
352
|
if not schema:
|
|
343
353
|
raise ValueError(f"Schema {name!r} on branch {self.name} has incorrect hash: {key!r}")
|
|
@@ -1116,7 +1126,7 @@ class SchemaBranch:
|
|
|
1116
1126
|
) from None
|
|
1117
1127
|
|
|
1118
1128
|
for rel in node.relationships:
|
|
1119
|
-
if rel.peer
|
|
1129
|
+
if rel.peer == InfrahubKind.GENERICGROUP:
|
|
1120
1130
|
continue
|
|
1121
1131
|
if not self.has(rel.peer) or self.get(rel.peer, duplicate=False).state == HashableModelState.ABSENT:
|
|
1122
1132
|
raise ValueError(
|
|
@@ -2163,10 +2173,8 @@ class SchemaBranch:
|
|
|
2163
2173
|
or not node.generate_profile
|
|
2164
2174
|
or node.state == HashableModelState.ABSENT
|
|
2165
2175
|
):
|
|
2166
|
-
|
|
2176
|
+
with contextlib.suppress(SchemaNotFoundError):
|
|
2167
2177
|
self.delete(name=self._get_profile_kind(node_kind=node.kind))
|
|
2168
|
-
except SchemaNotFoundError:
|
|
2169
|
-
...
|
|
2170
2178
|
continue
|
|
2171
2179
|
|
|
2172
2180
|
profile = self.generate_profile_from_node(node=node)
|
|
@@ -2212,15 +2220,6 @@ class SchemaBranch:
|
|
|
2212
2220
|
):
|
|
2213
2221
|
continue
|
|
2214
2222
|
|
|
2215
|
-
profiles_rel_settings: dict[str, Any] = {
|
|
2216
|
-
"name": "profiles",
|
|
2217
|
-
"identifier": PROFILE_NODE_RELATIONSHIP_IDENTIFIER,
|
|
2218
|
-
"peer": InfrahubKind.PROFILE,
|
|
2219
|
-
"kind": RelationshipKind.PROFILE,
|
|
2220
|
-
"cardinality": RelationshipCardinality.MANY,
|
|
2221
|
-
"branch": BranchSupportType.AWARE,
|
|
2222
|
-
}
|
|
2223
|
-
|
|
2224
2223
|
# Add relationship between node and profile
|
|
2225
2224
|
if "profiles" not in node.relationship_names:
|
|
2226
2225
|
node_schema = self.get(name=node_name, duplicate=True)
|
|
@@ -2285,6 +2284,18 @@ class SchemaBranch:
|
|
|
2285
2284
|
)
|
|
2286
2285
|
],
|
|
2287
2286
|
)
|
|
2287
|
+
if f"Template{node.kind}" in self.all_names:
|
|
2288
|
+
template = self.get(name=f"Template{node.kind}", duplicate=False)
|
|
2289
|
+
profile.relationships.append(
|
|
2290
|
+
RelationshipSchema(
|
|
2291
|
+
name="related_templates",
|
|
2292
|
+
identifier=PROFILE_TEMPLATE_RELATIONSHIP_IDENTIFIER,
|
|
2293
|
+
peer=template.kind,
|
|
2294
|
+
kind=RelationshipKind.PROFILE,
|
|
2295
|
+
cardinality=RelationshipCardinality.MANY,
|
|
2296
|
+
branch=BranchSupportType.AWARE,
|
|
2297
|
+
)
|
|
2298
|
+
)
|
|
2288
2299
|
|
|
2289
2300
|
for node_attr in node.attributes:
|
|
2290
2301
|
if not node_attr.support_profiles:
|
|
@@ -2415,6 +2426,14 @@ class SchemaBranch:
|
|
|
2415
2426
|
template_schema.human_friendly_id = [parent_hfid] + template_schema.human_friendly_id
|
|
2416
2427
|
template_schema.uniqueness_constraints[0].append(relationship.name)
|
|
2417
2428
|
|
|
2429
|
+
if getattr(node, "generate_profile", False):
|
|
2430
|
+
if "profiles" not in [r.name for r in template_schema.relationships]:
|
|
2431
|
+
settings = dict(profiles_rel_settings)
|
|
2432
|
+
settings["identifier"] = PROFILE_TEMPLATE_RELATIONSHIP_IDENTIFIER
|
|
2433
|
+
template_schema.relationships.append(RelationshipSchema(**settings))
|
|
2434
|
+
|
|
2435
|
+
self.set(name=template_schema.kind, schema=template_schema)
|
|
2436
|
+
|
|
2418
2437
|
def generate_object_template_from_node(
|
|
2419
2438
|
self, node: NodeSchema | GenericSchema, need_templates: set[NodeSchema | GenericSchema]
|
|
2420
2439
|
) -> TemplateSchema | GenericSchema:
|
infrahub/core/task/user_task.py
CHANGED
|
@@ -6,6 +6,7 @@ from typing_extensions import Self
|
|
|
6
6
|
|
|
7
7
|
from infrahub.core import registry
|
|
8
8
|
from infrahub.core.constants import Severity, TaskConclusion
|
|
9
|
+
from infrahub.core.protocols import CoreGenericAccount
|
|
9
10
|
from infrahub.log import get_logger
|
|
10
11
|
|
|
11
12
|
from .task import Task
|
|
@@ -16,7 +17,6 @@ if TYPE_CHECKING:
|
|
|
16
17
|
|
|
17
18
|
from structlog.stdlib import BoundLogger
|
|
18
19
|
|
|
19
|
-
from infrahub.core.protocols import CoreGenericAccount
|
|
20
20
|
from infrahub.database import InfrahubDatabase
|
|
21
21
|
from infrahub.graphql.initialization import GraphqlContext
|
|
22
22
|
from infrahub.services.protocols import InfrahubLogger
|
|
@@ -67,7 +67,7 @@ class UserTask:
|
|
|
67
67
|
if self._account:
|
|
68
68
|
return False
|
|
69
69
|
|
|
70
|
-
account
|
|
70
|
+
account = await registry.manager.get_one(id=self.account_id, db=self.db, kind=CoreGenericAccount)
|
|
71
71
|
if not account:
|
|
72
72
|
raise ValueError(f"Unable to find the account associated with {self.account_id}")
|
|
73
73
|
self._account = account
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import contextlib
|
|
1
2
|
from typing import TYPE_CHECKING, Any
|
|
2
3
|
|
|
3
4
|
from infrahub.core.constants import RelationshipKind, SchemaPathType
|
|
@@ -84,14 +85,10 @@ class ConstraintValidatorDeterminer:
|
|
|
84
85
|
constraints: list[SchemaUpdateConstraintInfo] = []
|
|
85
86
|
schemas = list(self.schema_branch.get_all(duplicate=False).values())
|
|
86
87
|
# added here to check their uniqueness constraints
|
|
87
|
-
|
|
88
|
+
with contextlib.suppress(SchemaNotFoundError):
|
|
88
89
|
schemas.append(self.schema_branch.get_node(name="SchemaAttribute", duplicate=False))
|
|
89
|
-
|
|
90
|
-
pass
|
|
91
|
-
try:
|
|
90
|
+
with contextlib.suppress(SchemaNotFoundError):
|
|
92
91
|
schemas.append(self.schema_branch.get_node(name="SchemaRelationship", duplicate=False))
|
|
93
|
-
except SchemaNotFoundError:
|
|
94
|
-
pass
|
|
95
92
|
for schema in schemas:
|
|
96
93
|
constraints.extend(await self._get_property_constraints_for_one_schema(schema=schema))
|
|
97
94
|
return constraints
|
infrahub/core/validators/enum.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
from enum import
|
|
1
|
+
from enum import StrEnum
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
class ConstraintIdentifier(
|
|
4
|
+
class ConstraintIdentifier(StrEnum):
|
|
5
5
|
ATTRIBUTE_PARAMETERS_REGEX_UPDATE = "attribute.parameters.regex.update"
|
|
6
6
|
ATTRIBUTE_PARAMETERS_MIN_LENGTH_UPDATE = "attribute.parameters.min_length.update"
|
|
7
7
|
ATTRIBUTE_PARAMETERS_MAX_LENGTH_UPDATE = "attribute.parameters.max_length.update"
|
infrahub/database/__init__.py
CHANGED
|
@@ -356,7 +356,7 @@ class InfrahubDatabase:
|
|
|
356
356
|
type
|
|
357
357
|
and type == QueryType.READ
|
|
358
358
|
and runtime not in [Neo4jRuntime.DEFAULT, Neo4jRuntime.UNDEFINED]
|
|
359
|
-
and not (self.is_transaction and runtime
|
|
359
|
+
and not (self.is_transaction and runtime == Neo4jRuntime.PARALLEL)
|
|
360
360
|
):
|
|
361
361
|
query = f"CYPHER runtime = {runtime.value}\n" + query
|
|
362
362
|
else:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
2
|
from dataclasses import dataclass
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import TypeVar
|
|
4
4
|
|
|
5
5
|
from infrahub.core.branch import Branch
|
|
6
6
|
from infrahub.database import InfrahubDatabase
|
|
@@ -14,7 +14,7 @@ class DependencyBuilderContext:
|
|
|
14
14
|
branch: Branch
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
class DependencyBuilder
|
|
17
|
+
class DependencyBuilder[T](ABC):
|
|
18
18
|
@classmethod
|
|
19
19
|
@abstractmethod
|
|
20
20
|
def build(cls, context: DependencyBuilderContext) -> T: ...
|
infrahub/events/constants.py
CHANGED
infrahub/git/base.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import contextlib
|
|
3
4
|
import shutil
|
|
4
5
|
from abc import ABC, abstractmethod
|
|
5
6
|
from pathlib import Path
|
|
@@ -16,10 +17,12 @@ from prefect.logging import get_run_logger
|
|
|
16
17
|
from pydantic import BaseModel, ConfigDict, Field
|
|
17
18
|
from pydantic import ValidationError as PydanticValidationError
|
|
18
19
|
|
|
20
|
+
from infrahub import config
|
|
19
21
|
from infrahub.core.branch import Branch
|
|
20
22
|
from infrahub.core.constants import InfrahubKind, RepositoryOperationalStatus, RepositorySyncStatus
|
|
21
23
|
from infrahub.core.registry import registry
|
|
22
24
|
from infrahub.exceptions import (
|
|
25
|
+
BranchNotFoundError,
|
|
23
26
|
CommitNotFoundError,
|
|
24
27
|
FileOutOfRepositoryError,
|
|
25
28
|
RepositoryConnectionError,
|
|
@@ -31,6 +34,7 @@ from infrahub.exceptions import (
|
|
|
31
34
|
)
|
|
32
35
|
from infrahub.git.constants import BRANCHES_DIRECTORY_NAME, COMMITS_DIRECTORY_NAME, TEMPORARY_DIRECTORY_NAME
|
|
33
36
|
from infrahub.git.directory import get_repositories_directory, initialize_repositories_directory
|
|
37
|
+
from infrahub.git.utils import branch_name_in_import_sync_branches
|
|
34
38
|
from infrahub.git.worktree import Worktree
|
|
35
39
|
from infrahub.log import get_logger
|
|
36
40
|
from infrahub.workers.dependencies import get_client
|
|
@@ -733,6 +737,43 @@ class InfrahubRepositoryBase(BaseModel, ABC):
|
|
|
733
737
|
|
|
734
738
|
return True
|
|
735
739
|
|
|
740
|
+
async def get_filtered_remote_branches(self) -> dict[str, BranchInRemote]:
|
|
741
|
+
branches = self.get_branches_from_remote()
|
|
742
|
+
|
|
743
|
+
if not config.SETTINGS.git.import_sync_branch_names:
|
|
744
|
+
return branches
|
|
745
|
+
|
|
746
|
+
filtered_branches = {}
|
|
747
|
+
skipped_branch_names = []
|
|
748
|
+
|
|
749
|
+
for short_name, branch_data in branches.items():
|
|
750
|
+
branch = None
|
|
751
|
+
|
|
752
|
+
with contextlib.suppress(BranchNotFoundError):
|
|
753
|
+
branch = registry.get_branch_from_registry(branch=short_name)
|
|
754
|
+
|
|
755
|
+
branch_exists_import_sync_condition = branch and (
|
|
756
|
+
branch.name not in {registry.default_branch, self.default_branch}
|
|
757
|
+
and not branch.sync_with_git
|
|
758
|
+
and not branch_name_in_import_sync_branches(branch_short_name=short_name)
|
|
759
|
+
)
|
|
760
|
+
branch_does_not_exist_import_sync_condition = not branch and not branch_name_in_import_sync_branches(
|
|
761
|
+
branch_short_name=short_name
|
|
762
|
+
)
|
|
763
|
+
|
|
764
|
+
if branch_exists_import_sync_condition or branch_does_not_exist_import_sync_condition:
|
|
765
|
+
skipped_branch_names.append(short_name)
|
|
766
|
+
continue
|
|
767
|
+
|
|
768
|
+
filtered_branches[short_name] = branch_data
|
|
769
|
+
|
|
770
|
+
if skipped_branch_names:
|
|
771
|
+
log.debug(
|
|
772
|
+
f"Skipped the following branches {skipped_branch_names} "
|
|
773
|
+
f"because no match was found in import_sync_branch_names {config.SETTINGS.git.import_sync_branch_names}"
|
|
774
|
+
)
|
|
775
|
+
return filtered_branches
|
|
776
|
+
|
|
736
777
|
async def compare_local_remote(self) -> tuple[list[str], list[str]]:
|
|
737
778
|
"""
|
|
738
779
|
Returns:
|
|
@@ -745,7 +786,7 @@ class InfrahubRepositoryBase(BaseModel, ABC):
|
|
|
745
786
|
# TODO move this section into a dedicated function to compare and bring in sync the remote repo with the local one.
|
|
746
787
|
# It can be useful just after a clone etc ...
|
|
747
788
|
local_branches = self.get_branches_from_local()
|
|
748
|
-
remote_branches = self.
|
|
789
|
+
remote_branches = await self.get_filtered_remote_branches()
|
|
749
790
|
|
|
750
791
|
new_branches = set(remote_branches.keys()) - set(local_branches.keys())
|
|
751
792
|
existing_branches = set(local_branches.keys()) - new_branches
|
infrahub/git/models.py
CHANGED
|
@@ -92,7 +92,8 @@ class GitRepositoryMerge(BaseModel):
|
|
|
92
92
|
source_branch: str = Field(..., description="The source branch")
|
|
93
93
|
destination_branch: str = Field(..., description="The destination branch")
|
|
94
94
|
destination_branch_id: str = Field(..., description="The ID of the destination branch")
|
|
95
|
-
default_branch: str = Field(
|
|
95
|
+
default_branch: str | None = Field(default=None, description="The default branch in Git")
|
|
96
|
+
repository_kind: str = Field(..., description="The kind of the repository.")
|
|
96
97
|
|
|
97
98
|
|
|
98
99
|
class GitRepositoryImportObjects(BaseModel):
|
infrahub/git/repository.py
CHANGED
|
@@ -11,6 +11,7 @@ from prefect import task
|
|
|
11
11
|
from prefect.cache_policies import NONE
|
|
12
12
|
from pydantic import Field
|
|
13
13
|
|
|
14
|
+
from infrahub import config
|
|
14
15
|
from infrahub.core.constants import InfrahubKind, RepositoryInternalStatus
|
|
15
16
|
from infrahub.exceptions import RepositoryError
|
|
16
17
|
from infrahub.git.integrator import InfrahubRepositoryIntegrator
|
|
@@ -170,7 +171,10 @@ class InfrahubRepository(InfrahubRepositoryIntegrator):
|
|
|
170
171
|
commit = self.get_commit_value(branch_name=source_branch, remote=False)
|
|
171
172
|
|
|
172
173
|
try:
|
|
173
|
-
|
|
174
|
+
if config.SETTINGS.git.use_explicit_merge_commit:
|
|
175
|
+
repo.git.merge(commit, "--no-ff", m="Merged by Infrahub")
|
|
176
|
+
else:
|
|
177
|
+
repo.git.merge(commit)
|
|
174
178
|
except GitCommandError as exc:
|
|
175
179
|
repo.git.merge("--abort")
|
|
176
180
|
raise RepositoryError(identifier=self.name, message=exc.stderr) from exc
|
infrahub/git/tasks.py
CHANGED
|
@@ -502,6 +502,7 @@ async def pull_read_only(model: GitRepositoryPullReadOnly) -> None:
|
|
|
502
502
|
flow_run_name="Merge {model.source_branch} > {model.destination_branch} in git repository",
|
|
503
503
|
)
|
|
504
504
|
async def merge_git_repository(model: GitRepositoryMerge) -> None:
|
|
505
|
+
log = get_run_logger()
|
|
505
506
|
await add_tags(branches=[model.source_branch, model.destination_branch], nodes=[model.repository_id])
|
|
506
507
|
|
|
507
508
|
client = get_client()
|
|
@@ -510,7 +511,11 @@ async def merge_git_repository(model: GitRepositoryMerge) -> None:
|
|
|
510
511
|
id=model.repository_id, name=model.repository_name, client=client, default_branch_name=model.default_branch
|
|
511
512
|
)
|
|
512
513
|
|
|
513
|
-
if
|
|
514
|
+
if (
|
|
515
|
+
model.internal_status == RepositoryInternalStatus.STAGING.value
|
|
516
|
+
and model.repository_kind == InfrahubKind.REPOSITORY
|
|
517
|
+
):
|
|
518
|
+
log.info(f"Merging {model.repository_kind}")
|
|
514
519
|
repo_source = await client.get(
|
|
515
520
|
kind=InfrahubKind.GENERICREPOSITORY, id=model.repository_id, branch=model.source_branch
|
|
516
521
|
)
|
|
@@ -522,6 +527,28 @@ async def merge_git_repository(model: GitRepositoryMerge) -> None:
|
|
|
522
527
|
repo_main.commit.value = commit
|
|
523
528
|
|
|
524
529
|
await repo_main.save()
|
|
530
|
+
log.info(f"Finished merging {model.repository_kind}")
|
|
531
|
+
|
|
532
|
+
elif model.repository_kind == InfrahubKind.READONLYREPOSITORY:
|
|
533
|
+
repo_source = await client.get(
|
|
534
|
+
kind=InfrahubKind.READONLYREPOSITORY, id=model.repository_id, branch=model.source_branch
|
|
535
|
+
)
|
|
536
|
+
repo_destination = await client.get(
|
|
537
|
+
kind=InfrahubKind.READONLYREPOSITORY, id=model.repository_id, branch=model.destination_branch
|
|
538
|
+
)
|
|
539
|
+
|
|
540
|
+
if (
|
|
541
|
+
repo_destination.ref.value != repo_source.ref.value
|
|
542
|
+
or repo_destination.commit.value != repo_source.commit.value
|
|
543
|
+
):
|
|
544
|
+
log.info(f"Merging {model.repository_kind}")
|
|
545
|
+
|
|
546
|
+
repo_destination.ref.value = repo_source.ref.value
|
|
547
|
+
repo_destination.commit.value = repo_source.commit.value
|
|
548
|
+
await repo_destination.save()
|
|
549
|
+
|
|
550
|
+
log.info(f"Finished merging {model.repository_kind}")
|
|
551
|
+
|
|
525
552
|
else:
|
|
526
553
|
async with lock.registry.get(name=model.repository_name, namespace="repository"):
|
|
527
554
|
await repo.merge(source_branch=model.source_branch, dest_branch=model.destination_branch)
|
infrahub/git/utils.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import re
|
|
1
2
|
from collections import defaultdict
|
|
2
3
|
from typing import TYPE_CHECKING, Any
|
|
3
4
|
|
|
@@ -12,6 +13,7 @@ from infrahub.core.manager import NodeManager
|
|
|
12
13
|
from infrahub.database import InfrahubDatabase
|
|
13
14
|
from infrahub.generators.models import ProposedChangeGeneratorDefinition
|
|
14
15
|
|
|
16
|
+
from .. import config
|
|
15
17
|
from .models import RepositoryBranchInfo, RepositoryData
|
|
16
18
|
|
|
17
19
|
if TYPE_CHECKING:
|
|
@@ -168,3 +170,10 @@ async def fetch_proposed_change_generator_definition_targets(
|
|
|
168
170
|
return await _fetch_definition_targets(
|
|
169
171
|
client=client, branch=branch, group_id=definition.group_id, parameters=definition.parameters
|
|
170
172
|
)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def branch_name_in_import_sync_branches(branch_short_name: str) -> bool:
|
|
176
|
+
for branch_filter in config.SETTINGS.git.import_sync_branch_names:
|
|
177
|
+
if re.fullmatch(branch_filter, branch_short_name) or branch_filter == branch_short_name:
|
|
178
|
+
return True
|
|
179
|
+
return False
|