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
infrahub/py.typed ADDED
File without changes
infrahub/server.py CHANGED
@@ -23,8 +23,8 @@ from infrahub import __version__, config
23
23
  from infrahub.api import router as api
24
24
  from infrahub.api.exception_handlers import generic_api_exception_handler
25
25
  from infrahub.components import ComponentType
26
+ from infrahub.constants.environment import INSTALLATION_TYPE
26
27
  from infrahub.core.initialization import initialization
27
- from infrahub.database import InfrahubDatabase, InfrahubDatabaseMode, get_db
28
28
  from infrahub.dependencies.registry import build_component_registry
29
29
  from infrahub.exceptions import Error, ValidationError
30
30
  from infrahub.graphql.api.endpoints import router as graphql_router
@@ -32,18 +32,24 @@ from infrahub.lock import initialize_lock
32
32
  from infrahub.log import clear_log_context, get_logger, set_log_data
33
33
  from infrahub.middleware import InfrahubCORSMiddleware
34
34
  from infrahub.services import InfrahubServices
35
- from infrahub.services.adapters.cache import InfrahubCache
36
- from infrahub.services.adapters.message_bus import InfrahubMessageBus
37
- from infrahub.services.adapters.workflow.local import WorkflowLocalExecution
38
- from infrahub.services.adapters.workflow.worker import WorkflowWorkerExecution
39
35
  from infrahub.trace import add_span_exception, configure_trace, get_traceid
40
36
  from infrahub.worker import WORKER_IDENTITY
37
+ from infrahub.workers.dependencies import (
38
+ get_cache,
39
+ get_component,
40
+ get_database,
41
+ get_installation_type,
42
+ get_message_bus,
43
+ get_workflow,
44
+ set_component_type,
45
+ )
41
46
 
42
47
  CURRENT_DIRECTORY = Path(__file__).parent.resolve()
43
48
 
44
49
 
45
50
  async def app_initialization(application: FastAPI, enable_scheduler: bool = True) -> None:
46
51
  config.SETTINGS.initialize_and_exit()
52
+ _validate_feature_selection(configuration=config.SETTINGS.active_settings)
47
53
 
48
54
  # Initialize trace
49
55
  if config.SETTINGS.trace.enable:
@@ -55,27 +61,24 @@ async def app_initialization(application: FastAPI, enable_scheduler: bool = True
55
61
  exporter_protocol=config.SETTINGS.trace.exporter_protocol,
56
62
  )
57
63
 
64
+ component_type = ComponentType.API_SERVER
65
+ set_component_type(component_type=component_type)
66
+
58
67
  # Initialize database Driver and load local registry
59
- database = application.state.db = InfrahubDatabase(mode=InfrahubDatabaseMode.DRIVER, driver=await get_db())
68
+ database = application.state.db = await get_database()
60
69
 
61
70
  build_component_registry()
62
71
 
63
- workflow = config.OVERRIDE.workflow or (
64
- WorkflowWorkerExecution()
65
- if config.SETTINGS.workflow.driver == config.WorkflowDriver.WORKER
66
- else WorkflowLocalExecution()
67
- )
68
- component_type = ComponentType.API_SERVER
69
- message_bus = config.OVERRIDE.message_bus or await InfrahubMessageBus.new_from_driver(
70
- component_type=component_type, driver=config.SETTINGS.broker.driver
71
- )
72
-
73
- cache = config.OVERRIDE.cache or (await InfrahubCache.new_from_driver(driver=config.SETTINGS.cache.driver))
72
+ workflow = get_workflow()
73
+ message_bus = await get_message_bus()
74
+ cache = await get_cache()
75
+ component = await get_component()
74
76
  service = await InfrahubServices.new(
75
77
  cache=cache,
76
78
  database=database,
77
79
  message_bus=message_bus,
78
80
  workflow=workflow,
81
+ component=component,
79
82
  component_type=component_type,
80
83
  )
81
84
  initialize_lock(service=service)
@@ -212,3 +215,12 @@ async def documentation() -> RedirectResponse:
212
215
  @app.get("/{rest_of_path:path}", include_in_schema=False)
213
216
  async def react_app(req: Request, rest_of_path: str) -> Response: # noqa: ARG001
214
217
  return templates.TemplateResponse("index.html", {"request": req})
