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.
Files changed (158) hide show
  1. infrahub/api/internal.py +5 -0
  2. infrahub/artifacts/tasks.py +17 -22
  3. infrahub/branch/merge_mutation_checker.py +38 -0
  4. infrahub/cli/__init__.py +2 -2
  5. infrahub/cli/context.py +7 -3
  6. infrahub/cli/db.py +5 -16
  7. infrahub/cli/upgrade.py +7 -29
  8. infrahub/computed_attribute/tasks.py +36 -46
  9. infrahub/config.py +53 -2
  10. infrahub/constants/environment.py +1 -0
  11. infrahub/core/attribute.py +9 -7
  12. infrahub/core/branch/tasks.py +43 -41
  13. infrahub/core/constants/__init__.py +20 -6
  14. infrahub/core/constants/infrahubkind.py +2 -0
  15. infrahub/core/diff/coordinator.py +3 -1
  16. infrahub/core/diff/repository/repository.py +0 -8
  17. infrahub/core/diff/tasks.py +11 -8
  18. infrahub/core/graph/__init__.py +1 -1
  19. infrahub/core/graph/index.py +1 -2
  20. infrahub/core/graph/schema.py +50 -29
  21. infrahub/core/initialization.py +62 -33
  22. infrahub/core/ipam/tasks.py +4 -3
  23. infrahub/core/merge.py +8 -10
  24. infrahub/core/migrations/graph/__init__.py +2 -0
  25. infrahub/core/migrations/graph/m035_drop_attr_value_index.py +45 -0
  26. infrahub/core/migrations/query/attribute_add.py +27 -2
  27. infrahub/core/migrations/schema/tasks.py +6 -5
  28. infrahub/core/node/proposed_change.py +43 -0
  29. infrahub/core/protocols.py +12 -0
  30. infrahub/core/query/attribute.py +32 -14
  31. infrahub/core/query/diff.py +11 -0
  32. infrahub/core/query/ipam.py +13 -7
  33. infrahub/core/query/node.py +51 -10
  34. infrahub/core/query/resource_manager.py +3 -3
  35. infrahub/core/schema/basenode_schema.py +8 -0
  36. infrahub/core/schema/definitions/core/__init__.py +10 -1
  37. infrahub/core/schema/definitions/core/ipam.py +28 -2
  38. infrahub/core/schema/definitions/core/propose_change.py +15 -0
  39. infrahub/core/schema/definitions/core/webhook.py +3 -0
  40. infrahub/core/schema/generic_schema.py +10 -0
  41. infrahub/core/schema/manager.py +10 -1
  42. infrahub/core/schema/node_schema.py +22 -17
  43. infrahub/core/schema/profile_schema.py +8 -0
  44. infrahub/core/schema/schema_branch.py +9 -5
  45. infrahub/core/schema/template_schema.py +8 -0
  46. infrahub/core/validators/checks_runner.py +5 -5
  47. infrahub/core/validators/tasks.py +6 -7
  48. infrahub/core/validators/uniqueness/checker.py +4 -2
  49. infrahub/core/validators/uniqueness/model.py +1 -0
  50. infrahub/core/validators/uniqueness/query.py +57 -7
  51. infrahub/database/__init__.py +2 -1
  52. infrahub/events/__init__.py +18 -0
  53. infrahub/events/constants.py +7 -0
  54. infrahub/events/generator.py +29 -2
  55. infrahub/events/proposed_change_action.py +181 -0
  56. infrahub/generators/tasks.py +24 -20
  57. infrahub/git/base.py +4 -7
  58. infrahub/git/integrator.py +21 -12
  59. infrahub/git/repository.py +15 -30
  60. infrahub/git/tasks.py +121 -106
  61. infrahub/graphql/field_extractor.py +69 -0
  62. infrahub/graphql/manager.py +15 -11
  63. infrahub/graphql/mutations/account.py +2 -2
  64. infrahub/graphql/mutations/action.py +8 -2
  65. infrahub/graphql/mutations/artifact_definition.py +4 -1
  66. infrahub/graphql/mutations/branch.py +10 -5
  67. infrahub/graphql/mutations/graphql_query.py +2 -1
  68. infrahub/graphql/mutations/main.py +14 -8
  69. infrahub/graphql/mutations/menu.py +2 -1
  70. infrahub/graphql/mutations/proposed_change.py +225 -8
  71. infrahub/graphql/mutations/relationship.py +5 -0
  72. infrahub/graphql/mutations/repository.py +2 -1
  73. infrahub/graphql/mutations/tasks.py +7 -9
  74. infrahub/graphql/mutations/webhook.py +4 -1
  75. infrahub/graphql/parser.py +15 -6
  76. infrahub/graphql/queries/__init__.py +10 -1
  77. infrahub/graphql/queries/account.py +3 -3
  78. infrahub/graphql/queries/branch.py +2 -2
  79. infrahub/graphql/queries/diff/tree.py +3 -3
  80. infrahub/graphql/queries/event.py +13 -3
  81. infrahub/graphql/queries/ipam.py +23 -1
  82. infrahub/graphql/queries/proposed_change.py +84 -0
  83. infrahub/graphql/queries/relationship.py +2 -2
  84. infrahub/graphql/queries/resource_manager.py +3 -3
  85. infrahub/graphql/queries/search.py +3 -2
  86. infrahub/graphql/queries/status.py +3 -2
  87. infrahub/graphql/queries/task.py +2 -2
  88. infrahub/graphql/resolvers/ipam.py +440 -0
  89. infrahub/graphql/resolvers/many_relationship.py +4 -3
  90. infrahub/graphql/resolvers/resolver.py +5 -5
  91. infrahub/graphql/resolvers/single_relationship.py +3 -2
  92. infrahub/graphql/schema.py +25 -5
  93. infrahub/graphql/types/__init__.py +2 -2
  94. infrahub/graphql/types/attribute.py +3 -3
  95. infrahub/graphql/types/event.py +60 -0
  96. infrahub/groups/tasks.py +6 -6
  97. infrahub/lock.py +3 -2
  98. infrahub/menu/generator.py +8 -0
  99. infrahub/message_bus/operations/__init__.py +9 -12
  100. infrahub/message_bus/operations/git/file.py +6 -5
  101. infrahub/message_bus/operations/git/repository.py +12 -20
  102. infrahub/message_bus/operations/refresh/registry.py +15 -9
  103. infrahub/message_bus/operations/send/echo.py +7 -4
  104. infrahub/message_bus/types.py +1 -0
  105. infrahub/permissions/globals.py +1 -4
  106. infrahub/permissions/manager.py +8 -5
  107. infrahub/pools/prefix.py +7 -5
  108. infrahub/prefect_server/app.py +31 -0
  109. infrahub/prefect_server/bootstrap.py +18 -0
  110. infrahub/proposed_change/action_checker.py +206 -0
  111. infrahub/proposed_change/approval_revoker.py +40 -0
  112. infrahub/proposed_change/branch_diff.py +3 -1
  113. infrahub/proposed_change/checker.py +45 -0
  114. infrahub/proposed_change/constants.py +32 -2
  115. infrahub/proposed_change/tasks.py +182 -150
  116. infrahub/py.typed +0 -0
  117. infrahub/server.py +29 -17
  118. infrahub/services/__init__.py +13 -28
  119. infrahub/services/adapters/cache/__init__.py +4 -0
  120. infrahub/services/adapters/cache/nats.py +2 -0
  121. infrahub/services/adapters/cache/redis.py +3 -0
  122. infrahub/services/adapters/message_bus/__init__.py +0 -2
  123. infrahub/services/adapters/message_bus/local.py +1 -2
  124. infrahub/services/adapters/message_bus/nats.py +6 -8
  125. infrahub/services/adapters/message_bus/rabbitmq.py +7 -9
  126. infrahub/services/adapters/workflow/__init__.py +1 -0
  127. infrahub/services/adapters/workflow/local.py +1 -8
  128. infrahub/services/component.py +2 -1
  129. infrahub/task_manager/event.py +52 -0
  130. infrahub/task_manager/models.py +9 -0
  131. infrahub/tasks/artifact.py +6 -7
  132. infrahub/tasks/check.py +4 -7
  133. infrahub/telemetry/tasks.py +15 -18
  134. infrahub/transformations/tasks.py +10 -6
  135. infrahub/trigger/tasks.py +4 -3
  136. infrahub/types.py +4 -0
  137. infrahub/validators/events.py +7 -7
  138. infrahub/validators/tasks.py +6 -7
  139. infrahub/webhook/models.py +45 -45
  140. infrahub/webhook/tasks.py +25 -24
  141. infrahub/workers/dependencies.py +143 -0
  142. infrahub/workers/infrahub_async.py +19 -43
  143. infrahub/workflows/catalogue.py +16 -2
  144. infrahub/workflows/initialization.py +5 -4
  145. infrahub/workflows/models.py +2 -0
  146. infrahub_sdk/client.py +6 -6
  147. infrahub_sdk/ctl/repository.py +51 -0
  148. infrahub_sdk/ctl/schema.py +9 -9
  149. infrahub_sdk/protocols.py +40 -6
  150. {infrahub_server-1.3.5.dist-info → infrahub_server-1.4.0b0.dist-info}/METADATA +5 -4
  151. {infrahub_server-1.3.5.dist-info → infrahub_server-1.4.0b0.dist-info}/RECORD +158 -144
  152. infrahub_testcontainers/container.py +17 -0
  153. infrahub_testcontainers/docker-compose-cluster.test.yml +56 -1
  154. infrahub_testcontainers/docker-compose.test.yml +56 -1
  155. infrahub_testcontainers/helpers.py +4 -1
  156. {infrahub_server-1.3.5.dist-info → infrahub_server-1.4.0b0.dist-info}/LICENSE.txt +0 -0
  157. {infrahub_server-1.3.5.dist-info → infrahub_server-1.4.0b0.dist-info}/WHEEL +0 -0
  158. {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 = await extract_fields(info.field_nodes[0].selection_set)
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 = await extract_fields(info.field_nodes[0].selection_set)
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.field_nodes[0], schema=schema)
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 = await extract_fields(info.field_nodes[0].selection_set)
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, extract_fields
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 = await extract_fields(info.field_nodes[0].selection_set)
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():
@@ -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 ProposedChangeMerge, ProposedChangeRequestRunCheck
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
- IPAddressGetNextAvailable = InfrahubIPAddressGetNextAvailable
78
- IPPrefixGetNextAvailable = InfrahubIPPrefixGetNextAvailable
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
- IPPrefixPoolGetResource = IPPrefixPoolGetResource.Field()
94
- IPAddressPoolGetResource = IPAddressPoolGetResource.Field()
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 PrefixPoolInput(GenericPoolInput):
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 RelatedPrefixNodeInput(InputObjectType):
50
+ class RelatedIPPrefixNodeInput(InputObjectType):
51
51
  id = String(required=False)
