infrahub-server 1.3.5__py3-none-any.whl → 1.4.0b0__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/internal.py +5 -0
- infrahub/artifacts/tasks.py +17 -22
- infrahub/branch/merge_mutation_checker.py +38 -0
- infrahub/cli/__init__.py +2 -2
- infrahub/cli/context.py +7 -3
- infrahub/cli/db.py +5 -16
- infrahub/cli/upgrade.py +7 -29
- infrahub/computed_attribute/tasks.py +36 -46
- infrahub/config.py +53 -2
- infrahub/constants/environment.py +1 -0
- infrahub/core/attribute.py +9 -7
- infrahub/core/branch/tasks.py +43 -41
- infrahub/core/constants/__init__.py +20 -6
- infrahub/core/constants/infrahubkind.py +2 -0
- infrahub/core/diff/coordinator.py +3 -1
- infrahub/core/diff/repository/repository.py +0 -8
- infrahub/core/diff/tasks.py +11 -8
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/graph/index.py +1 -2
- infrahub/core/graph/schema.py +50 -29
- infrahub/core/initialization.py +62 -33
- infrahub/core/ipam/tasks.py +4 -3
- infrahub/core/merge.py +8 -10
- infrahub/core/migrations/graph/__init__.py +2 -0
- infrahub/core/migrations/graph/m035_drop_attr_value_index.py +45 -0
- infrahub/core/migrations/query/attribute_add.py +27 -2
- infrahub/core/migrations/schema/tasks.py +6 -5
- infrahub/core/node/proposed_change.py +43 -0
- infrahub/core/protocols.py +12 -0
- infrahub/core/query/attribute.py +32 -14
- infrahub/core/query/diff.py +11 -0
- infrahub/core/query/ipam.py +13 -7
- infrahub/core/query/node.py +51 -10
- infrahub/core/query/resource_manager.py +3 -3
- infrahub/core/schema/basenode_schema.py +8 -0
- infrahub/core/schema/definitions/core/__init__.py +10 -1
- infrahub/core/schema/definitions/core/ipam.py +28 -2
- infrahub/core/schema/definitions/core/propose_change.py +15 -0
- infrahub/core/schema/definitions/core/webhook.py +3 -0
- infrahub/core/schema/generic_schema.py +10 -0
- infrahub/core/schema/manager.py +10 -1
- infrahub/core/schema/node_schema.py +22 -17
- infrahub/core/schema/profile_schema.py +8 -0
- infrahub/core/schema/schema_branch.py +9 -5
- infrahub/core/schema/template_schema.py +8 -0
- infrahub/core/validators/checks_runner.py +5 -5
- infrahub/core/validators/tasks.py +6 -7
- infrahub/core/validators/uniqueness/checker.py +4 -2
- infrahub/core/validators/uniqueness/model.py +1 -0
- infrahub/core/validators/uniqueness/query.py +57 -7
- infrahub/database/__init__.py +2 -1
- infrahub/events/__init__.py +18 -0
- infrahub/events/constants.py +7 -0
- infrahub/events/generator.py +29 -2
- infrahub/events/proposed_change_action.py +181 -0
- infrahub/generators/tasks.py +24 -20
- infrahub/git/base.py +4 -7
- infrahub/git/integrator.py +21 -12
- infrahub/git/repository.py +15 -30
- infrahub/git/tasks.py +121 -106
- infrahub/graphql/field_extractor.py +69 -0
- infrahub/graphql/manager.py +15 -11
- infrahub/graphql/mutations/account.py +2 -2
- infrahub/graphql/mutations/action.py +8 -2
- infrahub/graphql/mutations/artifact_definition.py +4 -1
- infrahub/graphql/mutations/branch.py +10 -5
- infrahub/graphql/mutations/graphql_query.py +2 -1
- infrahub/graphql/mutations/main.py +14 -8
- infrahub/graphql/mutations/menu.py +2 -1
- infrahub/graphql/mutations/proposed_change.py +225 -8
- infrahub/graphql/mutations/relationship.py +5 -0
- infrahub/graphql/mutations/repository.py +2 -1
- infrahub/graphql/mutations/tasks.py +7 -9
- infrahub/graphql/mutations/webhook.py +4 -1
- infrahub/graphql/parser.py +15 -6
- infrahub/graphql/queries/__init__.py +10 -1
- infrahub/graphql/queries/account.py +3 -3
- infrahub/graphql/queries/branch.py +2 -2
- infrahub/graphql/queries/diff/tree.py +3 -3
- infrahub/graphql/queries/event.py +13 -3
- infrahub/graphql/queries/ipam.py +23 -1
- infrahub/graphql/queries/proposed_change.py +84 -0
- infrahub/graphql/queries/relationship.py +2 -2
- infrahub/graphql/queries/resource_manager.py +3 -3
- infrahub/graphql/queries/search.py +3 -2
- infrahub/graphql/queries/status.py +3 -2
- infrahub/graphql/queries/task.py +2 -2
- infrahub/graphql/resolvers/ipam.py +440 -0
- infrahub/graphql/resolvers/many_relationship.py +4 -3
- infrahub/graphql/resolvers/resolver.py +5 -5
- infrahub/graphql/resolvers/single_relationship.py +3 -2
- infrahub/graphql/schema.py +25 -5
- infrahub/graphql/types/__init__.py +2 -2
- infrahub/graphql/types/attribute.py +3 -3
- infrahub/graphql/types/event.py +60 -0
- infrahub/groups/tasks.py +6 -6
- infrahub/lock.py +3 -2
- infrahub/menu/generator.py +8 -0
- infrahub/message_bus/operations/__init__.py +9 -12
- infrahub/message_bus/operations/git/file.py +6 -5
- infrahub/message_bus/operations/git/repository.py +12 -20
- infrahub/message_bus/operations/refresh/registry.py +15 -9
- infrahub/message_bus/operations/send/echo.py +7 -4
- infrahub/message_bus/types.py +1 -0
- infrahub/permissions/globals.py +1 -4
- infrahub/permissions/manager.py +8 -5
- infrahub/pools/prefix.py +7 -5
- infrahub/prefect_server/app.py +31 -0
- infrahub/prefect_server/bootstrap.py +18 -0
- infrahub/proposed_change/action_checker.py +206 -0
- infrahub/proposed_change/approval_revoker.py +40 -0
- infrahub/proposed_change/branch_diff.py +3 -1
- infrahub/proposed_change/checker.py +45 -0
- infrahub/proposed_change/constants.py +32 -2
- infrahub/proposed_change/tasks.py +182 -150
- infrahub/py.typed +0 -0
- infrahub/server.py +29 -17
- infrahub/services/__init__.py +13 -28
- infrahub/services/adapters/cache/__init__.py +4 -0
- infrahub/services/adapters/cache/nats.py +2 -0
- infrahub/services/adapters/cache/redis.py +3 -0
- infrahub/services/adapters/message_bus/__init__.py +0 -2
- infrahub/services/adapters/message_bus/local.py +1 -2
- infrahub/services/adapters/message_bus/nats.py +6 -8
- infrahub/services/adapters/message_bus/rabbitmq.py +7 -9
- infrahub/services/adapters/workflow/__init__.py +1 -0
- infrahub/services/adapters/workflow/local.py +1 -8
- infrahub/services/component.py +2 -1
- infrahub/task_manager/event.py +52 -0
- infrahub/task_manager/models.py +9 -0
- infrahub/tasks/artifact.py +6 -7
- infrahub/tasks/check.py +4 -7
- infrahub/telemetry/tasks.py +15 -18
- infrahub/transformations/tasks.py +10 -6
- infrahub/trigger/tasks.py +4 -3
- infrahub/types.py +4 -0
- infrahub/validators/events.py +7 -7
- infrahub/validators/tasks.py +6 -7
- infrahub/webhook/models.py +45 -45
- infrahub/webhook/tasks.py +25 -24
- infrahub/workers/dependencies.py +143 -0
- infrahub/workers/infrahub_async.py +19 -43
- infrahub/workflows/catalogue.py +16 -2
- infrahub/workflows/initialization.py +5 -4
- infrahub/workflows/models.py +2 -0
- infrahub_sdk/client.py +6 -6
- infrahub_sdk/ctl/repository.py +51 -0
- infrahub_sdk/ctl/schema.py +9 -9
- infrahub_sdk/protocols.py +40 -6
- {infrahub_server-1.3.5.dist-info → infrahub_server-1.4.0b0.dist-info}/METADATA +5 -4
- {infrahub_server-1.3.5.dist-info → infrahub_server-1.4.0b0.dist-info}/RECORD +158 -144
- infrahub_testcontainers/container.py +17 -0
- infrahub_testcontainers/docker-compose-cluster.test.yml +56 -1
- infrahub_testcontainers/docker-compose.test.yml +56 -1
- infrahub_testcontainers/helpers.py +4 -1
- {infrahub_server-1.3.5.dist-info → infrahub_server-1.4.0b0.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.3.5.dist-info → infrahub_server-1.4.0b0.dist-info}/WHEEL +0 -0
- {infrahub_server-1.3.5.dist-info → infrahub_server-1.4.0b0.dist-info}/entry_points.txt +0 -0
|
@@ -3,12 +3,12 @@ from __future__ import annotations
|
|
|
3
3
|
from typing import TYPE_CHECKING, Any
|
|
4
4
|
|
|
5
5
|
from graphql.type.definition import GraphQLNonNull
|
|
6
|
-
from infrahub_sdk.utils import extract_fields
|
|
7
6
|
from opentelemetry import trace
|
|
8
7
|
|
|
9
8
|
from infrahub.core.constants import BranchSupportType, InfrahubKind, RelationshipHierarchyDirection
|
|
10
9
|
from infrahub.core.manager import NodeManager
|
|
11
10
|
from infrahub.exceptions import NodeNotFoundError
|
|
11
|
+
from infrahub.graphql.field_extractor import extract_graphql_fields
|
|
12
12
|
|
|
13
13
|
from ..models import OrderModel
|
|
14
14
|
from ..parser import extract_selection
|
|
@@ -26,7 +26,7 @@ async def account_resolver(
|
|
|
26
26
|
root: dict, # noqa: ARG001
|
|
27
27
|
info: GraphQLResolveInfo,
|
|
28
28
|
) -> dict:
|
|
29
|
-
fields =
|
|
29
|
+
fields = extract_graphql_fields(info=info)
|
|
30
30
|
graphql_context: GraphqlContext = info.context
|
|
31
31
|
|
|
32
32
|
async with graphql_context.db.start_session(read_only=True) as db:
|
|
@@ -90,7 +90,7 @@ async def default_resolver(*args: Any, **kwargs) -> dict | list[dict] | None:
|
|
|
90
90
|
graphql_context: GraphqlContext = info.context
|
|
91
91
|
|
|
92
92
|
# Extract the name of the fields in the GQL query
|
|
93
|
-
fields =
|
|
93
|
+
fields = extract_graphql_fields(info=info)
|
|
94
94
|
|
|
95
95
|
# Extract the schema of the node on the other end of the relationship from the GQL Schema
|
|
96
96
|
node_rel = node_schema.get_relationship(info.field_name)
|
|
@@ -155,7 +155,7 @@ async def default_paginated_list_resolver(
|
|
|
155
155
|
else info.return_type.graphene_type._meta.schema
|
|
156
156
|
)
|
|
157
157
|
|
|
158
|
-
fields = await extract_selection(info
|
|
158
|
+
fields = await extract_selection(info=info, schema=schema)
|
|
159
159
|
|
|
160
160
|
graphql_context: GraphqlContext = info.context
|
|
161
161
|
async with graphql_context.db.start_session(read_only=True) as db:
|
|
@@ -277,7 +277,7 @@ async def hierarchy_resolver(
|
|
|
277
277
|
graphql_context: GraphqlContext = info.context
|
|
278
278
|
|
|
279
279
|
# Extract the name of the fields in the GQL query
|
|
280
|
-
fields =
|
|
280
|
+
fields = extract_graphql_fields(info=info)
|
|
281
281
|
edges = fields.get("edges", {})
|
|
282
282
|
node_fields = edges.get("node", {})
|
|
283
283
|
|
|
@@ -2,7 +2,7 @@ from typing import TYPE_CHECKING, Any
|
|
|
2
2
|
|
|
3
3
|
from graphql import GraphQLResolveInfo
|
|
4
4
|
from graphql.type.definition import GraphQLNonNull
|
|
5
|
-
from infrahub_sdk.utils import deep_merge_dict
|
|
5
|
+
from infrahub_sdk.utils import deep_merge_dict
|
|
6
6
|
|
|
7
7
|
from infrahub.core.branch.models import Branch
|
|
8
8
|
from infrahub.core.constants import BranchSupportType
|
|
@@ -10,6 +10,7 @@ from infrahub.core.manager import NodeManager
|
|
|
10
10
|
from infrahub.core.schema.relationship_schema import RelationshipSchema
|
|
11
11
|
from infrahub.core.timestamp import Timestamp
|
|
12
12
|
from infrahub.database import InfrahubDatabase
|
|
13
|
+
from infrahub.graphql.field_extractor import extract_graphql_fields
|
|
13
14
|
|
|
14
15
|
from ..loaders.node import GetManyParams, NodeDataLoader
|
|
15
16
|
from ..types import RELATIONS_PROPERTY_MAP, RELATIONS_PROPERTY_MAP_REVERSED
|
|
@@ -42,7 +43,7 @@ class SingleRelationshipResolver:
|
|
|
42
43
|
graphql_context: GraphqlContext = info.context
|
|
43
44
|
|
|
44
45
|
# Extract the name of the fields in the GQL query
|
|
45
|
-
fields =
|
|
46
|
+
fields = extract_graphql_fields(info=info)
|
|
46
47
|
node_fields = fields.get("node", {})
|
|
47
48
|
property_fields = fields.get("properties", {})
|
|
48
49
|
for key, value in property_fields.items():
|
infrahub/graphql/schema.py
CHANGED
|
@@ -20,7 +20,12 @@ from .mutations.convert_object_type import ConvertObjectType
|
|
|
20
20
|
from .mutations.diff import DiffUpdateMutation
|
|
21
21
|
from .mutations.diff_conflict import ResolveDiffConflict
|
|
22
22
|
from .mutations.generator import GeneratorDefinitionRequestRun
|
|
23
|
-
from .mutations.proposed_change import
|
|
23
|
+
from .mutations.proposed_change import (
|
|
24
|
+
ProposedChangeCheckForApprovalRevoke,
|
|
25
|
+
ProposedChangeMerge,
|
|
26
|
+
ProposedChangeRequestRunCheck,
|
|
27
|
+
ProposedChangeReview,
|
|
28
|
+
)
|
|
24
29
|
from .mutations.relationship import (
|
|
25
30
|
RelationshipAdd,
|
|
26
31
|
RelationshipRemove,
|
|
@@ -40,6 +45,8 @@ from .queries import (
|
|
|
40
45
|
AccountPermissions,
|
|
41
46
|
AccountToken,
|
|
42
47
|
BranchQueryList,
|
|
48
|
+
DeprecatedIPAddressGetNextAvailable,
|
|
49
|
+
DeprecatedIPPrefixGetNextAvailable,
|
|
43
50
|
InfrahubInfo,
|
|
44
51
|
InfrahubIPAddressGetNextAvailable,
|
|
45
52
|
InfrahubIPPrefixGetNextAvailable,
|
|
@@ -47,6 +54,7 @@ from .queries import (
|
|
|
47
54
|
InfrahubResourcePoolUtilization,
|
|
48
55
|
InfrahubSearchAnywhere,
|
|
49
56
|
InfrahubStatus,
|
|
57
|
+
ProposedChangeAvailableActions,
|
|
50
58
|
Relationship,
|
|
51
59
|
)
|
|
52
60
|
from .queries.convert_object_type_mapping import FieldsMappingTypeConversion
|
|
@@ -74,8 +82,12 @@ class InfrahubBaseQuery(ObjectType):
|
|
|
74
82
|
InfrahubEvent = Event
|
|
75
83
|
InfrahubTaskBranchStatus = TaskBranchStatus
|
|
76
84
|
|
|
77
|
-
|
|
78
|
-
|
|
85
|
+
CoreProposedChangeAvailableActions = ProposedChangeAvailableActions
|
|
86
|
+
|
|
87
|
+
IPAddressGetNextAvailable = DeprecatedIPAddressGetNextAvailable
|
|
88
|
+
IPPrefixGetNextAvailable = DeprecatedIPPrefixGetNextAvailable
|
|
89
|
+
InfrahubIPAddressGetNextAvailable = InfrahubIPAddressGetNextAvailable
|
|
90
|
+
InfrahubIPPrefixGetNextAvailable = InfrahubIPPrefixGetNextAvailable
|
|
79
91
|
InfrahubResourcePoolAllocated = InfrahubResourcePoolAllocated
|
|
80
92
|
InfrahubResourcePoolUtilization = InfrahubResourcePoolUtilization
|
|
81
93
|
|
|
@@ -88,10 +100,17 @@ class InfrahubBaseMutation(ObjectType):
|
|
|
88
100
|
InfrahubAccountTokenDelete = InfrahubAccountTokenDelete.Field()
|
|
89
101
|
CoreProposedChangeRunCheck = ProposedChangeRequestRunCheck.Field()
|
|
90
102
|
CoreProposedChangeMerge = ProposedChangeMerge.Field()
|
|
103
|
+
CoreProposedChangeReview = ProposedChangeReview.Field()
|
|
91
104
|
CoreGeneratorDefinitionRun = GeneratorDefinitionRequestRun.Field()
|
|
92
105
|
|
|
93
|
-
|
|
94
|
-
|
|
106
|
+
InfrahubIPPrefixPoolGetResource = IPPrefixPoolGetResource.Field()
|
|
107
|
+
InfrahubIPAddressPoolGetResource = IPAddressPoolGetResource.Field()
|
|
108
|
+
IPPrefixPoolGetResource = IPPrefixPoolGetResource.Field(
|
|
109
|
+
deprecation_reason="This mutation has been renamed to 'InfrahubIPPrefixPoolGetResource'. It will be removed in the next version of Infrahub."
|
|
110
|
+
)
|
|
111
|
+
IPAddressPoolGetResource = IPAddressPoolGetResource.Field(
|
|
112
|
+
deprecation_reason="This mutation has been renamed to 'InfrahubIPAddressPoolGetResource'. It will be removed in the next version of Infrahub."
|
|
113
|
+
)
|
|
95
114
|
|
|
96
115
|
BranchCreate = BranchCreate.Field()
|
|
97
116
|
BranchDelete = BranchDelete.Field()
|
|
@@ -115,3 +134,4 @@ class InfrahubBaseMutation(ObjectType):
|
|
|
115
134
|
ResolveDiffConflict = ResolveDiffConflict.Field()
|
|
116
135
|
|
|
117
136
|
ConvertObjectType = ConvertObjectType.Field()
|
|
137
|
+
CoreProposedChangeCheckForApprovalRevoke = ProposedChangeCheckForApprovalRevoke.Field()
|
|
@@ -16,8 +16,8 @@ from .attribute import (
|
|
|
16
16
|
MacAddressType,
|
|
17
17
|
NumberAttributeType,
|
|
18
18
|
RelatedIPAddressNodeInput,
|
|
19
|
+
RelatedIPPrefixNodeInput,
|
|
19
20
|
RelatedNodeInput,
|
|
20
|
-
RelatedPrefixNodeInput,
|
|
21
21
|
StrAttributeType,
|
|
22
22
|
TextAttributeType,
|
|
23
23
|
)
|
|
@@ -51,8 +51,8 @@ __all__ = [
|
|
|
51
51
|
"NumberAttributeType",
|
|
52
52
|
"PaginatedObjectPermission",
|
|
53
53
|
"RelatedIPAddressNodeInput",
|
|
54
|
+
"RelatedIPPrefixNodeInput",
|
|
54
55
|
"RelatedNodeInput",
|
|
55
|
-
"RelatedPrefixNodeInput",
|
|
56
56
|
"RelationshipNode",
|
|
57
57
|
"StrAttributeType",
|
|
58
58
|
"TaskLog",
|
|
@@ -32,7 +32,7 @@ class IPAddressPoolInput(GenericPoolInput):
|
|
|
32
32
|
prefixlen = Int(required=False)
|
|
33
33
|
|
|
34
34
|
|
|
35
|
-
class
|
|
35
|
+
class IPPrefixPoolInput(GenericPoolInput):
|
|
36
36
|
size = Int(required=False)
|
|
37
37
|
member_type = String(required=False)
|
|
38
38
|
prefix_type = String(required=False)
|
|
@@ -47,10 +47,10 @@ class RelatedIPAddressNodeInput(InputObjectType):
|
|
|
47
47
|
_relation__source = String(required=False)
|
|
48
48
|
|
|
49
49
|
|
|
50
|
-
class
|
|
50
|
+
class RelatedIPPrefixNodeInput(InputObjectType):
|
|
51
51
|
id = String(required=False)
|
|
52
52
|
hfid = Field(List(of_type=String), required=False)
|
|
53
|
-
from_pool = Field(
|
|
53
|
+
from_pool = Field(IPPrefixPoolInput, required=False)
|
|
54
54
|
_relation__is_visible = Boolean(required=False)
|
|
55
55
|
_relation__is_protected = Boolean(required=False)
|
|
56
56
|
_relation__owner = String(required=False)
|
infrahub/graphql/types/event.py
CHANGED
|
@@ -113,6 +113,58 @@ class BranchDeletedEvent(ObjectType):
|
|
|
113
113
|
payload = Field(GenericScalar, required=True)
|
|
114
114
|
|
|
115
115
|
|
|
116
|
+
# ---------------------------------------
|
|
117
|
+
# Proposed change events
|
|
118
|
+
# ---------------------------------------
|
|
119
|
+
class ProposedChangeReviewEvent(ObjectType):
|
|
120
|
+
class Meta:
|
|
121
|
+
interfaces = (EventNodeInterface,)
|
|
122
|
+
|
|
123
|
+
reviewer_account_id = String(required=True, description="The ID of the user who reviewed the proposed change")
|
|
124
|
+
reviewer_account_name = String(required=True, description="The name of the user who reviewed the proposed change")
|
|
125
|
+
reviewer_decision = String(required=True, description="The decision made by the reviewer")
|
|
126
|
+
payload = Field(GenericScalar, required=True)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class ProposedChangeReviewRevokedEvent(ObjectType):
|
|
130
|
+
class Meta:
|
|
131
|
+
interfaces = (EventNodeInterface,)
|
|
132
|
+
|
|
133
|
+
reviewer_account_id = String(required=True, description="The ID of the user who reviewed the proposed change")
|
|
134
|
+
reviewer_account_name = String(required=True, description="The name of the user who reviewed the proposed change")
|
|
135
|
+
reviewer_former_decision = String(required=True, description="The decision made by the reviewer")
|
|
136
|
+
payload = Field(GenericScalar, required=True)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class ProposedChangeReviewRequestedEvent(ObjectType):
|
|
140
|
+
class Meta:
|
|
141
|
+
interfaces = (EventNodeInterface,)
|
|
142
|
+
|
|
143
|
+
requested_by_account_id = String(
|
|
144
|
+
required=True, description="The ID of the user who requested the proposed change to be reviewed"
|
|
145
|
+
)
|
|
146
|
+
requested_by_account_name = String(
|
|
147
|
+
required=True, description="The name of the user who requested the proposed change to be reviewed"
|
|
148
|
+
)
|
|
149
|
+
payload = Field(GenericScalar, required=True)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class ProposedChangeMergedEvent(ObjectType):
|
|
153
|
+
class Meta:
|
|
154
|
+
interfaces = (EventNodeInterface,)
|
|
155
|
+
|
|
156
|
+
merged_by_account_id = String(required=True, description="The ID of the user who merged the proposed change")
|
|
157
|
+
merged_by_account_name = String(required=True, description="The name of the user who merged the proposed change")
|
|
158
|
+
payload = Field(GenericScalar, required=True)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class ProposedChangeThreadEvent(ObjectType):
|
|
162
|
+
class Meta:
|
|
163
|
+
interfaces = (EventNodeInterface,)
|
|
164
|
+
|
|
165
|
+
payload = Field(GenericScalar, required=True)
|
|
166
|
+
|
|
167
|
+
|
|
116
168
|
# ---------------------------------------
|
|
117
169
|
# Node/Object events
|
|
118
170
|
# ---------------------------------------
|
|
@@ -163,5 +215,13 @@ EVENT_TYPES: dict[str, type[ObjectType]] = {
|
|
|
163
215
|
events.BranchDeletedEvent.event_name: BranchDeletedEvent,
|
|
164
216
|
events.GroupMemberAddedEvent.event_name: GroupEvent,
|
|
165
217
|
events.GroupMemberRemovedEvent.event_name: GroupEvent,
|
|
218
|
+
events.ProposedChangeApprovedEvent.event_name: ProposedChangeReviewEvent,
|
|
219
|
+
events.ProposedChangeApprovalRevokedEvent.event_name: ProposedChangeReviewRevokedEvent,
|
|
220
|
+
events.ProposedChangeRejectedEvent.event_name: ProposedChangeReviewEvent,
|
|
221
|
+
events.ProposedChangeRejectionRevokedEvent.event_name: ProposedChangeReviewRevokedEvent,
|
|
222
|
+
events.ProposedChangeReviewRequestedEvent.event_name: ProposedChangeReviewRequestedEvent,
|
|
223
|
+
events.ProposedChangeMergedEvent.event_name: ProposedChangeMergedEvent,
|
|
224
|
+
events.ProposedChangeThreadCreatedEvent.event_name: ProposedChangeThreadEvent,
|
|
225
|
+
events.ProposedChangeThreadUpdatedEvent.event_name: ProposedChangeThreadEvent,
|
|
166
226
|
"undefined": StandardEvent,
|
|
167
227
|
}
|
infrahub/groups/tasks.py
CHANGED
|
@@ -4,14 +4,16 @@ from prefect import flow
|
|
|
4
4
|
|
|
5
5
|
from infrahub.core.constants import InfrahubKind
|
|
6
6
|
from infrahub.groups.models import RequestGraphQLQueryGroupUpdate
|
|
7
|
-
from infrahub.
|
|
7
|
+
from infrahub.workers.dependencies import get_client
|
|
8
8
|
from infrahub.workflows.utils import add_tags
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
@flow(name="graphql-query-group-update", flow_run_name="Update GraphQLQuery Group '{model.query_name}'")
|
|
12
|
-
async def update_graphql_query_group(model: RequestGraphQLQueryGroupUpdate
|
|
12
|
+
async def update_graphql_query_group(model: RequestGraphQLQueryGroupUpdate) -> None:
|
|
13
13
|
"""Create or Update a GraphQLQueryGroup."""
|
|
14
14
|
|
|
15
|
+
client = get_client()
|
|
16
|
+
|
|
15
17
|
# If there is only one subscriber, associate the task to it
|
|
16
18
|
# If there are more than one, for now we can't associate all of them
|
|
17
19
|
related_nodes = []
|
|
@@ -23,7 +25,7 @@ async def update_graphql_query_group(model: RequestGraphQLQueryGroupUpdate, serv
|
|
|
23
25
|
params_hash = dict_hash(model.params)
|
|
24
26
|
group_name = f"{model.query_name}__{params_hash}"
|
|
25
27
|
group_label = f"Query {model.query_name} Hash({params_hash[:8]})"
|
|
26
|
-
group = await
|
|
28
|
+
group = await client.create(
|
|
27
29
|
kind=InfrahubKind.GRAPHQLQUERYGROUP,
|
|
28
30
|
branch=model.branch,
|
|
29
31
|
name=group_name,
|
|
@@ -36,6 +38,4 @@ async def update_graphql_query_group(model: RequestGraphQLQueryGroupUpdate, serv
|
|
|
36
38
|
await group.save(allow_upsert=True)
|
|
37
39
|
|
|
38
40
|
if model.subscribers:
|
|
39
|
-
await group_add_subscriber(
|
|
40
|
-
client=service.client, group=group, subscribers=model.subscribers, branch=model.branch
|
|
41
|
-
)
|
|
41
|
+
await group_add_subscriber(client=client, group=group, subscribers=model.subscribers, branch=model.branch)
|
infrahub/lock.py
CHANGED
|
@@ -12,6 +12,7 @@ from prometheus_client import Histogram
|
|
|
12
12
|
from redis.asyncio.lock import Lock as GlobalLock
|
|
13
13
|
|
|
14
14
|
from infrahub import config
|
|
15
|
+
from infrahub.core.timestamp import current_timestamp
|
|
15
16
|
|
|
16
17
|
if TYPE_CHECKING:
|
|
17
18
|
from types import TracebackType
|
|
@@ -91,7 +92,7 @@ class NATSLock:
|
|
|
91
92
|
await self.release()
|
|
92
93
|
|
|
93
94
|
async def acquire(self) -> None:
|
|
94
|
-
token =
|
|
95
|
+
token = current_timestamp()
|
|
95
96
|
while True:
|
|
96
97
|
if await self.do_acquire(token):
|
|
97
98
|
self.token = token
|
|
@@ -155,7 +156,7 @@ class InfrahubLock:
|
|
|
155
156
|
async def acquire(self) -> None:
|
|
156
157
|
with LOCK_ACQUIRE_TIME_METRICS.labels(self.name, self.lock_type).time():
|
|
157
158
|
if not self.use_local:
|
|
158
|
-
await self.remote.acquire()
|
|
159
|
+
await self.remote.acquire(token=current_timestamp())
|
|
159
160
|
else:
|
|
160
161
|
await self.local.acquire()
|
|
161
162
|
self.acquire_time = time.time_ns()
|
infrahub/menu/generator.py
CHANGED
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
from typing import TYPE_CHECKING
|
|
4
4
|
|
|
5
5
|
from infrahub.core import registry
|
|
6
|
+
from infrahub.core.constants import InfrahubKind
|
|
6
7
|
from infrahub.core.protocols import CoreMenuItem
|
|
7
8
|
from infrahub.log import get_logger
|
|
8
9
|
|
|
@@ -133,4 +134,11 @@ async def generate_menu(db: InfrahubDatabase, branch: Branch, menu_items: list[C
|
|
|
133
134
|
default_menu.children[str(menu_item.identifier)] = menu_item
|
|
134
135
|
items_to_add[item_name] = True
|
|
135
136
|
|
|
137
|
+
builtin_ipaddress = registry.schema.get_generic_schema(name=InfrahubKind.IPADDRESS, branch=branch, duplicate=False)
|
|
138
|
+
builtin_ipprefix = registry.schema.get_generic_schema(name=InfrahubKind.IPPREFIX, branch=branch, duplicate=False)
|
|
139
|
+
ipam_missing = len(builtin_ipaddress.used_by + builtin_ipprefix.used_by) == 0
|
|
140
|
+
|
|
141
|
+
if ipam_missing:
|
|
142
|
+
structure.data.pop("BuiltinIPAM")
|
|
143
|
+
|
|
136
144
|
return structure
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import ujson
|
|
2
2
|
from prefect import Flow
|
|
3
3
|
|
|
4
|
+
from infrahub.log import get_logger
|
|
4
5
|
from infrahub.message_bus import RPCErrorResponse, messages
|
|
5
|
-
from infrahub.message_bus.operations import
|
|
6
|
-
git,
|
|
7
|
-
refresh,
|
|
8
|
-
send,
|
|
9
|
-
)
|
|
6
|
+
from infrahub.message_bus.operations import git, refresh, send
|
|
10
7
|
from infrahub.message_bus.types import MessageTTL
|
|
11
|
-
from infrahub.services import
|
|
8
|
+
from infrahub.services.adapters.message_bus import InfrahubMessageBus
|
|
12
9
|
from infrahub.tasks.check import set_check_status
|
|
13
10
|
|
|
14
11
|
COMMAND_MAP = {
|
|
@@ -22,7 +19,7 @@ COMMAND_MAP = {
|
|
|
22
19
|
|
|
23
20
|
|
|
24
21
|
async def execute_message(
|
|
25
|
-
routing_key: str, message_body: bytes,
|
|
22
|
+
routing_key: str, message_body: bytes, message_bus: InfrahubMessageBus, skip_flow: bool = False
|
|
26
23
|
) -> MessageTTL | None:
|
|
27
24
|
message_data = ujson.loads(message_body)
|
|
28
25
|
message = messages.MESSAGE_MAP[routing_key](**message_data)
|
|
@@ -31,16 +28,16 @@ async def execute_message(
|
|
|
31
28
|
func = COMMAND_MAP[routing_key]
|
|
32
29
|
if skip_flow and isinstance(func, Flow):
|
|
33
30
|
func = func.fn
|
|
34
|
-
await func(message=message
|
|
31
|
+
await func(message=message)
|
|
35
32
|
except Exception as exc:
|
|
36
33
|
if message.reply_requested:
|
|
37
34
|
response = RPCErrorResponse(errors=[str(exc)], initial_message=message.model_dump())
|
|
38
|
-
await
|
|
35
|
+
await message_bus.reply_if_initiator_meta(message=response, initiator=message)
|
|
39
36
|
return None
|
|
40
37
|
if message.reached_max_retries:
|
|
41
|
-
|
|
42
|
-
await set_check_status(message, conclusion="failure"
|
|
38
|
+
get_logger().exception("Message failed after maximum number of retries", error=exc)
|
|
39
|
+
await set_check_status(message, conclusion="failure")
|
|
43
40
|
return None
|
|
44
41
|
message.increase_retry_count()
|
|
45
|
-
await
|
|
42
|
+
await message_bus.send(message, delay=MessageTTL.FIVE, is_retry=True)
|
|
46
43
|
return MessageTTL.FIVE
|
|
@@ -6,29 +6,30 @@ from infrahub.message_bus.messages.git_file_get import (
|
|
|
6
6
|
GitFileGetResponse,
|
|
7
7
|
GitFileGetResponseData,
|
|
8
8
|
)
|
|
9
|
-
from infrahub.
|
|
9
|
+
from infrahub.workers.dependencies import get_client, get_message_bus
|
|
10
10
|
|
|
11
11
|
log = get_logger()
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
async def get(message: messages.GitFileGet
|
|
14
|
+
async def get(message: messages.GitFileGet) -> None:
|
|
15
15
|
log.info("Collecting file from repository", repository=message.repository_name, file=message.file)
|
|
16
16
|
|
|
17
17
|
repo = await get_initialized_repo(
|
|
18
|
+
client=get_client(),
|
|
18
19
|
repository_id=message.repository_id,
|
|
19
20
|
name=message.repository_name,
|
|
20
|
-
service=service,
|
|
21
21
|
repository_kind=message.repository_kind,
|
|
22
22
|
commit=message.commit,
|
|
23
23
|
)
|
|
24
24
|
|
|
25
|
+
message_bus = await get_message_bus()
|
|
25
26
|
try:
|
|
26
27
|
content = await repo.get_file(commit=message.commit, location=message.file)
|
|
27
28
|
except (FileOutOfRepositoryError, RepositoryFileNotFoundError) as e:
|
|
28
29
|
if message.reply_requested:
|
|
29
30
|
response = GitFileGetResponse(data=GitFileGetResponseData(error_message=e.message, http_code=e.HTTP_CODE))
|
|
30
|
-
await
|
|
31
|
+
await message_bus.reply_if_initiator_meta(message=response, initiator=message)
|
|
31
32
|
else:
|
|
32
33
|
if message.reply_requested:
|
|
33
34
|
response = GitFileGetResponse(data=GitFileGetResponseData(content=content))
|
|
34
|
-
await
|
|
35
|
+
await message_bus.reply_if_initiator_meta(message=response, initiator=message)
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
from prefect import flow
|
|
2
2
|
|
|
3
3
|
from infrahub.exceptions import RepositoryError
|
|
4
|
-
from infrahub.git.repository import InfrahubRepository, get_initialized_repo
|
|
4
|
+
from infrahub.git.repository import InfrahubRepository, get_initialized_repo
|
|
5
5
|
from infrahub.log import get_logger
|
|
6
6
|
from infrahub.message_bus import messages
|
|
7
7
|
from infrahub.message_bus.messages.git_repository_connectivity import (
|
|
8
8
|
GitRepositoryConnectivityResponse,
|
|
9
9
|
GitRepositoryConnectivityResponseData,
|
|
10
10
|
)
|
|
11
|
-
from infrahub.services import InfrahubServices
|
|
12
11
|
from infrahub.worker import WORKER_IDENTITY
|
|
12
|
+
from infrahub.workers.dependencies import get_client, get_message_bus
|
|
13
13
|
|
|
14
14
|
log = get_logger()
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
@flow(name="git-repository-check-connectivity", flow_run_name="Check connectivity for {message.repository_name}")
|
|
18
|
-
async def connectivity(message: messages.GitRepositoryConnectivity
|
|
18
|
+
async def connectivity(message: messages.GitRepositoryConnectivity) -> None:
|
|
19
19
|
response_data = GitRepositoryConnectivityResponseData(message="Successfully accessed repository", success=True)
|
|
20
20
|
|
|
21
21
|
try:
|
|
@@ -28,30 +28,22 @@ async def connectivity(message: messages.GitRepositoryConnectivity, service: Inf
|
|
|
28
28
|
response = GitRepositoryConnectivityResponse(
|
|
29
29
|
data=response_data,
|
|
30
30
|
)
|
|
31
|
-
await
|
|
31
|
+
message_bus = await get_message_bus()
|
|
32
|
+
await message_bus.reply_if_initiator_meta(message=response, initiator=message)
|
|
32
33
|
|
|
33
34
|
|
|
34
35
|
@flow(name="refresh-git-fetch", flow_run_name="Fetch git repository {message.repository_name} on " + WORKER_IDENTITY)
|
|
35
|
-
async def fetch(message: messages.RefreshGitFetch
|
|
36
|
+
async def fetch(message: messages.RefreshGitFetch) -> None:
|
|
36
37
|
if message.meta and message.meta.initiator_id == WORKER_IDENTITY:
|
|
37
38
|
log.info("Ignoring git fetch request originating from self", worker=WORKER_IDENTITY)
|
|
38
39
|
return
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
)
|
|
47
|
-
except RepositoryError:
|
|
48
|
-
repo = await initialize_repo(
|
|
49
|
-
location=message.location,
|
|
50
|
-
repository_id=message.repository_id,
|
|
51
|
-
name=message.repository_name,
|
|
52
|
-
service=service,
|
|
53
|
-
repository_kind=message.repository_kind,
|
|
54
|
-
)
|
|
41
|
+
repo = await get_initialized_repo(
|
|
42
|
+
client=get_client(),
|
|
43
|
+
repository_id=message.repository_id,
|
|
44
|
+
name=message.repository_name,
|
|
45
|
+
repository_kind=message.repository_kind,
|
|
46
|
+
)
|
|
55
47
|
|
|
56
48
|
await repo.fetch()
|
|
57
49
|
await repo.pull(
|
|
@@ -1,28 +1,34 @@
|
|
|
1
|
+
from infrahub.log import get_logger
|
|
1
2
|
from infrahub.message_bus import messages
|
|
2
|
-
from infrahub.services import InfrahubServices
|
|
3
3
|
from infrahub.tasks.registry import refresh_branches
|
|
4
4
|
from infrahub.worker import WORKER_IDENTITY
|
|
5
|
+
from infrahub.workers.dependencies import get_component, get_database
|
|
5
6
|
|
|
6
7
|
|
|
7
|
-
async def branches(message: messages.RefreshRegistryBranches
|
|
8
|
+
async def branches(message: messages.RefreshRegistryBranches) -> None:
|
|
8
9
|
if message.meta and message.meta.initiator_id == WORKER_IDENTITY:
|
|
9
|
-
|
|
10
|
+
get_logger().info("Ignoring refresh registry refresh request originating from self", worker=WORKER_IDENTITY)
|
|
10
11
|
return
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
database = await get_database()
|
|
14
|
+
async with database.start_session(read_only=False) as db:
|
|
13
15
|
await refresh_branches(db=db)
|
|
14
16
|
|
|
15
|
-
await
|
|
17
|
+
component = await get_component()
|
|
18
|
+
await component.refresh_schema_hash()
|
|
16
19
|
|
|
17
20
|
|
|
18
|
-
async def rebased_branch(message: messages.RefreshRegistryRebasedBranch
|
|
21
|
+
async def rebased_branch(message: messages.RefreshRegistryRebasedBranch) -> None:
|
|
19
22
|
if message.meta and message.meta.initiator_id == WORKER_IDENTITY:
|
|
20
|
-
|
|
23
|
+
get_logger().info(
|
|
21
24
|
"Ignoring refresh registry refreshed branch for request originating from self", worker=WORKER_IDENTITY
|
|
22
25
|
)
|
|
23
26
|
return
|
|
24
27
|
|
|
25
|
-
|
|
28
|
+
database = await get_database()
|
|
29
|
+
|
|
30
|
+
async with database.start_session(read_only=True) as db:
|
|
26
31
|
await refresh_branches(db=db)
|
|
27
32
|
|
|
28
|
-
await
|
|
33
|
+
component = await get_component()
|
|
34
|
+
await component.refresh_schema_hash()
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
from prefect import flow
|
|
2
2
|
|
|
3
|
+
from infrahub.log import get_logger
|
|
3
4
|
from infrahub.message_bus import messages
|
|
4
5
|
from infrahub.message_bus.messages.send_echo_request import SendEchoRequestResponse, SendEchoRequestResponseData
|
|
5
|
-
from infrahub.
|
|
6
|
+
from infrahub.workers.dependencies import get_message_bus
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
@flow(name="echo-request")
|
|
9
|
-
async def request(message: messages.SendEchoRequest
|
|
10
|
-
|
|
10
|
+
async def request(message: messages.SendEchoRequest) -> None:
|
|
11
|
+
get_logger().info(f"Received message: {message.message}")
|
|
12
|
+
|
|
11
13
|
if message.reply_requested:
|
|
12
14
|
response = SendEchoRequestResponse(data=SendEchoRequestResponseData(response=f"Reply to: {message.message}"))
|
|
13
|
-
await
|
|
15
|
+
message_bus = await get_message_bus()
|
|
16
|
+
await message_bus.reply_if_initiator_meta(message=response, initiator=message)
|
infrahub/message_bus/types.py
CHANGED
infrahub/permissions/globals.py
CHANGED
|
@@ -9,7 +9,4 @@ def define_global_permission_from_branch(permission: GlobalPermissions, branch_n
|
|
|
9
9
|
else:
|
|
10
10
|
decision = PermissionDecision.ALLOW_OTHER
|
|
11
11
|
|
|
12
|
-
return GlobalPermission(
|
|
13
|
-
action=permission.value,
|
|
14
|
-
decision=decision.value,
|
|
15
|
-
)
|
|
12
|
+
return GlobalPermission(action=permission.value, decision=decision.value)
|
infrahub/permissions/manager.py
CHANGED
|
@@ -48,6 +48,13 @@ class PermissionManager:
|
|
|
48
48
|
specificity += 1
|
|
49
49
|
return specificity
|
|
50
50
|
|
|
51
|
+
def is_super_admin(self) -> bool:
|
|
52
|
+
return self.resolve_global_permission(
|
|
53
|
+
permission_to_check=GlobalPermission(
|
|
54
|
+
action=GlobalPermissions.SUPER_ADMIN, decision=PermissionDecision.ALLOW_ALL
|
|
55
|
+
),
|
|
56
|
+
)
|
|
57
|
+
|
|
51
58
|
def report_object_permission(self, namespace: str, name: str, action: str) -> PermissionDecisionFlag:
|
|
52
59
|
"""Given a set of permissions, return the permission decision for a given kind and action."""
|
|
53
60
|
highest_specificity: int = -1
|
|
@@ -94,11 +101,7 @@ class PermissionManager:
|
|
|
94
101
|
|
|
95
102
|
def has_permission(self, permission: GlobalPermission | ObjectPermission) -> bool:
|
|
96
103
|
"""Tell if a permission is granted given the permissions loaded in memory."""
|
|
97
|
-
is_super_admin = self.
|
|
98
|
-
permission_to_check=GlobalPermission(
|
|
99
|
-
action=GlobalPermissions.SUPER_ADMIN, decision=PermissionDecision.ALLOW_ALL
|
|
100
|
-
),
|
|
101
|
-
)
|
|
104
|
+
is_super_admin = self.is_super_admin()
|
|
102
105
|
|
|
103
106
|
if isinstance(permission, GlobalPermission):
|
|
104
107
|
return self.resolve_global_permission(permission_to_check=permission) or is_super_admin
|
infrahub/pools/prefix.py
CHANGED
|
@@ -9,7 +9,9 @@ if TYPE_CHECKING:
|
|
|
9
9
|
from infrahub.core.ipam.constants import IPNetworkType
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
def get_next_available_prefix(
|
|
12
|
+
def get_next_available_prefix(
|
|
13
|
+
pool: IPSet, prefix_length: int | None = None, prefix_ver: Literal[4, 6] = 4
|
|
14
|
+
) -> IPNetworkType:
|
|
13
15
|
"""Get the next available prefix of a given prefix length from an IPSet.
|
|
14
16
|
|
|
15
17
|
Args:
|
|
@@ -20,10 +22,7 @@ def get_next_available_prefix(pool: IPSet, prefix_length: int, prefix_ver: Liter
|
|
|
20
22
|
Raises:
|
|
21
23
|
ValueError: If there are no available subnets in the pool
|
|
22
24
|
"""
|
|
23
|
-
prefix_ver_map = {
|
|
24
|
-
4: ipaddress.IPv4Network,
|
|
25
|
-
6: ipaddress.IPv6Network,
|
|
26
|
-
}
|
|
25
|
+
prefix_ver_map = {4: ipaddress.IPv4Network, 6: ipaddress.IPv6Network}
|
|
27
26
|
|
|
28
27
|
filtered_pool = IPSet([])
|
|
29
28
|
for subnet in pool.iter_cidrs():
|
|
@@ -31,6 +30,9 @@ def get_next_available_prefix(pool: IPSet, prefix_length: int, prefix_ver: Liter
|
|
|
31
30
|
filtered_pool.add(subnet)
|
|
32
31
|
|
|
33
32
|
for cidr in filtered_pool.iter_cidrs():
|
|
33
|
+
if prefix_length is None:
|
|
34
|
+
return cidr
|
|
35
|
+
|
|
34
36
|
if cidr.prefixlen <= prefix_length:
|
|
35
37
|
next_available = ipaddress.ip_network(f"{cidr.network}/{prefix_length}")
|
|
36
38
|
return next_available
|