218
+
219
+
220
+ def _validate_feature_selection(configuration: config.Settings) -> None:
221
+ if configuration.enterprise_features and not configuration.dev.allow_enterprise_configuration:
222
+ installation_type = get_installation_type()
223
+ if installation_type == INSTALLATION_TYPE:
224
+ raise ValidationError(
225
+ f"Enterprise features [{','.join(configuration.enterprise_features)}] are not supported when running Infrahub 'community'."
226
+ )
@@ -9,9 +9,7 @@ from infrahub.message_bus.messages import ROUTING_KEY_MAP
9
9
 
10
10
  from .adapters.event import InfrahubEventService
11
11
  from .adapters.http.httpx import HttpxAdapter
12
- from .adapters.workflow.local import WorkflowLocalExecution
13
12
  from .adapters.workflow.worker import WorkflowWorkerExecution
14
- from .component import InfrahubComponent
15
13
  from .scheduler import InfrahubScheduler
16
14
 
17
15
  if TYPE_CHECKING:
@@ -25,6 +23,7 @@ if TYPE_CHECKING:
25
23
  from .adapters.http import InfrahubHTTP
26
24
  from .adapters.message_bus import InfrahubMessageBus
27
25
  from .adapters.workflow import InfrahubWorkflow
26
+ from .component import InfrahubComponent
28
27
  from .protocols import InfrahubLogger
29
28
 
30
29
 
@@ -54,6 +53,7 @@ class InfrahubServices:
54
53
  database: InfrahubDatabase | None = None,
55
54
  message_bus: InfrahubMessageBus | None = None,
56
55
  workflow: InfrahubWorkflow | None = None,
56
+ component: InfrahubComponent | None = None,
57
57
  ):
