infrahub-server 1.1.6__py3-none-any.whl → 1.2.0rc0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (346) hide show
  1. infrahub/api/artifact.py +16 -4
  2. infrahub/api/dependencies.py +8 -0
  3. infrahub/api/oauth2.py +0 -1
  4. infrahub/api/oidc.py +0 -1
  5. infrahub/api/query.py +18 -7
  6. infrahub/api/schema.py +32 -6
  7. infrahub/api/transformation.py +12 -5
  8. infrahub/{message_bus/messages/check_artifact_create.py → artifacts/models.py} +2 -4
  9. infrahub/{message_bus/operations/check/artifact.py → artifacts/tasks.py} +26 -25
  10. infrahub/cli/__init__.py +0 -2
  11. infrahub/cli/db.py +6 -7
  12. infrahub/cli/events.py +8 -3
  13. infrahub/cli/git_agent.py +9 -7
  14. infrahub/cli/tasks.py +4 -6
  15. infrahub/computed_attribute/tasks.py +63 -17
  16. infrahub/computed_attribute/triggers.py +90 -0
  17. infrahub/config.py +1 -1
  18. infrahub/context.py +39 -0
  19. infrahub/core/account.py +5 -8
  20. infrahub/core/attribute.py +53 -21
  21. infrahub/core/branch/models.py +4 -4
  22. infrahub/core/branch/tasks.py +89 -130
  23. infrahub/core/changelog/__init__.py +0 -0
  24. infrahub/core/changelog/diff.py +232 -0
  25. infrahub/core/changelog/models.py +488 -0
  26. infrahub/core/constants/__init__.py +19 -2
  27. infrahub/core/constants/infrahubkind.py +1 -0
  28. infrahub/core/diff/combiner.py +12 -8
  29. infrahub/core/diff/coordinator.py +49 -70
  30. infrahub/core/diff/data_check_synchronizer.py +86 -7
  31. infrahub/core/diff/enricher/aggregated.py +3 -3
  32. infrahub/core/diff/enricher/cardinality_one.py +2 -7
  33. infrahub/core/diff/enricher/hierarchy.py +5 -3
  34. infrahub/core/diff/enricher/labels.py +14 -4
  35. infrahub/core/diff/enricher/path_identifier.py +3 -9
  36. infrahub/core/diff/enricher/summary_counts.py +3 -1
  37. infrahub/core/diff/merger/merger.py +8 -4
  38. infrahub/core/diff/model/path.py +47 -29
  39. infrahub/core/diff/query/all_conflicts.py +6 -3
  40. infrahub/core/diff/query/artifact.py +1 -1
  41. infrahub/core/diff/query/delete_query.py +1 -1
  42. infrahub/core/diff/query/diff_get.py +3 -2
  43. infrahub/core/diff/query/diff_summary.py +1 -1
  44. infrahub/core/diff/query/field_specifiers.py +3 -1
  45. infrahub/core/diff/query/field_summary.py +3 -2
  46. infrahub/core/diff/query/filters.py +12 -1
  47. infrahub/core/diff/query/get_conflict_query.py +1 -1
  48. infrahub/core/diff/query/has_conflicts_query.py +6 -3
  49. infrahub/core/diff/query/merge.py +3 -3
  50. infrahub/core/diff/query/{drop_tracking_id.py → merge_tracking_id.py} +4 -4
  51. infrahub/core/diff/query/roots_metadata.py +9 -2
  52. infrahub/core/diff/query/save.py +151 -66
  53. infrahub/core/diff/query/summary_counts_enricher.py +220 -0
  54. infrahub/core/diff/query/time_range_query.py +3 -2
  55. infrahub/core/diff/query/update_conflict_query.py +1 -1
  56. infrahub/core/diff/query_parser.py +49 -24
  57. infrahub/core/diff/repository/deserializer.py +24 -25
  58. infrahub/core/diff/repository/repository.py +76 -20
  59. infrahub/core/diff/tasks.py +9 -8
  60. infrahub/core/enums.py +1 -1
  61. infrahub/core/integrity/object_conflict/conflict_recorder.py +1 -1
  62. infrahub/core/ipam/reconciler.py +1 -1
  63. infrahub/core/ipam/tasks.py +2 -3
  64. infrahub/core/manager.py +18 -13
  65. infrahub/core/merge.py +5 -2
  66. infrahub/core/migrations/graph/m001_add_version_to_graph.py +1 -1
  67. infrahub/core/migrations/graph/m002_attribute_is_default.py +2 -2
  68. infrahub/core/migrations/graph/m003_relationship_parent_optional.py +2 -2
  69. infrahub/core/migrations/graph/m004_add_attr_documentation.py +1 -1
  70. infrahub/core/migrations/graph/m005_add_rel_read_only.py +1 -1
  71. infrahub/core/migrations/graph/m006_add_rel_on_delete.py +1 -1
  72. infrahub/core/migrations/graph/m007_add_rel_allow_override.py +1 -1
  73. infrahub/core/migrations/graph/m008_add_human_friendly_id.py +1 -1
  74. infrahub/core/migrations/graph/m009_add_generate_profile_attr.py +1 -1
  75. infrahub/core/migrations/graph/m010_add_generate_profile_attr_generic.py +1 -1
  76. infrahub/core/migrations/graph/m011_remove_profile_relationship_schema.py +2 -2
  77. infrahub/core/migrations/graph/m012_convert_account_generic.py +12 -23
  78. infrahub/core/migrations/graph/m013_convert_git_password_credential.py +7 -11
  79. infrahub/core/migrations/graph/m014_remove_index_attr_value.py +2 -2
  80. infrahub/core/migrations/graph/m015_diff_format_update.py +1 -1
  81. infrahub/core/migrations/graph/m016_diff_delete_bug_fix.py +1 -1
  82. infrahub/core/migrations/graph/m017_add_core_profile.py +1 -1
  83. infrahub/core/migrations/graph/m018_uniqueness_nulls.py +2 -2
  84. infrahub/core/migrations/query/attribute_add.py +1 -1
  85. infrahub/core/migrations/query/attribute_rename.py +1 -1
  86. infrahub/core/migrations/query/delete_element_in_schema.py +1 -1
  87. infrahub/core/migrations/query/node_duplicate.py +1 -1
  88. infrahub/core/migrations/query/relationship_duplicate.py +1 -1
  89. infrahub/core/migrations/query/schema_attribute_update.py +1 -1
  90. infrahub/core/migrations/schema/node_attribute_remove.py +1 -1
  91. infrahub/core/migrations/schema/node_remove.py +1 -1
  92. infrahub/core/migrations/schema/tasks.py +5 -5
  93. infrahub/core/migrations/shared.py +4 -4
  94. infrahub/core/models.py +7 -8
  95. infrahub/core/node/__init__.py +161 -40
  96. infrahub/core/node/base.py +1 -1
  97. infrahub/core/node/constraints/grouped_uniqueness.py +9 -2
  98. infrahub/core/node/delete_validator.py +4 -4
  99. infrahub/core/node/ipam.py +13 -8
  100. infrahub/core/node/permissions.py +4 -0
  101. infrahub/core/node/resource_manager/ip_prefix_pool.py +8 -5
  102. infrahub/core/node/standard.py +3 -5
  103. infrahub/core/property.py +1 -1
  104. infrahub/core/protocols.py +4 -0
  105. infrahub/core/protocols_base.py +4 -2
  106. infrahub/core/query/__init__.py +2 -5
  107. infrahub/core/query/attribute.py +9 -9
  108. infrahub/core/query/branch.py +5 -5
  109. infrahub/core/query/delete.py +1 -1
  110. infrahub/core/query/diff.py +45 -7
  111. infrahub/core/query/ipam.py +4 -4
  112. infrahub/core/query/node.py +19 -14
  113. infrahub/core/query/relationship.py +10 -11
  114. infrahub/core/query/resource_manager.py +13 -11
  115. infrahub/core/query/standard_node.py +6 -6
  116. infrahub/core/query/task.py +3 -3
  117. infrahub/core/query/task_log.py +1 -1
  118. infrahub/core/query/utils.py +5 -5
  119. infrahub/core/registry.py +0 -2
  120. infrahub/core/relationship/constraints/count.py +1 -1
  121. infrahub/core/relationship/constraints/peer_kind.py +1 -1
  122. infrahub/core/relationship/model.py +66 -26
  123. infrahub/core/schema/__init__.py +6 -4
  124. infrahub/core/schema/basenode_schema.py +1 -3
  125. infrahub/core/schema/definitions/core.py +14 -2
  126. infrahub/core/schema/definitions/internal.py +16 -0
  127. infrahub/core/schema/generated/genericnode_schema.py +5 -0
  128. infrahub/core/schema/generated/node_schema.py +5 -0
  129. infrahub/core/schema/generic_schema.py +5 -1
  130. infrahub/core/schema/manager.py +45 -42
  131. infrahub/core/schema/node_schema.py +4 -0
  132. infrahub/core/schema/profile_schema.py +4 -0
  133. infrahub/core/schema/relationship_schema.py +2 -2
  134. infrahub/core/schema/schema_branch.py +248 -14
  135. infrahub/core/schema/template_schema.py +36 -0
  136. infrahub/core/task/user_task.py +7 -5
  137. infrahub/core/timestamp.py +1 -1
  138. infrahub/core/utils.py +3 -2
  139. infrahub/core/validators/attribute/choices.py +1 -1
  140. infrahub/core/validators/attribute/enum.py +1 -1
  141. infrahub/core/validators/attribute/kind.py +1 -1
  142. infrahub/core/validators/attribute/length.py +1 -1
  143. infrahub/core/validators/attribute/optional.py +1 -1
  144. infrahub/core/validators/attribute/regex.py +1 -1
  145. infrahub/core/validators/attribute/unique.py +1 -1
  146. infrahub/core/validators/checks_runner.py +37 -0
  147. infrahub/core/validators/node/generate_profile.py +1 -1
  148. infrahub/core/validators/node/hierarchy.py +1 -1
  149. infrahub/core/validators/query.py +1 -1
  150. infrahub/core/validators/relationship/count.py +1 -1
  151. infrahub/core/validators/relationship/optional.py +1 -1
  152. infrahub/core/validators/relationship/peer.py +1 -1
  153. infrahub/core/validators/tasks.py +8 -6
  154. infrahub/core/validators/uniqueness/query.py +20 -17
  155. infrahub/database/__init__.py +15 -2
  156. infrahub/database/memgraph.py +1 -1
  157. infrahub/dependencies/builder/constraint/grouped/node_runner.py +0 -2
  158. infrahub/dependencies/builder/diff/combiner.py +1 -1
  159. infrahub/dependencies/builder/diff/conflicts_enricher.py +1 -1
  160. infrahub/dependencies/builder/diff/coordinator.py +0 -2
  161. infrahub/dependencies/builder/diff/deserializer.py +1 -1
  162. infrahub/dependencies/builder/diff/enricher/summary_counts.py +1 -1
  163. infrahub/events/branch_action.py +47 -21
  164. infrahub/events/group_action.py +73 -0
  165. infrahub/events/models.py +159 -51
  166. infrahub/events/node_action.py +74 -8
  167. infrahub/events/repository_action.py +8 -8
  168. infrahub/events/schema_action.py +21 -8
  169. infrahub/generators/tasks.py +12 -13
  170. infrahub/git/base.py +3 -5
  171. infrahub/git/constants.py +0 -1
  172. infrahub/git/integrator.py +36 -35
  173. infrahub/git/repository.py +7 -8
  174. infrahub/git/tasks.py +43 -107
  175. infrahub/git_credential/helper.py +2 -3
  176. infrahub/graphql/analyzer.py +572 -11
  177. infrahub/graphql/app.py +34 -26
  178. infrahub/graphql/auth/query_permission_checker/anonymous_checker.py +5 -5
  179. infrahub/graphql/auth/query_permission_checker/default_branch_checker.py +4 -4
  180. infrahub/graphql/auth/query_permission_checker/merge_operation_checker.py +4 -4
  181. infrahub/graphql/auth/query_permission_checker/object_permission_checker.py +28 -35
  182. infrahub/graphql/auth/query_permission_checker/super_admin_checker.py +5 -5
  183. infrahub/graphql/enums.py +1 -1
  184. infrahub/graphql/initialization.py +5 -1
  185. infrahub/graphql/loaders/node.py +2 -2
  186. infrahub/graphql/manager.py +59 -54
  187. infrahub/graphql/mutations/account.py +20 -13
  188. infrahub/graphql/mutations/artifact_definition.py +16 -12
  189. infrahub/graphql/mutations/branch.py +61 -40
  190. infrahub/graphql/mutations/computed_attribute.py +19 -13
  191. infrahub/graphql/mutations/diff.py +37 -9
  192. infrahub/graphql/mutations/diff_conflict.py +9 -8
  193. infrahub/graphql/mutations/graphql_query.py +19 -11
  194. infrahub/graphql/mutations/ipam.py +21 -19
  195. infrahub/graphql/mutations/main.py +197 -44
  196. infrahub/graphql/mutations/menu.py +8 -8
  197. infrahub/graphql/mutations/proposed_change.py +36 -28
  198. infrahub/graphql/mutations/relationship.py +302 -105
  199. infrahub/graphql/mutations/repository.py +41 -35
  200. infrahub/graphql/mutations/resource_manager.py +26 -26
  201. infrahub/graphql/mutations/schema.py +51 -33
  202. infrahub/graphql/mutations/tasks.py +16 -10
  203. infrahub/graphql/parser.py +1 -1
  204. infrahub/graphql/permissions.py +6 -4
  205. infrahub/graphql/queries/account.py +22 -18
  206. infrahub/graphql/queries/branch.py +6 -4
  207. infrahub/graphql/queries/diff/tree.py +48 -42
  208. infrahub/graphql/queries/event.py +112 -0
  209. infrahub/graphql/queries/internal.py +3 -3
  210. infrahub/graphql/queries/ipam.py +23 -18
  211. infrahub/graphql/queries/relationship.py +11 -10
  212. infrahub/graphql/queries/resource_manager.py +43 -27
  213. infrahub/graphql/queries/search.py +9 -8
  214. infrahub/graphql/queries/status.py +12 -9
  215. infrahub/graphql/queries/task.py +11 -9
  216. infrahub/graphql/resolvers/resolver.py +69 -43
  217. infrahub/graphql/resolvers/single_relationship.py +16 -10
  218. infrahub/graphql/schema.py +2 -0
  219. infrahub/graphql/subscription/__init__.py +1 -1
  220. infrahub/graphql/subscription/events.py +1 -1
  221. infrahub/graphql/subscription/graphql_query.py +8 -8
  222. infrahub/graphql/types/branch.py +2 -2
  223. infrahub/graphql/types/common.py +6 -1
  224. infrahub/graphql/types/enums.py +2 -0
  225. infrahub/graphql/types/event.py +100 -0
  226. infrahub/graphql/types/interface.py +2 -2
  227. infrahub/graphql/types/node.py +3 -3
  228. infrahub/graphql/types/permission.py +2 -2
  229. infrahub/graphql/types/relationship.py +3 -3
  230. infrahub/graphql/types/standard_node.py +9 -11
  231. infrahub/graphql/utils.py +28 -182
  232. infrahub/groups/tasks.py +2 -3
  233. infrahub/lock.py +1 -1
  234. infrahub/menu/constants.py +1 -0
  235. infrahub/menu/generator.py +14 -3
  236. infrahub/menu/menu.py +116 -127
  237. infrahub/menu/models.py +4 -4
  238. infrahub/message_bus/messages/__init__.py +0 -4
  239. infrahub/message_bus/messages/event_branch_merge.py +3 -0
  240. infrahub/message_bus/messages/request_proposedchange_pipeline.py +2 -0
  241. infrahub/message_bus/operations/__init__.py +3 -5
  242. infrahub/message_bus/operations/check/__init__.py +2 -2
  243. infrahub/message_bus/operations/check/generator.py +1 -3
  244. infrahub/message_bus/operations/check/repository.py +1 -1
  245. infrahub/message_bus/operations/event/branch.py +7 -3
  246. infrahub/message_bus/operations/event/schema.py +1 -1
  247. infrahub/message_bus/operations/finalize/validator.py +1 -1
  248. infrahub/message_bus/operations/git/file.py +2 -2
  249. infrahub/message_bus/operations/git/repository.py +1 -1
  250. infrahub/message_bus/operations/requests/__init__.py +0 -2
  251. infrahub/message_bus/operations/requests/generator_definition.py +1 -1
  252. infrahub/message_bus/operations/requests/proposed_change.py +26 -11
  253. infrahub/message_bus/operations/requests/repository.py +2 -2
  254. infrahub/message_bus/operations/send/echo.py +1 -1
  255. infrahub/message_bus/types.py +1 -1
  256. infrahub/permissions/__init__.py +2 -1
  257. infrahub/permissions/types.py +26 -0
  258. infrahub/pools/prefix.py +29 -165
  259. infrahub/prefect_server/__init__.py +0 -0
  260. infrahub/prefect_server/app.py +18 -0
  261. infrahub/prefect_server/database.py +20 -0
  262. infrahub/prefect_server/events.py +28 -0
  263. infrahub/prefect_server/models.py +46 -0
  264. infrahub/proposed_change/models.py +15 -1
  265. infrahub/proposed_change/tasks.py +173 -35
  266. infrahub/pytest_plugin.py +4 -4
  267. infrahub/server.py +12 -11
  268. infrahub/services/__init__.py +147 -62
  269. infrahub/services/adapters/cache/__init__.py +7 -5
  270. infrahub/services/adapters/cache/nats.py +40 -22
  271. infrahub/services/adapters/cache/redis.py +0 -4
  272. infrahub/services/adapters/event/__init__.py +10 -18
  273. infrahub/services/adapters/http/__init__.py +0 -5
  274. infrahub/services/adapters/http/httpx.py +22 -15
  275. infrahub/services/adapters/message_bus/__init__.py +23 -6
  276. infrahub/services/adapters/message_bus/local.py +8 -6
  277. infrahub/services/adapters/message_bus/nats.py +12 -6
  278. infrahub/services/adapters/message_bus/rabbitmq.py +22 -9
  279. infrahub/services/adapters/workflow/__init__.py +11 -8
  280. infrahub/services/adapters/workflow/local.py +28 -7
  281. infrahub/services/adapters/workflow/worker.py +23 -7
  282. infrahub/services/component.py +38 -35
  283. infrahub/services/scheduler.py +32 -29
  284. infrahub/storage.py +2 -4
  285. infrahub/task_manager/constants.py +1 -1
  286. infrahub/task_manager/event.py +182 -0
  287. infrahub/task_manager/models.py +125 -1
  288. infrahub/task_manager/task.py +1 -1
  289. infrahub/tasks/artifact.py +14 -16
  290. infrahub/tasks/registry.py +1 -1
  291. infrahub/tasks/telemetry.py +13 -14
  292. infrahub/transformations/tasks.py +3 -5
  293. infrahub/trigger/__init__.py +0 -0
  294. infrahub/trigger/catalogue.py +15 -0
  295. infrahub/trigger/constants.py +9 -0
  296. infrahub/trigger/models.py +69 -0
  297. infrahub/trigger/tasks.py +85 -0
  298. infrahub/types.py +1 -1
  299. infrahub/utils.py +1 -1
  300. infrahub/webhook/constants.py +0 -2
  301. infrahub/webhook/models.py +8 -2
  302. infrahub/webhook/tasks.py +20 -73
  303. infrahub/webhook/triggers.py +20 -0
  304. infrahub/workers/infrahub_async.py +36 -25
  305. infrahub/workers/utils.py +63 -0
  306. infrahub/workflows/catalogue.py +13 -37
  307. infrahub/workflows/initialization.py +6 -8
  308. infrahub/workflows/models.py +3 -5
  309. infrahub/workflows/utils.py +1 -1
  310. infrahub_sdk/ctl/check.py +3 -3
  311. infrahub_sdk/ctl/cli_commands.py +11 -10
  312. infrahub_sdk/ctl/exceptions.py +0 -6
  313. infrahub_sdk/ctl/exporter.py +1 -1
  314. infrahub_sdk/ctl/generator.py +5 -5
  315. infrahub_sdk/ctl/importer.py +3 -2
  316. infrahub_sdk/ctl/menu.py +1 -1
  317. infrahub_sdk/ctl/object.py +1 -1
  318. infrahub_sdk/ctl/repository.py +23 -15
  319. infrahub_sdk/ctl/schema.py +2 -2
  320. infrahub_sdk/ctl/utils.py +4 -3
  321. infrahub_sdk/ctl/validate.py +2 -1
  322. infrahub_sdk/exceptions.py +6 -0
  323. infrahub_sdk/generator.py +3 -0
  324. infrahub_sdk/node.py +2 -2
  325. infrahub_sdk/schema/__init__.py +14 -2
  326. infrahub_sdk/schema/main.py +7 -0
  327. infrahub_sdk/utils.py +11 -1
  328. infrahub_sdk/yaml.py +2 -3
  329. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/METADATA +46 -12
  330. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/RECORD +338 -321
  331. infrahub_testcontainers/container.py +14 -6
  332. infrahub_testcontainers/docker-compose.test.yml +24 -5
  333. infrahub_testcontainers/haproxy.cfg +43 -0
  334. infrahub_testcontainers/helpers.py +85 -1
  335. infrahub/core/branch/constants.py +0 -2
  336. infrahub/graphql/query.py +0 -52
  337. infrahub/message_bus/messages/request_artifactdefinition_check.py +0 -17
  338. infrahub/message_bus/operations/requests/artifact_definition.py +0 -148
  339. infrahub/schema/constants.py +0 -1
  340. infrahub/schema/tasks.py +0 -76
  341. infrahub/services/adapters/database/__init__.py +0 -9
  342. infrahub_sdk/ctl/_file.py +0 -13
  343. /infrahub/{schema → artifacts}/__init__.py +0 -0
  344. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/LICENSE.txt +0 -0
  345. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/WHEEL +0 -0
  346. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/entry_points.txt +0 -0
