infrahub-server 1.5.0b0__py3-none-any.whl → 1.5.0b2__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/actions/tasks.py +8 -0
- infrahub/api/diff/diff.py +1 -1
- infrahub/api/internal.py +2 -0
- infrahub/api/oauth2.py +13 -19
- infrahub/api/oidc.py +15 -21
- infrahub/api/schema.py +24 -3
- infrahub/artifacts/models.py +2 -1
- infrahub/auth.py +137 -3
- infrahub/cli/__init__.py +2 -0
- infrahub/cli/db.py +103 -98
- infrahub/cli/db_commands/clean_duplicate_schema_fields.py +212 -0
- infrahub/cli/dev.py +118 -0
- infrahub/cli/tasks.py +46 -0
- infrahub/cli/upgrade.py +30 -3
- infrahub/computed_attribute/tasks.py +20 -8
- infrahub/core/attribute.py +13 -5
- infrahub/core/branch/enums.py +1 -1
- infrahub/core/branch/models.py +7 -3
- infrahub/core/branch/tasks.py +70 -8
- infrahub/core/changelog/models.py +4 -12
- infrahub/core/constants/__init__.py +3 -0
- infrahub/core/constants/infrahubkind.py +1 -0
- infrahub/core/diff/model/path.py +4 -0
- infrahub/core/diff/payload_builder.py +1 -1
- infrahub/core/diff/query/artifact.py +1 -0
- infrahub/core/diff/query/field_summary.py +1 -0
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/initialization.py +5 -2
- infrahub/core/ipam/utilization.py +1 -1
- infrahub/core/manager.py +6 -3
- infrahub/core/migrations/__init__.py +3 -0
- infrahub/core/migrations/exceptions.py +4 -0
- infrahub/core/migrations/graph/__init__.py +12 -11
- infrahub/core/migrations/graph/load_schema_branch.py +21 -0
- infrahub/core/migrations/graph/m013_convert_git_password_credential.py +1 -1
- infrahub/core/migrations/graph/m040_duplicated_attributes.py +81 -0
- infrahub/core/migrations/graph/m041_profile_attrs_in_db.py +145 -0
- infrahub/core/migrations/graph/m042_create_hfid_display_label_in_db.py +164 -0
- infrahub/core/migrations/graph/m043_backfill_hfid_display_label_in_db.py +866 -0
- infrahub/core/migrations/query/__init__.py +7 -8
- infrahub/core/migrations/query/attribute_add.py +8 -6
- infrahub/core/migrations/query/attribute_remove.py +134 -0
- infrahub/core/migrations/runner.py +54 -0
- infrahub/core/migrations/schema/attribute_kind_update.py +9 -3
- infrahub/core/migrations/schema/attribute_supports_profile.py +90 -0
- infrahub/core/migrations/schema/node_attribute_add.py +35 -4
- infrahub/core/migrations/schema/node_attribute_remove.py +13 -109
- infrahub/core/migrations/schema/node_kind_update.py +2 -1
- infrahub/core/migrations/schema/node_remove.py +2 -1
- infrahub/core/migrations/schema/placeholder_dummy.py +3 -2
- infrahub/core/migrations/shared.py +52 -19
- infrahub/core/node/__init__.py +158 -51
- infrahub/core/node/constraints/attribute_uniqueness.py +3 -1
- infrahub/core/node/create.py +46 -63
- infrahub/core/node/lock_utils.py +70 -44
- infrahub/core/node/node_property_attribute.py +230 -0
- infrahub/core/node/resource_manager/ip_address_pool.py +2 -1
- infrahub/core/node/resource_manager/ip_prefix_pool.py +2 -1
- infrahub/core/node/resource_manager/number_pool.py +2 -1
- infrahub/core/node/standard.py +1 -1
- infrahub/core/protocols.py +7 -1
- infrahub/core/query/attribute.py +55 -0
- infrahub/core/query/ipam.py +1 -0
- infrahub/core/query/node.py +23 -4
- infrahub/core/query/relationship.py +1 -0
- infrahub/core/registry.py +2 -2
- infrahub/core/relationship/constraints/count.py +1 -1
- infrahub/core/relationship/model.py +1 -1
- infrahub/core/schema/__init__.py +56 -0
- infrahub/core/schema/attribute_schema.py +4 -0
- infrahub/core/schema/basenode_schema.py +42 -2
- infrahub/core/schema/definitions/core/__init__.py +2 -0
- infrahub/core/schema/definitions/core/generator.py +2 -0
- infrahub/core/schema/definitions/core/group.py +16 -2
- infrahub/core/schema/definitions/internal.py +16 -3
- infrahub/core/schema/generated/attribute_schema.py +2 -2
- infrahub/core/schema/generated/base_node_schema.py +6 -1
- infrahub/core/schema/manager.py +22 -1
- infrahub/core/schema/node_schema.py +5 -2
- infrahub/core/schema/schema_branch.py +300 -8
- infrahub/core/schema/schema_branch_display.py +123 -0
- infrahub/core/schema/schema_branch_hfid.py +114 -0
- infrahub/core/validators/aggregated_checker.py +1 -1
- infrahub/core/validators/determiner.py +12 -1
- infrahub/core/validators/relationship/peer.py +1 -1
- infrahub/core/validators/tasks.py +1 -1
- infrahub/database/graph.py +21 -0
- infrahub/display_labels/__init__.py +0 -0
- infrahub/display_labels/gather.py +48 -0
- infrahub/display_labels/models.py +240 -0
- infrahub/display_labels/tasks.py +192 -0
- infrahub/display_labels/triggers.py +22 -0
- infrahub/events/branch_action.py +27 -1
- infrahub/events/group_action.py +1 -1
- infrahub/events/node_action.py +1 -1
- infrahub/generators/constants.py +7 -0
- infrahub/generators/models.py +7 -0
- infrahub/generators/tasks.py +34 -22
- infrahub/git/base.py +4 -1
- infrahub/git/integrator.py +23 -15
- infrahub/git/models.py +2 -1
- infrahub/git/repository.py +22 -5
- infrahub/git/tasks.py +66 -10
- infrahub/git/utils.py +123 -1
- infrahub/graphql/analyzer.py +1 -1
- infrahub/graphql/api/endpoints.py +14 -4
- infrahub/graphql/manager.py +4 -9
- infrahub/graphql/mutations/convert_object_type.py +11 -1
- infrahub/graphql/mutations/display_label.py +118 -0
- infrahub/graphql/mutations/generator.py +25 -7
- infrahub/graphql/mutations/hfid.py +125 -0
- infrahub/graphql/mutations/ipam.py +54 -35
- infrahub/graphql/mutations/main.py +27 -28
- infrahub/graphql/mutations/relationship.py +2 -2
- infrahub/graphql/mutations/resource_manager.py +2 -2
- infrahub/graphql/mutations/schema.py +5 -5
- infrahub/graphql/queries/resource_manager.py +1 -1
- infrahub/graphql/resolvers/resolver.py +2 -0
- infrahub/graphql/schema.py +4 -0
- infrahub/graphql/schema_sort.py +170 -0
- infrahub/graphql/types/branch.py +4 -1
- infrahub/graphql/types/enums.py +3 -0
- infrahub/groups/tasks.py +1 -1
- infrahub/hfid/__init__.py +0 -0
- infrahub/hfid/gather.py +48 -0
- infrahub/hfid/models.py +240 -0
- infrahub/hfid/tasks.py +191 -0
- infrahub/hfid/triggers.py +22 -0
- infrahub/lock.py +67 -16
- infrahub/message_bus/types.py +2 -1
- infrahub/middleware.py +26 -1
- infrahub/permissions/constants.py +2 -0
- infrahub/proposed_change/tasks.py +35 -17
- infrahub/server.py +21 -4
- infrahub/services/__init__.py +8 -5
- infrahub/services/adapters/http/__init__.py +5 -0
- infrahub/services/adapters/workflow/worker.py +14 -3
- infrahub/task_manager/event.py +5 -0
- infrahub/task_manager/models.py +7 -0
- infrahub/task_manager/task.py +73 -0
- infrahub/trigger/catalogue.py +4 -0
- infrahub/trigger/models.py +2 -0
- infrahub/trigger/setup.py +13 -4
- infrahub/trigger/tasks.py +6 -0
- infrahub/workers/dependencies.py +10 -1
- infrahub/workers/infrahub_async.py +10 -2
- infrahub/workflows/catalogue.py +80 -0
- infrahub/workflows/initialization.py +21 -0
- infrahub/workflows/utils.py +2 -1
- infrahub_sdk/checks.py +1 -1
- infrahub_sdk/client.py +13 -10
- infrahub_sdk/config.py +29 -2
- infrahub_sdk/ctl/cli_commands.py +2 -0
- infrahub_sdk/ctl/generator.py +4 -0
- infrahub_sdk/ctl/graphql.py +184 -0
- infrahub_sdk/ctl/schema.py +28 -9
- infrahub_sdk/generator.py +7 -1
- infrahub_sdk/graphql/__init__.py +12 -0
- infrahub_sdk/graphql/constants.py +1 -0
- infrahub_sdk/graphql/plugin.py +85 -0
- infrahub_sdk/graphql/query.py +77 -0
- infrahub_sdk/{graphql.py → graphql/renderers.py} +81 -73
- infrahub_sdk/graphql/utils.py +40 -0
- infrahub_sdk/protocols.py +14 -0
- infrahub_sdk/schema/__init__.py +70 -4
- infrahub_sdk/schema/repository.py +8 -0
- infrahub_sdk/spec/models.py +7 -0
- infrahub_sdk/spec/object.py +53 -44
- infrahub_sdk/spec/processors/__init__.py +0 -0
- infrahub_sdk/spec/processors/data_processor.py +10 -0
- infrahub_sdk/spec/processors/factory.py +34 -0
- infrahub_sdk/spec/processors/range_expand_processor.py +56 -0
- infrahub_sdk/spec/range_expansion.py +1 -1
- infrahub_sdk/transforms.py +1 -1
- {infrahub_server-1.5.0b0.dist-info → infrahub_server-1.5.0b2.dist-info}/METADATA +7 -4
- {infrahub_server-1.5.0b0.dist-info → infrahub_server-1.5.0b2.dist-info}/RECORD +182 -143
- infrahub_testcontainers/container.py +115 -3
- infrahub_testcontainers/docker-compose-cluster.test.yml +6 -1
- infrahub_testcontainers/docker-compose.test.yml +6 -1
- infrahub/core/migrations/graph/m040_profile_attrs_in_db.py +0 -166
- {infrahub_server-1.5.0b0.dist-info → infrahub_server-1.5.0b2.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.5.0b0.dist-info → infrahub_server-1.5.0b2.dist-info}/WHEEL +0 -0
- {infrahub_server-1.5.0b0.dist-info → infrahub_server-1.5.0b2.dist-info}/entry_points.txt +0 -0
|
@@ -8,7 +8,12 @@ if TYPE_CHECKING:
|
|
|
8
8
|
from ..shared import AttributeSchemaMigration, SchemaMigration
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
class
|
|
11
|
+
class MigrationBaseQuery(Query):
|
|
12
|
+
def get_nbr_migrations_executed(self) -> int:
|
|
13
|
+
return self.num_of_results
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class MigrationQuery(MigrationBaseQuery):
|
|
12
17
|
type: QueryType = QueryType.WRITE
|
|
13
18
|
|
|
14
19
|
def __init__(
|
|
@@ -19,11 +24,8 @@ class MigrationQuery(Query):
|
|
|
19
24
|
self.migration = migration
|
|
20
25
|
super().__init__(**kwargs)
|
|
21
26
|
|
|
22
|
-
def get_nbr_migrations_executed(self) -> int:
|
|
23
|
-
return self.num_of_results
|
|
24
|
-
|
|
25
27
|
|
|
26
|
-
class AttributeMigrationQuery(
|
|
28
|
+
class AttributeMigrationQuery(MigrationBaseQuery):
|
|
27
29
|
type: QueryType = QueryType.WRITE
|
|
28
30
|
|
|
29
31
|
def __init__(
|
|
@@ -33,6 +35,3 @@ class AttributeMigrationQuery(Query):
|
|
|
33
35
|
):
|
|
34
36
|
self.migration = migration
|
|
35
37
|
super().__init__(**kwargs)
|
|
36
|
-
|
|
37
|
-
def get_nbr_migrations_executed(self) -> int:
|
|
38
|
-
return self.num_of_results
|
|
@@ -17,26 +17,27 @@ class AttributeAddQuery(Query):
|
|
|
17
17
|
|
|
18
18
|
def __init__(
|
|
19
19
|
self,
|
|
20
|
-
|
|
20
|
+
node_kinds: list[str],
|
|
21
21
|
attribute_name: str,
|
|
22
22
|
attribute_kind: str,
|
|
23
23
|
branch_support: str,
|
|
24
24
|
default_value: Any | None = None,
|
|
25
|
+
uuids: list[str] | None = None,
|
|
25
26
|
**kwargs: Any,
|
|
26
27
|
) -> None:
|
|
27
|
-
self.
|
|
28
|
+
self.node_kinds = node_kinds
|
|
28
29
|
self.attribute_name = attribute_name
|
|
29
30
|
self.attribute_kind = attribute_kind
|
|
30
31
|
self.branch_support = branch_support
|
|
31
32
|
self.default_value = default_value
|
|
32
|
-
|
|
33
|
+
self.uuids = uuids
|
|
33
34
|
super().__init__(**kwargs)
|
|
34
35
|
|
|
35
36
|
async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
|
|
36
37
|
branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string())
|
|
37
38
|
self.params.update(branch_params)
|
|
38
39
|
|
|
39
|
-
self.params["
|
|
40
|
+
self.params["node_kinds"] = self.node_kinds
|
|
40
41
|
self.params["attr_name"] = self.attribute_name
|
|
41
42
|
self.params["branch_support"] = self.branch_support
|
|
42
43
|
self.params["current_time"] = self.at.to_string()
|
|
@@ -79,12 +80,13 @@ class AttributeAddQuery(Query):
|
|
|
79
80
|
LIMIT 1
|
|
80
81
|
""" % {"attr_value_label": attr_value_label}
|
|
81
82
|
|
|
83
|
+
node_kinds_str = "|".join(self.node_kinds)
|
|
82
84
|
query = """
|
|
83
85
|
%(match_query)s
|
|
84
86
|
MERGE (is_protected_value:Boolean { value: $is_protected_default })
|
|
85
87
|
MERGE (is_visible_value:Boolean { value: $is_visible_default })
|
|
86
88
|
WITH av, is_protected_value, is_visible_value
|
|
87
|
-
MATCH
|
|
89
|
+
MATCH (n:%(node_kinds_str)s)
|
|
88
90
|
CALL (n) {
|
|
89
91
|
MATCH (:Root)<-[r:IS_PART_OF]-(n)
|
|
90
92
|
WHERE %(branch_filter)s
|
|
@@ -110,7 +112,7 @@ class AttributeAddQuery(Query):
|
|
|
110
112
|
""" % {
|
|
111
113
|
"match_query": match_query,
|
|
112
114
|
"branch_filter": branch_filter,
|
|
113
|
-
"
|
|
115
|
+
"node_kinds_str": node_kinds_str,
|
|
114
116
|
"uuid_generation": db.render_uuid_generation(node_label="a", node_attr="uuid"),
|
|
115
117
|
}
|
|
116
118
|
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any
|
|
4
|
+
|
|
5
|
+
from infrahub.core.constants import RelationshipStatus
|
|
6
|
+
from infrahub.core.graph.schema import GraphAttributeRelationships
|
|
7
|
+
from infrahub.core.query import Query
|
|
8
|
+
from infrahub.core.schema.generic_schema import GenericSchema
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from pydantic.fields import FieldInfo
|
|
12
|
+
|
|
13
|
+
from infrahub.database import InfrahubDatabase
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class AttributeRemoveQuery(Query):
|
|
17
|
+
name = "attribute_remove"
|
|
18
|
+
insert_return: bool = False
|
|
19
|
+
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
attribute_name: str,
|
|
23
|
+
node_kinds: list[str],
|
|
24
|
+
**kwargs: Any,
|
|
25
|
+
) -> None:
|
|
26
|
+
self.attribute_name = attribute_name
|
|
27
|
+
self.node_kinds = node_kinds
|
|
28
|
+
super().__init__(**kwargs)
|
|
29
|
+
|
|
30
|
+
async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
|
|
31
|
+
branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string())
|
|
32
|
+
self.params.update(branch_params)
|
|
33
|
+
|
|
34
|
+
kinds_to_ignore = []
|
|
35
|
+
profile_kinds_to_update = []
|
|
36
|
+
|
|
37
|
+
for node_kind in self.node_kinds:
|
|
38
|
+
new_schema = db.schema.get(name=node_kind, branch=self.branch, duplicate=False)
|
|
39
|
+
|
|
40
|
+
if isinstance(new_schema, GenericSchema):
|
|
41
|
+
for inheriting_schema_kind in new_schema.used_by:
|
|
42
|
+
node_schema = db.schema.get_node_schema(
|
|
43
|
+
name=inheriting_schema_kind, branch=self.branch, duplicate=False
|
|
44
|
+
)
|
|
45
|
+
attr_schema = node_schema.get_attribute_or_none(name=self.attribute_name)
|
|
46
|
+
if attr_schema and not attr_schema.inherited:
|
|
47
|
+
kinds_to_ignore.append(inheriting_schema_kind)
|
|
48
|
+
else:
|
|
49
|
+
profile_kinds_to_update.append(f"Profile{inheriting_schema_kind}")
|
|
50
|
+
|
|
51
|
+
self.params["kinds_to_ignore"] = kinds_to_ignore
|
|
52
|
+
self.params["attr_name"] = self.attribute_name
|
|
53
|
+
self.params["current_time"] = self.at.to_string()
|
|
54
|
+
self.params["branch_name"] = self.branch.name
|
|
55
|
+
|
|
56
|
+
self.params["rel_props"] = {
|
|
57
|
+
"branch": self.branch.name,
|
|
58
|
+
"branch_level": self.branch.hierarchy_level,
|
|
59
|
+
"status": RelationshipStatus.DELETED.value,
|
|
60
|
+
"from": self.at.to_string(),
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
def render_sub_query_per_rel_type(rel_type: str, rel_def: FieldInfo) -> str:
|
|
64
|
+
subquery = [
|
|
65
|
+
"WITH peer_node, rb, active_attr",
|
|
66
|
+
f'WHERE type(rb) = "{rel_type}"',
|
|
67
|
+
]
|
|
68
|
+
if rel_def.default.direction.value == "outbound":
|
|
69
|
+
subquery.append(f"CREATE (active_attr)-[:{rel_type} $rel_props ]->(peer_node)")
|
|
70
|
+
elif rel_def.default.direction.value == "inbound":
|
|
71
|
+
subquery.append(f"CREATE (active_attr)<-[:{rel_type} $rel_props ]-(peer_node)")
|
|
72
|
+
else:
|
|
73
|
+
subquery.append(f"CREATE (active_attr)-[:{rel_type} $rel_props ]-(peer_node)")
|
|
74
|
+
|
|
75
|
+
subquery.append("RETURN peer_node as p2")
|
|
76
|
+
return "\n".join(subquery)
|
|
77
|
+
|
|
78
|
+
sub_queries = [
|
|
79
|
+
render_sub_query_per_rel_type(rel_type, rel_def)
|
|
80
|
+
for rel_type, rel_def in GraphAttributeRelationships.model_fields.items()
|
|
81
|
+
]
|
|
82
|
+
sub_query_all = "\nUNION\n".join(sub_queries)
|
|
83
|
+
|
|
84
|
+
node_kinds_str = "|".join(self.node_kinds + profile_kinds_to_update)
|
|
85
|
+
query = """
|
|
86
|
+
// Find all the active nodes
|
|
87
|
+
MATCH (node:%(node_kinds)s)
|
|
88
|
+
WHERE (size($kinds_to_ignore) = 0 OR NOT any(l IN labels(node) WHERE l IN $kinds_to_ignore))
|
|
89
|
+
AND exists((node)-[:HAS_ATTRIBUTE]-(:Attribute { name: $attr_name }))
|
|
90
|
+
CALL (node) {
|
|
91
|
+
MATCH (root:Root)<-[r:IS_PART_OF]-(node)
|
|
92
|
+
WHERE %(branch_filter)s
|
|
93
|
+
RETURN node as n1, r as r1
|
|
94
|
+
ORDER BY r.branch_level DESC, r.from DESC
|
|
95
|
+
LIMIT 1
|
|
96
|
+
}
|
|
97
|
+
WITH n1 as active_node, r1 as rb
|
|
98
|
+
WHERE rb.status = "active"
|
|
99
|
+
// Find all the attributes that need to be updated
|
|
100
|
+
CALL (active_node) {
|
|
101
|
+
MATCH (active_node)-[r:HAS_ATTRIBUTE]-(attr:Attribute { name: $attr_name })
|
|
102
|
+
WHERE %(branch_filter)s
|
|
103
|
+
RETURN active_node as n1, r as r1, attr as attr1
|
|
104
|
+
ORDER BY r.branch_level DESC, r.from DESC
|
|
105
|
+
LIMIT 1
|
|
106
|
+
}
|
|
107
|
+
WITH n1 as active_node, r1 as rb, attr1 as active_attr
|
|
108
|
+
WHERE rb.status = "active"
|
|
109
|
+
WITH active_attr
|
|
110
|
+
MATCH (active_attr)-[]-(peer)
|
|
111
|
+
WITH DISTINCT active_attr, peer
|
|
112
|
+
CALL (active_attr, peer) {
|
|
113
|
+
MATCH (active_attr)-[r]-(peer)
|
|
114
|
+
WHERE %(branch_filter)s
|
|
115
|
+
RETURN active_attr as a1, r as r1, peer as p1
|
|
116
|
+
ORDER BY r.branch_level DESC, r.from DESC
|
|
117
|
+
LIMIT 1
|
|
118
|
+
}
|
|
119
|
+
WITH a1 as active_attr, r1 as rb, p1 as peer_node
|
|
120
|
+
WHERE rb.status = "active"
|
|
121
|
+
CALL (peer_node, rb, active_attr) {
|
|
122
|
+
%(sub_query_all)s
|
|
123
|
+
}
|
|
124
|
+
WITH p2 as peer_node, rb, active_attr
|
|
125
|
+
FOREACH (i in CASE WHEN rb.branch = $branch_name THEN [1] ELSE [] END |
|
|
126
|
+
SET rb.to = $current_time
|
|
127
|
+
)
|
|
128
|
+
RETURN DISTINCT active_attr
|
|
129
|
+
""" % {
|
|
130
|
+
"branch_filter": branch_filter,
|
|
131
|
+
"sub_query_all": sub_query_all,
|
|
132
|
+
"node_kinds": node_kinds_str,
|
|
133
|
+
}
|
|
134
|
+
self.add_to_query(query)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Sequence
|
|
4
|
+
|
|
5
|
+
from infrahub.core import registry
|
|
6
|
+
from infrahub.core.constants import GLOBAL_BRANCH_NAME
|
|
7
|
+
from infrahub.core.migrations.graph import MIGRATIONS
|
|
8
|
+
|
|
9
|
+
from .exceptions import MigrationFailureError
|
|
10
|
+
from .shared import MigrationRequiringRebase
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from infrahub.core.branch import Branch
|
|
14
|
+
from infrahub.database import InfrahubDatabase
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class MigrationRunner:
|
|
18
|
+
def __init__(self, branch: Branch) -> None:
|
|
19
|
+
if branch.name in (registry.default_branch, GLOBAL_BRANCH_NAME):
|
|
20
|
+
raise ValueError("MigrationRunner cannot be used to apply migration on default branches")
|
|
21
|
+
|
|
22
|
+
self.branch = branch
|
|
23
|
+
self.applicable_migrations = self._get_applicable_migrations()
|
|
24
|
+
|
|
25
|
+
def _get_applicable_migrations(self) -> Sequence[MigrationRequiringRebase]:
|
|
26
|
+
applicable_migrations = []
|
|
27
|
+
for migration_class in [m for m in MIGRATIONS if issubclass(m, MigrationRequiringRebase)]:
|
|
28
|
+
migration = migration_class.init()
|
|
29
|
+
if self.branch.graph_version and self.branch.graph_version > migration.minimum_version:
|
|
30
|
+
continue
|
|
31
|
+
applicable_migrations.append(migration)
|
|
32
|
+
|
|
33
|
+
return applicable_migrations
|
|
34
|
+
|
|
35
|
+
def has_migrations(self) -> bool:
|
|
36
|
+
return bool(self.applicable_migrations)
|
|
37
|
+
|
|
38
|
+
async def run(self, db: InfrahubDatabase) -> None:
|
|
39
|
+
if not self.has_migrations():
|
|
40
|
+
return
|
|
41
|
+
|
|
42
|
+
for migration in self.applicable_migrations:
|
|
43
|
+
execution_result = await migration.execute_against_branch(db=db, branch=self.branch)
|
|
44
|
+
validation_result = None
|
|
45
|
+
|
|
46
|
+
if execution_result.success:
|
|
47
|
+
validation_result = await migration.validate_migration(db=db)
|
|
48
|
+
|
|
49
|
+
if not execution_result.success or (validation_result and not validation_result.success):
|
|
50
|
+
if execution_result.errors:
|
|
51
|
+
raise MigrationFailureError(errors=execution_result.errors)
|
|
52
|
+
|
|
53
|
+
if validation_result and not validation_result.success:
|
|
54
|
+
raise MigrationFailureError(errors=validation_result.errors)
|
|
@@ -4,7 +4,7 @@ from typing import TYPE_CHECKING, Any, Sequence
|
|
|
4
4
|
|
|
5
5
|
from infrahub.types import is_large_attribute_type
|
|
6
6
|
|
|
7
|
-
from ..query import AttributeMigrationQuery
|
|
7
|
+
from ..query import AttributeMigrationQuery, MigrationBaseQuery
|
|
8
8
|
from ..shared import AttributeSchemaMigration, MigrationResult
|
|
9
9
|
|
|
10
10
|
if TYPE_CHECKING:
|
|
@@ -147,10 +147,16 @@ class AttributeKindUpdateMigration(AttributeSchemaMigration):
|
|
|
147
147
|
name: str = "attribute.kind.update"
|
|
148
148
|
queries: Sequence[type[AttributeMigrationQuery]] = [AttributeKindUpdateMigrationQuery] # type: ignore[assignment]
|
|
149
149
|
|
|
150
|
-
async def execute(
|
|
150
|
+
async def execute(
|
|
151
|
+
self,
|
|
152
|
+
db: InfrahubDatabase,
|
|
153
|
+
branch: Branch,
|
|
154
|
+
at: Timestamp | str | None = None,
|
|
155
|
+
queries: Sequence[type[MigrationBaseQuery]] | None = None,
|
|
156
|
+
) -> MigrationResult:
|
|
151
157
|
is_indexed_previous = is_large_attribute_type(self.previous_attribute_schema.kind)
|
|
152
158
|
is_indexed_new = is_large_attribute_type(self.new_attribute_schema.kind)
|
|
153
159
|
if is_indexed_previous is is_indexed_new:
|
|
154
160
|
return MigrationResult()
|
|
155
161
|
|
|
156
|
-
return await super().execute(db=db, branch=branch, at=at)
|
|
162
|
+
return await super().execute(db=db, branch=branch, at=at, queries=queries)
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Sequence
|
|
4
|
+
|
|
5
|
+
from infrahub.core.migrations.query.attribute_remove import AttributeRemoveQuery
|
|
6
|
+
from infrahub.core.schema.generic_schema import GenericSchema
|
|
7
|
+
from infrahub.core.schema.node_schema import NodeSchema
|
|
8
|
+
|
|
9
|
+
from ..query import AttributeMigrationQuery, MigrationBaseQuery
|
|
10
|
+
from ..query.attribute_add import AttributeAddQuery
|
|
11
|
+
from ..shared import AttributeSchemaMigration, MigrationResult
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from infrahub.core.branch.models import Branch
|
|
15
|
+
from infrahub.core.schema import MainSchemaTypes
|
|
16
|
+
from infrahub.core.timestamp import Timestamp
|
|
17
|
+
from infrahub.database import InfrahubDatabase
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _get_node_kinds(schema: MainSchemaTypes) -> list[str]:
|
|
21
|
+
if not isinstance(schema, (NodeSchema, GenericSchema)):
|
|
22
|
+
return [schema.kind]
|
|
23
|
+
schema_kinds = [f"Profile{schema.kind}"]
|
|
24
|
+
if isinstance(schema, GenericSchema) and schema.used_by:
|
|
25
|
+
schema_kinds += [f"Profile{kind}" for kind in schema.used_by]
|
|
26
|
+
return schema_kinds
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ProfilesAttributeAddMigrationQuery(AttributeMigrationQuery, AttributeAddQuery):
|
|
30
|
+
name = "migration_profiles_attribute_add"
|
|
31
|
+
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
migration: AttributeSchemaMigration,
|
|
35
|
+
**kwargs: Any,
|
|
36
|
+
):
|
|
37
|
+
node_kinds = _get_node_kinds(migration.new_schema)
|
|
38
|
+
super().__init__(
|
|
39
|
+
migration=migration,
|
|
40
|
+
node_kinds=node_kinds,
|
|
41
|
+
attribute_name=migration.new_attribute_schema.name,
|
|
42
|
+
attribute_kind=migration.new_attribute_schema.kind,
|
|
43
|
+
branch_support=migration.new_attribute_schema.get_branch(),
|
|
44
|
+
default_value=migration.new_attribute_schema.default_value,
|
|
45
|
+
**kwargs,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ProfilesAttributeRemoveMigrationQuery(AttributeMigrationQuery, AttributeRemoveQuery):
|
|
50
|
+
name = "migration_profiles_attribute_remove"
|
|
51
|
+
|
|
52
|
+
def __init__(
|
|
53
|
+
self,
|
|
54
|
+
migration: AttributeSchemaMigration,
|
|
55
|
+
**kwargs: Any,
|
|
56
|
+
):
|
|
57
|
+
node_kinds = _get_node_kinds(migration.new_schema)
|
|
58
|
+
super().__init__(
|
|
59
|
+
migration=migration,
|
|
60
|
+
attribute_name=migration.new_attribute_schema.name,
|
|
61
|
+
node_kinds=node_kinds,
|
|
62
|
+
**kwargs,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class AttributeSupportsProfileUpdateMigration(AttributeSchemaMigration):
|
|
67
|
+
name: str = "attribute.supports_profile.update"
|
|
68
|
+
queries: Sequence[type[MigrationBaseQuery]] = []
|
|
69
|
+
|
|
70
|
+
async def execute(
|
|
71
|
+
self,
|
|
72
|
+
db: InfrahubDatabase,
|
|
73
|
+
branch: Branch,
|
|
74
|
+
at: Timestamp | str | None = None,
|
|
75
|
+
queries: Sequence[type[MigrationBaseQuery]] | None = None, # noqa: ARG002
|
|
76
|
+
) -> MigrationResult:
|
|
77
|
+
if (
|
|
78
|
+
# no change in whether the attribute should be used on profiles
|
|
79
|
+
self.previous_attribute_schema.support_profiles == self.new_attribute_schema.support_profiles
|
|
80
|
+
# the attribute is new, so there cannot be existing profiles to update
|
|
81
|
+
or self.previous_attribute_schema.id is None
|
|
82
|
+
):
|
|
83
|
+
return MigrationResult()
|
|
84
|
+
profiles_queries: list[type[AttributeMigrationQuery]] = []
|
|
85
|
+
if self.new_attribute_schema.support_profiles:
|
|
86
|
+
profiles_queries.append(ProfilesAttributeAddMigrationQuery)
|
|
87
|
+
if not self.new_attribute_schema.support_profiles:
|
|
88
|
+
profiles_queries.append(ProfilesAttributeRemoveMigrationQuery)
|
|
89
|
+
|
|
90
|
+
return await super().execute(db=db, branch=branch, at=at, queries=profiles_queries)
|
|
@@ -4,15 +4,19 @@ from typing import TYPE_CHECKING, Any, Sequence
|
|
|
4
4
|
|
|
5
5
|
from infrahub.core import registry
|
|
6
6
|
from infrahub.core.node import Node
|
|
7
|
+
from infrahub.core.schema.generic_schema import GenericSchema
|
|
8
|
+
from infrahub.core.schema.node_schema import NodeSchema
|
|
7
9
|
from infrahub.exceptions import PoolExhaustedError
|
|
8
10
|
from infrahub.tasks.registry import update_branch_registry
|
|
9
11
|
|
|
10
|
-
from ..query import AttributeMigrationQuery
|
|
12
|
+
from ..query import AttributeMigrationQuery, MigrationBaseQuery
|
|
11
13
|
from ..query.attribute_add import AttributeAddQuery
|
|
12
14
|
from ..shared import AttributeSchemaMigration, MigrationResult
|
|
13
15
|
|
|
14
16
|
if TYPE_CHECKING:
|
|
15
17
|
from infrahub.core.node.resource_manager.number_pool import CoreNumberPool
|
|
18
|
+
from infrahub.core.schema import MainSchemaTypes
|
|
19
|
+
from infrahub.core.schema.attribute_schema import AttributeSchema
|
|
16
20
|
from infrahub.database import InfrahubDatabase
|
|
17
21
|
|
|
18
22
|
from ...branch import Branch
|
|
@@ -22,14 +26,27 @@ if TYPE_CHECKING:
|
|
|
22
26
|
class NodeAttributeAddMigrationQuery01(AttributeMigrationQuery, AttributeAddQuery):
|
|
23
27
|
name = "migration_node_attribute_add_01"
|
|
24
28
|
|
|
29
|
+
def _get_node_kinds(self, schema: MainSchemaTypes, new_attribute_schema: AttributeSchema) -> list[str]:
|
|
30
|
+
schema_kinds = [schema.kind]
|
|
31
|
+
if not isinstance(schema, (NodeSchema, GenericSchema)):
|
|
32
|
+
return schema_kinds
|
|
33
|
+
if new_attribute_schema.support_profiles:
|
|
34
|
+
schema_kinds.append(f"Profile{schema.kind}")
|
|
35
|
+
if isinstance(schema, GenericSchema) and schema.used_by:
|
|
36
|
+
schema_kinds.extend([f"Profile{kind}" for kind in schema.used_by])
|
|
37
|
+
return schema_kinds
|
|
38
|
+
|
|
25
39
|
def __init__(
|
|
26
40
|
self,
|
|
27
41
|
migration: AttributeSchemaMigration,
|
|
28
42
|
**kwargs: Any,
|
|
29
43
|
):
|
|
44
|
+
node_kinds = self._get_node_kinds(
|
|
45
|
+
schema=migration.new_schema, new_attribute_schema=migration.new_attribute_schema
|
|
46
|
+
)
|
|
30
47
|
super().__init__(
|
|
31
48
|
migration=migration,
|
|
32
|
-
|
|
49
|
+
node_kinds=node_kinds,
|
|
33
50
|
attribute_name=migration.new_attribute_schema.name,
|
|
34
51
|
attribute_kind=migration.new_attribute_schema.kind,
|
|
35
52
|
branch_support=migration.new_attribute_schema.get_branch().value,
|
|
@@ -42,6 +59,17 @@ class NodeAttributeAddMigration(AttributeSchemaMigration):
|
|
|
42
59
|
name: str = "node.attribute.add"
|
|
43
60
|
queries: Sequence[type[AttributeMigrationQuery]] = [NodeAttributeAddMigrationQuery01] # type: ignore[assignment]
|
|
44
61
|
|
|
62
|
+
async def execute(
|
|
63
|
+
self,
|
|
64
|
+
db: InfrahubDatabase,
|
|
65
|
+
branch: Branch,
|
|
66
|
+
at: Timestamp | str | None = None,
|
|
67
|
+
queries: Sequence[type[MigrationBaseQuery]] | None = None,
|
|
68
|
+
) -> MigrationResult:
|
|
69
|
+
if self.new_attribute_schema.inherited is True:
|
|
70
|
+
return MigrationResult()
|
|
71
|
+
return await super().execute(db=db, branch=branch, at=at, queries=queries)
|
|
72
|
+
|
|
45
73
|
async def execute_post_queries(
|
|
46
74
|
self,
|
|
47
75
|
db: InfrahubDatabase,
|
|
@@ -52,8 +80,11 @@ class NodeAttributeAddMigration(AttributeSchemaMigration):
|
|
|
52
80
|
if self.new_attribute_schema.kind != "NumberPool":
|
|
53
81
|
return result
|
|
54
82
|
|
|
55
|
-
number_pool: CoreNumberPool = await Node.fetch_or_create_number_pool(
|
|
56
|
-
db=db,
|
|
83
|
+
number_pool: CoreNumberPool = await Node.fetch_or_create_number_pool(
|
|
84
|
+
db=db,
|
|
85
|
+
branch=branch,
|
|
86
|
+
schema_node=self.new_schema, # type: ignore
|
|
87
|
+
schema_attribute=self.new_attribute_schema,
|
|
57
88
|
)
|
|
58
89
|
|
|
59
90
|
await update_branch_registry(db=db, branch=branch)
|
|
@@ -1,123 +1,27 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import
|
|
4
|
-
|
|
5
|
-
from infrahub.core.constants import RelationshipStatus
|
|
6
|
-
from infrahub.core.graph.schema import GraphAttributeRelationships
|
|
7
|
-
from infrahub.core.schema.generic_schema import GenericSchema
|
|
3
|
+
from typing import Any, Sequence
|
|
8
4
|
|
|
9
5
|
from ..query import AttributeMigrationQuery
|
|
6
|
+
from ..query.attribute_remove import AttributeRemoveQuery
|
|
10
7
|
from ..shared import AttributeSchemaMigration
|
|
11
8
|
|
|
12
|
-
if TYPE_CHECKING:
|
|
13
|
-
from pydantic.fields import FieldInfo
|
|
14
|
-
|
|
15
|
-
from infrahub.database import InfrahubDatabase
|
|
16
|
-
|
|
17
9
|
|
|
18
|
-
class NodeAttributeRemoveMigrationQuery01(AttributeMigrationQuery):
|
|
10
|
+
class NodeAttributeRemoveMigrationQuery01(AttributeMigrationQuery, AttributeRemoveQuery):
|
|
19
11
|
name = "migration_node_attribute_remove_01"
|
|
20
12
|
insert_return: bool = False
|
|
21
13
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
)
|
|
33
|
-
attr_schema = node_schema.get_attribute_or_none(name=attr_name)
|
|
34
|
-
if attr_schema and not attr_schema.inherited:
|
|
35
|
-
kinds_to_ignore.append(inheriting_schema_kind)
|
|
36
|
-
|
|
37
|
-
self.params["node_kind"] = self.migration.new_schema.kind
|
|
38
|
-
self.params["kinds_to_ignore"] = kinds_to_ignore
|
|
39
|
-
self.params["attr_name"] = attr_name
|
|
40
|
-
self.params["current_time"] = self.at.to_string()
|
|
41
|
-
self.params["branch_name"] = self.branch.name
|
|
42
|
-
self.params["branch_support"] = self.migration.previous_attribute_schema.get_branch().value
|
|
43
|
-
|
|
44
|
-
self.params["rel_props"] = {
|
|
45
|
-
"branch": self.branch.name,
|
|
46
|
-
"branch_level": self.branch.hierarchy_level,
|
|
47
|
-
"status": RelationshipStatus.DELETED.value,
|
|
48
|
-
"from": self.at.to_string(),
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
def render_sub_query_per_rel_type(rel_type: str, rel_def: FieldInfo) -> str:
|
|
52
|
-
subquery = [
|
|
53
|
-
"WITH peer_node, rb, active_attr",
|
|
54
|
-
f'WHERE type(rb) = "{rel_type}"',
|
|
55
|
-
]
|
|
56
|
-
if rel_def.default.direction.value == "outbound":
|
|
57
|
-
subquery.append(f"CREATE (active_attr)-[:{rel_type} $rel_props ]->(peer_node)")
|
|
58
|
-
elif rel_def.default.direction.value == "inbound":
|
|
59
|
-
subquery.append(f"CREATE (active_attr)<-[:{rel_type} $rel_props ]-(peer_node)")
|
|
60
|
-
else:
|
|
61
|
-
subquery.append(f"CREATE (active_attr)-[:{rel_type} $rel_props ]-(peer_node)")
|
|
62
|
-
|
|
63
|
-
subquery.append("RETURN peer_node as p2")
|
|
64
|
-
return "\n".join(subquery)
|
|
65
|
-
|
|
66
|
-
sub_queries = [
|
|
67
|
-
render_sub_query_per_rel_type(rel_type, rel_def)
|
|
68
|
-
for rel_type, rel_def in GraphAttributeRelationships.model_fields.items()
|
|
69
|
-
]
|
|
70
|
-
sub_query_all = "\nUNION\n".join(sub_queries)
|
|
71
|
-
|
|
72
|
-
query = """
|
|
73
|
-
// Find all the active nodes
|
|
74
|
-
MATCH (node:%(node_kind)s)
|
|
75
|
-
WHERE (size($kinds_to_ignore) = 0 OR NOT any(l IN labels(node) WHERE l IN $kinds_to_ignore))
|
|
76
|
-
AND exists((node)-[:HAS_ATTRIBUTE]-(:Attribute { name: $attr_name }))
|
|
77
|
-
CALL (node) {
|
|
78
|
-
MATCH (root:Root)<-[r:IS_PART_OF]-(node)
|
|
79
|
-
WHERE %(branch_filter)s
|
|
80
|
-
RETURN node as n1, r as r1
|
|
81
|
-
ORDER BY r.branch_level DESC, r.from DESC
|
|
82
|
-
LIMIT 1
|
|
83
|
-
}
|
|
84
|
-
WITH n1 as active_node, r1 as rb
|
|
85
|
-
WHERE rb.status = "active"
|
|
86
|
-
// Find all the attributes that need to be updated
|
|
87
|
-
CALL (active_node) {
|
|
88
|
-
MATCH (active_node)-[r:HAS_ATTRIBUTE]-(attr:Attribute { name: $attr_name })
|
|
89
|
-
WHERE %(branch_filter)s
|
|
90
|
-
RETURN active_node as n1, r as r1, attr as attr1
|
|
91
|
-
ORDER BY r.branch_level DESC, r.from DESC
|
|
92
|
-
LIMIT 1
|
|
93
|
-
}
|
|
94
|
-
WITH n1 as active_node, r1 as rb, attr1 as active_attr
|
|
95
|
-
WHERE rb.status = "active"
|
|
96
|
-
WITH active_attr
|
|
97
|
-
MATCH (active_attr)-[]-(peer)
|
|
98
|
-
CALL (active_attr, peer) {
|
|
99
|
-
MATCH (active_attr)-[r]-(peer)
|
|
100
|
-
WHERE %(branch_filter)s
|
|
101
|
-
RETURN active_attr as a1, r as r1, peer as p1
|
|
102
|
-
ORDER BY r.branch_level DESC, r.from DESC
|
|
103
|
-
LIMIT 1
|
|
104
|
-
}
|
|
105
|
-
WITH a1 as active_attr, r1 as rb, p1 as peer_node
|
|
106
|
-
WHERE rb.status = "active"
|
|
107
|
-
CALL (peer_node, rb, active_attr) {
|
|
108
|
-
%(sub_query_all)s
|
|
109
|
-
}
|
|
110
|
-
WITH p2 as peer_node, rb, active_attr
|
|
111
|
-
FOREACH (i in CASE WHEN rb.branch = $branch_name THEN [1] ELSE [] END |
|
|
112
|
-
SET rb.to = $current_time
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
migration: AttributeSchemaMigration,
|
|
17
|
+
**kwargs: Any,
|
|
18
|
+
):
|
|
19
|
+
super().__init__(
|
|
20
|
+
migration=migration,
|
|
21
|
+
attribute_name=migration.previous_attribute_schema.name,
|
|
22
|
+
node_kinds=[migration.new_schema.kind],
|
|
23
|
+
**kwargs,
|
|
113
24
|
)
|
|
114
|
-
RETURN DISTINCT active_attr
|
|
115
|
-
""" % {
|
|
116
|
-
"branch_filter": branch_filter,
|
|
117
|
-
"sub_query_all": sub_query_all,
|
|
118
|
-
"node_kind": self.migration.new_schema.kind,
|
|
119
|
-
}
|
|
120
|
-
self.add_to_query(query)
|
|
121
25
|
|
|
122
26
|
|
|
123
27
|
class NodeAttributeRemoveMigration(AttributeSchemaMigration):
|
|
@@ -2,8 +2,9 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import Any, Sequence
|
|
4
4
|
|
|
5
|
+
from ..query import MigrationQuery
|
|
5
6
|
from ..query.node_duplicate import NodeDuplicateQuery, SchemaNodeInfo
|
|
6
|
-
from ..shared import
|
|
7
|
+
from ..shared import SchemaMigration
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
class NodeKindUpdateMigrationQuery01(MigrationQuery, NodeDuplicateQuery):
|
|
@@ -5,7 +5,8 @@ from typing import TYPE_CHECKING, Any, Sequence
|
|
|
5
5
|
from infrahub.core.constants import RelationshipStatus
|
|
6
6
|
from infrahub.core.graph.schema import GraphNodeRelationships, GraphRelDirection
|
|
7
7
|
|
|
8
|
-
from ..
|
|
8
|
+
from ..query import MigrationQuery
|
|
9
|
+
from ..shared import SchemaMigration
|
|
9
10
|
|
|
10
11
|
if TYPE_CHECKING:
|
|
11
12
|
from pydantic.fields import FieldInfo
|
|
@@ -4,9 +4,10 @@ from typing import Sequence
|
|
|
4
4
|
|
|
5
5
|
from pydantic import Field
|
|
6
6
|
|
|
7
|
-
from ..
|
|
7
|
+
from ..query import MigrationBaseQuery # noqa: TC001
|
|
8
|
+
from ..shared import SchemaMigration
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
class PlaceholderDummyMigration(SchemaMigration):
|
|
11
12
|
name: str = "dummy.placeholder"
|
|
12
|
-
queries: Sequence[type[
|
|
13
|
+
queries: Sequence[type[MigrationBaseQuery]] = Field(default_factory=list)
|