58
58
  """
59
59
  This method should not be called directly, use `new` instead for a proper initialization.
@@ -64,12 +64,12 @@ class InfrahubServices:
64
64
  self._database = database
65
65
  self._message_bus = message_bus
66
66
  self._workflow = workflow
67
+ self._component = component
67
68
  self.log = log
68
69
  self.component_type = component_type
69
70
  self.http = http
70
71
  self.event = event
71
72
  self.scheduler = scheduler
72
- self._component = None
73
73
 
74
74
  @classmethod
75
75
  async def new(
@@ -81,6 +81,7 @@ class InfrahubServices:
81
81
  message_bus: InfrahubMessageBus | None = None,
82
82
  workflow: InfrahubWorkflow | None = None,
83
83
  log: InfrahubLogger | None = None,
84
+ component: InfrahubComponent | None = None,
84
85
  component_type: ComponentType | None = None,
85
86
  http: InfrahubHTTP | None = None,
86
87
  ) -> InfrahubServices:
@@ -99,6 +100,7 @@ class InfrahubServices:
99
100
  message_bus=message_bus,
100
101
  workflow=workflow,
101
102
  log=log or get_logger(),
103
+ component=component,
102
104
  component_type=component_type,
103
105
  scheduler=scheduler,
104
106
  event=event or InfrahubEventService(message_bus),
@@ -108,31 +110,12 @@ class InfrahubServices:
108
110
  # This circular dependency could be removed if InfrahubScheduler only depends on what it needs.
109
111
  scheduler.service = service
110
112
 
111
- if message_bus is not None:
112
- # Need circular dependency for injecting `service` within `execute_message`. This might be removed
113
- # using proper dependency injections.
114
- message_bus.service = service
115
-
116
- if cache is not None and database is not None:
117
- component = await InfrahubComponent.new(
118
- cache=cache, component_type=component_type, db=database, message_bus=message_bus
119
- )
120
- # We need to post init `service._component` because InfrahubComponent.new relies on message_bus
121
- # itself relying on service.
122
- service._component = component
123
-
124
- if workflow is not None:
125
- if isinstance(workflow, WorkflowWorkerExecution):
126
- assert service.component is not None
127
- # Ideally `WorkflowWorkerExecution.initialize` would be directly part of WorkflowWorkerExecution
128
- # constructor but this requires some redesign as it depends on InfrahubComponent which is instantiated
129
- # after workflow instantiation.
130
- await workflow.initialize(
131
- component_is_primary_server=await service.component.is_primary_gunicorn_worker()
132
- )
133
- elif isinstance(workflow, WorkflowLocalExecution):
134
- # Circular dependency is only needed for injecting `service` within `execute_workflow` while testing.
135
- workflow.service = service
113
+ if workflow is not None and isinstance(workflow, WorkflowWorkerExecution):
114
+ assert service.component is not None
115
+ # Ideally `WorkflowWorkerExecution.initialize` would be directly part of WorkflowWorkerExecution
116
+ # constructor but this requires some redesign as it depends on InfrahubComponent which is instantiated
117
+ # after workflow instantiation.
118
+ await workflow.initialize(component_is_primary_server=await service.component.is_primary_gunicorn_worker())
136
119
 
137
120
  return service
138
121
 
@@ -181,6 +164,8 @@ class InfrahubServices:
181
164
  async def shutdown(self) -> None:
182
165
  await self.scheduler.shutdown()
183
166
  await self.message_bus.shutdown()
167
+ if self._cache is not None:
168
+ await self._cache.close_connection()
184
169
 
185
170
  async def send(self, message: InfrahubMessage, delay: MessageTTL | None = None, is_retry: bool = False) -> None:
186
171
  routing_key = ROUTING_KEY_MAP.get(type(message))
@@ -51,3 +51,7 @@ class InfrahubCache(ABC):
51
51
  @classmethod
52
52
  async def new(cls) -> InfrahubCache:
53
53
  raise NotImplementedError()
54
+
55
+ @abstractmethod
56
+ async def close_connection(self) -> None:
57
+ raise NotImplementedError()
@@ -149,3 +149,5 @@ class NATSCache(InfrahubCache):
149
149
  return False
150
150
  await self._get_kv(key).put(key=key, value=value.encode())
151
151
  return True
152
+
153
+ async def close_connection(self) -> None: ...
@@ -54,3 +54,6 @@ class RedisCache(InfrahubCache):
54
54
  @classmethod
55
55
  async def new(cls) -> RedisCache:
56
56
  return cls()
57
+
58
+ async def close_connection(self) -> None:
59
+ await self.connection.aclose()
@@ -13,7 +13,6 @@ if TYPE_CHECKING:
13
13
  from infrahub.config import BrokerDriver, BrokerSettings
14
14
  from infrahub.message_bus import InfrahubMessage, InfrahubResponse
15
15
  from infrahub.message_bus.types import MessageTTL
16
- from infrahub.services import InfrahubServices
17
16
 
18
17
 
19
18
  class InfrahubMessageBus(ABC):
@@ -32,7 +31,6 @@ class InfrahubMessageBus(ABC):
32
31
  ]
33
32
  event_bindings: list[str] = ["refresh.registry.*"]
34
33
  broadcasted_event_bindings: list[str] = ["refresh.git.*"]
35
- service: InfrahubServices
36
34
 
37
35
  async def shutdown(self) -> None: # noqa: B027 We want a default empty behavior, so it's ok to have an empty non-abstract method.
38
36
  """Shutdown the Message bus"""
@@ -36,8 +36,7 @@ class BusSimulator(InfrahubMessageBus):
36
36
  if routing_key not in self.messages_per_routing_key:
37
37
  self.messages_per_routing_key[routing_key] = []
38
38
  self.messages_per_routing_key[routing_key].append(message)
39
- assert self.service is not None
40
- await execute_message(routing_key=routing_key, message_body=message.body, service=self.service)
39
+ await execute_message(routing_key=routing_key, message_body=message.body, message_bus=self)
41
40
 
42
41
  async def reply(self, message: InfrahubMessage, routing_key: str) -> None: # noqa: ARG002
43
42
  correlation_id = message.meta.correlation_id or "default"
@@ -12,7 +12,7 @@ from opentelemetry.instrumentation.utils import is_instrumentation_enabled
12
12
 
13
13
  from infrahub import config
14
14
  from infrahub.components import ComponentType
15
- from infrahub.log import clear_log_context, get_log_data
15
+ from infrahub.log import clear_log_context, get_log_data, get_logger
16
16
  from infrahub.message_bus import InfrahubMessage, Meta, messages
17
17
  from infrahub.message_bus.operations import execute_message
18
18
  from infrahub.services.adapters.message_bus import InfrahubMessageBus
@@ -21,7 +21,6 @@ from infrahub.worker import WORKER_IDENTITY
21
21
  if TYPE_CHECKING:
22
22
  from infrahub.config import BrokerSettings
23
23
  from infrahub.message_bus.types import MessageTTL
24
- from infrahub.services import InfrahubServices
25
24
 
26
25
  MessageFunction = Callable[[InfrahubMessage], Awaitable[None]]
27
26
  ResponseClass = TypeVar("ResponseClass")
@@ -38,7 +37,6 @@ class NATSMessageBus(InfrahubMessageBus):
38
37
  def __init__(self, component_type: ComponentType, settings: BrokerSettings | None = None) -> None:
39
38
  self.settings = settings or config.SETTINGS.broker
40
39
 
41
- self.service: InfrahubServices
42
40
  self.connection: nats.NATS
43
41
  self.jetstream: nats.js.JetStreamContext
44
42
  self.callback_queue: nats.js.api.StreamInfo
@@ -102,9 +100,9 @@ class NATSMessageBus(InfrahubMessageBus):
102
100
 
103
101
  clear_log_context()
104
102
  if message.subject in messages.MESSAGE_MAP:
105
- await execute_message(routing_key=message.subject, message_body=message.data, service=self.service)
103
+ await execute_message(routing_key=message.subject, message_body=message.data, message_bus=self)
106
104
  else:
107
- self.service.log.error("Invalid message received", message=f"{message!r}")
105
+ get_logger().error("Invalid message received", message=f"{message!r}")
108
106
  finally:
109
107
  if is_instrumentation_enabled() and message.headers:
110
108
  context.detach(token)
@@ -121,12 +119,12 @@ class NATSMessageBus(InfrahubMessageBus):
121
119
  clear_log_context()
122
120
  if message.subject in messages.MESSAGE_MAP:
123
121
  delay = await execute_message(
124
- routing_key=message.subject, message_body=message.data, service=self.service
122
+ routing_key=message.subject, message_body=message.data, message_bus=self
125
123
  )
126
124
  if delay:
127
125
  return await message.nak(delay / 1000)
128
126
  else:
129
- self.service.log.error("Invalid message received", message=f"{message!r}")
127
+ get_logger().error("Invalid message received", message=f"{message!r}")
130
128
 
131
129
  return await message.ack()
132
130
  finally:
@@ -292,7 +290,7 @@ class NATSMessageBus(InfrahubMessageBus):
292
290
  request_id=request_id, correlation_id=correlation_id, reply_to=self.callback_queue.config.name
293
291
  )
294
292
 
295
- await self.service.message_bus.send(message=message)
293
+ await self.send(message=message)
296
294
 
297
295
  response: nats.aio.msg.Msg = await future
298
296
  data = ujson.loads(response.data)
@@ -12,7 +12,7 @@ from opentelemetry.semconv.trace import SpanAttributes
12
12
 
13
13
  from infrahub import config
14
14
  from infrahub.components import ComponentType
15
- from infrahub.log import clear_log_context, get_log_data
15
+ from infrahub.log import clear_log_context, get_log_data, get_logger
16
16
  from infrahub.message_bus import InfrahubMessage, Meta, messages
17
17
  from infrahub.message_bus.operations import execute_message
18
18
  from infrahub.message_bus.types import MessageTTL
@@ -30,7 +30,6 @@ if TYPE_CHECKING:
30
30
  from opentelemetry.instrumentation.aio_pika.span_builder import SpanBuilder
31
31
 
32
32
  from infrahub.config import BrokerSettings
33
- from infrahub.services import InfrahubServices
34
33
 
35
34
  MessageFunction = Callable[[InfrahubMessage], Awaitable[None]]
36
35
  ResponseClass = TypeVar("ResponseClass")
@@ -72,7 +71,6 @@ class RabbitMQMessageBus(InfrahubMessageBus):
72
71
  self.channel: AbstractChannel
73
72
  self.exchange: AbstractExchange
74
73
  self.delayed_exchange: AbstractExchange
75
- self.service: InfrahubServices # Needed because we inject `service` while sending messages.
76
74
  self.connection: AbstractRobustConnection
77
75
  self.callback_queue: AbstractQueue
78
76
  self.events_queue: AbstractQueue
@@ -130,23 +128,23 @@ class RabbitMQMessageBus(InfrahubMessageBus):
130
128
 
131
129
  clear_log_context()
132
130
  if message.routing_key in messages.MESSAGE_MAP:
133
- await execute_message(routing_key=message.routing_key, message_body=message.body, service=self.service)
131
+ await execute_message(routing_key=message.routing_key, message_body=message.body, message_bus=self)
134
132
  else:
135
- self.service.log.error("Invalid message received", message=f"{message!r}")
133
+ get_logger().error("Invalid message received", message=f"{message!r}")
136
134
 
137
135
  async def on_message(self, message: AbstractIncomingMessage) -> None:
138
136
  async with message.process():
139
137
  clear_log_context()
140
138
  if message.routing_key in messages.MESSAGE_MAP:
141
- await execute_message(routing_key=message.routing_key, message_body=message.body, service=self.service)
139
+ await execute_message(routing_key=message.routing_key, message_body=message.body, message_bus=self)
142
140
  else:
143
- self.service.log.error("Invalid message received", message=f"{message!r}")
141
+ get_logger().error("Invalid message received", message=f"{message!r}")
144
142
 
145
143
  async def on_reconnect(
146
144
  self,
147
145
  weak: bool = False, # noqa: ARG002
148
146
  ) -> None:
149
- self.service.log.info("Reconnected to RabbitMQ, reinitializing connection")
147
+ get_logger().info("Reconnected to RabbitMQ, reinitializing connection")
150
148
  await self._initialize_connection()
151
149
 
152
150
  async def _initialize_api_server(self) -> None:
@@ -246,7 +244,7 @@ class RabbitMQMessageBus(InfrahubMessageBus):
246
244
  request_id = log_data.get("request_id", "")
247
245
  message.meta = Meta(request_id=request_id, correlation_id=correlation_id, reply_to=self.callback_queue.name)
248
246
 
249
- await self.service.message_bus.send(message=message)
247
+ await self.send(message=message)
250
248
 
251
249
  response: AbstractIncomingMessage = await future
252
250
  data = ujson.loads(response.body)
@@ -6,6 +6,7 @@ from typing import TYPE_CHECKING, Any, Callable, ParamSpec, TypeVar, overload
6
6
  if TYPE_CHECKING:
7
7
  from infrahub.context import InfrahubContext
8
8
  from infrahub.workflows.models import WorkflowDefinition, WorkflowInfo
9
+
9
10
  Return = TypeVar("Return")
10
11
  Params = ParamSpec("Params")
11
12
 
@@ -5,19 +5,16 @@ from typing import Any
5
5
 
6
6
  from typing_extensions import TYPE_CHECKING
7
7
 
8
- from infrahub.workers.utils import inject_context_parameter, inject_service_parameter
8
+ from infrahub.workers.utils import inject_context_parameter
9
9
  from infrahub.workflows.models import WorkflowDefinition, WorkflowInfo
10
10
 
11
11
  from . import InfrahubWorkflow, Return
12
12
 
13
13
  if TYPE_CHECKING:
14
14
  from infrahub.context import InfrahubContext
15
- from infrahub.services import InfrahubServices
16
15
 
17
16
 
18
17
  class WorkflowLocalExecution(InfrahubWorkflow):
19
- service: InfrahubServices | None = None # needed for local injections
20
-
21
18
  async def execute_workflow(
22
19
  self,
23
20
  workflow: WorkflowDefinition,
@@ -26,12 +23,8 @@ class WorkflowLocalExecution(InfrahubWorkflow):
26
23
  parameters: dict[str, Any] | None = None,
27
24
  tags: list[str] | None = None, # noqa: ARG002
28
25
  ) -> Any:
29
- if self.service is None:
30
- raise ValueError("WorkflowLocalExecution.service is not initialized")
31
-
32
26
  flow_func = workflow.load_function()
33
27
  parameters = dict(parameters) if parameters is not None else {} # avoid mutating input parameters
34
- inject_service_parameter(func=flow_func, parameters=parameters, service=self.service)
35
28
  inject_context_parameter(func=flow_func, parameters=parameters, context=context)
36
29
 
37
30
  parameters = flow_func.validate_parameters(parameters=parameters)
@@ -15,7 +15,8 @@ from infrahub.worker import WORKER_IDENTITY
15
15
 
16
16
  if TYPE_CHECKING:
17
17
  from infrahub.database import InfrahubDatabase
18
- from infrahub.services import InfrahubCache, InfrahubMessageBus
18
+ from infrahub.services import InfrahubCache
19
+ from infrahub.services.adapters.message_bus import InfrahubMessageBus
19
20
 
20
21
  PRIMARY_API_SERVER = "workers:primary:api_server"
21
22
  WORKER_MATCH = re.compile(r":worker:([^:]+)")
@@ -172,6 +172,46 @@ class PrefectEventData(PrefectEventModel):
172
172
 
173
173
  return {"members": members, "ancestors": ancestors}
174
174
 
175
+ def _return_proposed_change_event(self) -> dict[str, Any]:
176
+ data = {}
177
+ for resource in self.related:
178
+ if resource.role != "infrahub.related.node":
179
+ continue
180
+ match self.event:
181
+ case "infrahub.proposed_change.merged":
182
+ data.update(
183
+ {
184
+ "merged_by_account_id": resource.get("infrahub.node.id"),
185
+ "merged_by_account_name": resource.get("infrahub.merged_by.account.name"),
186
+ }
187
+ )
188
+ case "infrahub.proposed_change.review_requested":
189
+ data.update(
190
+ {
191
+ "requested_by_account_id": resource.get("infrahub.node.id"),
192
+ "requested_by_account_name": resource.get("infrahub.requested_by.account.name"),
193
+ }
194
+ )
195
+ case (
196
+ "infrahub.proposed_change.approved"
197
+ | "infrahub.proposed_change.rejected"
198
+ | "infrahub.proposed_change.approval_revoked"
199
+ | "infrahub.proposed_change.rejection_revoked"
200
+ ):
201
+ data.update(
202
+ {
203
+ "reviewer_account_id": resource.get("infrahub.node.id"),
204
+ "reviewer_account_name": resource.get("infrahub.reviewer.account.name"),
205
+ }
206
+ )
207
+ return data
208
+
209
+ def _return_proposed_change_reviewer_decision(self) -> dict[str, Any]:
210
+ return {"reviewer_decision": self.resource.get("infrahub.proposed_change.reviewer_decision")}
211
+
212
+ def _return_proposed_change_reviewer_former_decision(self) -> dict[str, Any]:
213
+ return {"reviewer_former_decision": self.resource.get("infrahub.proposed_change.reviewer_former_decision")}
214
+
175
215
  def _return_event_specifics(self) -> dict[str, Any]:
176
216
  """Return event specific data based on the type of event being processed"""
177
217
 
@@ -192,6 +232,18 @@ class PrefectEventData(PrefectEventModel):
192
232
  event_specifics = self._return_branch_rebased()
193
233
  case "infrahub.group.member_added" | "infrahub.group.member_removed":
194
234
  event_specifics = self._return_group_event()
235
+ case "infrahub.proposed_change.approved" | "infrahub.proposed_change.rejected":
236
+ event_specifics = {
237
+ **self._return_proposed_change_event(),
238
+ **self._return_proposed_change_reviewer_decision(),
239
+ }
240
+ case "infrahub.proposed_change.approval_revoked" | "infrahub.proposed_change.rejection_revoked":
241
+ event_specifics = {
242
+ **self._return_proposed_change_event(),
243
+ **self._return_proposed_change_reviewer_former_decision(),
244
+ }
245
+ case "infrahub.proposed_change.review_requested" | "infrahub.proposed_change.merged":
246
+ event_specifics = self._return_proposed_change_event()
195
247
 
196
248
  return event_specifics
197
249
 
@@ -14,10 +14,12 @@ from prefect.events.filters import (
14
14
  EventRelatedFilter,
15
15
  EventResourceFilter,
16
16
  )
17
+ from prefect.events.filters import EventOrder as PrefectEventOrder
17
18
  from prefect.events.schemas.events import ResourceSpecification
18
19
  from pydantic import BaseModel, Field
19
20
 
20
21
  from infrahub.core.timestamp import Timestamp
22
+ from infrahub.events.constants import EventSortOrder
21
23
 
22
24
  from .constants import LOG_LEVEL_MAPPING
23
25
 
@@ -176,6 +178,7 @@ class InfrahubEventFilter(EventFilter):
176
178
  @classmethod
177
179
  def from_filters(
178
180
  cls,
181
+ order: EventSortOrder,
179
182
  ids: list[str] | None = None,
180
183
  account__ids: list[str] | None = None,
181
184
  related_node__ids: list[str] | None = None,
@@ -201,6 +204,12 @@ class InfrahubEventFilter(EventFilter):
201
204
  else:
202
205
  filters = cls()
203
206
 
207
+ match order:
208
+ case EventSortOrder.ASC:
209
+ filters.order = PrefectEventOrder.ASC
210
+ case EventSortOrder.DESC:
211
+ filters.order = PrefectEventOrder.DESC
212
+
204
213
  filters.add_event_filter(level=level, has_children=has_children)
205
214
  filters.add_event_id_filter(ids=ids)
206
215
  filters.add_event_type_filter(event_type=event_type, event_type_filter=event_type_filter)
@@ -6,20 +6,19 @@ from infrahub import lock
6
6
  from infrahub.artifacts.models import CheckArtifactCreate
7
7
  from infrahub.core.constants import InfrahubKind
8
8
  from infrahub.git.models import RequestArtifactGenerate
9
- from infrahub.services import InfrahubServices
9
+ from infrahub.workers.dependencies import get_client
10
10
 
11
11
 
12
12
  @task(name="define-artifact", task_run_name="Define Artifact", cache_policy=NONE) # type: ignore[arg-type]
13
- async def define_artifact(
14
- model: CheckArtifactCreate | RequestArtifactGenerate, service: InfrahubServices
15
- ) -> tuple[InfrahubNode, bool]:
13
+ async def define_artifact(model: CheckArtifactCreate | RequestArtifactGenerate) -> tuple[InfrahubNode, bool]:
16
14
  """Return an artifact together with a flag to indicate if the artifact is created now or already existed."""
15
+ client = get_client()
17
16
  created = False
18
17
  if model.artifact_id:
19
- artifact = await service.client.get(kind=InfrahubKind.ARTIFACT, id=model.artifact_id, branch=model.branch_name)
18
+ artifact = await client.get(kind=InfrahubKind.ARTIFACT, id=model.artifact_id, branch=model.branch_name)
20
19
  else:
21
20
  async with lock.registry.get(f"{model.target_id}-{model.artifact_definition}", namespace="artifact"):
22
- artifacts = await service.client.filters(
21
+ artifacts = await client.filters(
23
22
  kind=InfrahubKind.ARTIFACT,
24
23
  branch=model.branch_name,
25
24
  definition__ids=[model.artifact_definition],
@@ -28,7 +27,7 @@ async def define_artifact(
28
27
  if artifacts:
29
28
  artifact = artifacts[0]
30
29
  else:
31
- artifact = await service.client.create(
30
+ artifact = await client.create(
32
31
  kind=InfrahubKind.ARTIFACT,
33
32
  branch=model.branch_name,
34
33
  data={
infrahub/tasks/check.py CHANGED
@@ -1,17 +1,14 @@
1
1
  from infrahub.log import get_logger
2
2
  from infrahub.message_bus import InfrahubMessage
3
3
  from infrahub.message_bus.types import KVTTL
4
- from infrahub.services import InfrahubServices
4
+ from infrahub.workers.dependencies import get_cache
5
5
 
6
6
  log = get_logger()
7
7
 
8
8
 
9
- async def set_check_status(message: InfrahubMessage, conclusion: str, service: InfrahubServices) -> None:
9
+ async def set_check_status(message: InfrahubMessage, conclusion: str) -> None:
10
10
  if message.meta.validator_execution_id and message.meta.check_execution_id:
11
11
  key = f"validator_execution_id:{message.meta.validator_execution_id}:check_execution_id:{message.meta.check_execution_id}"
12
- await service.cache.set(
13
- key=key,
14
- value=conclusion,
15
- expires=KVTTL.TWO_HOURS,
16
- )
12
+ cache = await get_cache()
13
+ await cache.set(key=key, value=conclusion, expires=KVTTL.TWO_HOURS)
17
14
  log.debug("setting check status in cache", key=key, conclusion=conclusion)