@@ -33,7 +33,7 @@ async def _add_request_id(message: InfrahubMessage) -> None:
33
33
 
34
34
 
35
35
  class NATSMessageBus(InfrahubMessageBus):
36
- def __init__(self, settings: Optional[BrokerSettings] = None) -> None:
36
+ def __init__(self, component_type: ComponentType, settings: Optional[BrokerSettings] = None) -> None:
37
37
  self.settings = settings or config.SETTINGS.broker
38
38
 
39
39
  self.service: InfrahubServices
@@ -46,9 +46,15 @@ class NATSMessageBus(InfrahubMessageBus):
46
46
  self.loop = asyncio.get_running_loop()
47
47
  self.futures: MutableMapping[str, asyncio.Future] = {}
48
48
 
49
- async def initialize(self, service: InfrahubServices) -> None:
50
- self.service = service
49
+ self.component_type: ComponentType = component_type
51
50
 
51
+ @classmethod
52
+ async def new(cls, component_type: ComponentType, settings: Optional[BrokerSettings] = None) -> NATSMessageBus:
53
+ message_bus = cls(component_type=component_type, settings=settings)
54
+ await message_bus._initialize()
55
+ return message_bus
56
+
57
+ async def _initialize(self) -> None:
52
58
  tls_context = None
53
59
  if self.settings.tls_enabled:
