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
|
@@ -41,14 +41,16 @@ from .m036_drop_attr_value_index import Migration036
|
|
|
41
41
|
from .m037_index_attr_vals import Migration037
|
|
42
42
|
from .m038_redo_0000_prefix_fix import Migration038
|
|
43
43
|
from .m039_ipam_reconcile import Migration039
|
|
44
|
-
from .
|
|
44
|
+
from .m040_duplicated_attributes import Migration040
|
|
45
|
+
from .m041_profile_attrs_in_db import Migration041
|
|
46
|
+
from .m042_create_hfid_display_label_in_db import Migration042
|
|
47
|
+
from .m043_backfill_hfid_display_label_in_db import Migration043
|
|
45
48
|
|
|
46
49
|
if TYPE_CHECKING:
|
|
47
|
-
from
|
|
50
|
+
from ..shared import MigrationTypes
|
|
48
51
|
|
|
49
|
-
from ..shared import ArbitraryMigration, GraphMigration, InternalSchemaMigration
|
|
50
52
|
|
|
51
|
-
MIGRATIONS: list[type[
|
|
53
|
+
MIGRATIONS: list[type[MigrationTypes]] = [
|
|
52
54
|
Migration001,
|
|
53
55
|
Migration002,
|
|
54
56
|
Migration003,
|
|
@@ -89,25 +91,24 @@ MIGRATIONS: list[type[GraphMigration | InternalSchemaMigration | ArbitraryMigrat
|
|
|
89
91
|
Migration038,
|
|
90
92
|
Migration039,
|
|
91
93
|
Migration040,
|
|
94
|
+
Migration041,
|
|
95
|
+
Migration042,
|
|
96
|
+
Migration043,
|
|
92
97
|
]
|
|
93
98
|
|
|
94
99
|
|
|
95
|
-
async def get_graph_migrations(
|
|
96
|
-
root: Root,
|
|
97
|
-
) -> Sequence[GraphMigration | InternalSchemaMigration | ArbitraryMigration]:
|
|
100
|
+
async def get_graph_migrations(current_graph_version: int) -> Sequence[MigrationTypes]:
|
|
98
101
|
applicable_migrations = []
|
|
99
102
|
for migration_class in MIGRATIONS:
|
|
100
103
|
migration = migration_class.init()
|
|
101
|
-
if
|
|
104
|
+
if current_graph_version > migration.minimum_version:
|
|
102
105
|
continue
|
|
103
106
|
applicable_migrations.append(migration)
|
|
104
107
|
|
|
105
108
|
return applicable_migrations
|
|
106
109
|
|
|
107
110
|
|
|
108
|
-
def get_migration_by_number(
|
|
109
|
-
migration_number: int | str,
|
|
110
|
-
) -> GraphMigration | InternalSchemaMigration | ArbitraryMigration:
|
|
111
|
+
def get_migration_by_number(migration_number: int | str) -> MigrationTypes:
|
|
111
112
|
# Convert to string and pad with zeros if needed
|
|
112
113
|
try:
|
|
113
114
|
num = int(migration_number)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from infrahub.core import registry
|
|
2
|
+
from infrahub.core.branch import Branch
|
|
3
|
+
from infrahub.core.schema import SchemaRoot, internal_schema
|
|
4
|
+
from infrahub.core.schema.manager import SchemaManager
|
|
5
|
+
from infrahub.core.schema.schema_branch import SchemaBranch
|
|
6
|
+
from infrahub.database import InfrahubDatabase
|
|
7
|
+
from infrahub.exceptions import InitializationError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
async def get_or_load_schema_branch(db: InfrahubDatabase, branch: Branch) -> SchemaBranch:
|
|
11
|
+
try:
|
|
12
|
+
if registry.schema.has_schema_branch(branch.name):
|
|
13
|
+
return registry.schema.get_schema_branch(branch.name)
|
|
14
|
+
except InitializationError:
|
|
15
|
+
schema_manager = SchemaManager()
|
|
16
|
+
registry.schema = schema_manager
|
|
17
|
+
internal_schema_root = SchemaRoot(**internal_schema)
|
|
18
|
+
registry.schema.register_schema(schema=internal_schema_root)
|
|
19
|
+
schema_branch = await registry.schema.load_schema_from_db(db=db, branch=branch)
|
|
20
|
+
registry.schema.set_schema_branch(name=branch.name, schema=schema_branch)
|
|
21
|
+
return schema_branch
|
|
@@ -286,7 +286,7 @@ class Migration013AddInternalStatusData(AttributeAddQuery):
|
|
|
286
286
|
kwargs.pop("branch", None)
|
|
287
287
|
|
|
288
288
|
super().__init__(
|
|
289
|
-
|
|
289
|
+
node_kinds=["CoreGenericRepository"],
|
|
290
290
|
attribute_name="internal_status",
|
|
291
291
|
attribute_kind="Dropdown",
|
|
292
292
|
branch_support=BranchSupportType.LOCAL.value,
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Sequence
|
|
4
|
+
|
|
5
|
+
from infrahub.core.migrations.shared import MigrationResult
|
|
6
|
+
from infrahub.core.query import Query, QueryType
|
|
7
|
+
|
|
8
|
+
from ..shared import GraphMigration
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from infrahub.database import InfrahubDatabase
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class DeleteDuplicatedAttributesQuery(Query):
|
|
15
|
+
name: str = "delete_duplicated_attributes"
|
|
16
|
+
type: QueryType = QueryType.WRITE
|
|
17
|
+
insert_return: bool = False
|
|
18
|
+
insert_limit: bool = False
|
|
19
|
+
|
|
20
|
+
async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
|
|
21
|
+
query = """
|
|
22
|
+
// -------------
|
|
23
|
+
// get all the Nodes linked to multiple Attributes with the same name to drastically reduce the search space
|
|
24
|
+
// -------------
|
|
25
|
+
MATCH (n:Node)-[:HAS_ATTRIBUTE]->(attr:Attribute)
|
|
26
|
+
WITH DISTINCT n, attr
|
|
27
|
+
WITH n, attr.name AS attr_name, count(*) AS num_attrs
|
|
28
|
+
WHERE num_attrs > 1
|
|
29
|
+
// -------------
|
|
30
|
+
// for each Node-attr_name pair, get the possible duplicate Attributes
|
|
31
|
+
// -------------
|
|
32
|
+
MATCH (n)-[:HAS_ATTRIBUTE]->(dup_attr:Attribute {name: attr_name})
|
|
33
|
+
WITH DISTINCT n, dup_attr
|
|
34
|
+
// -------------
|
|
35
|
+
// get the branch(es) for each possible duplicate Attribute
|
|
36
|
+
// -------------
|
|
37
|
+
CALL (n, dup_attr) {
|
|
38
|
+
MATCH (n)-[r:HAS_ATTRIBUTE {status: "active"}]->(dup_attr)
|
|
39
|
+
WHERE r.to IS NULL
|
|
40
|
+
AND NOT exists((n)-[:HAS_ATTRIBUTE {status: "deleted", branch: r.branch}]->(dup_attr))
|
|
41
|
+
RETURN r.branch AS branch
|
|
42
|
+
}
|
|
43
|
+
// -------------
|
|
44
|
+
// get the latest update time for each duplicate Attribute on each branch
|
|
45
|
+
// -------------
|
|
46
|
+
CALL (dup_attr, branch) {
|
|
47
|
+
MATCH (dup_attr)-[r {branch: branch}]-()
|
|
48
|
+
RETURN max(r.from) AS latest_update
|
|
49
|
+
}
|
|
50
|
+
// -------------
|
|
51
|
+
// order the duplicate Attributes by latest update time
|
|
52
|
+
// -------------
|
|
53
|
+
WITH n, dup_attr, branch, latest_update
|
|
54
|
+
ORDER BY n, branch, dup_attr.name, latest_update DESC
|
|
55
|
+
// -------------
|
|
56
|
+
// for any Node-dup_attr_name pairs with multiple duplicate Attributes, keep the Attribute with the latest update
|
|
57
|
+
// on this branch and delete all the other edges on this branch for this Attribute
|
|
58
|
+
// -------------
|
|
59
|
+
WITH n, branch, dup_attr.name AS dup_attr_name, collect(dup_attr) AS dup_attrs_reverse_chronological
|
|
60
|
+
WHERE size(dup_attrs_reverse_chronological) > 1
|
|
61
|
+
WITH branch, tail(dup_attrs_reverse_chronological) AS dup_attrs_to_delete
|
|
62
|
+
UNWIND dup_attrs_to_delete AS dup_attr_to_delete
|
|
63
|
+
MATCH (dup_attr_to_delete)-[r {branch: branch}]-()
|
|
64
|
+
DELETE r
|
|
65
|
+
// -------------
|
|
66
|
+
// delete any orphaned Attributes
|
|
67
|
+
// -------------
|
|
68
|
+
WITH DISTINCT dup_attr_to_delete
|
|
69
|
+
WHERE NOT exists((dup_attr_to_delete)--())
|
|
70
|
+
DELETE dup_attr_to_delete
|
|
71
|
+
"""
|
|
72
|
+
self.add_to_query(query)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class Migration040(GraphMigration):
|
|
76
|
+
name: str = "040_duplicated_attributes"
|
|
77
|
+
queries: Sequence[type[Query]] = [DeleteDuplicatedAttributesQuery]
|
|
78
|
+
minimum_version: int = 39
|
|
79
|
+
|
|
80
|
+
async def validate_migration(self, db: InfrahubDatabase) -> MigrationResult: # noqa: ARG002
|
|
81
|
+
return MigrationResult()
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any
|
|
4
|
+
|
|
5
|
+
from rich.console import Console
|
|
6
|
+
from rich.progress import Progress
|
|
7
|
+
|
|
8
|
+
from infrahub.core.branch.models import Branch
|
|
9
|
+
from infrahub.core.initialization import get_root_node
|
|
10
|
+
from infrahub.core.manager import NodeManager
|
|
11
|
+
from infrahub.core.migrations.shared import MigrationResult
|
|
12
|
+
from infrahub.core.query import Query, QueryType
|
|
13
|
+
from infrahub.core.timestamp import Timestamp
|
|
14
|
+
from infrahub.log import get_logger
|
|
15
|
+
from infrahub.profiles.node_applier import NodeProfilesApplier
|
|
16
|
+
|
|
17
|
+
from ..shared import MigrationRequiringRebase
|
|
18
|
+
from .load_schema_branch import get_or_load_schema_branch
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from infrahub.database import InfrahubDatabase
|
|
22
|
+
|
|
23
|
+
log = get_logger()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class GetUpdatedProfilesForBranchQuery(Query):
|
|
27
|
+
"""
|
|
28
|
+
Get CoreProfile UUIDs with updated attributes on this branch
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
name = "get_profiles_by_branch"
|
|
32
|
+
type = QueryType.READ
|
|
33
|
+
|
|
34
|
+
async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
|
|
35
|
+
self.params["branch"] = self.branch.name
|
|
36
|
+
query = """
|
|
37
|
+
MATCH (profile:CoreProfile)-[:HAS_ATTRIBUTE]->(attr:Attribute)-[e:HAS_VALUE]->(:AttributeValue)
|
|
38
|
+
WHERE e.branch = $branch
|
|
39
|
+
WITH DISTINCT profile.uuid AS profile_uuid
|
|
40
|
+
"""
|
|
41
|
+
self.add_to_query(query)
|
|
42
|
+
self.return_labels = ["profile_uuid"]
|
|
43
|
+
|
|
44
|
+
def get_profile_ids(self) -> list[str]:
|
|
45
|
+
"""Get list of updated profile UUIDs"""
|
|
46
|
+
return [result.get_as_type("profile_uuid", str) for result in self.get_results()]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class GetNodesWithProfileUpdatesForBranchQuery(Query):
|
|
50
|
+
"""
|
|
51
|
+
Get Node UUIDs by which branches they have updated profiles on
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
name = "get_nodes_with_profile_updates_by_branch"
|
|
55
|
+
type = QueryType.READ
|
|
56
|
+
|
|
57
|
+
async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
|
|
58
|
+
self.params["branch"] = self.branch.name
|
|
59
|
+
query = """
|
|
60
|
+
MATCH (node:Node)-[e:IS_RELATED]->(:Relationship {name: "node__profile"})
|
|
61
|
+
WHERE NOT node:CoreProfile
|
|
62
|
+
AND e.branch = $branch
|
|
63
|
+
WITH DISTINCT node.uuid AS node_uuid
|
|
64
|
+
"""
|
|
65
|
+
self.add_to_query(query)
|
|
66
|
+
self.return_labels = ["node_uuid"]
|
|
67
|
+
|
|
68
|
+
def get_node_ids(self) -> list[str]:
|
|
69
|
+
"""Get list of updated node UUIDs"""
|
|
70
|
+
return [result.get_as_type("node_uuid", str) for result in self.get_results()]
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class Migration041(MigrationRequiringRebase):
|
|
74
|
+
"""
|
|
75
|
+
Save profile attribute values on each node using the profile in the database
|
|
76
|
+
For any profile that has updates on a given branch (including default branch)
|
|
77
|
+
- run NodeProfilesApplier.apply_profiles on each node related to the profile on that branch
|
|
78
|
+
For any node that has an updated relationship to a profile on a given branch
|
|
79
|
+
- run NodeProfilesApplier.apply_profiles on the node on that branch
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
name: str = "041_profile_attrs_in_db"
|
|
83
|
+
minimum_version: int = 40
|
|
84
|
+
|
|
85
|
+
def _get_profile_applier(self, db: InfrahubDatabase, branch: Branch) -> NodeProfilesApplier:
|
|
86
|
+
return NodeProfilesApplier(db=db, branch=branch)
|
|
87
|
+
|
|
88
|
+
async def validate_migration(self, db: InfrahubDatabase) -> MigrationResult: # noqa: ARG002
|
|
89
|
+
return MigrationResult()
|
|
90
|
+
|
|
91
|
+
async def execute(self, db: InfrahubDatabase) -> MigrationResult:
|
|
92
|
+
root_node = await get_root_node(db=db, initialize=False)
|
|
93
|
+
default_branch_name = root_node.default_branch
|
|
94
|
+
default_branch = await Branch.get_by_name(db=db, name=default_branch_name)
|
|
95
|
+
return await self._do_execute_for_branch(db=db, branch=default_branch)
|
|
96
|
+
|
|
97
|
+
async def execute_against_branch(self, db: InfrahubDatabase, branch: Branch) -> MigrationResult:
|
|
98
|
+
return await self._do_execute_for_branch(db=db, branch=branch)
|
|
99
|
+
|
|
100
|
+
async def _do_execute_for_branch(self, db: InfrahubDatabase, branch: Branch) -> MigrationResult:
|
|
101
|
+
console = Console()
|
|
102
|
+
result = MigrationResult()
|
|
103
|
+
await get_or_load_schema_branch(db=db, branch=branch)
|
|
104
|
+
|
|
105
|
+
console.print(f"Gathering profiles for branch {branch.name}...", end="")
|
|
106
|
+
get_updated_profiles_for_branch_query = await GetUpdatedProfilesForBranchQuery.init(db=db, branch=branch)
|
|
107
|
+
await get_updated_profiles_for_branch_query.execute(db=db)
|
|
108
|
+
profile_ids = get_updated_profiles_for_branch_query.get_profile_ids()
|
|
109
|
+
|
|
110
|
+
profiles_map = await NodeManager.get_many(db=db, branch=branch, ids=list(profile_ids))
|
|
111
|
+
console.print("done")
|
|
112
|
+
|
|
113
|
+
node_ids_to_update: set[str] = set()
|
|
114
|
+
with Progress() as progress:
|
|
115
|
+
gather_nodes_task = progress.add_task(
|
|
116
|
+
f"Gathering affected objects for each profile on branch {branch.name}...", total=len(profiles_map)
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
for profile in profiles_map.values():
|
|
120
|
+
node_relationship_manager = profile.get_relationship("related_nodes")
|
|
121
|
+
node_peers = await node_relationship_manager.get_db_peers(db=db)
|
|
122
|
+
node_ids_to_update.update(str(peer.peer_id) for peer in node_peers)
|
|
123
|
+
progress.update(gather_nodes_task, advance=1)
|
|
124
|
+
|
|
125
|
+
console.print("Identifying nodes with profile updates by branch...", end="")
|
|
126
|
+
get_nodes_with_profile_updates_by_branch_query = await GetNodesWithProfileUpdatesForBranchQuery.init(
|
|
127
|
+
db=db, branch=branch
|
|
128
|
+
)
|
|
129
|
+
await get_nodes_with_profile_updates_by_branch_query.execute(db=db)
|
|
130
|
+
node_ids_to_update.update(get_nodes_with_profile_updates_by_branch_query.get_node_ids())
|
|
131
|
+
console.print("done")
|
|
132
|
+
|
|
133
|
+
right_now = Timestamp()
|
|
134
|
+
with Progress() as progress:
|
|
135
|
+
apply_task = progress.add_task("Applying profiles to nodes...", total=len(node_ids_to_update))
|
|
136
|
+
applier = self._get_profile_applier(db=db, branch=branch)
|
|
137
|
+
for node_id in node_ids_to_update:
|
|
138
|
+
node = await NodeManager.get_one(db=db, branch=branch, id=node_id, at=right_now)
|
|
139
|
+
if node:
|
|
140
|
+
updated_field_names = await applier.apply_profiles(node=node)
|
|
141
|
+
if updated_field_names:
|
|
142
|
+
await node.save(db=db, fields=updated_field_names, at=right_now)
|
|
143
|
+
progress.update(apply_task, advance=1)
|
|
144
|
+
|
|
145
|
+
return result
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any
|
|
4
|
+
|
|
5
|
+
from rich.progress import Progress
|
|
6
|
+
|
|
7
|
+
from infrahub.core import registry
|
|
8
|
+
from infrahub.core.branch import Branch
|
|
9
|
+
from infrahub.core.constants import SchemaPathType
|
|
10
|
+
from infrahub.core.initialization import get_root_node
|
|
11
|
+
from infrahub.core.migrations.schema.node_attribute_add import NodeAttributeAddMigration
|
|
12
|
+
from infrahub.core.migrations.shared import MigrationRequiringRebase, MigrationResult
|
|
13
|
+
from infrahub.core.path import SchemaPath
|
|
14
|
+
from infrahub.core.query import Query, QueryType
|
|
15
|
+
|
|
16
|
+
from .load_schema_branch import get_or_load_schema_branch
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from infrahub.database import InfrahubDatabase
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class GetAddedNodesByKindForBranchQuery(Query):
|
|
23
|
+
name = "get_added_nodes_by_kind_for_branch_query"
|
|
24
|
+
type = QueryType.READ
|
|
25
|
+
insert_return = True
|
|
26
|
+
|
|
27
|
+
async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
|
|
28
|
+
self.params["branch"] = self.branch.name
|
|
29
|
+
query = """
|
|
30
|
+
MATCH (n:Node)-[e:IS_PART_OF {branch: $branch, status: "active"}]->(:Root)
|
|
31
|
+
WHERE e.to IS NULL
|
|
32
|
+
AND NOT exists((n)-[:IS_PART_OF {branch: $branch, status: "deleted"}]->(:Root))
|
|
33
|
+
WITH n.kind AS kind, collect(n.uuid) AS node_ids
|
|
34
|
+
"""
|
|
35
|
+
self.return_labels = ["kind", "node_ids"]
|
|
36
|
+
self.add_to_query(query)
|
|
37
|
+
|
|
38
|
+
def get_node_ids_by_kind(self) -> dict[str, list[str]]:
|
|
39
|
+
node_ids_by_kind: dict[str, list[str]] = {}
|
|
40
|
+
for result in self.get_results():
|
|
41
|
+
kind = result.get_as_type(label="kind", return_type=str)
|
|
42
|
+
node_ids: list[str] = result.get_as_type(label="node_ids", return_type=list)
|
|
43
|
+
node_ids_by_kind[kind] = node_ids
|
|
44
|
+
return node_ids_by_kind
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class Migration042(MigrationRequiringRebase):
|
|
48
|
+
name: str = "042_create_hfid_display_label_in_db"
|
|
49
|
+
minimum_version: int = 41
|
|
50
|
+
|
|
51
|
+
async def execute(self, db: InfrahubDatabase) -> MigrationResult:
|
|
52
|
+
result = MigrationResult()
|
|
53
|
+
|
|
54
|
+
root_node = await get_root_node(db=db, initialize=False)
|
|
55
|
+
default_branch_name = root_node.default_branch
|
|
56
|
+
default_branch = await Branch.get_by_name(db=db, name=default_branch_name)
|
|
57
|
+
main_schema_branch = await get_or_load_schema_branch(db=db, branch=default_branch)
|
|
58
|
+
schema_node = main_schema_branch.get_node(name="SchemaNode")
|
|
59
|
+
schema_generic = main_schema_branch.get_node(name="SchemaGeneric")
|
|
60
|
+
|
|
61
|
+
migrations = [
|
|
62
|
+
# HFID is not needed, it was introduced at graph v8
|
|
63
|
+
NodeAttributeAddMigration(
|
|
64
|
+
new_node_schema=schema_node,
|
|
65
|
+
previous_node_schema=schema_node,
|
|
66
|
+
schema_path=SchemaPath(
|
|
67
|
+
schema_kind="SchemaNode", path_type=SchemaPathType.ATTRIBUTE, field_name="display_label"
|
|
68
|
+
),
|
|
69
|
+
),
|
|
70
|
+
NodeAttributeAddMigration(
|
|
71
|
+
new_node_schema=schema_generic,
|
|
72
|
+
previous_node_schema=schema_generic,
|
|
73
|
+
schema_path=SchemaPath(
|
|
74
|
+
schema_kind="SchemaGeneric", path_type=SchemaPathType.ATTRIBUTE, field_name="display_label"
|
|
75
|
+
),
|
|
76
|
+
),
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
for node_schema_kind in main_schema_branch.node_names:
|
|
80
|
+
schema = main_schema_branch.get(name=node_schema_kind, duplicate=False)
|
|
81
|
+
migrations.extend(
|
|
82
|
+
[
|
|
83
|
+
NodeAttributeAddMigration(
|
|
84
|
+
new_node_schema=schema,
|
|
85
|
+
previous_node_schema=schema,
|
|
86
|
+
schema_path=SchemaPath(
|
|
87
|
+
schema_kind=schema.kind, path_type=SchemaPathType.ATTRIBUTE, field_name="human_friendly_id"
|
|
88
|
+
),
|
|
89
|
+
),
|
|
90
|
+
NodeAttributeAddMigration(
|
|
91
|
+
new_node_schema=schema,
|
|
92
|
+
previous_node_schema=schema,
|
|
93
|
+
schema_path=SchemaPath(
|
|
94
|
+
schema_kind=schema.kind, path_type=SchemaPathType.ATTRIBUTE, field_name="display_label"
|
|
95
|
+
),
|
|
96
|
+
),
|
|
97
|
+
]
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
with Progress() as progress:
|
|
101
|
+
update_task = progress.add_task("Adding HFID and display label to nodes", total=len(migrations))
|
|
102
|
+
|
|
103
|
+
for migration in migrations:
|
|
104
|
+
try:
|
|
105
|
+
execution_result = await migration.execute(db=db, branch=default_branch)
|
|
106
|
+
result.errors.extend(execution_result.errors)
|
|
107
|
+
progress.update(update_task, advance=1)
|
|
108
|
+
except Exception as exc:
|
|
109
|
+
result.errors.append(str(exc))
|
|
110
|
+
return result
|
|
111
|
+
|
|
112
|
+
return result
|
|
113
|
+
|
|
114
|
+
async def execute_against_branch(self, db: InfrahubDatabase, branch: Branch) -> MigrationResult:
|
|
115
|
+
result = MigrationResult()
|
|
116
|
+
|
|
117
|
+
schema_branch = await registry.schema.load_schema_from_db(db=db, branch=branch)
|
|
118
|
+
|
|
119
|
+
migrations = []
|
|
120
|
+
get_added_nodes_by_kind_for_branch_query = await GetAddedNodesByKindForBranchQuery.init(db=db, branch=branch)
|
|
121
|
+
await get_added_nodes_by_kind_for_branch_query.execute(db=db)
|
|
122
|
+
node_ids_by_kind = get_added_nodes_by_kind_for_branch_query.get_node_ids_by_kind()
|
|
123
|
+
|
|
124
|
+
for node_kind, node_ids in node_ids_by_kind.items():
|
|
125
|
+
schema = schema_branch.get(name=node_kind, duplicate=False)
|
|
126
|
+
migrations.extend(
|
|
127
|
+
[
|
|
128
|
+
NodeAttributeAddMigration(
|
|
129
|
+
uuids=node_ids,
|
|
130
|
+
new_node_schema=schema,
|
|
131
|
+
previous_node_schema=schema,
|
|
132
|
+
schema_path=SchemaPath(
|
|
133
|
+
schema_kind=schema.kind, path_type=SchemaPathType.ATTRIBUTE, field_name="human_friendly_id"
|
|
134
|
+
),
|
|
135
|
+
),
|
|
136
|
+
NodeAttributeAddMigration(
|
|
137
|
+
uuids=node_ids,
|
|
138
|
+
new_node_schema=schema,
|
|
139
|
+
previous_node_schema=schema,
|
|
140
|
+
schema_path=SchemaPath(
|
|
141
|
+
schema_kind=schema.kind, path_type=SchemaPathType.ATTRIBUTE, field_name="display_label"
|
|
142
|
+
),
|
|
143
|
+
),
|
|
144
|
+
]
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
with Progress() as progress:
|
|
148
|
+
update_task = progress.add_task(
|
|
149
|
+
f"Adding HFID and display label to nodes on branch {branch.name}", total=len(migrations)
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
for migration in migrations:
|
|
153
|
+
try:
|
|
154
|
+
execution_result = await migration.execute(db=db, branch=branch)
|
|
155
|
+
result.errors.extend(execution_result.errors)
|
|
156
|
+
progress.update(update_task, advance=1)
|
|
157
|
+
except Exception as exc:
|
|
158
|
+
result.errors.append(str(exc))
|
|
159
|
+
return result
|
|
160
|
+
|
|
161
|
+
return result
|
|
162
|
+
|
|
163
|
+
async def validate_migration(self, db: InfrahubDatabase) -> MigrationResult: # noqa: ARG002
|
|
164
|
+
return MigrationResult()
|