52
52
  hfid = Field(List(of_type=String), required=False)
53
- from_pool = Field(PrefixPoolInput, required=False)
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)
@@ -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.services import InfrahubServices
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, service: InfrahubServices) -> None:
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 service.client.create(
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 = uuid.uuid1().hex
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()
@@ -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 InfrahubServices
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, service: InfrahubServices, skip_flow: bool = False
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, service=service)
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 service.message_bus.reply_if_initiator_meta(message=response, initiator=message)
35
+ await message_bus.reply_if_initiator_meta(message=response, initiator=message)
39
36
  return None
40
37
  if message.reached_max_retries:
41
- service.log.exception("Message failed after maximum number of retries", error=exc)
42
- await set_check_status(message, conclusion="failure", service=service)
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 service.message_bus.send(message, delay=MessageTTL.FIVE, is_retry=True)
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.services import InfrahubServices
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, service: InfrahubServices) -> None:
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 service.message_bus.reply_if_initiator_meta(message=response, initiator=message)
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 service.message_bus.reply_if_initiator_meta(message=response, initiator=message)
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, initialize_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, service: InfrahubServices) -> None:
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 service.message_bus.reply_if_initiator_meta(message=response, initiator=message)
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, service: InfrahubServices) -> None:
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
- try:
41
- repo = await get_initialized_repo(
42
- repository_id=message.repository_id,
43
- name=message.repository_name,
44
- service=service,
45
- repository_kind=message.repository_kind,
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, service: InfrahubServices) -> None:
8
+ async def branches(message: messages.RefreshRegistryBranches) -> None:
8
9
  if message.meta and message.meta.initiator_id == WORKER_IDENTITY:
9
- service.log.info("Ignoring refresh registry refresh request originating from self", worker=WORKER_IDENTITY)
10
+ get_logger().info("Ignoring refresh registry refresh request originating from self", worker=WORKER_IDENTITY)
10
11
  return
11
12
 
12
- async with service.database.start_session(read_only=True) as db:
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 service.component.refresh_schema_hash()
17
+ component = await get_component()
18
+ await component.refresh_schema_hash()
16
19
 
17
20
 
18
- async def rebased_branch(message: messages.RefreshRegistryRebasedBranch, service: InfrahubServices) -> None:
21
+ async def rebased_branch(message: messages.RefreshRegistryRebasedBranch) -> None:
19
22
  if message.meta and message.meta.initiator_id == WORKER_IDENTITY:
20
- service.log.info(
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
- async with service.database.start_session(read_only=True) as db:
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 service.component.refresh_schema_hash()
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.services import InfrahubServices
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, service: InfrahubServices) -> None:
10
- service.log.info(f"Received message: {message.message}")
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 service.message_bus.reply_if_initiator_meta(message=response, initiator=message)
15
+ message_bus = await get_message_bus()
16
+ await message_bus.reply_if_initiator_meta(message=response, initiator=message)
@@ -31,6 +31,7 @@ class KVTTL(int, Enum):
31
31
  ONE = 1
32
32
  TEN = 10
33
33
  FIFTEEN = 15
34
+ ONE_MINUTE = 60
34
35
  TWO_HOURS = 7200
35
36
 
36
37
  @classmethod
@@ -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)
@@ -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.resolve_global_permission(
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(pool: IPSet, prefix_length: int, prefix_ver: Literal[4, 6] = 4) -> IPNetworkType:
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