54
60
  tls_context = ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH)
@@ -67,9 +73,9 @@ class NATSMessageBus(InfrahubMessageBus):
67
73
 
68
74
  self.jetstream = self.connection.jetstream()
69
75
 
70
- if self.service.component_type == ComponentType.API_SERVER:
76
+ if self.component_type == ComponentType.API_SERVER:
71
77
  await self._initialize_api_server()
72
- elif self.service.component_type == ComponentType.GIT_AGENT:
78
+ elif self.component_type == ComponentType.GIT_AGENT:
73
79
  await self._initialize_git_worker()
74
80
 
75
81
  async def shutdown(self) -> None:
@@ -282,7 +288,7 @@ class NATSMessageBus(InfrahubMessageBus):
282
288
  request_id=request_id, correlation_id=correlation_id, reply_to=self.callback_queue.config.name
283
289
  )
284
290
 
285
- await self.service.send(message=message)
291
+ await self.service.message_bus.send(message=message)
286
292
 
287
293
  response: nats.aio.msg.Msg = await future
288
294
  data = ujson.loads(response.data)
@@ -65,12 +65,14 @@ async def _add_request_id(message: InfrahubMessage) -> None:
65
65
 
66
66
 
67
67
  class RabbitMQMessageBus(InfrahubMessageBus):
