infrahub-server 1.2.2__py3-none-any.whl → 1.2.4__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/cli/git_agent.py +4 -10
- infrahub/computed_attribute/tasks.py +8 -8
- infrahub/config.py +35 -0
- infrahub/core/constants/__init__.py +1 -0
- infrahub/core/constraint/node/runner.py +6 -5
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/migrations/graph/__init__.py +6 -1
- infrahub/core/migrations/graph/m018_uniqueness_nulls.py +68 -70
- infrahub/core/migrations/graph/m024_missing_hierarchy_backfill.py +69 -0
- infrahub/core/migrations/graph/m025_uniqueness_nulls.py +26 -0
- infrahub/core/migrations/schema/node_attribute_remove.py +16 -2
- infrahub/core/models.py +7 -1
- infrahub/core/node/__init__.py +4 -4
- infrahub/core/node/constraints/grouped_uniqueness.py +30 -10
- infrahub/core/query/ipam.py +1 -1
- infrahub/core/registry.py +18 -0
- infrahub/core/schema/basenode_schema.py +21 -1
- infrahub/core/schema/definitions/internal.py +2 -1
- infrahub/core/schema/generated/base_node_schema.py +1 -1
- infrahub/core/schema/manager.py +21 -0
- infrahub/core/schema/schema_branch.py +17 -9
- infrahub/database/__init__.py +10 -0
- infrahub/events/group_action.py +6 -1
- infrahub/events/node_action.py +5 -1
- infrahub/git/integrator.py +9 -7
- infrahub/graphql/mutations/main.py +10 -12
- infrahub/menu/repository.py +6 -6
- infrahub/message_bus/messages/__init__.py +0 -2
- infrahub/message_bus/operations/__init__.py +0 -1
- infrahub/message_bus/operations/event/__init__.py +2 -2
- infrahub/server.py +6 -11
- infrahub/services/adapters/cache/__init__.py +17 -0
- infrahub/services/adapters/cache/redis.py +11 -1
- infrahub/services/adapters/message_bus/__init__.py +20 -0
- infrahub/services/component.py +1 -2
- infrahub/tasks/registry.py +3 -7
- infrahub/workers/infrahub_async.py +4 -10
- infrahub_sdk/client.py +6 -6
- infrahub_sdk/ctl/cli_commands.py +32 -37
- infrahub_sdk/ctl/render.py +39 -0
- infrahub_sdk/exceptions.py +6 -2
- infrahub_sdk/generator.py +1 -1
- infrahub_sdk/node.py +38 -11
- infrahub_sdk/protocols_base.py +8 -1
- infrahub_sdk/pytest_plugin/items/jinja2_transform.py +22 -26
- infrahub_sdk/schema/__init__.py +10 -1
- infrahub_sdk/store.py +351 -75
- infrahub_sdk/template/__init__.py +209 -0
- infrahub_sdk/template/exceptions.py +38 -0
- infrahub_sdk/template/filters.py +151 -0
- infrahub_sdk/template/models.py +10 -0
- infrahub_sdk/utils.py +7 -0
- {infrahub_server-1.2.2.dist-info → infrahub_server-1.2.4.dist-info}/METADATA +2 -1
- {infrahub_server-1.2.2.dist-info → infrahub_server-1.2.4.dist-info}/RECORD +61 -59
- infrahub_testcontainers/container.py +6 -0
- infrahub_testcontainers/docker-compose.test.yml +1 -0
- infrahub_testcontainers/haproxy.cfg +3 -3
- infrahub_testcontainers/helpers.py +1 -1
- infrahub/message_bus/messages/event_worker_newprimaryapi.py +0 -9
- infrahub/message_bus/operations/event/worker.py +0 -9
- infrahub/support/__init__.py +0 -0
- infrahub/support/macro.py +0 -69
- {infrahub_server-1.2.2.dist-info → infrahub_server-1.2.4.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.2.2.dist-info → infrahub_server-1.2.4.dist-info}/WHEEL +0 -0
- {infrahub_server-1.2.2.dist-info → infrahub_server-1.2.4.dist-info}/entry_points.txt +0 -0
|
@@ -11,7 +11,7 @@ from infrahub_sdk.utils import compare_lists, intersection
|
|
|
11
11
|
from pydantic import field_validator
|
|
12
12
|
|
|
13
13
|
from infrahub.core.constants import RelationshipCardinality, RelationshipKind
|
|
14
|
-
from infrahub.core.models import HashableModelDiff
|
|
14
|
+
from infrahub.core.models import HashableModel, HashableModelDiff
|
|
15
15
|
|
|
16
16
|
from .attribute_schema import AttributeSchema
|
|
17
17
|
from .generated.base_node_schema import GeneratedBaseNodeSchema
|
|
@@ -27,6 +27,16 @@ if TYPE_CHECKING:
|
|
|
27
27
|
NODE_METADATA_ATTRIBUTES = ["_source", "_owner"]
|
|
28
28
|
INHERITED = "INHERITED"
|
|
29
29
|
|
|
30
|
+
OPTIONAL_TEXT_FIELDS = [
|
|
31
|
+
"default_filter",
|
|
32
|
+
"description",
|
|
33
|
+
"label",
|
|
34
|
+
"menu_placement",
|
|
35
|
+
"documentation",
|
|
36
|
+
"parent",
|
|
37
|
+
"children",
|
|
38
|
+
]
|
|
39
|
+
|
|
30
40
|
|
|
31
41
|
class BaseNodeSchema(GeneratedBaseNodeSchema):
|
|
32
42
|
_exclude_from_hash: list[str] = ["attributes", "relationships"]
|
|
@@ -480,6 +490,16 @@ class BaseNodeSchema(GeneratedBaseNodeSchema):
|
|
|
480
490
|
return UniquenessConstraintType.SUBSET_OF_HFID
|
|
481
491
|
return UniquenessConstraintType.STANDARD
|
|
482
492
|
|
|
493
|
+
def update(self, other: HashableModel) -> Self:
|
|
494
|
+
super().update(other=other)
|
|
495
|
+
|
|
496
|
+
# Allow to specify empty string to remove existing fields values
|
|
497
|
+
for field_name in OPTIONAL_TEXT_FIELDS:
|
|
498
|
+
if getattr(other, field_name, None) == "": # noqa: PLC1901
|
|
499
|
+
setattr(self, field_name, None)
|
|
500
|
+
|
|
501
|
+
return self
|
|
502
|
+
|
|
483
503
|
|
|
484
504
|
@dataclass
|
|
485
505
|
class SchemaUniquenessConstraintPath:
|
|
@@ -18,6 +18,7 @@ from infrahub.core.constants import (
|
|
|
18
18
|
DEFAULT_NAME_MIN_LENGTH,
|
|
19
19
|
DEFAULT_REL_IDENTIFIER_LENGTH,
|
|
20
20
|
NAME_REGEX,
|
|
21
|
+
NAME_REGEX_OR_EMPTY,
|
|
21
22
|
NAMESPACE_REGEX,
|
|
22
23
|
NODE_KIND_REGEX,
|
|
23
24
|
NODE_NAME_REGEX,
|
|
@@ -269,7 +270,7 @@ base_node_schema = SchemaNode(
|
|
|
269
270
|
SchemaAttribute(
|
|
270
271
|
name="default_filter",
|
|
271
272
|
kind="Text",
|
|
272
|
-
regex=str(
|
|
273
|
+
regex=str(NAME_REGEX_OR_EMPTY),
|
|
273
274
|
description="Default filter used to search for a node in addition to its ID. (deprecated: please use human_friendly_id instead)",
|
|
274
275
|
optional=True,
|
|
275
276
|
extra={"update": UpdateSupport.ALLOWED},
|
|
@@ -50,7 +50,7 @@ class GeneratedBaseNodeSchema(HashableModel):
|
|
|
50
50
|
default_filter: str | None = Field(
|
|
51
51
|
default=None,
|
|
52
52
|
description="Default filter used to search for a node in addition to its ID. (deprecated: please use human_friendly_id instead)",
|
|
53
|
-
pattern=r"^[a-z0-9\_]
|
|
53
|
+
pattern=r"^[a-z0-9\_]*$",
|
|
54
54
|
json_schema_extra={"update": "allowed"},
|
|
55
55
|
)
|
|
56
56
|
human_friendly_id: list[str] | None = Field(
|
infrahub/core/schema/manager.py
CHANGED
|
@@ -744,3 +744,24 @@ class SchemaManager(NodeManager):
|
|
|
744
744
|
"""Convert a schema_node object loaded from the database into GenericSchema object."""
|
|
745
745
|
node_data = await cls._prepare_node_data(schema_node=schema_node, db=db)
|
|
746
746
|
return GenericSchema(**node_data)
|
|
747
|
+
|
|
748
|
+
def purge_inactive_branches(self, active_branches: list[str]) -> list[str]:
|
|
749
|
+
"""Return non active branches that were purged."""
|
|
750
|
+
|
|
751
|
+
hashes_to_keep: set[str] = set()
|
|
752
|
+
for active_branch in active_branches:
|
|
753
|
+
if branch := self._branches.get(active_branch):
|
|
754
|
+
nodes = branch.get_all(include_internal=True, duplicate=False)
|
|
755
|
+
hashes_to_keep.update([node.get_hash() for node in nodes.values()])
|
|
756
|
+
|
|
757
|
+
removed_branches: list[str] = []
|
|
758
|
+
for branch_name in list(self._branches.keys()):
|
|
759
|
+
if branch_name not in active_branches:
|
|
760
|
+
del self._branches[branch_name]
|
|
761
|
+
removed_branches.append(branch_name)
|
|
762
|
+
|
|
763
|
+
for hash_key in list(self._cache.keys()):
|
|
764
|
+
if hash_key not in hashes_to_keep:
|
|
765
|
+
del self._cache[hash_key]
|
|
766
|
+
|
|
767
|
+
return removed_branches
|
|
@@ -6,6 +6,8 @@ from collections import defaultdict
|
|
|
6
6
|
from itertools import chain, combinations
|
|
7
7
|
from typing import Any
|
|
8
8
|
|
|
9
|
+
from infrahub_sdk.template import Jinja2Template
|
|
10
|
+
from infrahub_sdk.template.exceptions import JinjaTemplateError, JinjaTemplateOperationViolationError
|
|
9
11
|
from infrahub_sdk.topological_sort import DependencyCycleExistsError, topological_sort
|
|
10
12
|
from infrahub_sdk.utils import compare_lists, deep_merge_dict, duplicates, intersection
|
|
11
13
|
from typing_extensions import Self
|
|
@@ -51,7 +53,6 @@ from infrahub.core.schema.definitions.core import core_profile_schema_definition
|
|
|
51
53
|
from infrahub.core.validators import CONSTRAINT_VALIDATOR_MAP
|
|
52
54
|
from infrahub.exceptions import SchemaNotFoundError, ValidationError
|
|
53
55
|
from infrahub.log import get_logger
|
|
54
|
-
from infrahub.support.macro import MacroDefinition
|
|
55
56
|
from infrahub.types import ATTRIBUTE_TYPES
|
|
56
57
|
from infrahub.utils import format_label
|
|
57
58
|
from infrahub.visuals import select_color
|
|
@@ -1037,14 +1038,22 @@ class SchemaBranch:
|
|
|
1037
1038
|
| SchemaElementPathType.REL_ONE_MANDATORY_ATTR_WITH_PROP
|
|
1038
1039
|
| SchemaElementPathType.REL_ONE_ATTR_WITH_PROP
|
|
1039
1040
|
)
|
|
1041
|
+
|
|
1042
|
+
jinja_template = Jinja2Template(template=attribute.computed_attribute.jinja2_template)
|
|
1040
1043
|
try:
|
|
1041
|
-
|
|
1042
|
-
|
|
1044
|
+
variables = jinja_template.get_variables()
|
|
1045
|
+
jinja_template.validate(restricted=config.SETTINGS.security.restrict_untrusted_jinja2_filters)
|
|
1046
|
+
except JinjaTemplateOperationViolationError as exc:
|
|
1047
|
+
raise ValueError(
|
|
1048
|
+
f"{node.kind}: Attribute {attribute.name!r} is assigned by a jinja2 template, but has an invalid template: {exc.message}"
|
|
1049
|
+
) from exc
|
|
1050
|
+
|
|
1051
|
+
except JinjaTemplateError as exc:
|
|
1043
1052
|
raise ValueError(
|
|
1044
|
-
f"{node.kind}: Attribute {attribute.name!r} is assigned by a jinja2 template, but has an invalid template"
|
|
1053
|
+
f"{node.kind}: Attribute {attribute.name!r} is assigned by a jinja2 template, but has an invalid template: : {exc.message}"
|
|
1045
1054
|
) from exc
|
|
1046
1055
|
|
|
1047
|
-
for variable in
|
|
1056
|
+
for variable in variables:
|
|
1048
1057
|
try:
|
|
1049
1058
|
schema_path = self.validate_schema_path(
|
|
1050
1059
|
node_schema=node, path=variable, allowed_path_types=allowed_path_types
|
|
@@ -1946,10 +1955,9 @@ class SchemaBranch:
|
|
|
1946
1955
|
)
|
|
1947
1956
|
)
|
|
1948
1957
|
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
] + template_schema.human_friendly_id
|
|
1958
|
+
parent_hfid = f"{relationship.name}__template_name__value"
|
|
1959
|
+
if relationship.kind == RelationshipKind.PARENT and parent_hfid not in template_schema.human_friendly_id:
|
|
1960
|
+
template_schema.human_friendly_id = [parent_hfid] + template_schema.human_friendly_id
|
|
1953
1961
|
template_schema.uniqueness_constraints[0].append(relationship.name)
|
|
1954
1962
|
|
|
1955
1963
|
def generate_object_template_from_node(
|
infrahub/database/__init__.py
CHANGED
|
@@ -205,6 +205,16 @@ class InfrahubDatabase:
|
|
|
205
205
|
def add_schema(self, schema: SchemaBranch, name: str | None = None) -> None:
|
|
206
206
|
self._schemas[name or schema.name] = schema
|
|
207
207
|
|
|
208
|
+
def purge_inactive_schemas(self, active_branches: list[str]) -> list[str]:
|
|
209
|
+
"""Return non active schema branches that were purged."""
|
|
210
|
+
removed_branches: list[str] = []
|
|
211
|
+
for branch_name in list(self._schemas.keys()):
|
|
212
|
+
if branch_name not in active_branches:
|
|
213
|
+
del self._schemas[branch_name]
|
|
214
|
+
removed_branches.append(branch_name)
|
|
215
|
+
|
|
216
|
+
return removed_branches
|
|
217
|
+
|
|
208
218
|
def start_session(self, read_only: bool = False, schemas: list[SchemaBranch] | None = None) -> InfrahubDatabase:
|
|
209
219
|
"""Create a new InfrahubDatabase object in Session mode."""
|
|
210
220
|
session_mode = InfrahubDatabaseSessionMode.WRITE
|
infrahub/events/group_action.py
CHANGED
|
@@ -2,7 +2,7 @@ from typing import ClassVar
|
|
|
2
2
|
|
|
3
3
|
from pydantic import Field
|
|
4
4
|
|
|
5
|
-
from infrahub.core.constants import MutationAction
|
|
5
|
+
from infrahub.core.constants import InfrahubKind, MutationAction
|
|
6
6
|
|
|
7
7
|
from .constants import EVENT_NAMESPACE
|
|
8
8
|
from .models import EventNode, InfrahubEvent
|
|
@@ -21,6 +21,11 @@ class GroupMutatedEvent(InfrahubEvent):
|
|
|
21
21
|
|
|
22
22
|
def get_related(self) -> list[dict[str, str]]:
|
|
23
23
|
related = super().get_related()
|
|
24
|
+
|
|
25
|
+
if self.kind == InfrahubKind.GRAPHQLQUERYGROUP:
|
|
26
|
+
# Temporary workaround to avoid too large payloads for the related field
|
|
27
|
+
return related
|
|
28
|
+
|
|
24
29
|
related.append(
|
|
25
30
|
{
|
|
26
31
|
"prefect.resource.id": self.node_id,
|
infrahub/events/node_action.py
CHANGED
|
@@ -7,7 +7,7 @@ from infrahub.core.changelog.models import (
|
|
|
7
7
|
RelationshipCardinalityManyChangelog,
|
|
8
8
|
RelationshipCardinalityOneChangelog,
|
|
9
9
|
)
|
|
10
|
-
from infrahub.core.constants import DiffAction, MutationAction
|
|
10
|
+
from infrahub.core.constants import DiffAction, InfrahubKind, MutationAction
|
|
11
11
|
|
|
12
12
|
from .constants import EVENT_NAMESPACE
|
|
13
13
|
from .models import InfrahubEvent
|
|
@@ -24,6 +24,10 @@ class NodeMutatedEvent(InfrahubEvent):
|
|
|
24
24
|
|
|
25
25
|
def get_related(self) -> list[dict[str, str]]:
|
|
26
26
|
related = super().get_related()
|
|
27
|
+
if self.kind == InfrahubKind.GRAPHQLQUERYGROUP:
|
|
28
|
+
# Temporary workaround to avoid too large payloads for the related field
|
|
29
|
+
return related
|
|
30
|
+
|
|
27
31
|
for attribute in self.changelog.attributes.values():
|
|
28
32
|
related.append(
|
|
29
33
|
{
|
infrahub/git/integrator.py
CHANGED
|
@@ -3,9 +3,9 @@ from __future__ import annotations
|
|
|
3
3
|
import hashlib
|
|
4
4
|
import importlib
|
|
5
5
|
import sys
|
|
6
|
+
from pathlib import Path
|
|
6
7
|
from typing import TYPE_CHECKING, Any
|
|
7
8
|
|
|
8
|
-
import jinja2
|
|
9
9
|
import ujson
|
|
10
10
|
import yaml
|
|
11
11
|
from infrahub_sdk import InfrahubClient # noqa: TC002
|
|
@@ -28,6 +28,8 @@ from infrahub_sdk.schema.repository import (
|
|
|
28
28
|
InfrahubPythonTransformConfig,
|
|
29
29
|
InfrahubRepositoryConfig,
|
|
30
30
|
)
|
|
31
|
+
from infrahub_sdk.template import Jinja2Template
|
|
32
|
+
from infrahub_sdk.template.exceptions import JinjaTemplateError
|
|
31
33
|
from infrahub_sdk.utils import compare_lists
|
|
32
34
|
from infrahub_sdk.yaml import SchemaFile
|
|
33
35
|
from prefect import flow, task
|
|
@@ -1057,14 +1059,14 @@ class InfrahubRepositoryIntegrator(InfrahubRepositoryBase):
|
|
|
1057
1059
|
|
|
1058
1060
|
self.validate_location(commit=commit, worktree_directory=commit_worktree.directory, file_path=location)
|
|
1059
1061
|
|
|
1062
|
+
jinja2_template = Jinja2Template(template=Path(location), template_directory=Path(commit_worktree.directory))
|
|
1060
1063
|
try:
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
template = templateEnv.get_template(location)
|
|
1064
|
-
return template.render(**data)
|
|
1065
|
-
except Exception as exc:
|
|
1064
|
+
return await jinja2_template.render(variables=data)
|
|
1065
|
+
except JinjaTemplateError as exc:
|
|
1066
1066
|
log.error(str(exc), exc_info=True)
|
|
1067
|
-
raise TransformError(
|
|
1067
|
+
raise TransformError(
|
|
1068
|
+
repository_name=self.name, commit=commit, location=location, message=exc.message
|
|
1069
|
+
) from exc
|
|
1068
1070
|
|
|
1069
1071
|
@task(name="python-check-execute", task_run_name="Execute Python Check", cache_policy=NONE) # type: ignore[arg-type]
|
|
1070
1072
|
async def execute_python_check(
|
|
@@ -363,7 +363,7 @@ class InfrahubMutationMixin:
|
|
|
363
363
|
branch: Branch,
|
|
364
364
|
db: InfrahubDatabase,
|
|
365
365
|
obj: Node,
|
|
366
|
-
|
|
366
|
+
skip_uniqueness_check: bool = False,
|
|
367
367
|
) -> tuple[Node, Self]:
|
|
368
368
|
"""
|
|
369
369
|
Wrapper around mutate_update to potentially activate locking and call it within a database transaction.
|
|
@@ -378,11 +378,11 @@ class InfrahubMutationMixin:
|
|
|
378
378
|
if lock_names:
|
|
379
379
|
async with InfrahubMultiLock(lock_registry=lock.registry, locks=lock_names):
|
|
380
380
|
obj = await cls.mutate_update_object(
|
|
381
|
-
db=db, info=info, data=data, branch=branch, obj=obj,
|
|
381
|
+
db=db, info=info, data=data, branch=branch, obj=obj, skip_uniqueness_check=skip_uniqueness_check
|
|
382
382
|
)
|
|
383
383
|
else:
|
|
384
384
|
obj = await cls.mutate_update_object(
|
|
385
|
-
db=db, info=info, data=data, branch=branch, obj=obj,
|
|
385
|
+
db=db, info=info, data=data, branch=branch, obj=obj, skip_uniqueness_check=skip_uniqueness_check
|
|
386
386
|
)
|
|
387
387
|
result = await cls.mutate_update_to_graphql(db=db, info=info, obj=obj)
|
|
388
388
|
return obj, result
|
|
@@ -396,11 +396,11 @@ class InfrahubMutationMixin:
|
|
|
396
396
|
data=data,
|
|
397
397
|
branch=branch,
|
|
398
398
|
obj=obj,
|
|
399
|
-
|
|
399
|
+
skip_uniqueness_check=skip_uniqueness_check,
|
|
400
400
|
)
|
|
401
401
|
else:
|
|
402
402
|
obj = await cls.mutate_update_object(
|
|
403
|
-
db=dbt, info=info, data=data, branch=branch, obj=obj,
|
|
403
|
+
db=dbt, info=info, data=data, branch=branch, obj=obj, skip_uniqueness_check=skip_uniqueness_check
|
|
404
404
|
)
|
|
405
405
|
result = await cls.mutate_update_to_graphql(db=dbt, info=info, obj=obj)
|
|
406
406
|
return obj, result
|
|
@@ -434,7 +434,7 @@ class InfrahubMutationMixin:
|
|
|
434
434
|
data: InputObjectType,
|
|
435
435
|
branch: Branch,
|
|
436
436
|
obj: Node,
|
|
437
|
-
|
|
437
|
+
skip_uniqueness_check: bool = False,
|
|
438
438
|
) -> Node:
|
|
439
439
|
component_registry = get_component_registry()
|
|
440
440
|
node_constraint_runner = await component_registry.get_component(NodeConstraintRunner, db=db, branch=branch)
|
|
@@ -442,8 +442,9 @@ class InfrahubMutationMixin:
|
|
|
442
442
|
before_mutate_profile_ids = await cls._get_profile_ids(db=db, obj=obj)
|
|
443
443
|
await obj.from_graphql(db=db, data=data)
|
|
444
444
|
fields_to_validate = list(data)
|
|
445
|
-
|
|
446
|
-
|
|
445
|
+
await node_constraint_runner.check(
|
|
446
|
+
node=obj, field_filters=fields_to_validate, skip_uniqueness_check=skip_uniqueness_check
|
|
447
|
+
)
|
|
447
448
|
|
|
448
449
|
fields = list(data.keys())
|
|
449
450
|
for field_to_remove in ("id", "hfid"):
|
|
@@ -494,7 +495,6 @@ class InfrahubMutationMixin:
|
|
|
494
495
|
db = database or graphql_context.db
|
|
495
496
|
dict_data = dict(data)
|
|
496
497
|
node = None
|
|
497
|
-
run_constraint_checks = True
|
|
498
498
|
|
|
499
499
|
if "id" in dict_data:
|
|
500
500
|
node = await NodeManager.get_one(
|
|
@@ -506,7 +506,6 @@ class InfrahubMutationMixin:
|
|
|
506
506
|
db=db,
|
|
507
507
|
branch=branch,
|
|
508
508
|
obj=node,
|
|
509
|
-
run_constraint_checks=run_constraint_checks,
|
|
510
509
|
)
|
|
511
510
|
return updated_obj, mutation, False
|
|
512
511
|
|
|
@@ -525,7 +524,6 @@ class InfrahubMutationMixin:
|
|
|
525
524
|
db=db,
|
|
526
525
|
branch=branch,
|
|
527
526
|
obj=node,
|
|
528
|
-
run_constraint_checks=run_constraint_checks,
|
|
529
527
|
)
|
|
530
528
|
return updated_obj, mutation, False
|
|
531
529
|
|
|
@@ -545,7 +543,7 @@ class InfrahubMutationMixin:
|
|
|
545
543
|
db=db,
|
|
546
544
|
branch=branch,
|
|
547
545
|
obj=node,
|
|
548
|
-
|
|
546
|
+
skip_uniqueness_check=True,
|
|
549
547
|
)
|
|
550
548
|
return updated_obj, mutation, False
|
|
551
549
|
|
infrahub/menu/repository.py
CHANGED
|
@@ -20,9 +20,9 @@ class MenuRepository:
|
|
|
20
20
|
async def add_children(menu_item: MenuItemDict, menu_node: CoreMenuItem) -> MenuItemDict:
|
|
21
21
|
children = await menu_node.children.get_peers(db=self.db, peer_type=CoreMenuItem)
|
|
22
22
|
for child_id, child_node in children.items():
|
|
23
|
-
child_menu_item
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
if child_menu_item := menu_by_ids.get(child_id):
|
|
24
|
+
child = await add_children(child_menu_item, child_node)
|
|
25
|
+
menu_item.children[str(child.identifier)] = child
|
|
26
26
|
return menu_item
|
|
27
27
|
|
|
28
28
|
for menu_node in nodes.values():
|
|
@@ -33,9 +33,9 @@ class MenuRepository:
|
|
|
33
33
|
|
|
34
34
|
children = await menu_node.children.get_peers(db=self.db, peer_type=CoreMenuItem)
|
|
35
35
|
for child_id, child_node in children.items():
|
|
36
|
-
child_menu_item
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
if child_menu_item := menu_by_ids.get(child_id):
|
|
37
|
+
child = await add_children(child_menu_item, child_node)
|
|
38
|
+
menu_item.children[str(child.identifier)] = child
|
|
39
39
|
|
|
40
40
|
menu.data[str(menu_item.identifier)] = menu_item
|
|
41
41
|
|
|
@@ -2,7 +2,6 @@ from infrahub.message_bus import InfrahubMessage, InfrahubResponse
|
|
|
2
2
|
|
|
3
3
|
from .check_generator_run import CheckGeneratorRun
|
|
4
4
|
from .event_branch_merge import EventBranchMerge
|
|
5
|
-
from .event_worker_newprimaryapi import EventWorkerNewPrimaryAPI
|
|
6
5
|
from .finalize_validator_execution import FinalizeValidatorExecution
|
|
7
6
|
from .git_file_get import GitFileGet, GitFileGetResponse
|
|
8
7
|
from .git_repository_connectivity import GitRepositoryConnectivity
|
|
@@ -17,7 +16,6 @@ from .send_echo_request import SendEchoRequest, SendEchoRequestResponse
|
|
|
17
16
|
MESSAGE_MAP: dict[str, type[InfrahubMessage]] = {
|
|
18
17
|
"check.generator.run": CheckGeneratorRun,
|
|
19
18
|
"event.branch.merge": EventBranchMerge,
|
|
20
|
-
"event.worker.new_primary_api": EventWorkerNewPrimaryAPI,
|
|
21
19
|
"finalize.validator.execution": FinalizeValidatorExecution,
|
|
22
20
|
"git.file.get": GitFileGet,
|
|
23
21
|
"git.repository.connectivity": GitRepositoryConnectivity,
|
|
@@ -18,7 +18,6 @@ from infrahub.tasks.check import set_check_status
|
|
|
18
18
|
COMMAND_MAP = {
|
|
19
19
|
"check.generator.run": check.generator.run,
|
|
20
20
|
"event.branch.merge": event.branch.merge,
|
|
21
|
-
"event.worker.new_primary_api": event.worker.new_primary_api,
|
|
22
21
|
"finalize.validator.execution": finalize.validator.execution,
|
|
23
22
|
"git.file.get": git.file.get,
|
|
24
23
|
"git.repository.connectivity": git.repository.connectivity,
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
from . import branch
|
|
1
|
+
from . import branch
|
|
2
2
|
|
|
3
|
-
__all__ = ["branch"
|
|
3
|
+
__all__ = ["branch"]
|
infrahub/server.py
CHANGED
|
@@ -33,10 +33,8 @@ from infrahub.lock import initialize_lock
|
|
|
33
33
|
from infrahub.log import clear_log_context, get_logger, set_log_data
|
|
34
34
|
from infrahub.middleware import InfrahubCORSMiddleware
|
|
35
35
|
from infrahub.services import InfrahubServices
|
|
36
|
-
from infrahub.services.adapters.cache
|
|
37
|
-
from infrahub.services.adapters.
|
|
38
|
-
from infrahub.services.adapters.message_bus.nats import NATSMessageBus
|
|
39
|
-
from infrahub.services.adapters.message_bus.rabbitmq import RabbitMQMessageBus
|
|
36
|
+
from infrahub.services.adapters.cache import InfrahubCache
|
|
37
|
+
from infrahub.services.adapters.message_bus import InfrahubMessageBus
|
|
40
38
|
from infrahub.services.adapters.workflow.local import WorkflowLocalExecution
|
|
41
39
|
from infrahub.services.adapters.workflow.worker import WorkflowWorkerExecution
|
|
42
40
|
from infrahub.trace import add_span_exception, configure_trace, get_traceid
|
|
@@ -70,14 +68,11 @@ async def app_initialization(application: FastAPI, enable_scheduler: bool = True
|
|
|
70
68
|
else WorkflowLocalExecution()
|
|
71
69
|
)
|
|
72
70
|
component_type = ComponentType.API_SERVER
|
|
73
|
-
message_bus = config.OVERRIDE.message_bus or (
|
|
74
|
-
|
|
75
|
-
if config.SETTINGS.broker.driver == config.BrokerDriver.NATS
|
|
76
|
-
else await RabbitMQMessageBus.new(component_type=component_type)
|
|
77
|
-
)
|
|
78
|
-
cache = config.OVERRIDE.cache or (
|
|
79
|
-
await NATSCache.new() if config.SETTINGS.cache.driver == config.CacheDriver.NATS else RedisCache()
|
|
71
|
+
message_bus = config.OVERRIDE.message_bus or await InfrahubMessageBus.new_from_driver(
|
|
72
|
+
component_type=component_type, driver=config.SETTINGS.broker.driver
|
|
80
73
|
)
|
|
74
|
+
|
|
75
|
+
cache = config.OVERRIDE.cache or (await InfrahubCache.new_from_driver(driver=config.SETTINGS.cache.driver))
|
|
81
76
|
service = await InfrahubServices.new(
|
|
82
77
|
cache=cache,
|
|
83
78
|
database=database,
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import importlib
|
|
3
4
|
from abc import ABC, abstractmethod
|
|
4
5
|
from typing import TYPE_CHECKING
|
|
5
6
|
|
|
6
7
|
if TYPE_CHECKING:
|
|
8
|
+
from infrahub.config import CacheDriver
|
|
7
9
|
from infrahub.message_bus.types import KVTTL
|
|
8
10
|
|
|
9
11
|
|
|
@@ -34,3 +36,18 @@ class InfrahubCache(ABC):
|
|
|
34
36
|
async def set(self, key: str, value: str, expires: KVTTL | None = None, not_exists: bool = False) -> bool | None:
|
|
35
37
|
"""Set a value in the cache."""
|
|
36
38
|
raise NotImplementedError()
|
|
39
|
+
|
|
40
|
+
@classmethod
|
|
41
|
+
async def new_from_driver(cls, driver: CacheDriver) -> InfrahubCache:
|
|
42
|
+
"""Imports and initializes the correct class based on the supplied driver.
|
|
43
|
+
|
|
44
|
+
This is to ensure that we only import the Python modules that we actually
|
|
45
|
+
need to operate and not import all possible options.
|
|
46
|
+
"""
|
|
47
|
+
module = importlib.import_module(driver.driver_module_path)
|
|
48
|
+
broker_driver: InfrahubCache = getattr(module, driver.driver_class_name)
|
|
49
|
+
return await broker_driver.new()
|
|
50
|
+
|
|
51
|
+
@classmethod
|
|
52
|
+
async def new(cls) -> InfrahubCache:
|
|
53
|
+
raise NotImplementedError()
|
|
@@ -1,9 +1,15 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
1
5
|
import redis.asyncio as redis
|
|
2
6
|
|
|
3
7
|
from infrahub import config
|
|
4
|
-
from infrahub.message_bus.types import KVTTL
|
|
5
8
|
from infrahub.services.adapters.cache import InfrahubCache
|
|
6
9
|
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from infrahub.message_bus.types import KVTTL
|
|
12
|
+
|
|
7
13
|
|
|
8
14
|
class RedisCache(InfrahubCache):
|
|
9
15
|
def __init__(self) -> None:
|
|
@@ -44,3 +50,7 @@ class RedisCache(InfrahubCache):
|
|
|
44
50
|
|
|
45
51
|
async def set(self, key: str, value: str, expires: KVTTL | None = None, not_exists: bool = False) -> bool | None:
|
|
46
52
|
return await self.connection.set(name=key, value=value, ex=expires.value if expires else None, nx=not_exists)
|
|
53
|
+
|
|
54
|
+
@classmethod
|
|
55
|
+
async def new(cls) -> RedisCache:
|
|
56
|
+
return cls()
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import importlib
|
|
3
4
|
from abc import ABC, abstractmethod
|
|
4
5
|
from typing import TYPE_CHECKING, TypeVar
|
|
5
6
|
|
|
@@ -8,6 +9,8 @@ from infrahub.message_bus.messages import ROUTING_KEY_MAP
|
|
|
8
9
|
ResponseClass = TypeVar("ResponseClass")
|
|
9
10
|
|
|
10
11
|
if TYPE_CHECKING:
|
|
12
|
+
from infrahub.components import ComponentType
|
|
13
|
+
from infrahub.config import BrokerDriver, BrokerSettings
|
|
11
14
|
from infrahub.message_bus import InfrahubMessage, InfrahubResponse
|
|
12
15
|
from infrahub.message_bus.types import MessageTTL
|
|
13
16
|
from infrahub.services import InfrahubServices
|
|
@@ -34,6 +37,23 @@ class InfrahubMessageBus(ABC):
|
|
|
34
37
|
async def shutdown(self) -> None: # noqa: B027 We want a default empty behavior, so it's ok to have an empty non-abstract method.
|
|
35
38
|
"""Shutdown the Message bus"""
|
|
36
39
|
|
|
40
|
+
@classmethod
|
|
41
|
+
async def new(cls, component_type: ComponentType, settings: BrokerSettings | None = None) -> InfrahubMessageBus:
|
|
42
|
+
raise NotImplementedError()
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
async def new_from_driver(
|
|
46
|
+
cls, component_type: ComponentType, driver: BrokerDriver, settings: BrokerSettings | None = None
|
|
47
|
+
) -> InfrahubMessageBus:
|
|
48
|
+
"""Imports and initializes the correct class based on the supplied driver.
|
|
49
|
+
|
|
50
|
+
This is to ensure that we only import the Python modules that we actually
|
|
51
|
+
need to operate and not import all possible options.
|
|
52
|
+
"""
|
|
53
|
+
module = importlib.import_module(driver.driver_module_path)
|
|
54
|
+
broker_driver: InfrahubMessageBus = getattr(module, driver.driver_class_name)
|
|
55
|
+
return await broker_driver.new(component_type=component_type, settings=settings)
|
|
56
|
+
|
|
37
57
|
@abstractmethod
|
|
38
58
|
async def publish(
|
|
39
59
|
self, message: InfrahubMessage, routing_key: str, delay: MessageTTL | None = None, is_retry: bool = False
|
infrahub/services/component.py
CHANGED
|
@@ -10,7 +10,6 @@ from infrahub.core.constants import GLOBAL_BRANCH_NAME
|
|
|
10
10
|
from infrahub.core.registry import registry
|
|
11
11
|
from infrahub.core.timestamp import Timestamp
|
|
12
12
|
from infrahub.log import get_logger
|
|
13
|
-
from infrahub.message_bus import messages
|
|
14
13
|
from infrahub.message_bus.types import KVTTL
|
|
15
14
|
from infrahub.worker import WORKER_IDENTITY
|
|
16
15
|
|
|
@@ -116,7 +115,7 @@ class InfrahubComponent:
|
|
|
116
115
|
key=PRIMARY_API_SERVER, value=WORKER_IDENTITY, expires=KVTTL.FIFTEEN, not_exists=True
|
|
117
116
|
)
|
|
118
117
|
if result:
|
|
119
|
-
|
|
118
|
+
log.info("api_worker promoted to primary", worker_id=WORKER_IDENTITY)
|
|
120
119
|
else:
|
|
121
120
|
log.debug("Primary node already set")
|
|
122
121
|
primary_id = await self.cache.get(key=PRIMARY_API_SERVER)
|
infrahub/tasks/registry.py
CHANGED
|
@@ -22,7 +22,6 @@ async def refresh_branches(db: InfrahubDatabase) -> None:
|
|
|
22
22
|
|
|
23
23
|
async with lock.registry.local_schema_lock():
|
|
24
24
|
branches = await registry.branch_object.get_list(db=db)
|
|
25
|
-
active_branches = [branch.name for branch in branches]
|
|
26
25
|
for new_branch in branches:
|
|
27
26
|
if new_branch.name in registry.branch:
|
|
28
27
|
branch_registry: Branch = registry.branch[new_branch.name]
|
|
@@ -61,9 +60,6 @@ async def refresh_branches(db: InfrahubDatabase) -> None:
|
|
|
61
60
|
include_types=True,
|
|
62
61
|
)
|
|
63
62
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
log.info(
|
|
68
|
-
f"Removed branch {branch_name!r} from the registry", branch=branch_name, worker=WORKER_IDENTITY
|
|
69
|
-
)
|
|
63
|
+
purged_branches = await registry.purge_inactive_branches(db=db, active_branches=branches)
|
|
64
|
+
for branch_name in purged_branches:
|
|
65
|
+
log.info(f"Removed branch {branch_name!r} from the registry", branch=branch_name, worker=WORKER_IDENTITY)
|
|
@@ -24,11 +24,7 @@ from infrahub.git import initialize_repositories_directory
|
|
|
24
24
|
from infrahub.lock import initialize_lock
|
|
25
25
|
from infrahub.services import InfrahubServices
|
|
26
26
|
from infrahub.services.adapters.cache import InfrahubCache
|
|
27
|
-
from infrahub.services.adapters.cache.nats import NATSCache
|
|
28
|
-
from infrahub.services.adapters.cache.redis import RedisCache
|
|
29
27
|
from infrahub.services.adapters.message_bus import InfrahubMessageBus
|
|
30
|
-
from infrahub.services.adapters.message_bus.nats import NATSMessageBus
|
|
31
|
-
from infrahub.services.adapters.message_bus.rabbitmq import RabbitMQMessageBus
|
|
32
28
|
from infrahub.services.adapters.workflow import InfrahubWorkflow
|
|
33
29
|
from infrahub.services.adapters.workflow.local import WorkflowLocalExecution
|
|
34
30
|
from infrahub.services.adapters.workflow.worker import WorkflowWorkerExecution
|
|
@@ -198,15 +194,13 @@ class InfrahubWorkerAsync(BaseWorker):
|
|
|
198
194
|
|
|
199
195
|
async def _init_message_bus(self, component_type: ComponentType) -> InfrahubMessageBus:
|
|
200
196
|
return config.OVERRIDE.message_bus or (
|
|
201
|
-
await
|
|
202
|
-
|
|
203
|
-
|
|
197
|
+
await InfrahubMessageBus.new_from_driver(
|
|
198
|
+
component_type=component_type, driver=config.SETTINGS.broker.driver
|
|
199
|
+
)
|
|
204
200
|
)
|
|
205
201
|
|
|
206
202
|
async def _init_cache(self) -> InfrahubCache:
|
|
207
|
-
return config.OVERRIDE.cache or (
|
|
208
|
-
await NATSCache.new() if config.SETTINGS.cache.driver == config.CacheDriver.NATS else RedisCache()
|
|
209
|
-
)
|
|
203
|
+
return config.OVERRIDE.cache or (await InfrahubCache.new_from_driver(driver=config.SETTINGS.cache.driver))
|
|
210
204
|
|
|
211
205
|
async def _init_services(self, client: InfrahubClient) -> None:
|
|
212
206
|
component_type = ComponentType.GIT_AGENT
|
infrahub_sdk/client.py
CHANGED
|
@@ -281,7 +281,7 @@ class InfrahubClient(BaseClient):
|
|
|
281
281
|
self.schema = InfrahubSchema(self)
|
|
282
282
|
self.branch = InfrahubBranchManager(self)
|
|
283
283
|
self.object_store = ObjectStore(self)
|
|
284
|
-
self.store = NodeStore()
|
|
284
|
+
self.store = NodeStore(default_branch=self.default_branch)
|
|
285
285
|
self.task = InfrahubTaskManager(self)
|
|
286
286
|
self.concurrent_execution_limit = asyncio.Semaphore(self.max_concurrent_execution)
|
|
287
287
|
self._request_method: AsyncRequester = self.config.requester or self._default_request_method
|
|
@@ -840,11 +840,11 @@ class InfrahubClient(BaseClient):
|
|
|
840
840
|
if populate_store:
|
|
841
841
|
for node in nodes:
|
|
842
842
|
if node.id:
|
|
843
|
-
self.store.set(
|
|
843
|
+
self.store.set(node=node)
|
|
844
844
|
related_nodes = list(set(related_nodes))
|
|
845
845
|
for node in related_nodes:
|
|
846
846
|
if node.id:
|
|
847
|
-
self.store.set(
|
|
847
|
+
self.store.set(node=node)
|
|
848
848
|
return nodes
|
|
849
849
|
|
|
850
850
|
def clone(self) -> InfrahubClient:
|
|
@@ -1529,7 +1529,7 @@ class InfrahubClientSync(BaseClient):
|
|
|
1529
1529
|
self.schema = InfrahubSchemaSync(self)
|
|
1530
1530
|
self.branch = InfrahubBranchManagerSync(self)
|
|
1531
1531
|
self.object_store = ObjectStoreSync(self)
|
|
1532
|
-
self.store = NodeStoreSync()
|
|
1532
|
+
self.store = NodeStoreSync(default_branch=self.default_branch)
|
|
1533
1533
|
self.task = InfrahubTaskManagerSync(self)
|
|
1534
1534
|
self._request_method: SyncRequester = self.config.sync_requester or self._default_request_method
|
|
1535
1535
|
self.group_context = InfrahubGroupContextSync(self)
|
|
@@ -1997,11 +1997,11 @@ class InfrahubClientSync(BaseClient):
|
|
|
1997
1997
|
if populate_store:
|
|
1998
1998
|
for node in nodes:
|
|
1999
1999
|
if node.id:
|
|
2000
|
-
self.store.set(
|
|
2000
|
+
self.store.set(node=node)
|
|
2001
2001
|
related_nodes = list(set(related_nodes))
|
|
2002
2002
|
for node in related_nodes:
|
|
2003
2003
|
if node.id:
|
|
2004
|
-
self.store.set(
|
|
2004
|
+
self.store.set(node=node)
|
|
2005
2005
|
return nodes
|
|
2006
2006
|
|
|
2007
2007
|
@overload
|