infrahub-server 1.1.6__py3-none-any.whl → 1.2.0rc0__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 +16 -4
- infrahub/api/dependencies.py +8 -0
- infrahub/api/oauth2.py +0 -1
- infrahub/api/oidc.py +0 -1
- infrahub/api/query.py +18 -7
- infrahub/api/schema.py +32 -6
- infrahub/api/transformation.py +12 -5
- infrahub/{message_bus/messages/check_artifact_create.py → artifacts/models.py} +2 -4
- infrahub/{message_bus/operations/check/artifact.py → artifacts/tasks.py} +26 -25
- infrahub/cli/__init__.py +0 -2
- infrahub/cli/db.py +6 -7
- infrahub/cli/events.py +8 -3
- infrahub/cli/git_agent.py +9 -7
- infrahub/cli/tasks.py +4 -6
- infrahub/computed_attribute/tasks.py +63 -17
- infrahub/computed_attribute/triggers.py +90 -0
- infrahub/config.py +1 -1
- infrahub/context.py +39 -0
- infrahub/core/account.py +5 -8
- infrahub/core/attribute.py +53 -21
- infrahub/core/branch/models.py +4 -4
- infrahub/core/branch/tasks.py +89 -130
- infrahub/core/changelog/__init__.py +0 -0
- infrahub/core/changelog/diff.py +232 -0
- infrahub/core/changelog/models.py +488 -0
- infrahub/core/constants/__init__.py +19 -2
- infrahub/core/constants/infrahubkind.py +1 -0
- infrahub/core/diff/combiner.py +12 -8
- infrahub/core/diff/coordinator.py +49 -70
- infrahub/core/diff/data_check_synchronizer.py +86 -7
- infrahub/core/diff/enricher/aggregated.py +3 -3
- infrahub/core/diff/enricher/cardinality_one.py +2 -7
- infrahub/core/diff/enricher/hierarchy.py +5 -3
- infrahub/core/diff/enricher/labels.py +14 -4
- infrahub/core/diff/enricher/path_identifier.py +3 -9
- infrahub/core/diff/enricher/summary_counts.py +3 -1
- infrahub/core/diff/merger/merger.py +8 -4
- infrahub/core/diff/model/path.py +47 -29
- infrahub/core/diff/query/all_conflicts.py +6 -3
- infrahub/core/diff/query/artifact.py +1 -1
- infrahub/core/diff/query/delete_query.py +1 -1
- infrahub/core/diff/query/diff_get.py +3 -2
- infrahub/core/diff/query/diff_summary.py +1 -1
- infrahub/core/diff/query/field_specifiers.py +3 -1
- infrahub/core/diff/query/field_summary.py +3 -2
- infrahub/core/diff/query/filters.py +12 -1
- infrahub/core/diff/query/get_conflict_query.py +1 -1
- infrahub/core/diff/query/has_conflicts_query.py +6 -3
- infrahub/core/diff/query/merge.py +3 -3
- infrahub/core/diff/query/{drop_tracking_id.py → merge_tracking_id.py} +4 -4
- infrahub/core/diff/query/roots_metadata.py +9 -2
- infrahub/core/diff/query/save.py +151 -66
- infrahub/core/diff/query/summary_counts_enricher.py +220 -0
- infrahub/core/diff/query/time_range_query.py +3 -2
- infrahub/core/diff/query/update_conflict_query.py +1 -1
- infrahub/core/diff/query_parser.py +49 -24
- infrahub/core/diff/repository/deserializer.py +24 -25
- infrahub/core/diff/repository/repository.py +76 -20
- infrahub/core/diff/tasks.py +9 -8
- infrahub/core/enums.py +1 -1
- infrahub/core/integrity/object_conflict/conflict_recorder.py +1 -1
- infrahub/core/ipam/reconciler.py +1 -1
- infrahub/core/ipam/tasks.py +2 -3
- infrahub/core/manager.py +18 -13
- infrahub/core/merge.py +5 -2
- infrahub/core/migrations/graph/m001_add_version_to_graph.py +1 -1
- infrahub/core/migrations/graph/m002_attribute_is_default.py +2 -2
- infrahub/core/migrations/graph/m003_relationship_parent_optional.py +2 -2
- infrahub/core/migrations/graph/m004_add_attr_documentation.py +1 -1
- infrahub/core/migrations/graph/m005_add_rel_read_only.py +1 -1
- infrahub/core/migrations/graph/m006_add_rel_on_delete.py +1 -1
- infrahub/core/migrations/graph/m007_add_rel_allow_override.py +1 -1
- infrahub/core/migrations/graph/m008_add_human_friendly_id.py +1 -1
- infrahub/core/migrations/graph/m009_add_generate_profile_attr.py +1 -1
- infrahub/core/migrations/graph/m010_add_generate_profile_attr_generic.py +1 -1
- infrahub/core/migrations/graph/m011_remove_profile_relationship_schema.py +2 -2
- infrahub/core/migrations/graph/m012_convert_account_generic.py +12 -23
- infrahub/core/migrations/graph/m013_convert_git_password_credential.py +7 -11
- infrahub/core/migrations/graph/m014_remove_index_attr_value.py +2 -2
- infrahub/core/migrations/graph/m015_diff_format_update.py +1 -1
- infrahub/core/migrations/graph/m016_diff_delete_bug_fix.py +1 -1
- infrahub/core/migrations/graph/m017_add_core_profile.py +1 -1
- infrahub/core/migrations/graph/m018_uniqueness_nulls.py +2 -2
- infrahub/core/migrations/query/attribute_add.py +1 -1
- infrahub/core/migrations/query/attribute_rename.py +1 -1
- infrahub/core/migrations/query/delete_element_in_schema.py +1 -1
- infrahub/core/migrations/query/node_duplicate.py +1 -1
- infrahub/core/migrations/query/relationship_duplicate.py +1 -1
- infrahub/core/migrations/query/schema_attribute_update.py +1 -1
- infrahub/core/migrations/schema/node_attribute_remove.py +1 -1
- infrahub/core/migrations/schema/node_remove.py +1 -1
- infrahub/core/migrations/schema/tasks.py +5 -5
- infrahub/core/migrations/shared.py +4 -4
- infrahub/core/models.py +7 -8
- infrahub/core/node/__init__.py +161 -40
- infrahub/core/node/base.py +1 -1
- infrahub/core/node/constraints/grouped_uniqueness.py +9 -2
- infrahub/core/node/delete_validator.py +4 -4
- infrahub/core/node/ipam.py +13 -8
- infrahub/core/node/permissions.py +4 -0
- infrahub/core/node/resource_manager/ip_prefix_pool.py +8 -5
- infrahub/core/node/standard.py +3 -5
- infrahub/core/property.py +1 -1
- infrahub/core/protocols.py +4 -0
- infrahub/core/protocols_base.py +4 -2
- infrahub/core/query/__init__.py +2 -5
- infrahub/core/query/attribute.py +9 -9
- infrahub/core/query/branch.py +5 -5
- infrahub/core/query/delete.py +1 -1
- infrahub/core/query/diff.py +45 -7
- infrahub/core/query/ipam.py +4 -4
- infrahub/core/query/node.py +19 -14
- infrahub/core/query/relationship.py +10 -11
- infrahub/core/query/resource_manager.py +13 -11
- infrahub/core/query/standard_node.py +6 -6
- infrahub/core/query/task.py +3 -3
- infrahub/core/query/task_log.py +1 -1
- infrahub/core/query/utils.py +5 -5
- infrahub/core/registry.py +0 -2
- infrahub/core/relationship/constraints/count.py +1 -1
- infrahub/core/relationship/constraints/peer_kind.py +1 -1
- infrahub/core/relationship/model.py +66 -26
- infrahub/core/schema/__init__.py +6 -4
- infrahub/core/schema/basenode_schema.py +1 -3
- infrahub/core/schema/definitions/core.py +14 -2
- infrahub/core/schema/definitions/internal.py +16 -0
- infrahub/core/schema/generated/genericnode_schema.py +5 -0
- infrahub/core/schema/generated/node_schema.py +5 -0
- infrahub/core/schema/generic_schema.py +5 -1
- infrahub/core/schema/manager.py +45 -42
- infrahub/core/schema/node_schema.py +4 -0
- infrahub/core/schema/profile_schema.py +4 -0
- infrahub/core/schema/relationship_schema.py +2 -2
- infrahub/core/schema/schema_branch.py +248 -14
- infrahub/core/schema/template_schema.py +36 -0
- infrahub/core/task/user_task.py +7 -5
- infrahub/core/timestamp.py +1 -1
- infrahub/core/utils.py +3 -2
- infrahub/core/validators/attribute/choices.py +1 -1
- infrahub/core/validators/attribute/enum.py +1 -1
- infrahub/core/validators/attribute/kind.py +1 -1
- infrahub/core/validators/attribute/length.py +1 -1
- infrahub/core/validators/attribute/optional.py +1 -1
- infrahub/core/validators/attribute/regex.py +1 -1
- infrahub/core/validators/attribute/unique.py +1 -1
- infrahub/core/validators/checks_runner.py +37 -0
- infrahub/core/validators/node/generate_profile.py +1 -1
- infrahub/core/validators/node/hierarchy.py +1 -1
- infrahub/core/validators/query.py +1 -1
- infrahub/core/validators/relationship/count.py +1 -1
- infrahub/core/validators/relationship/optional.py +1 -1
- infrahub/core/validators/relationship/peer.py +1 -1
- infrahub/core/validators/tasks.py +8 -6
- infrahub/core/validators/uniqueness/query.py +20 -17
- infrahub/database/__init__.py +15 -2
- infrahub/database/memgraph.py +1 -1
- infrahub/dependencies/builder/constraint/grouped/node_runner.py +0 -2
- infrahub/dependencies/builder/diff/combiner.py +1 -1
- infrahub/dependencies/builder/diff/conflicts_enricher.py +1 -1
- infrahub/dependencies/builder/diff/coordinator.py +0 -2
- infrahub/dependencies/builder/diff/deserializer.py +1 -1
- infrahub/dependencies/builder/diff/enricher/summary_counts.py +1 -1
- infrahub/events/branch_action.py +47 -21
- infrahub/events/group_action.py +73 -0
- infrahub/events/models.py +159 -51
- infrahub/events/node_action.py +74 -8
- infrahub/events/repository_action.py +8 -8
- infrahub/events/schema_action.py +21 -8
- infrahub/generators/tasks.py +12 -13
- infrahub/git/base.py +3 -5
- infrahub/git/constants.py +0 -1
- infrahub/git/integrator.py +36 -35
- infrahub/git/repository.py +7 -8
- infrahub/git/tasks.py +43 -107
- infrahub/git_credential/helper.py +2 -3
- infrahub/graphql/analyzer.py +572 -11
- infrahub/graphql/app.py +34 -26
- infrahub/graphql/auth/query_permission_checker/anonymous_checker.py +5 -5
- infrahub/graphql/auth/query_permission_checker/default_branch_checker.py +4 -4
- infrahub/graphql/auth/query_permission_checker/merge_operation_checker.py +4 -4
- infrahub/graphql/auth/query_permission_checker/object_permission_checker.py +28 -35
- infrahub/graphql/auth/query_permission_checker/super_admin_checker.py +5 -5
- infrahub/graphql/enums.py +1 -1
- infrahub/graphql/initialization.py +5 -1
- infrahub/graphql/loaders/node.py +2 -2
- infrahub/graphql/manager.py +59 -54
- infrahub/graphql/mutations/account.py +20 -13
- infrahub/graphql/mutations/artifact_definition.py +16 -12
- infrahub/graphql/mutations/branch.py +61 -40
- infrahub/graphql/mutations/computed_attribute.py +19 -13
- infrahub/graphql/mutations/diff.py +37 -9
- infrahub/graphql/mutations/diff_conflict.py +9 -8
- infrahub/graphql/mutations/graphql_query.py +19 -11
- infrahub/graphql/mutations/ipam.py +21 -19
- infrahub/graphql/mutations/main.py +197 -44
- infrahub/graphql/mutations/menu.py +8 -8
- infrahub/graphql/mutations/proposed_change.py +36 -28
- infrahub/graphql/mutations/relationship.py +302 -105
- infrahub/graphql/mutations/repository.py +41 -35
- infrahub/graphql/mutations/resource_manager.py +26 -26
- infrahub/graphql/mutations/schema.py +51 -33
- infrahub/graphql/mutations/tasks.py +16 -10
- infrahub/graphql/parser.py +1 -1
- infrahub/graphql/permissions.py +6 -4
- infrahub/graphql/queries/account.py +22 -18
- infrahub/graphql/queries/branch.py +6 -4
- infrahub/graphql/queries/diff/tree.py +48 -42
- infrahub/graphql/queries/event.py +112 -0
- infrahub/graphql/queries/internal.py +3 -3
- infrahub/graphql/queries/ipam.py +23 -18
- infrahub/graphql/queries/relationship.py +11 -10
- infrahub/graphql/queries/resource_manager.py +43 -27
- infrahub/graphql/queries/search.py +9 -8
- infrahub/graphql/queries/status.py +12 -9
- infrahub/graphql/queries/task.py +11 -9
- infrahub/graphql/resolvers/resolver.py +69 -43
- infrahub/graphql/resolvers/single_relationship.py +16 -10
- infrahub/graphql/schema.py +2 -0
- infrahub/graphql/subscription/__init__.py +1 -1
- infrahub/graphql/subscription/events.py +1 -1
- infrahub/graphql/subscription/graphql_query.py +8 -8
- infrahub/graphql/types/branch.py +2 -2
- infrahub/graphql/types/common.py +6 -1
- infrahub/graphql/types/enums.py +2 -0
- infrahub/graphql/types/event.py +100 -0
- infrahub/graphql/types/interface.py +2 -2
- infrahub/graphql/types/node.py +3 -3
- infrahub/graphql/types/permission.py +2 -2
- infrahub/graphql/types/relationship.py +3 -3
- infrahub/graphql/types/standard_node.py +9 -11
- infrahub/graphql/utils.py +28 -182
- infrahub/groups/tasks.py +2 -3
- infrahub/lock.py +1 -1
- infrahub/menu/constants.py +1 -0
- infrahub/menu/generator.py +14 -3
- infrahub/menu/menu.py +116 -127
- infrahub/menu/models.py +4 -4
- infrahub/message_bus/messages/__init__.py +0 -4
- infrahub/message_bus/messages/event_branch_merge.py +3 -0
- infrahub/message_bus/messages/request_proposedchange_pipeline.py +2 -0
- infrahub/message_bus/operations/__init__.py +3 -5
- infrahub/message_bus/operations/check/__init__.py +2 -2
- infrahub/message_bus/operations/check/generator.py +1 -3
- infrahub/message_bus/operations/check/repository.py +1 -1
- infrahub/message_bus/operations/event/branch.py +7 -3
- infrahub/message_bus/operations/event/schema.py +1 -1
- infrahub/message_bus/operations/finalize/validator.py +1 -1
- infrahub/message_bus/operations/git/file.py +2 -2
- infrahub/message_bus/operations/git/repository.py +1 -1
- infrahub/message_bus/operations/requests/__init__.py +0 -2
- infrahub/message_bus/operations/requests/generator_definition.py +1 -1
- infrahub/message_bus/operations/requests/proposed_change.py +26 -11
- infrahub/message_bus/operations/requests/repository.py +2 -2
- infrahub/message_bus/operations/send/echo.py +1 -1
- infrahub/message_bus/types.py +1 -1
- infrahub/permissions/__init__.py +2 -1
- infrahub/permissions/types.py +26 -0
- infrahub/pools/prefix.py +29 -165
- infrahub/prefect_server/__init__.py +0 -0
- infrahub/prefect_server/app.py +18 -0
- infrahub/prefect_server/database.py +20 -0
- infrahub/prefect_server/events.py +28 -0
- infrahub/prefect_server/models.py +46 -0
- infrahub/proposed_change/models.py +15 -1
- infrahub/proposed_change/tasks.py +173 -35
- infrahub/pytest_plugin.py +4 -4
- infrahub/server.py +12 -11
- infrahub/services/__init__.py +147 -62
- infrahub/services/adapters/cache/__init__.py +7 -5
- infrahub/services/adapters/cache/nats.py +40 -22
- infrahub/services/adapters/cache/redis.py +0 -4
- infrahub/services/adapters/event/__init__.py +10 -18
- infrahub/services/adapters/http/__init__.py +0 -5
- infrahub/services/adapters/http/httpx.py +22 -15
- infrahub/services/adapters/message_bus/__init__.py +23 -6
- infrahub/services/adapters/message_bus/local.py +8 -6
- infrahub/services/adapters/message_bus/nats.py +12 -6
- infrahub/services/adapters/message_bus/rabbitmq.py +22 -9
- infrahub/services/adapters/workflow/__init__.py +11 -8
- infrahub/services/adapters/workflow/local.py +28 -7
- infrahub/services/adapters/workflow/worker.py +23 -7
- infrahub/services/component.py +38 -35
- infrahub/services/scheduler.py +32 -29
- infrahub/storage.py +2 -4
- infrahub/task_manager/constants.py +1 -1
- infrahub/task_manager/event.py +182 -0
- infrahub/task_manager/models.py +125 -1
- infrahub/task_manager/task.py +1 -1
- infrahub/tasks/artifact.py +14 -16
- infrahub/tasks/registry.py +1 -1
- infrahub/tasks/telemetry.py +13 -14
- infrahub/transformations/tasks.py +3 -5
- infrahub/trigger/__init__.py +0 -0
- infrahub/trigger/catalogue.py +15 -0
- infrahub/trigger/constants.py +9 -0
- infrahub/trigger/models.py +69 -0
- infrahub/trigger/tasks.py +85 -0
- infrahub/types.py +1 -1
- infrahub/utils.py +1 -1
- infrahub/webhook/constants.py +0 -2
- infrahub/webhook/models.py +8 -2
- infrahub/webhook/tasks.py +20 -73
- infrahub/webhook/triggers.py +20 -0
- infrahub/workers/infrahub_async.py +36 -25
- infrahub/workers/utils.py +63 -0
- infrahub/workflows/catalogue.py +13 -37
- infrahub/workflows/initialization.py +6 -8
- infrahub/workflows/models.py +3 -5
- infrahub/workflows/utils.py +1 -1
- infrahub_sdk/ctl/check.py +3 -3
- infrahub_sdk/ctl/cli_commands.py +11 -10
- infrahub_sdk/ctl/exceptions.py +0 -6
- infrahub_sdk/ctl/exporter.py +1 -1
- infrahub_sdk/ctl/generator.py +5 -5
- infrahub_sdk/ctl/importer.py +3 -2
- infrahub_sdk/ctl/menu.py +1 -1
- infrahub_sdk/ctl/object.py +1 -1
- infrahub_sdk/ctl/repository.py +23 -15
- infrahub_sdk/ctl/schema.py +2 -2
- infrahub_sdk/ctl/utils.py +4 -3
- infrahub_sdk/ctl/validate.py +2 -1
- infrahub_sdk/exceptions.py +6 -0
- infrahub_sdk/generator.py +3 -0
- infrahub_sdk/node.py +2 -2
- infrahub_sdk/schema/__init__.py +14 -2
- infrahub_sdk/schema/main.py +7 -0
- infrahub_sdk/utils.py +11 -1
- infrahub_sdk/yaml.py +2 -3
- {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/METADATA +46 -12
- {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/RECORD +338 -321
- infrahub_testcontainers/container.py +14 -6
- infrahub_testcontainers/docker-compose.test.yml +24 -5
- infrahub_testcontainers/haproxy.cfg +43 -0
- infrahub_testcontainers/helpers.py +85 -1
- infrahub/core/branch/constants.py +0 -2
- infrahub/graphql/query.py +0 -52
- infrahub/message_bus/messages/request_artifactdefinition_check.py +0 -17
- infrahub/message_bus/operations/requests/artifact_definition.py +0 -148
- infrahub/schema/constants.py +0 -1
- infrahub/schema/tasks.py +0 -76
- infrahub/services/adapters/database/__init__.py +0 -9
- infrahub_sdk/ctl/_file.py +0 -13
- /infrahub/{schema → artifacts}/__init__.py +0 -0
- {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/WHEEL +0 -0
- {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/entry_points.txt +0 -0
|
@@ -12,6 +12,7 @@ from typing_extensions import Self
|
|
|
12
12
|
|
|
13
13
|
from infrahub.computed_attribute.constants import VALID_KINDS as VALID_COMPUTED_ATTRIBUTE_KINDS
|
|
14
14
|
from infrahub.core.constants import (
|
|
15
|
+
OBJECT_TEMPLATE_NAME_ATTR,
|
|
15
16
|
RESERVED_ATTR_GEN_NAMES,
|
|
16
17
|
RESERVED_ATTR_REL_NAMES,
|
|
17
18
|
RESTRICTED_NAMESPACES,
|
|
@@ -43,6 +44,7 @@ from infrahub.core.schema import (
|
|
|
43
44
|
RelationshipSchema,
|
|
44
45
|
SchemaAttributePath,
|
|
45
46
|
SchemaRoot,
|
|
47
|
+
TemplateSchema,
|
|
46
48
|
)
|
|
47
49
|
from infrahub.core.schema.definitions.core import core_profile_schema_definition
|
|
48
50
|
from infrahub.core.validators import CONSTRAINT_VALIDATOR_MAP
|
|
@@ -62,9 +64,6 @@ if TYPE_CHECKING:
|
|
|
62
64
|
from pydantic import ValidationInfo
|
|
63
65
|
|
|
64
66
|
|
|
65
|
-
# pylint: disable=redefined-builtin,too-many-public-methods,too-many-lines
|
|
66
|
-
|
|
67
|
-
|
|
68
67
|
class SchemaBranch:
|
|
69
68
|
def __init__(
|
|
70
69
|
self,
|
|
@@ -78,19 +77,21 @@ class SchemaBranch:
|
|
|
78
77
|
self.nodes: dict[str, str] = {}
|
|
79
78
|
self.generics: dict[str, str] = {}
|
|
80
79
|
self.profiles: dict[str, str] = {}
|
|
80
|
+
self.templates: dict[str, str] = {}
|
|
81
81
|
self.computed_attributes = computed_attributes or ComputedAttributes()
|
|
82
82
|
|
|
83
83
|
if data:
|
|
84
84
|
self.nodes = data.get("nodes", {})
|
|
85
85
|
self.generics = data.get("generics", {})
|
|
86
86
|
self.profiles = data.get("profiles", {})
|
|
87
|
+
self.templates = data.get("templates", {})
|
|
87
88
|
|
|
88
89
|
@classmethod
|
|
89
90
|
def __get_validators__(cls) -> Iterator[Callable[..., Any]]: # noqa: PLW3201
|
|
90
91
|
yield cls.validate
|
|
91
92
|
|
|
92
93
|
@classmethod
|
|
93
|
-
def validate(cls, v: Any, info: ValidationInfo) -> Self: #
|
|
94
|
+
def validate(cls, v: Any, info: ValidationInfo) -> Self: # noqa: ARG003
|
|
94
95
|
if isinstance(v, cls):
|
|
95
96
|
return v
|
|
96
97
|
if isinstance(v, dict):
|
|
@@ -109,6 +110,10 @@ class SchemaBranch:
|
|
|
109
110
|
def profile_names(self) -> list[str]:
|
|
110
111
|
return list(self.profiles.keys())
|
|
111
112
|
|
|
113
|
+
@property
|
|
114
|
+
def template_names(self) -> list[str]:
|
|
115
|
+
return list(self.templates.keys())
|
|
116
|
+
|
|
112
117
|
def get_all_kind_id_map(self, exclude_profiles: bool = False) -> dict[str, str]:
|
|
113
118
|
kind_id_map = {}
|
|
114
119
|
if exclude_profiles:
|
|
@@ -124,7 +129,7 @@ class SchemaBranch:
|
|
|
124
129
|
|
|
125
130
|
@property
|
|
126
131
|
def all_names(self) -> list[str]:
|
|
127
|
-
return self.node_names + self.generic_names + self.profile_names
|
|
132
|
+
return self.node_names + self.generic_names + self.profile_names + self.template_names
|
|
128
133
|
|
|
129
134
|
def get_hash(self) -> str:
|
|
130
135
|
"""Calculate the hash for this objects based on the content of nodes and generics.
|
|
@@ -142,13 +147,14 @@ class SchemaBranch:
|
|
|
142
147
|
return SchemaBranchHash(main=self.get_hash(), nodes=self.nodes, generics=self.generics)
|
|
143
148
|
|
|
144
149
|
def to_dict(self) -> dict[str, Any]:
|
|
145
|
-
return {"nodes": self.nodes, "generics": self.generics, "profiles": self.profiles}
|
|
150
|
+
return {"nodes": self.nodes, "generics": self.generics, "profiles": self.profiles, "templates": self.templates}
|
|
146
151
|
|
|
147
152
|
def to_dict_schema_object(self, duplicate: bool = False) -> dict[str, dict[str, MainSchemaTypes]]:
|
|
148
153
|
return {
|
|
149
154
|
"nodes": {name: self.get(name, duplicate=duplicate) for name in self.nodes},
|
|
150
155
|
"profiles": {name: self.get(name, duplicate=duplicate) for name in self.profiles},
|
|
151
156
|
"generics": {name: self.get(name, duplicate=duplicate) for name in self.generics},
|
|
157
|
+
"templates": {name: self.get(name, duplicate=duplicate) for name in self.templates},
|
|
152
158
|
}
|
|
153
159
|
|
|
154
160
|
@classmethod
|
|
@@ -157,10 +163,11 @@ class SchemaBranch:
|
|
|
157
163
|
"nodes": NodeSchema,
|
|
158
164
|
"generics": GenericSchema,
|
|
159
165
|
"profiles": ProfileSchema,
|
|
166
|
+
"templates": TemplateSchema,
|
|
160
167
|
}
|
|
161
168
|
|
|
162
169
|
cache: dict[str, MainSchemaTypes] = {}
|
|
163
|
-
nodes: dict[str, dict[str, str]] = {"nodes": {}, "generics": {}, "profiles": {}}
|
|
170
|
+
nodes: dict[str, dict[str, str]] = {"nodes": {}, "generics": {}, "profiles": {}, "templates": {}}
|
|
164
171
|
|
|
165
172
|
for node_type, node_class in type_mapping.items():
|
|
166
173
|
for node_name, node_data in data[node_type].items():
|
|
@@ -281,6 +288,8 @@ class SchemaBranch:
|
|
|
281
288
|
self.generics[name] = schema_hash
|
|
282
289
|
elif "Profile" in schema.__class__.__name__:
|
|
283
290
|
self.profiles[name] = schema_hash
|
|
291
|
+
elif "Template" in schema.__class__.__name__:
|
|
292
|
+
self.templates[name] = schema_hash
|
|
284
293
|
|
|
285
294
|
return schema_hash
|
|
286
295
|
|
|
@@ -292,6 +301,7 @@ class SchemaBranch:
|
|
|
292
301
|
|
|
293
302
|
If duplicate is set to false, the real object will be returned.
|
|
294
303
|
"""
|
|
304
|
+
|
|
295
305
|
key = None
|
|
296
306
|
if name in self.nodes:
|
|
297
307
|
key = self.nodes[name]
|
|
@@ -299,6 +309,8 @@ class SchemaBranch:
|
|
|
299
309
|
key = self.generics[name]
|
|
300
310
|
elif name in self.profiles:
|
|
301
311
|
key = self.profiles[name]
|
|
312
|
+
elif name in self.templates:
|
|
313
|
+
key = self.templates[name]
|
|
302
314
|
|
|
303
315
|
if key and duplicate:
|
|
304
316
|
return self._cache[key].duplicate()
|
|
@@ -330,6 +342,13 @@ class SchemaBranch:
|
|
|
330
342
|
raise ValueError(f"{name!r} is not of type ProfileSchema")
|
|
331
343
|
return item
|
|
332
344
|
|
|
345
|
+
def get_template(self, name: str, duplicate: bool = True) -> TemplateSchema:
|
|
346
|
+
"""Access a specific TemplateSchema, defined by its kind."""
|
|
347
|
+
item = self.get(name=name, duplicate=duplicate)
|
|
348
|
+
if not isinstance(item, TemplateSchema):
|
|
349
|
+
raise ValueError(f"{name!r} is not of type TemplateSchema")
|
|
350
|
+
return item
|
|
351
|
+
|
|
333
352
|
def delete(self, name: str) -> None:
|
|
334
353
|
if name in self.nodes:
|
|
335
354
|
del self.nodes[name]
|
|
@@ -337,6 +356,8 @@ class SchemaBranch:
|
|
|
337
356
|
del self.generics[name]
|
|
338
357
|
elif name in self.profiles:
|
|
339
358
|
del self.profiles[name]
|
|
359
|
+
elif name in self.templates:
|
|
360
|
+
del self.templates[name]
|
|
340
361
|
else:
|
|
341
362
|
raise SchemaNotFoundError(
|
|
342
363
|
branch_name=self.name, identifier=name, message=f"Unable to find the schema {name!r} in the registry"
|
|
@@ -431,7 +452,7 @@ class SchemaBranch:
|
|
|
431
452
|
|
|
432
453
|
def generate_fields_for_display_label(self, name: str) -> Optional[dict]:
|
|
433
454
|
node = self.get(name=name, duplicate=False)
|
|
434
|
-
if isinstance(node,
|
|
455
|
+
if isinstance(node, NodeSchema | ProfileSchema | TemplateSchema):
|
|
435
456
|
return node.generate_fields_for_display_label()
|
|
436
457
|
|
|
437
458
|
fields: dict[str, Union[str, None, dict[str, None]]] = {}
|
|
@@ -490,6 +511,8 @@ class SchemaBranch:
|
|
|
490
511
|
self.process_inheritance()
|
|
491
512
|
self.process_hierarchy()
|
|
492
513
|
self.process_branch_support()
|
|
514
|
+
self.manage_object_template_schemas()
|
|
515
|
+
self.manage_object_template_relationships()
|
|
493
516
|
self.manage_profile_schemas()
|
|
494
517
|
self.manage_profile_relationships()
|
|
495
518
|
self.add_hierarchy_generic()
|
|
@@ -520,6 +543,9 @@ class SchemaBranch:
|
|
|
520
543
|
self.process_relationships()
|
|
521
544
|
self.process_human_friendly_id()
|
|
522
545
|
|
|
546
|
+
def _generate_identifier_string(self, node_kind: str, peer_kind: str) -> str:
|
|
547
|
+
return "__".join(sorted([node_kind, peer_kind])).lower()
|
|
548
|
+
|
|
523
549
|
def generate_identifiers(self) -> None:
|
|
524
550
|
"""Generate the identifier for all relationships if it's not already present."""
|
|
525
551
|
for name in self.all_names:
|
|
@@ -532,7 +558,7 @@ class SchemaBranch:
|
|
|
532
558
|
for rel in node.relationships:
|
|
533
559
|
if rel.identifier:
|
|
534
560
|
continue
|
|
535
|
-
rel.identifier =
|
|
561
|
+
rel.identifier = self._generate_identifier_string(node.kind, rel.peer)
|
|
536
562
|
self.set(name=name, schema=node)
|
|
537
563
|
|
|
538
564
|
def validate_identifiers(self) -> None:
|
|
@@ -1267,7 +1293,6 @@ class SchemaBranch:
|
|
|
1267
1293
|
|
|
1268
1294
|
if either node on a relationship support branch, the relationship must be branch aware.
|
|
1269
1295
|
"""
|
|
1270
|
-
# pylint: disable=too-many-branches
|
|
1271
1296
|
|
|
1272
1297
|
for name in self.all_names:
|
|
1273
1298
|
node = self.get(name=name, duplicate=False)
|
|
@@ -1329,7 +1354,6 @@ class SchemaBranch:
|
|
|
1329
1354
|
|
|
1330
1355
|
def process_cardinality_counts(self) -> None:
|
|
1331
1356
|
"""Ensure that all relationships with a cardinality of ONE have a min_count and max_count of 1."""
|
|
1332
|
-
# pylint: disable=too-many-branches
|
|
1333
1357
|
|
|
1334
1358
|
for name in self.all_names:
|
|
1335
1359
|
node = self.get(name=name, duplicate=False)
|
|
@@ -1416,7 +1440,6 @@ class SchemaBranch:
|
|
|
1416
1440
|
self.set(name=name, schema=node)
|
|
1417
1441
|
|
|
1418
1442
|
def cleanup_inherited_elements(self) -> None:
|
|
1419
|
-
# pylint: disable=too-many-branches
|
|
1420
1443
|
for name in self.node_names:
|
|
1421
1444
|
node = self.get_node(name=name, duplicate=False)
|
|
1422
1445
|
|
|
@@ -1642,7 +1665,7 @@ class SchemaBranch:
|
|
|
1642
1665
|
|
|
1643
1666
|
if new_used_by_profile:
|
|
1644
1667
|
core_profile_schema = self.get(name=InfrahubKind.PROFILE, duplicate=True)
|
|
1645
|
-
core_profile_schema.used_by = sorted(
|
|
1668
|
+
core_profile_schema.used_by = sorted(profile_schema_kinds)
|
|
1646
1669
|
self.set(name=InfrahubKind.PROFILE, schema=core_profile_schema)
|
|
1647
1670
|
|
|
1648
1671
|
if self.has(name=InfrahubKind.NODE):
|
|
@@ -1653,7 +1676,7 @@ class SchemaBranch:
|
|
|
1653
1676
|
if new_used_by_node:
|
|
1654
1677
|
core_node_schema = self.get(name=InfrahubKind.NODE, duplicate=True)
|
|
1655
1678
|
updated_used_by_node = set(chain(profile_schema_kinds, set(core_node_schema.used_by)))
|
|
1656
|
-
core_node_schema.used_by = sorted(
|
|
1679
|
+
core_node_schema.used_by = sorted(updated_used_by_node)
|
|
1657
1680
|
self.set(name=InfrahubKind.NODE, schema=core_node_schema)
|
|
1658
1681
|
|
|
1659
1682
|
def manage_profile_relationships(self) -> None:
|
|
@@ -1746,3 +1769,214 @@ class SchemaBranch:
|
|
|
1746
1769
|
profile.attributes.append(attr)
|
|
1747
1770
|
|
|
1748
1771
|
return profile
|
|
1772
|
+
|
|
1773
|
+
def _get_object_template_kind(self, node_kind: str) -> str:
|
|
1774
|
+
return f"Template{node_kind}"
|
|
1775
|
+
|
|
1776
|
+
def manage_object_template_relationships(self) -> None:
|
|
1777
|
+
"""Add an `object_template` relationship to all nodes that can be created from object templates.
|
|
1778
|
+
|
|
1779
|
+
This relationship allows to record from which template an object has been created.
|
|
1780
|
+
"""
|
|
1781
|
+
for node_name in self.node_names + self.generic_names:
|
|
1782
|
+
node = self.get(name=node_name, duplicate=False)
|
|
1783
|
+
|
|
1784
|
+
if (
|
|
1785
|
+
node.namespace in RESTRICTED_NAMESPACES
|
|
1786
|
+
or not node.generate_template
|
|
1787
|
+
or node.state == HashableModelState.ABSENT
|
|
1788
|
+
):
|
|
1789
|
+
continue
|
|
1790
|
+
|
|
1791
|
+
template_rel_settings: dict[str, Any] = {
|
|
1792
|
+
"name": "object_template",
|
|
1793
|
+
"identifier": "node__objecttemplate",
|
|
1794
|
+
"peer": self._get_object_template_kind(node.kind),
|
|
1795
|
+
"kind": RelationshipKind.TEMPLATE,
|
|
1796
|
+
"cardinality": RelationshipCardinality.ONE,
|
|
1797
|
+
"branch": BranchSupportType.AWARE,
|
|
1798
|
+
"order_weight": 1,
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
# Add relationship between node and template
|
|
1802
|
+
if "object_template" not in node.relationship_names:
|
|
1803
|
+
node_schema = self.get(name=node_name, duplicate=True)
|
|
1804
|
+
|
|
1805
|
+
node_schema.relationships.append(RelationshipSchema(**template_rel_settings))
|
|
1806
|
+
self.set(name=node_name, schema=node_schema)
|
|
1807
|
+
else:
|
|
1808
|
+
has_changes: bool = False
|
|
1809
|
+
rel_template = node.get_relationship(name="object_template")
|
|
1810
|
+
for name, value in template_rel_settings.items():
|
|
1811
|
+
if getattr(rel_template, name) != value:
|
|
1812
|
+
has_changes = True
|
|
1813
|
+
|
|
1814
|
+
if not has_changes:
|
|
1815
|
+
continue
|
|
1816
|
+
|
|
1817
|
+
node_schema = self.get(name=node_name, duplicate=True)
|
|
1818
|
+
rel_template = node_schema.get_relationship(name="object_template")
|
|
1819
|
+
for name, value in template_rel_settings.items():
|
|
1820
|
+
if getattr(rel_template, name) != value:
|
|
1821
|
+
setattr(rel_template, name, value)
|
|
1822
|
+
|
|
1823
|
+
self.set(name=node_name, schema=node_schema)
|
|
1824
|
+
|
|
1825
|
+
def add_relationships_to_template(self, node: NodeSchema) -> None:
|
|
1826
|
+
template_schema = self.get(name=self._get_object_template_kind(node_kind=node.kind), duplicate=False)
|
|
1827
|
+
|
|
1828
|
+
for relationship in node.relationships:
|
|
1829
|
+
if relationship.peer in [
|
|
1830
|
+
InfrahubKind.GENERICGROUP,
|
|
1831
|
+
InfrahubKind.PROFILE,
|
|
1832
|
+
] or relationship.kind not in [
|
|
1833
|
+
RelationshipKind.COMPONENT,
|
|
1834
|
+
RelationshipKind.PARENT,
|
|
1835
|
+
RelationshipKind.ATTRIBUTE,
|
|
1836
|
+
]:
|
|
1837
|
+
continue
|
|
1838
|
+
|
|
1839
|
+
rel_template_peer = (
|
|
1840
|
+
self._get_object_template_kind(node_kind=relationship.peer)
|
|
1841
|
+
if relationship.kind != RelationshipKind.ATTRIBUTE
|
|
1842
|
+
else relationship.peer
|
|
1843
|
+
)
|
|
1844
|
+
template_schema.relationships.append(
|
|
1845
|
+
RelationshipSchema(
|
|
1846
|
+
name=relationship.name,
|
|
1847
|
+
peer=rel_template_peer,
|
|
1848
|
+
kind=relationship.kind,
|
|
1849
|
+
optional=relationship.kind in [RelationshipKind.COMPONENT, RelationshipKind.ATTRIBUTE],
|
|
1850
|
+
cardinality=relationship.cardinality,
|
|
1851
|
+
branch=relationship.branch,
|
|
1852
|
+
identifier=self._generate_identifier_string(template_schema.kind, rel_template_peer),
|
|
1853
|
+
min_count=relationship.min_count,
|
|
1854
|
+
max_count=relationship.max_count,
|
|
1855
|
+
)
|
|
1856
|
+
)
|
|
1857
|
+
|
|
1858
|
+
def generate_object_template_from_node(self, node: NodeSchema) -> TemplateSchema:
|
|
1859
|
+
core_template_schema = self.get(name=InfrahubKind.OBJECTTEMPLATE, duplicate=False)
|
|
1860
|
+
core_name_attr = core_template_schema.get_attribute(name=OBJECT_TEMPLATE_NAME_ATTR)
|
|
1861
|
+
template_name_attr = AttributeSchema(
|
|
1862
|
+
**core_name_attr.model_dump(exclude=["id", "inherited"]),
|
|
1863
|
+
)
|
|
1864
|
+
template_name_attr.branch = node.branch
|
|
1865
|
+
|
|
1866
|
+
template = TemplateSchema(
|
|
1867
|
+
name=node.kind,
|
|
1868
|
+
namespace="Template",
|
|
1869
|
+
label=f"Object template {node.label}",
|
|
1870
|
+
description=f"Object template for {node.kind}",
|
|
1871
|
+
branch=node.branch,
|
|
1872
|
+
include_in_menu=True,
|
|
1873
|
+
display_labels=["template_name__value"],
|
|
1874
|
+
inherit_from=[InfrahubKind.LINEAGESOURCE, InfrahubKind.OBJECTTEMPLATE, InfrahubKind.NODE],
|
|
1875
|
+
human_friendly_id=["template_name__value"],
|
|
1876
|
+
default_filter="template_name__value",
|
|
1877
|
+
attributes=[template_name_attr],
|
|
1878
|
+
relationships=[
|
|
1879
|
+
RelationshipSchema(
|
|
1880
|
+
name="related_nodes",
|
|
1881
|
+
identifier="node__objecttemplate",
|
|
1882
|
+
peer=node.kind,
|
|
1883
|
+
kind=RelationshipKind.TEMPLATE,
|
|
1884
|
+
cardinality=RelationshipCardinality.MANY,
|
|
1885
|
+
branch=BranchSupportType.AWARE,
|
|
1886
|
+
)
|
|
1887
|
+
],
|
|
1888
|
+
)
|
|
1889
|
+
|
|
1890
|
+
for node_attr in node.attributes:
|
|
1891
|
+
if node_attr.unique:
|
|
1892
|
+
continue
|
|
1893
|
+
|
|
1894
|
+
attr = AttributeSchema(
|
|
1895
|
+
optional=True, **node_attr.model_dump(exclude=["id", "unique", "optional", "read_only", "inherited"])
|
|
1896
|
+
)
|
|
1897
|
+
template.attributes.append(attr)
|
|
1898
|
+
|
|
1899
|
+
return template
|
|
1900
|
+
|
|
1901
|
+
def identify_required_object_templates(
|
|
1902
|
+
self, node_schema: NodeSchema, identified: set[NodeSchema]
|
|
1903
|
+
) -> set[NodeSchema]:
|
|
1904
|
+
"""Identify all templates required to turn a given node into a template."""
|
|
1905
|
+
if node_schema in identified:
|
|
1906
|
+
return identified
|
|
1907
|
+
|
|
1908
|
+
identified.add(node_schema)
|
|
1909
|
+
|
|
1910
|
+
for relationship in node_schema.relationships:
|
|
1911
|
+
if relationship.peer in [
|
|
1912
|
+
InfrahubKind.GENERICGROUP,
|
|
1913
|
+
InfrahubKind.PROFILE,
|
|
1914
|
+
] or relationship.kind not in [RelationshipKind.COMPONENT, RelationshipKind.PARENT]:
|
|
1915
|
+
continue
|
|
1916
|
+
|
|
1917
|
+
peer_schema = self.get(name=relationship.peer, duplicate=False)
|
|
1918
|
+
if not isinstance(peer_schema, NodeSchema) or peer_schema in identified:
|
|
1919
|
+
continue
|
|
1920
|
+
|
|
1921
|
+
identified |= self.identify_required_object_templates(node_schema=peer_schema, identified=identified)
|
|
1922
|
+
|
|
1923
|
+
return identified
|
|
1924
|
+
|
|
1925
|
+
def manage_object_template_schemas(self) -> None:
|
|
1926
|
+
need_templates: set[NodeSchema] = set()
|
|
1927
|
+
template_schema_kinds: set[str] = set()
|
|
1928
|
+
|
|
1929
|
+
for node_name in self.node_names + self.generic_names:
|
|
1930
|
+
node = self.get(name=node_name, duplicate=False)
|
|
1931
|
+
|
|
1932
|
+
# Delete old object templates if schemas were removed
|
|
1933
|
+
if (
|
|
1934
|
+
node.namespace in RESTRICTED_NAMESPACES
|
|
1935
|
+
or not node.generate_template
|
|
1936
|
+
or node.state == HashableModelState.ABSENT
|
|
1937
|
+
):
|
|
1938
|
+
try:
|
|
1939
|
+
self.delete(name=self._get_object_template_kind(node_kind=node.kind))
|
|
1940
|
+
except SchemaNotFoundError:
|
|
1941
|
+
...
|
|
1942
|
+
continue
|
|
1943
|
+
|
|
1944
|
+
need_templates |= self.identify_required_object_templates(node_schema=node, identified=need_templates)
|
|
1945
|
+
|
|
1946
|
+
# Generate templates with their attributes
|
|
1947
|
+
for node in need_templates:
|
|
1948
|
+
template = self.generate_object_template_from_node(node=node)
|
|
1949
|
+
self.set(name=template.kind, schema=template)
|
|
1950
|
+
template_schema_kinds.add(template.kind)
|
|
1951
|
+
|
|
1952
|
+
# Go back on templates and add relationships to them
|
|
1953
|
+
for node in need_templates:
|
|
1954
|
+
self.add_relationships_to_template(node=node)
|
|
1955
|
+
|
|
1956
|
+
for previous_template in list(self.templates.keys()):
|
|
1957
|
+
# Ensure that we remove previous object template schemas if a node has been renamed
|
|
1958
|
+
if previous_template not in template_schema_kinds:
|
|
1959
|
+
self.delete(name=previous_template)
|
|
1960
|
+
|
|
1961
|
+
if not template_schema_kinds:
|
|
1962
|
+
return
|
|
1963
|
+
|
|
1964
|
+
core_template_schema = self.get(name=InfrahubKind.OBJECTTEMPLATE, duplicate=False)
|
|
1965
|
+
current_used_by_template = set(core_template_schema.used_by)
|
|
1966
|
+
new_used_by_template = template_schema_kinds - current_used_by_template
|
|
1967
|
+
|
|
1968
|
+
if new_used_by_template:
|
|
1969
|
+
core_template_schema = self.get(name=InfrahubKind.OBJECTTEMPLATE, duplicate=True)
|
|
1970
|
+
core_template_schema.used_by = sorted(template_schema_kinds)
|
|
1971
|
+
self.set(name=InfrahubKind.OBJECTTEMPLATE, schema=core_template_schema)
|
|
1972
|
+
|
|
1973
|
+
if self.has(name=InfrahubKind.NODE):
|
|
1974
|
+
core_node_schema = self.get(name=InfrahubKind.NODE, duplicate=False)
|
|
1975
|
+
current_used_by_node = set(core_node_schema.used_by)
|
|
1976
|
+
new_used_by_node = template_schema_kinds - current_used_by_node
|
|
1977
|
+
|
|
1978
|
+
if new_used_by_node:
|
|
1979
|
+
core_node_schema = self.get(name=InfrahubKind.NODE, duplicate=True)
|
|
1980
|
+
updated_used_by_node = set(chain(template_schema_kinds, set(core_node_schema.used_by)))
|
|
1981
|
+
core_node_schema.used_by = sorted(updated_used_by_node)
|
|
1982
|
+
self.set(name=InfrahubKind.NODE, schema=core_node_schema)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
|
|
5
|
+
from infrahub.core.constants import InfrahubKind
|
|
6
|
+
from infrahub.core.schema.basenode_schema import BaseNodeSchema
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TemplateSchema(BaseNodeSchema):
|
|
10
|
+
inherit_from: list[str] = Field(
|
|
11
|
+
default_factory=list, description="List of Generic Kind that this template is inheriting from"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
@property
|
|
15
|
+
def is_node_schema(self) -> bool:
|
|
16
|
+
return False
|
|
17
|
+
|
|
18
|
+
@property
|
|
19
|
+
def is_generic_schema(self) -> bool:
|
|
20
|
+
return False
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def is_profile_schema(self) -> bool:
|
|
24
|
+
return False
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def is_template_schema(self) -> bool:
|
|
28
|
+
return True
|
|
29
|
+
|
|
30
|
+
def get_labels(self) -> list[str]:
|
|
31
|
+
"""Return the labels for this object, composed of the kind and the list of Generic this object is inheriting from."""
|
|
32
|
+
|
|
33
|
+
labels: list[str] = [self.kind] + self.inherit_from
|
|
34
|
+
if self.namespace not in ["Schema", "Internal"] and InfrahubKind.GENERICGROUP not in self.inherit_from:
|
|
35
|
+
labels.append(InfrahubKind.OBJECTTEMPLATE)
|
|
36
|
+
return labels
|
infrahub/core/task/user_task.py
CHANGED
|
@@ -80,14 +80,16 @@ class UserTask:
|
|
|
80
80
|
|
|
81
81
|
@classmethod
|
|
82
82
|
def from_graphql_context(
|
|
83
|
-
cls, title: str,
|
|
83
|
+
cls, title: str, graphql_context: GraphqlContext, logger: Optional[Union[BoundLogger, InfrahubLogger]] = None
|
|
84
84
|
) -> Self:
|
|
85
|
-
if not
|
|
85
|
+
if not graphql_context.db or not graphql_context.account_session:
|
|
86
86
|
raise ValueError("db and account_session must be provided to initialize a GraphQLTaskReport")
|
|
87
87
|
|
|
88
|
-
if not logger and
|
|
89
|
-
logger =
|
|
90
|
-
return cls(
|
|
88
|
+
if not logger and graphql_context.service and graphql_context.service.log:
|
|
89
|
+
logger = graphql_context.service.log
|
|
90
|
+
return cls(
|
|
91
|
+
title=title, account_id=graphql_context.account_session.account_id, db=graphql_context.db, logger=logger
|
|
92
|
+
)
|
|
91
93
|
|
|
92
94
|
async def __aenter__(self) -> Self:
|
|
93
95
|
await self.create_task()
|
infrahub/core/timestamp.py
CHANGED
|
@@ -9,7 +9,7 @@ if TYPE_CHECKING:
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class Timestamp(BaseTimestamp):
|
|
12
|
-
async def to_graphql(self, *args: Any, **kwargs: Any) -> DateTime: #
|
|
12
|
+
async def to_graphql(self, *args: Any, **kwargs: Any) -> DateTime: # noqa: ARG002
|
|
13
13
|
return self.obj
|
|
14
14
|
|
|
15
15
|
def get_query_filter_path(self, rel_name: str = "r") -> tuple[str, dict]:
|
infrahub/core/utils.py
CHANGED
|
@@ -72,6 +72,7 @@ async def update_relationships_to(ids: list[str], db: InfrahubDatabase, to: Time
|
|
|
72
72
|
query = """
|
|
73
73
|
MATCH ()-[r]->()
|
|
74
74
|
WHERE %(id_func)s(r) IN $ids
|
|
75
|
+
AND r.to IS NULL
|
|
75
76
|
SET r.to = $to
|
|
76
77
|
RETURN %(id_func)s(r)
|
|
77
78
|
""" % {"id_func": db.get_id_function_name()}
|
|
@@ -184,7 +185,7 @@ def parse_node_kind(kind: str) -> NodeKind:
|
|
|
184
185
|
def convert_ip_to_binary_str(
|
|
185
186
|
obj: Union[ipaddress.IPv6Network, ipaddress.IPv4Network, ipaddress.IPv4Interface, ipaddress.IPv6Interface],
|
|
186
187
|
) -> str:
|
|
187
|
-
if isinstance(obj,
|
|
188
|
+
if isinstance(obj, ipaddress.IPv6Network | ipaddress.IPv4Network):
|
|
188
189
|
prefix_bin = f"{int(obj.network_address):b}"
|
|
189
190
|
return prefix_bin.zfill(obj.max_prefixlen)
|
|
190
191
|
|
|
@@ -204,7 +205,7 @@ def build_regex_attr(value: str | int | bool) -> str:
|
|
|
204
205
|
"""
|
|
205
206
|
if isinstance(value, str):
|
|
206
207
|
return f'"{value}"'
|
|
207
|
-
if isinstance(value,
|
|
208
|
+
if isinstance(value, bool | int):
|
|
208
209
|
value_str = str(value).lower()
|
|
209
210
|
return rf'(?<=[^\w"\d]){value_str}(?=[^\w"\d])'
|
|
210
211
|
|
|
@@ -18,7 +18,7 @@ if TYPE_CHECKING:
|
|
|
18
18
|
class AttributeChoicesUpdateValidatorQuery(AttributeSchemaValidatorQuery):
|
|
19
19
|
name: str = "attribute_constraints_choices_validator"
|
|
20
20
|
|
|
21
|
-
async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None:
|
|
21
|
+
async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
|
|
22
22
|
if self.attribute_schema.choices is None:
|
|
23
23
|
return
|
|
24
24
|
|
|
@@ -18,7 +18,7 @@ if TYPE_CHECKING:
|
|
|
18
18
|
class AttributeEnumUpdateValidatorQuery(AttributeSchemaValidatorQuery):
|
|
19
19
|
name: str = "attribute_constraints_enum_validator"
|
|
20
20
|
|
|
21
|
-
async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None:
|
|
21
|
+
async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
|
|
22
22
|
if self.attribute_schema.enum is None:
|
|
23
23
|
return
|
|
24
24
|
|
|
@@ -28,7 +28,7 @@ class NodeAttributeValue:
|
|
|
28
28
|
class AttributeKindUpdateValidatorQuery(AttributeSchemaValidatorQuery):
|
|
29
29
|
name: str = "attribute_constraints_kind_validator"
|
|
30
30
|
|
|
31
|
-
async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None:
|
|
31
|
+
async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
|
|
32
32
|
branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string())
|
|
33
33
|
self.params.update(branch_params)
|
|
34
34
|
|
|
@@ -18,7 +18,7 @@ if TYPE_CHECKING:
|
|
|
18
18
|
class AttributeLengthUpdateValidatorQuery(AttributeSchemaValidatorQuery):
|
|
19
19
|
name: str = "attribute_constraints_length_validator"
|
|
20
20
|
|
|
21
|
-
async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None:
|
|
21
|
+
async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
|
|
22
22
|
branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string())
|
|
23
23
|
self.params.update(branch_params)
|
|
24
24
|
|
|
@@ -18,7 +18,7 @@ if TYPE_CHECKING:
|
|
|
18
18
|
class AttributeOptionalUpdateValidatorQuery(AttributeSchemaValidatorQuery):
|
|
19
19
|
name: str = "attribute_constraints_optional_validator"
|
|
20
20
|
|
|
21
|
-
async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None:
|
|
21
|
+
async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
|
|
22
22
|
branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string())
|
|
23
23
|
self.params.update(branch_params)
|
|
24
24
|
|
|
@@ -18,7 +18,7 @@ if TYPE_CHECKING:
|
|
|
18
18
|
class AttributeRegexUpdateValidatorQuery(AttributeSchemaValidatorQuery):
|
|
19
19
|
name: str = "attribute_constraints_regex_validator"
|
|
20
20
|
|
|
21
|
-
async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None:
|
|
21
|
+
async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
|
|
22
22
|
branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string())
|
|
23
23
|
self.params.update(branch_params)
|
|
24
24
|
|
|
@@ -18,7 +18,7 @@ if TYPE_CHECKING:
|
|
|
18
18
|
class AttributeUniqueUpdateValidatorQuery(AttributeSchemaValidatorQuery):
|
|
19
19
|
name: str = "attribute_constraints_unique_validator"
|
|
20
20
|
|
|
21
|
-
async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None:
|
|
21
|
+
async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
|
|
22
22
|
branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string(), is_isolated=False)
|
|
23
23
|
self.params.update(branch_params)
|
|
24
24
|
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from typing import Any, Coroutine
|
|
3
|
+
|
|
4
|
+
from infrahub_sdk.node import InfrahubNode
|
|
5
|
+
|
|
6
|
+
from infrahub.core.constants import ValidatorConclusion, ValidatorState
|
|
7
|
+
from infrahub.core.timestamp import Timestamp
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
async def run_checks_and_update_validator(
|
|
11
|
+
checks: list[Coroutine[Any, None, ValidatorConclusion]], validator: InfrahubNode
|
|
12
|
+
) -> None:
|
|
13
|
+
"""
|
|
14
|
+
Execute a list of checks coroutines, and set validator fields accordingly.
|
|
15
|
+
Tasks are retrieved by completion order so as soon as we detect a failing check,
|
|
16
|
+
we set validator conclusion to failure.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
# First set validator to in progress, then wait for results
|
|
20
|
+
validator.state.value = ValidatorState.IN_PROGRESS.value
|
|
21
|
+
validator.started_at.value = Timestamp().to_string()
|
|
22
|
+
validator.completed_at.value = ""
|
|
23
|
+
await validator.save()
|
|
24
|
+
|
|
25
|
+
for earliest_task in asyncio.as_completed(checks):
|
|
26
|
+
result = await earliest_task
|
|
27
|
+
if validator.conclusion.value != ValidatorConclusion.FAILURE.value and result == ValidatorConclusion.FAILURE:
|
|
28
|
+
validator.conclusion.value = ValidatorConclusion.FAILURE.value
|
|
29
|
+
await validator.save()
|
|
30
|
+
# Continue to iterate to wait for the end of all checks
|
|
31
|
+
|
|
32
|
+
validator.state.value = ValidatorState.COMPLETED.value
|
|
33
|
+
validator.completed_at.value = Timestamp().to_string()
|
|
34
|
+
if validator.conclusion.value != ValidatorConclusion.FAILURE.value:
|
|
35
|
+
validator.conclusion.value = ValidatorConclusion.SUCCESS.value
|
|
36
|
+
|
|
37
|
+
await validator.save()
|
|
@@ -28,7 +28,7 @@ class NodeGenerateProfileValidatorQuery(SchemaValidatorQuery):
|
|
|
28
28
|
super().__init__(*args, **kwargs)
|
|
29
29
|
self.profile_kind = f"Profile{self.node_schema.kind}"
|
|
30
30
|
|
|
31
|
-
async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None:
|
|
31
|
+
async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
|
|
32
32
|
branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at)
|
|
33
33
|
self.params.update(branch_params)
|
|
34
34
|
|
|
@@ -31,7 +31,7 @@ class NodeHierarchyUpdateValidatorQuery(SchemaValidatorQuery):
|
|
|
31
31
|
self.check_parent = check_parent
|
|
32
32
|
super().__init__(**kwargs)
|
|
33
33
|
|
|
34
|
-
async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None:
|
|
34
|
+
async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
|
|
35
35
|
if self.check_children and self.check_parent:
|
|
36
36
|
raise RuntimeError("Cannot check children and parent at same time")
|
|
37
37
|
if self.check_children:
|
|
@@ -14,7 +14,7 @@ if TYPE_CHECKING:
|
|
|
14
14
|
class NodeNotPresentValidatorQuery(SchemaValidatorQuery):
|
|
15
15
|
name: str = "node_not_present_validator"
|
|
16
16
|
|
|
17
|
-
async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None:
|
|
17
|
+
async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
|
|
18
18
|
branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string())
|
|
19
19
|
self.params.update(branch_params)
|
|
20
20
|
|