68
- def __init__(self, settings: Optional[BrokerSettings] = None) -> None:
68
+ def __init__(
69
+ self, component_type: ComponentType = ComponentType.NONE, settings: Optional[BrokerSettings] = None
70
+ ) -> None:
69
71
  self.settings = settings or config.SETTINGS.broker
70
72
  self.channel: AbstractChannel
71
73
  self.exchange: AbstractExchange
72
74
  self.delayed_exchange: AbstractExchange
73
- self.service: InfrahubServices
75
+ self.service: InfrahubServices # Needed because we inject `service` while sending messages.
74
76
  self.connection: AbstractRobustConnection
75
77
  self.callback_queue: AbstractQueue
76
78
  self.events_queue: AbstractQueue
@@ -80,10 +82,17 @@ class RabbitMQMessageBus(InfrahubMessageBus):
80
82
  self.loop = asyncio.get_running_loop()
81
83
  self.futures: MutableMapping[str, asyncio.Future] = {}
82
84
 
83
- async def initialize(self, service: InfrahubServices) -> None:
85
+ self.component_type: ComponentType = component_type
86
+
87
+ @classmethod
88
+ async def new(cls, component_type: ComponentType, settings: Optional[BrokerSettings] = None) -> RabbitMQMessageBus:
89
+ message_bus = cls(component_type=component_type, settings=settings)
90
+ await message_bus._initialize()
91
+ return message_bus
92
+
93
+ async def _initialize(self) -> None:
84
94
  patch_spanbuilder_set_channel()
85
95
 
86
- self.service = service
87
96
  self.connection = await aio_pika.connect_robust(
88
97
  host=self.settings.address,
89
98
  login=self.settings.username,
@@ -103,9 +112,9 @@ class RabbitMQMessageBus(InfrahubMessageBus):
103
112
  async def _initialize_connection(self) -> None:
104
113
  self.channel = await self.connection.channel()
105
114
 
106
- if self.service.component_type == ComponentType.API_SERVER:
115
+ if self.component_type == ComponentType.API_SERVER:
107
116
  await self._initialize_api_server()
108
- elif self.service.component_type == ComponentType.GIT_AGENT:
117
+ elif self.component_type == ComponentType.GIT_AGENT:
109
118
  await self._initialize_git_worker()
110
119
 
111
120
  async def shutdown(self) -> None:
@@ -135,7 +144,7 @@ class RabbitMQMessageBus(InfrahubMessageBus):
135
144
 
136
145
  async def on_reconnect(
137
146
  self,
138
- weak: bool = False, # pylint: disable=unused-argument
147
+ weak: bool = False, # noqa: ARG002
139
148
  ) -> None:
140
149
  self.service.log.info("Reconnected to RabbitMQ, reinitializing connection")
141
150
  await self._initialize_connection()
@@ -209,7 +218,11 @@ class RabbitMQMessageBus(InfrahubMessageBus):
209
218
  await queue.consume(callback=self.on_message, no_ack=False)
210
219
 
211
220
  async def publish(
212
- self, message: InfrahubMessage, routing_key: str, delay: Optional[MessageTTL] = None, is_retry: bool = False
221
+ self,
222
+ message: InfrahubMessage,
223
+ routing_key: str,
224
+ delay: Optional[MessageTTL] = None,
225
+ is_retry: bool = False, # noqa: ARG002
213
226
  ) -> None:
214
227
  for enricher in self.message_enrichers:
215
228
  await enricher(message)
@@ -233,7 +246,7 @@ class RabbitMQMessageBus(InfrahubMessageBus):
233
246
  request_id = log_data.get("request_id", "")
234
247
  message.meta = Meta(request_id=request_id, correlation_id=correlation_id, reply_to=self.callback_queue.name)
235
248
 
236
- await self.service.send(message=message)
249
+ await self.service.message_bus.send(message=message)
237
250
 
238
251
  response: AbstractIncomingMessage = await future
239
252
  data = ujson.loads(response.body)
@@ -1,26 +1,24 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from abc import ABC, abstractmethod
3
4
  from typing import TYPE_CHECKING, Any, Callable, ParamSpec, TypeVar, overload
4
5
 
5
6
  if TYPE_CHECKING:
6
- from infrahub.services import InfrahubServices
7
+ from infrahub.context import InfrahubContext
7
8
  from infrahub.workflows.models import WorkflowDefinition, WorkflowInfo
8
-
9
9
  Return = TypeVar("Return")
10
10
  Params = ParamSpec("Params")
11
11
 
12
12
  FuncType = Callable[Params, Return]
13
13
 
14
14
 
15
- class InfrahubWorkflow:
16
- async def initialize(self, service: InfrahubServices) -> None:
17
- """Initialize the Workflow engine"""
18
-
15
+ class InfrahubWorkflow(ABC):
19
16
  @overload
20
17
  async def execute_workflow(
21
18
  self,
22
19
  workflow: WorkflowDefinition,
23
20
  expected_return: type[Return],
21
+ context: InfrahubContext | None = ...,
24
22
  parameters: dict[str, Any] | None = ...,
25
23
  tags: list[str] | None = ...,
26
24
  ) -> Return: ...
@@ -30,23 +28,28 @@ class InfrahubWorkflow:
30
28
  self,
31
29
  workflow: WorkflowDefinition,
32
30
  expected_return: None = ...,
31
+ context: InfrahubContext | None = ...,
33
32
  parameters: dict[str, Any] | None = ...,
34
33
  tags: list[str] | None = ...,
35
34
  ) -> Any: ...
36
35
 
36
+ @abstractmethod
37
37
  async def execute_workflow(
38
38
  self,
39
39
  workflow: WorkflowDefinition,
40
40
  expected_return: type[Return] | None = None,
41
+ context: InfrahubContext | None = None,
41
42
  parameters: dict[str, Any] | None = None,
42
43
  tags: list[str] | None = None,
43
44
  ) -> Any:
44
- raise NotImplementedError("InfrahubWorkflow.execute_workflow is an abstract method")
45
+ raise NotImplementedError()
45
46
 
47
+ @abstractmethod
46
48
  async def submit_workflow(
47
49
  self,
48
50
  workflow: WorkflowDefinition,
51
+ context: InfrahubContext | None = None,
49
52
  parameters: dict[str, Any] | None = None,
50
53
  tags: list[str] | None = None,
51
54
  ) -> WorkflowInfo:
52
- raise NotImplementedError("InfrahubWorkflow.submit_workflow is an abstract method")
55
+ raise NotImplementedError()
@@ -1,27 +1,48 @@
1
+ from __future__ import annotations
2
+
1
3
  import uuid
2
- from typing import Any
4
+ from typing import Any, Optional
5
+
6
+ from typing_extensions import TYPE_CHECKING
3
7
 
8
+ from infrahub.workers.utils import inject_context_parameter, inject_service_parameter
4
9
  from infrahub.workflows.models import WorkflowDefinition, WorkflowInfo
5
10
 
6
11
  from . import InfrahubWorkflow, Return
7
12
 
13
+ if TYPE_CHECKING:
14
+ from infrahub.context import InfrahubContext
15
+ from infrahub.services import InfrahubServices
16
+
8
17
 
9
18
  class WorkflowLocalExecution(InfrahubWorkflow):
19
+ service: Optional[InfrahubServices] = None # needed for local injections
20
+
10
21
  async def execute_workflow(
11
22
  self,
12
23
  workflow: WorkflowDefinition,
13
- expected_return: type[Return] | None = None,
24
+ expected_return: type[Return] | None = None, # noqa: ARG002
25
+ context: InfrahubContext | None = None,
14
26
  parameters: dict[str, Any] | None = None,
15
- tags: list[str] | None = None,
27
+ tags: list[str] | None = None, # noqa: ARG002
16
28
  ) -> Any:
17
- fn = workflow.get_function()
18
- return await fn(**parameters or {})
29
+ if self.service is None:
30
+ raise ValueError("WorkflowLocalExecution.service is not initialized")
31
+
32
+ flow_func = workflow.load_function()
33
+ 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
+ inject_context_parameter(func=flow_func, parameters=parameters, context=context)
36
+
37
+ parameters = flow_func.validate_parameters(parameters=parameters)
38
+ return await flow_func(**parameters)
19
39
 
20
40
  async def submit_workflow(
21
41
  self,
22
42
  workflow: WorkflowDefinition,
43
+ context: InfrahubContext | None = None,
23
44
  parameters: dict[str, Any] | None = None,
24
- tags: list[str] | None = None,
45
+ tags: list[str] | None = None, # noqa: ARG002
25
46
  ) -> WorkflowInfo:
26
- await self.execute_workflow(workflow=workflow, parameters=parameters)
47
+ await self.execute_workflow(workflow=workflow, context=context, parameters=parameters)
27
48
  return WorkflowInfo(id=uuid.uuid4())
@@ -5,6 +5,7 @@ from typing import TYPE_CHECKING, Any, overload
5
5
  from prefect.client.schemas.objects import StateType
6
6
  from prefect.deployments import run_deployment
7
7
 
8
+ from infrahub.workers.utils import inject_context_parameter
8
9
  from infrahub.workflows.initialization import setup_task_manager
9
10
  from infrahub.workflows.models import WorkflowInfo
10
11
 
@@ -13,15 +14,14 @@ from . import InfrahubWorkflow, Return
13
14
  if TYPE_CHECKING:
14
15
  from prefect.client.schemas.objects import FlowRun
15
16
 
16
- from infrahub.services import InfrahubServices
17
+ from infrahub.context import InfrahubContext
17
18
  from infrahub.workflows.models import WorkflowDefinition
18
19
 
19
20
 
20
21
  class WorkflowWorkerExecution(InfrahubWorkflow):
21
- async def initialize(self, service: InfrahubServices) -> None:
22
- """Initialize the Workflow engine"""
23
-
24
- if await service.component.is_primary_api():
22
+ @staticmethod
23
+ async def initialize(component_is_primary_server: bool) -> None:
24
+ if component_is_primary_server:
25
25
  await setup_task_manager()
26
26
 
27
27
  @overload
@@ -29,6 +29,7 @@ class WorkflowWorkerExecution(InfrahubWorkflow):
29
29
  self,
30
30
  workflow: WorkflowDefinition,
31
31
  expected_return: type[Return],
32
+ context: InfrahubContext | None = None,
32
33
  parameters: dict[str, Any] | None = ...,
33
34
  tags: list[str] | None = ...,
34
35
  ) -> Return: ...
@@ -38,17 +39,24 @@ class WorkflowWorkerExecution(InfrahubWorkflow):
38
39
  self,
39
40
  workflow: WorkflowDefinition,
40
41
  expected_return: None = ...,
42
+ context: InfrahubContext | None = ...,
41
43
  parameters: dict[str, Any] | None = ...,
42
44
  tags: list[str] | None = ...,
43
45
  ) -> Any: ...
44
46
 
47
+ # TODO Make expected_return mandatory and remove above overloads.
45
48
  async def execute_workflow(
46
49
  self,
47
50
  workflow: WorkflowDefinition,
48
- expected_return: type[Return] | None = None,
51
+ expected_return: type[Return] | None = None, # noqa: ARG002
52
+ context: InfrahubContext | None = None,
49
53
  parameters: dict[str, Any] | None = None,
50
54
  tags: list[str] | None = None,
51
55
  ) -> Any:
56
+ flow_func = workflow.load_function()
57
+ parameters = dict(parameters) if parameters is not None else {}
58
+ inject_context_parameter(func=flow_func, parameters=parameters, context=context)
59
+
52
60
  response: FlowRun = await run_deployment(
53
61
  name=workflow.full_name, poll_interval=1, parameters=parameters or {}, tags=tags
54
62
  ) # type: ignore[return-value, misc]
@@ -61,7 +69,15 @@ class WorkflowWorkerExecution(InfrahubWorkflow):
61
69
  return await response.state.result(raise_on_failure=True, fetch=True) # type: ignore[call-overload]
62
70
 
63
71
  async def submit_workflow(
64
- self, workflow: WorkflowDefinition, parameters: dict[str, Any] | None = None, tags: list[str] | None = None
72
+ self,
73
+ workflow: WorkflowDefinition,
74
+ context: InfrahubContext | None = None,
75
+ parameters: dict[str, Any] | None = None,
76
+ tags: list[str] | None = None,
65
77
  ) -> WorkflowInfo:
78
+ flow_func = workflow.load_function()
79
+ parameters = dict(parameters) if parameters is not None else {}
80
+ inject_context_parameter(func=flow_func, parameters=parameters, context=context)
81
+
66
82
  flow_run = await run_deployment(name=workflow.full_name, timeout=0, parameters=parameters or {}, tags=tags) # type: ignore[return-value, misc]
67
83
  return WorkflowInfo.from_flow(flow_run=flow_run)
@@ -3,54 +3,57 @@ from __future__ import annotations
3
3
  import re
4
4
  from typing import TYPE_CHECKING, Any, Optional
5
5
 
6
+ from attr import dataclass
7
+
6
8
  from infrahub.components import ComponentType
7
9
  from infrahub.core.constants import GLOBAL_BRANCH_NAME
8
10
  from infrahub.core.registry import registry
9
11
  from infrahub.core.timestamp import Timestamp
10
- from infrahub.exceptions import InitializationError
12
+ from infrahub.log import get_logger
11
13
  from infrahub.message_bus import messages
12
14
  from infrahub.message_bus.types import KVTTL
13
15
  from infrahub.worker import WORKER_IDENTITY
14
16
 
15
17
  if TYPE_CHECKING:
16
- from infrahub.services import InfrahubServices
18
+ from infrahub.database import InfrahubDatabase
19
+ from infrahub.services import InfrahubCache, InfrahubMessageBus
17
20
 
18
21
  PRIMARY_API_SERVER = "workers:primary:api_server"
19
22
  WORKER_MATCH = re.compile(r":worker:([^:]+)")
20
23
 
24
+ log = get_logger()
21
25
 
22
- class InfrahubComponent:
23
- def __init__(self) -> None:
24
- self._service: Optional[InfrahubServices] = None
25
-
26
- @property
27
- def service(self) -> InfrahubServices:
28
- if not self._service:
29
- raise InitializationError("Component has not been initialized")
30
26
 
31
- return self._service
27
+ @dataclass
28
+ class InfrahubComponent:
29
+ cache: InfrahubCache
30
+ db: InfrahubDatabase
31
+ message_bus: InfrahubMessageBus
32
+ component_type: ComponentType
33
+
34
+ @classmethod
35
+ async def new(
36
+ cls, cache: InfrahubCache, db: InfrahubDatabase, message_bus: InfrahubMessageBus, component_type: ComponentType
37
+ ) -> InfrahubComponent:
38
+ component = cls(cache=cache, db=db, message_bus=message_bus, component_type=component_type)
39
+ await component.refresh_heartbeat()
40
+ return component
32
41
 
33
42
  @property
34
43
  def component_names(self) -> list[str]:
35
44
  names = []
36
- if self.service.component_type == ComponentType.API_SERVER:
45
+ if self.component_type == ComponentType.API_SERVER:
37
46
  names.append("api_server")
38
- elif self.service.component_type == ComponentType.GIT_AGENT:
47
+ elif self.component_type == ComponentType.GIT_AGENT:
39
48
  names.append("git_agent")
40
49
  return names
41
50
 
42
- async def initialize(self, service: InfrahubServices) -> None:
43
- """Initialize the Message bus"""
44
- self._service = service
45
-
46
- await self.refresh_heartbeat()
47
-
48
- async def is_primary_api(self) -> bool:
49
- primary_identity = await self.service.cache.get(PRIMARY_API_SERVER)
51
+ async def is_primary_gunicorn_worker(self) -> bool:
52
+ primary_identity = await self.cache.get(PRIMARY_API_SERVER)
50
53
  return primary_identity == WORKER_IDENTITY
51
54
 
52
55
  async def list_workers(self, branch: str, schema_hash: bool) -> list[WorkerInfo]:
53
- keys = await self.service.cache.list_keys(filter_pattern="workers:*")
56
+ keys = await self.cache.list_keys(filter_pattern="workers:*")
54
57
 
55
58
  workers: dict[str, WorkerInfo] = {}
56
59
  for key in keys:
@@ -64,9 +67,9 @@ class InfrahubComponent:
64
67
  schema_hash_keys = []
65
68
  if schema_hash:
66
69
  schema_hash_keys = [key for key in keys if f":schema_hash:branch:{branch}" in key]
67
- response = await self.service.cache.get_values(keys=schema_hash_keys)
70
+ response = await self.cache.get_values(keys=schema_hash_keys)
68
71
 
69
- for key, value in zip(schema_hash_keys, response):
72
+ for key, value in zip(schema_hash_keys, response, strict=False):
70
73
  if match := WORKER_MATCH.search(key):
71
74
  identity = match.group(1)
72
75
  workers[identity].add_value(key=key, value=value)
@@ -82,14 +85,14 @@ class InfrahubComponent:
82
85
 
83
86
  # Use branch name if we cannot find branch id in cache
84
87
  branch_id: Optional[str] = None
85
- if branch_obj := await registry.get_branch(branch=branch, db=self.service.database):
88
+ if branch_obj := await registry.get_branch(branch=branch, db=self.db):
86
89
  branch_id = str(branch_obj.uuid)
87
90
 
88
91
  if not branch_id:
89
92
  branch_id = branch
90
93
 
91
94
  for component in self.component_names:
92
- await self.service.cache.set(
95
+ await self.cache.set(
93
96
  key=f"workers:schema_hash:branch:{branch_id}:{component}:worker:{WORKER_IDENTITY}",
94
97
  value=hash_value,
95
98
  expires=KVTTL.TWO_HOURS,
@@ -97,29 +100,29 @@ class InfrahubComponent:
97
100
 
98
101
  async def refresh_heartbeat(self) -> None:
99
102
  for component in self.component_names:
100
- await self.service.cache.set(
103
+ await self.cache.set(
101
104
  key=f"workers:active:{component}:worker:{WORKER_IDENTITY}",
102
105
  value=Timestamp().to_string(),
103
106
  expires=KVTTL.FIFTEEN,
104
107
  )
105
- if self.service.component_type == ComponentType.API_SERVER:
108
+ if self.component_type == ComponentType.API_SERVER:
106
109
  await self._set_primary_api_server()
107
- await self.service.cache.set(
110
+ await self.cache.set(
108
111
  key=f"workers:worker:{WORKER_IDENTITY}", value=Timestamp().to_string(), expires=KVTTL.TWO_HOURS
109
112
  )
110
113
 
111
114
  async def _set_primary_api_server(self) -> None:
112
- result = await self.service.cache.set(
115
+ result = await self.cache.set(
113
116
  key=PRIMARY_API_SERVER, value=WORKER_IDENTITY, expires=KVTTL.FIFTEEN, not_exists=True
114
117
  )
115
118
  if result:
116
- await self.service.send(message=messages.EventWorkerNewPrimaryAPI(worker_id=WORKER_IDENTITY))
119
+ await self.message_bus.send(message=messages.EventWorkerNewPrimaryAPI(worker_id=WORKER_IDENTITY))
117
120
  else:
118
- self.service.log.debug("Primary node already set")
119
- primary_id = await self.service.cache.get(key=PRIMARY_API_SERVER)
121
+ log.debug("Primary node already set")
122
+ primary_id = await self.cache.get(key=PRIMARY_API_SERVER)
120
123
  if primary_id == WORKER_IDENTITY:
121
- self.service.log.debug("Primary node set but same as ours, refreshing lifetime")
122
- await self.service.cache.set(key=PRIMARY_API_SERVER, value=WORKER_IDENTITY, expires=KVTTL.FIFTEEN)
124
+ log.debug("Primary node set but same as ours, refreshing lifetime")
125
+ await self.cache.set(key=PRIMARY_API_SERVER, value=WORKER_IDENTITY, expires=KVTTL.FIFTEEN)
123
126
 
124
127
 
125
128
  class WorkerInfo:
@@ -5,14 +5,19 @@ import random
5
5
  from dataclasses import dataclass
6
6
  from typing import TYPE_CHECKING
7
7
 
8
+ from typing_extensions import Optional
9
+
8
10
  from infrahub import config
9
11
  from infrahub.components import ComponentType
12
+ from infrahub.log import get_logger
10
13
  from infrahub.tasks.keepalive import refresh_heartbeat
11
14
  from infrahub.tasks.recurring import trigger_branch_refresh
12
15
 
13
16
  if TYPE_CHECKING:
14
17
  from infrahub.services import InfrahubServices, ServiceFunction
15
18
 
19
+ log = get_logger()
20
+
16
21
 
17
22
  @dataclass
18
23
  class Schedule:
@@ -23,18 +28,17 @@ class Schedule:
23
28
 
24
29
 
25
30
  class InfrahubScheduler:
26
- def __init__(self) -> None:
27
- self.service: InfrahubServices
31
+ # TODO we could remove service dependency by adding kwargs to Schedule instead of passing services
32
+ service: Optional[InfrahubServices]
33
+
34
+ def __init__(self, component_type: ComponentType) -> None:
28
35
  self.running: bool = False
29
36
  self.schedules: list[Schedule] = []
30
37
 
31
- async def initialize(self, service: InfrahubServices) -> None:
32
- self.service = service
33
-
34
38
  self.running = config.SETTINGS.miscellaneous.start_background_runner
35
39
  # Add some randomness to the interval to avoid having all workers pulling the latest update at the same time
36
40
  random_number = random.randint(0, 5)
37
- if self.service.component_type == ComponentType.API_SERVER:
41
+ if component_type == ComponentType.API_SERVER:
38
42
  schedules = [
39
43
  Schedule(name="refresh_api_components", interval=10, function=refresh_heartbeat, start_delay=0),
40
44
  Schedule(
@@ -43,7 +47,7 @@ class InfrahubScheduler:
43
47
  ]
44
48
  self.schedules.extend(schedules)
45
49
 
46
- if self.service.component_type == ComponentType.GIT_AGENT:
50
+ if component_type == ComponentType.GIT_AGENT:
47
51
  schedules = [
48
52
  Schedule(name="refresh_components", interval=10, function=refresh_heartbeat),
49
53
  Schedule(
@@ -54,32 +58,31 @@ class InfrahubScheduler:
54
58
 
55
59
  async def start_schedule(self) -> None:
56
60
  for schedule in self.schedules:
57
- asyncio.create_task(
58
- run_schedule(schedule=schedule, service=self.service), name=f"scheduled_task_{schedule.name}"
59
- )
61
+ asyncio.create_task(self.run_schedule(schedule=schedule), name=f"scheduled_task_{schedule.name}")
60
62
 
61
63
  async def shutdown(self) -> None:
62
64
  self.running = False
63
65
 
66
+ async def run_schedule(self, schedule: Schedule) -> None:
67
+ """Execute the task provided in the schedule as per the defined interval
64
68
 
65
- async def run_schedule(schedule: Schedule, service: InfrahubServices) -> None:
66
- """Execute the task provided in the schedule as per the defined interval
67
-
68
- Once the service is marked to be shutdown the scheduler will stop executing tasks.
69
- """
70
- for _ in range(schedule.start_delay):
71
- if not service.scheduler.running:
72
- return
73
- await asyncio.sleep(delay=1)
74
-
75
- service.log.info("Started recurring task", task=schedule.name)
76
-
77
- while service.scheduler.running:
78
- try:
79
- await schedule.function(service)
80
- except Exception as exc: # pylint: disable=broad-exception-caught
81
- service.log.error(str(exc))
82
- for _ in range(schedule.interval):
83
- if not service.scheduler.running:
69
+ Once the service is marked to be shutdown the scheduler will stop executing tasks.
70
+ """
71
+ for _ in range(schedule.start_delay):
72
+ if not self.running:
84
73
  return
85
74
  await asyncio.sleep(delay=1)
75
+
76
+ if self.service is None:
77
+ raise ValueError("InfrahubScheduler.service is None")
78
+
79
+ self.service.log.info("Started recurring task", task=schedule.name)
80
+ while self.running:
81
+ try:
82
+ await schedule.function(self.service)
83
+ except Exception as exc:
84
+ self.service.log.error(str(exc))
85
+ for _ in range(schedule.interval):
86
+ if not self.running:
87
+ return
88
+ await asyncio.sleep(delay=1)
infrahub/storage.py CHANGED
@@ -53,7 +53,5 @@ class InfrahubObjectStorage:
53
53
  try:
54
54
  with self._storage.open(identifier) as f:
55
55
  return f.read().decode()
56
- except (FileNotFoundError, botocore.exceptions.ClientError):
57
- raise NodeNotFoundError( # pylint: disable=raise-missing-from
58
- node_type="StorageObject", identifier=identifier
59
- )
56
+ except (FileNotFoundError, botocore.exceptions.ClientError) as err:
57
+ raise NodeNotFoundError(node_type="StorageObject", identifier=identifier) from err
@@ -2,7 +2,7 @@ from infrahub.core.constants import TaskConclusion
2
2
 
3
3
  LOG_LEVEL_MAPPING = {10: "debug", 20: "info", 30: "warning", 40: "error", 50: "critical"}
4
4
 
5
- CONCLUSION_STATE_MAPPING = {
5
+ CONCLUSION_STATE_MAPPING: dict[str, TaskConclusion] = {
6
6
  "Scheduled": TaskConclusion.UNKNOWN,
7
7
  "Pending": TaskConclusion.UNKNOWN,
8
8
  "Running": TaskConclusion.UNKNOWN,