infrahub-server 1.2.0b1__py3-none-any.whl → 1.2.1__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 (297) hide show
  1. infrahub/api/dependencies.py +6 -6
  2. infrahub/api/diff/validation_models.py +7 -7
  3. infrahub/api/schema.py +1 -1
  4. infrahub/artifacts/models.py +1 -3
  5. infrahub/artifacts/tasks.py +1 -3
  6. infrahub/cli/__init__.py +13 -9
  7. infrahub/cli/constants.py +3 -0
  8. infrahub/cli/db.py +165 -183
  9. infrahub/cli/upgrade.py +146 -0
  10. infrahub/computed_attribute/gather.py +185 -0
  11. infrahub/computed_attribute/models.py +239 -11
  12. infrahub/computed_attribute/tasks.py +77 -442
  13. infrahub/computed_attribute/triggers.py +11 -45
  14. infrahub/config.py +43 -32
  15. infrahub/context.py +14 -0
  16. infrahub/core/account.py +4 -4
  17. infrahub/core/attribute.py +57 -57
  18. infrahub/core/branch/tasks.py +12 -9
  19. infrahub/core/changelog/diff.py +16 -8
  20. infrahub/core/changelog/models.py +189 -26
  21. infrahub/core/constants/__init__.py +5 -1
  22. infrahub/core/constants/infrahubkind.py +2 -0
  23. infrahub/core/constraint/node/runner.py +9 -8
  24. infrahub/core/diff/branch_differ.py +10 -10
  25. infrahub/core/diff/ipam_diff_parser.py +4 -5
  26. infrahub/core/diff/model/diff.py +27 -27
  27. infrahub/core/diff/model/path.py +3 -3
  28. infrahub/core/diff/query/merge.py +20 -17
  29. infrahub/core/diff/query_parser.py +4 -4
  30. infrahub/core/graph/__init__.py +1 -1
  31. infrahub/core/initialization.py +1 -10
  32. infrahub/core/ipam/constants.py +3 -4
  33. infrahub/core/ipam/reconciler.py +12 -12
  34. infrahub/core/ipam/utilization.py +10 -13
  35. infrahub/core/manager.py +34 -34
  36. infrahub/core/merge.py +7 -7
  37. infrahub/core/migrations/__init__.py +2 -3
  38. infrahub/core/migrations/graph/__init__.py +9 -4
  39. infrahub/core/migrations/graph/m017_add_core_profile.py +1 -5
  40. infrahub/core/migrations/graph/m018_uniqueness_nulls.py +4 -4
  41. infrahub/core/migrations/graph/m020_duplicate_edges.py +160 -0
  42. infrahub/core/migrations/graph/m021_missing_hierarchy_merge.py +51 -0
  43. infrahub/core/migrations/graph/{m020_add_generate_template_attr.py → m022_add_generate_template_attr.py} +3 -3
  44. infrahub/core/migrations/graph/m023_deduplicate_cardinality_one_relationships.py +96 -0
  45. infrahub/core/migrations/query/attribute_add.py +2 -2
  46. infrahub/core/migrations/query/node_duplicate.py +18 -21
  47. infrahub/core/migrations/query/schema_attribute_update.py +2 -2
  48. infrahub/core/migrations/schema/models.py +19 -4
  49. infrahub/core/migrations/schema/tasks.py +2 -2
  50. infrahub/core/migrations/shared.py +16 -16
  51. infrahub/core/models.py +15 -6
  52. infrahub/core/node/__init__.py +29 -28
  53. infrahub/core/node/base.py +2 -4
  54. infrahub/core/node/constraints/attribute_uniqueness.py +2 -2
  55. infrahub/core/node/constraints/grouped_uniqueness.py +99 -47
  56. infrahub/core/node/constraints/interface.py +1 -2
  57. infrahub/core/node/delete_validator.py +3 -5
  58. infrahub/core/node/ipam.py +4 -4
  59. infrahub/core/node/permissions.py +7 -7
  60. infrahub/core/node/resource_manager/ip_address_pool.py +6 -6
  61. infrahub/core/node/resource_manager/ip_prefix_pool.py +6 -6
  62. infrahub/core/node/resource_manager/number_pool.py +3 -3
  63. infrahub/core/path.py +12 -12
  64. infrahub/core/property.py +11 -11
  65. infrahub/core/protocols.py +5 -0
  66. infrahub/core/protocols_base.py +21 -21
  67. infrahub/core/query/__init__.py +33 -33
  68. infrahub/core/query/attribute.py +6 -4
  69. infrahub/core/query/diff.py +3 -3
  70. infrahub/core/query/node.py +82 -32
  71. infrahub/core/query/relationship.py +24 -24
  72. infrahub/core/query/resource_manager.py +2 -0
  73. infrahub/core/query/standard_node.py +3 -3
  74. infrahub/core/query/subquery.py +9 -9
  75. infrahub/core/registry.py +13 -15
  76. infrahub/core/relationship/constraints/count.py +3 -4
  77. infrahub/core/relationship/constraints/peer_kind.py +3 -4
  78. infrahub/core/relationship/constraints/profiles_kind.py +2 -2
  79. infrahub/core/relationship/model.py +40 -46
  80. infrahub/core/schema/attribute_schema.py +9 -9
  81. infrahub/core/schema/basenode_schema.py +93 -44
  82. infrahub/core/schema/computed_attribute.py +3 -3
  83. infrahub/core/schema/definitions/core/__init__.py +13 -19
  84. infrahub/core/schema/definitions/core/account.py +151 -148
  85. infrahub/core/schema/definitions/core/artifact.py +122 -113
  86. infrahub/core/schema/definitions/core/builtin.py +19 -16
  87. infrahub/core/schema/definitions/core/check.py +61 -53
  88. infrahub/core/schema/definitions/core/core.py +17 -0
  89. infrahub/core/schema/definitions/core/generator.py +89 -85
  90. infrahub/core/schema/definitions/core/graphql_query.py +72 -70
  91. infrahub/core/schema/definitions/core/group.py +96 -93
  92. infrahub/core/schema/definitions/core/ipam.py +176 -235
  93. infrahub/core/schema/definitions/core/lineage.py +18 -16
  94. infrahub/core/schema/definitions/core/menu.py +42 -40
  95. infrahub/core/schema/definitions/core/permission.py +144 -142
  96. infrahub/core/schema/definitions/core/profile.py +16 -27
  97. infrahub/core/schema/definitions/core/propose_change.py +88 -79
  98. infrahub/core/schema/definitions/core/propose_change_comment.py +170 -165
  99. infrahub/core/schema/definitions/core/propose_change_validator.py +290 -288
  100. infrahub/core/schema/definitions/core/repository.py +231 -225
  101. infrahub/core/schema/definitions/core/resource_pool.py +156 -166
  102. infrahub/core/schema/definitions/core/template.py +27 -12
  103. infrahub/core/schema/definitions/core/transform.py +85 -76
  104. infrahub/core/schema/definitions/core/webhook.py +127 -101
  105. infrahub/core/schema/definitions/internal.py +16 -16
  106. infrahub/core/schema/dropdown.py +3 -4
  107. infrahub/core/schema/generated/attribute_schema.py +15 -18
  108. infrahub/core/schema/generated/base_node_schema.py +12 -14
  109. infrahub/core/schema/generated/node_schema.py +3 -5
  110. infrahub/core/schema/generated/relationship_schema.py +9 -11
  111. infrahub/core/schema/generic_schema.py +2 -2
  112. infrahub/core/schema/manager.py +20 -9
  113. infrahub/core/schema/node_schema.py +4 -2
  114. infrahub/core/schema/relationship_schema.py +7 -7
  115. infrahub/core/schema/schema_branch.py +276 -138
  116. infrahub/core/schema/schema_branch_computed.py +41 -4
  117. infrahub/core/task/task.py +3 -3
  118. infrahub/core/task/user_task.py +15 -15
  119. infrahub/core/utils.py +20 -18
  120. infrahub/core/validators/__init__.py +1 -3
  121. infrahub/core/validators/aggregated_checker.py +2 -2
  122. infrahub/core/validators/attribute/choices.py +2 -2
  123. infrahub/core/validators/attribute/enum.py +2 -2
  124. infrahub/core/validators/attribute/kind.py +2 -2
  125. infrahub/core/validators/attribute/length.py +2 -2
  126. infrahub/core/validators/attribute/optional.py +2 -2
  127. infrahub/core/validators/attribute/regex.py +2 -2
  128. infrahub/core/validators/attribute/unique.py +2 -2
  129. infrahub/core/validators/checks_runner.py +25 -2
  130. infrahub/core/validators/determiner.py +1 -3
  131. infrahub/core/validators/interface.py +6 -2
  132. infrahub/core/validators/model.py +22 -3
  133. infrahub/core/validators/models/validate_migration.py +17 -4
  134. infrahub/core/validators/node/attribute.py +2 -2
  135. infrahub/core/validators/node/generate_profile.py +2 -2
  136. infrahub/core/validators/node/hierarchy.py +3 -5
  137. infrahub/core/validators/node/inherit_from.py +27 -5
  138. infrahub/core/validators/node/relationship.py +2 -2
  139. infrahub/core/validators/relationship/count.py +4 -4
  140. infrahub/core/validators/relationship/optional.py +2 -2
  141. infrahub/core/validators/relationship/peer.py +2 -2
  142. infrahub/core/validators/shared.py +2 -2
  143. infrahub/core/validators/tasks.py +8 -0
  144. infrahub/core/validators/uniqueness/checker.py +22 -21
  145. infrahub/core/validators/uniqueness/index.py +2 -2
  146. infrahub/core/validators/uniqueness/model.py +11 -11
  147. infrahub/database/__init__.py +26 -22
  148. infrahub/database/metrics.py +7 -1
  149. infrahub/dependencies/builder/constraint/grouped/node_runner.py +1 -3
  150. infrahub/dependencies/component/registry.py +2 -2
  151. infrahub/events/__init__.py +25 -2
  152. infrahub/events/artifact_action.py +13 -25
  153. infrahub/events/branch_action.py +26 -18
  154. infrahub/events/generator.py +71 -0
  155. infrahub/events/group_action.py +10 -24
  156. infrahub/events/models.py +10 -16
  157. infrahub/events/node_action.py +87 -32
  158. infrahub/events/repository_action.py +5 -18
  159. infrahub/events/schema_action.py +4 -9
  160. infrahub/events/utils.py +16 -0
  161. infrahub/events/validator_action.py +55 -0
  162. infrahub/exceptions.py +23 -24
  163. infrahub/generators/models.py +1 -3
  164. infrahub/git/base.py +7 -7
  165. infrahub/git/integrator.py +26 -25
  166. infrahub/git/models.py +22 -9
  167. infrahub/git/repository.py +3 -3
  168. infrahub/git/tasks.py +67 -49
  169. infrahub/git/utils.py +48 -0
  170. infrahub/git/worktree.py +1 -2
  171. infrahub/git_credential/askpass.py +1 -2
  172. infrahub/graphql/analyzer.py +12 -0
  173. infrahub/graphql/app.py +13 -15
  174. infrahub/graphql/context.py +6 -0
  175. infrahub/graphql/initialization.py +3 -0
  176. infrahub/graphql/loaders/node.py +2 -12
  177. infrahub/graphql/loaders/peers.py +77 -0
  178. infrahub/graphql/loaders/shared.py +13 -0
  179. infrahub/graphql/manager.py +13 -10
  180. infrahub/graphql/mutations/artifact_definition.py +5 -5
  181. infrahub/graphql/mutations/computed_attribute.py +4 -5
  182. infrahub/graphql/mutations/graphql_query.py +5 -5
  183. infrahub/graphql/mutations/ipam.py +50 -70
  184. infrahub/graphql/mutations/main.py +164 -141
  185. infrahub/graphql/mutations/menu.py +5 -5
  186. infrahub/graphql/mutations/models.py +2 -4
  187. infrahub/graphql/mutations/node_getter/by_default_filter.py +10 -10
  188. infrahub/graphql/mutations/node_getter/by_hfid.py +1 -3
  189. infrahub/graphql/mutations/node_getter/by_id.py +1 -3
  190. infrahub/graphql/mutations/node_getter/interface.py +1 -2
  191. infrahub/graphql/mutations/proposed_change.py +7 -7
  192. infrahub/graphql/mutations/relationship.py +67 -35
  193. infrahub/graphql/mutations/repository.py +8 -8
  194. infrahub/graphql/mutations/resource_manager.py +3 -3
  195. infrahub/graphql/mutations/schema.py +4 -4
  196. infrahub/graphql/mutations/webhook.py +137 -0
  197. infrahub/graphql/parser.py +4 -4
  198. infrahub/graphql/queries/diff/tree.py +4 -4
  199. infrahub/graphql/queries/ipam.py +2 -2
  200. infrahub/graphql/queries/relationship.py +2 -2
  201. infrahub/graphql/queries/search.py +2 -2
  202. infrahub/graphql/resolvers/many_relationship.py +264 -0
  203. infrahub/graphql/resolvers/resolver.py +13 -110
  204. infrahub/graphql/subscription/graphql_query.py +2 -0
  205. infrahub/graphql/types/event.py +20 -11
  206. infrahub/graphql/types/node.py +2 -2
  207. infrahub/graphql/utils.py +2 -2
  208. infrahub/groups/ancestors.py +29 -0
  209. infrahub/groups/parsers.py +107 -0
  210. infrahub/menu/generator.py +7 -7
  211. infrahub/menu/menu.py +0 -10
  212. infrahub/menu/models.py +117 -16
  213. infrahub/menu/repository.py +111 -0
  214. infrahub/menu/utils.py +5 -8
  215. infrahub/message_bus/messages/__init__.py +1 -11
  216. infrahub/message_bus/messages/check_generator_run.py +2 -0
  217. infrahub/message_bus/messages/finalize_validator_execution.py +3 -0
  218. infrahub/message_bus/messages/request_generatordefinition_check.py +2 -0
  219. infrahub/message_bus/operations/__init__.py +0 -2
  220. infrahub/message_bus/operations/check/generator.py +1 -0
  221. infrahub/message_bus/operations/event/__init__.py +2 -2
  222. infrahub/message_bus/operations/finalize/validator.py +51 -1
  223. infrahub/message_bus/operations/requests/generator_definition.py +19 -19
  224. infrahub/message_bus/operations/requests/proposed_change.py +3 -1
  225. infrahub/pools/number.py +2 -4
  226. infrahub/proposed_change/tasks.py +37 -28
  227. infrahub/pytest_plugin.py +13 -10
  228. infrahub/server.py +1 -2
  229. infrahub/services/adapters/event/__init__.py +1 -1
  230. infrahub/task_manager/event.py +23 -9
  231. infrahub/tasks/artifact.py +2 -4
  232. infrahub/telemetry/__init__.py +0 -0
  233. infrahub/telemetry/constants.py +9 -0
  234. infrahub/telemetry/database.py +86 -0
  235. infrahub/telemetry/models.py +65 -0
  236. infrahub/telemetry/task_manager.py +77 -0
  237. infrahub/{tasks/telemetry.py → telemetry/tasks.py} +49 -56
  238. infrahub/telemetry/utils.py +11 -0
  239. infrahub/trace.py +4 -4
  240. infrahub/transformations/tasks.py +2 -2
  241. infrahub/trigger/catalogue.py +2 -5
  242. infrahub/trigger/constants.py +0 -8
  243. infrahub/trigger/models.py +14 -1
  244. infrahub/trigger/setup.py +90 -0
  245. infrahub/trigger/tasks.py +35 -90
  246. infrahub/utils.py +11 -1
  247. infrahub/validators/__init__.py +0 -0
  248. infrahub/validators/events.py +42 -0
  249. infrahub/validators/tasks.py +41 -0
  250. infrahub/webhook/gather.py +17 -0
  251. infrahub/webhook/models.py +22 -5
  252. infrahub/webhook/tasks.py +44 -19
  253. infrahub/webhook/triggers.py +22 -5
  254. infrahub/workers/infrahub_async.py +2 -2
  255. infrahub/workers/utils.py +2 -2
  256. infrahub/workflows/catalogue.py +28 -20
  257. infrahub/workflows/initialization.py +1 -3
  258. infrahub/workflows/models.py +1 -1
  259. infrahub/workflows/utils.py +10 -1
  260. infrahub_sdk/client.py +27 -8
  261. infrahub_sdk/config.py +3 -0
  262. infrahub_sdk/context.py +13 -0
  263. infrahub_sdk/exceptions.py +6 -0
  264. infrahub_sdk/generator.py +4 -1
  265. infrahub_sdk/graphql.py +45 -13
  266. infrahub_sdk/node.py +69 -20
  267. infrahub_sdk/protocols_base.py +32 -11
  268. infrahub_sdk/query_groups.py +6 -35
  269. infrahub_sdk/schema/__init__.py +55 -26
  270. infrahub_sdk/schema/main.py +8 -0
  271. infrahub_sdk/task/__init__.py +10 -0
  272. infrahub_sdk/task/manager.py +12 -6
  273. infrahub_sdk/testing/schemas/animal.py +9 -0
  274. infrahub_sdk/timestamp.py +12 -4
  275. {infrahub_server-1.2.0b1.dist-info → infrahub_server-1.2.1.dist-info}/METADATA +3 -2
  276. {infrahub_server-1.2.0b1.dist-info → infrahub_server-1.2.1.dist-info}/RECORD +289 -260
  277. {infrahub_server-1.2.0b1.dist-info → infrahub_server-1.2.1.dist-info}/entry_points.txt +1 -0
  278. infrahub_testcontainers/constants.py +2 -0
  279. infrahub_testcontainers/container.py +157 -12
  280. infrahub_testcontainers/docker-compose.test.yml +31 -6
  281. infrahub_testcontainers/helpers.py +18 -73
  282. infrahub_testcontainers/host.py +41 -0
  283. infrahub_testcontainers/measurements.py +93 -0
  284. infrahub_testcontainers/models.py +38 -0
  285. infrahub_testcontainers/performance_test.py +166 -0
  286. infrahub_testcontainers/plugin.py +136 -0
  287. infrahub_testcontainers/prometheus.yml +30 -0
  288. infrahub/message_bus/messages/event_branch_create.py +0 -11
  289. infrahub/message_bus/messages/event_branch_delete.py +0 -11
  290. infrahub/message_bus/messages/event_branch_rebased.py +0 -9
  291. infrahub/message_bus/messages/event_node_mutated.py +0 -15
  292. infrahub/message_bus/messages/event_schema_update.py +0 -9
  293. infrahub/message_bus/operations/event/node.py +0 -20
  294. infrahub/message_bus/operations/event/schema.py +0 -17
  295. infrahub/webhook/constants.py +0 -1
  296. {infrahub_server-1.2.0b1.dist-info → infrahub_server-1.2.1.dist-info}/LICENSE.txt +0 -0
  297. {infrahub_server-1.2.0b1.dist-info → infrahub_server-1.2.1.dist-info}/WHEEL +0 -0
infrahub/utils.py CHANGED
@@ -2,9 +2,10 @@ import hashlib
2
2
  from enum import Enum, EnumMeta
3
3
  from pathlib import Path
4
4
  from re import finditer
5
- from typing import Any
5
+ from typing import Any, TypeVar
6
6
 
7
7
  KWARGS_TO_DROP = ["session"]
8
+ AnyClass = TypeVar("AnyClass", bound=type)
8
9
 
9
10
 
10
11
  def get_fixtures_dir() -> Path:
@@ -73,3 +74,12 @@ def get_nested_dict(nested_dict: dict[str, Any], keys: list[str]) -> dict[str, A
73
74
  else:
74
75
  return {}
75
76
  return current_level if isinstance(current_level, dict) else {}
77
+
78
+
79
+ def get_all_subclasses(cls: AnyClass) -> list[AnyClass]:
80
+ """Recursively get all subclasses of the given class."""
81
+ subclasses: list[AnyClass] = []
82
+ for subclass in cls.__subclasses__():
83
+ subclasses.append(subclass)
84
+ subclasses.extend(get_all_subclasses(subclass))
85
+ return subclasses
File without changes
@@ -0,0 +1,42 @@
1
+ from infrahub_sdk.protocols import CoreValidator
2
+
3
+ from infrahub.context import InfrahubContext
4
+ from infrahub.events.models import EventMeta
5
+ from infrahub.events.validator_action import ValidatorFailedEvent, ValidatorPassedEvent, ValidatorStartedEvent
6
+ from infrahub.services import InfrahubServices
7
+
8
+
9
+ async def send_failed_validator(
10
+ service: InfrahubServices, validator: CoreValidator, proposed_change_id: str, context: InfrahubContext
11
+ ) -> None:
12
+ event = ValidatorFailedEvent(
13
+ node_id=validator.id,
14
+ kind=validator.get_kind(),
15
+ proposed_change_id=proposed_change_id,
16
+ meta=EventMeta.from_context(context=context),
17
+ )
18
+ await service.event.send(event=event)
19
+
20
+
21
+ async def send_passed_validator(
22
+ service: InfrahubServices, validator: CoreValidator, proposed_change_id: str, context: InfrahubContext
23
+ ) -> None:
24
+ event = ValidatorPassedEvent(
25
+ node_id=validator.id,
26
+ kind=validator.get_kind(),
27
+ proposed_change_id=proposed_change_id,
28
+ meta=EventMeta.from_context(context=context),
29
+ )
30
+ await service.event.send(event=event)
31
+
32
+
33
+ async def send_start_validator(
34
+ service: InfrahubServices, validator: CoreValidator, proposed_change_id: str, context: InfrahubContext
35
+ ) -> None:
36
+ event = ValidatorStartedEvent(
37
+ node_id=validator.id,
38
+ kind=validator.get_kind(),
39
+ proposed_change_id=proposed_change_id,
40
+ meta=EventMeta.from_context(context=context),
41
+ )
42
+ await service.event.send(event=event)
@@ -0,0 +1,41 @@
1
+ from typing import Any, TypeVar, cast
2
+
3
+ from infrahub_sdk.protocols import CoreValidator
4
+
5
+ from infrahub.context import InfrahubContext
6
+ from infrahub.core.constants import ValidatorConclusion, ValidatorState
7
+ from infrahub.services import InfrahubServices
8
+
9
+ from .events import send_start_validator
10
+
11
+ ValidatorType = TypeVar("ValidatorType", bound=CoreValidator)
12
+
13
+
14
+ async def start_validator(
15
+ service: InfrahubServices,
16
+ validator: CoreValidator | None,
17
+ validator_type: type[ValidatorType],
18
+ proposed_change: str,
19
+ context: InfrahubContext,
20
+ data: dict[str, Any],
21
+ ) -> ValidatorType:
22
+ if validator:
23
+ validator.conclusion.value = ValidatorConclusion.UNKNOWN.value
24
+ validator.state.value = ValidatorState.QUEUED.value
25
+ validator.started_at.value = ""
26
+ validator.completed_at.value = ""
27
+ await validator.save()
28
+ validator = cast(ValidatorType, validator)
29
+ else:
30
+ data["proposed_change"] = proposed_change
31
+ validator = await service.client.create(
32
+ kind=validator_type,
33
+ data=data,
34
+ )
35
+ await validator.save()
36
+
37
+ await send_start_validator(
38
+ service=service, validator=validator, proposed_change_id=proposed_change, context=context
39
+ )
40
+
41
+ return validator
@@ -0,0 +1,17 @@
1
+ from __future__ import annotations
2
+
3
+ from prefect import task
4
+ from prefect.cache_policies import NONE
5
+
6
+ from infrahub.core.manager import NodeManager
7
+ from infrahub.core.protocols import CoreWebhook
8
+ from infrahub.database import InfrahubDatabase # noqa: TC001 needed for prefect flow
9
+
10
+ from .models import WebhookTriggerDefinition
11
+
12
+
13
+ @task(name="gather-trigger-webhook", task_run_name="Gather webhook triggers", cache_policy=NONE)
14
+ async def gather_trigger_webhook(db: InfrahubDatabase) -> list[WebhookTriggerDefinition]:
15
+ webhooks = await NodeManager.query(db=db, schema=CoreWebhook)
16
+ triggers = [WebhookTriggerDefinition.from_object(webhook) for webhook in webhooks]
17
+ return triggers
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  import base64
4
4
  import hashlib
5
5
  import hmac
6
- from typing import TYPE_CHECKING, Any, Optional, Union
6
+ from typing import TYPE_CHECKING, Any
7
7
  from uuid import UUID, uuid4
8
8
 
9
9
  from pydantic import BaseModel, ConfigDict, Field, computed_field
@@ -12,7 +12,9 @@ from typing_extensions import Self
12
12
  from infrahub.core import registry
13
13
  from infrahub.core.constants import InfrahubKind
14
14
  from infrahub.core.timestamp import Timestamp
15
+ from infrahub.events.utils import get_all_infrahub_node_kind_events
15
16
  from infrahub.git.repository import InfrahubReadOnlyRepository, InfrahubRepository
17
+ from infrahub.trigger.constants import NAME_SEPARATOR
16
18
  from infrahub.trigger.models import EventTrigger, ExecuteWorkflow, TriggerDefinition, TriggerType
17
19
  from infrahub.workflows.catalogue import WEBHOOK_PROCESS
18
20
 
@@ -20,14 +22,23 @@ if TYPE_CHECKING:
20
22
  from httpx import Response
21
23
  from infrahub_sdk.protocols import CoreCustomWebhook, CoreStandardWebhook, CoreTransformPython, CoreWebhook
22
24
 
25
+ from infrahub.core.protocols import CoreWebhook as CoreWebhookNode
23
26
  from infrahub.services import InfrahubServices
24
27
 
25
28
 
26
29
  class WebhookTriggerDefinition(TriggerDefinition):
30
+ id: str
27
31
  type: TriggerType = TriggerType.WEBHOOK
28
32
 
33
+ def generate_name(self) -> str:
34
+ return f"{self.type.value}{NAME_SEPARATOR}{self.id}"
35
+
36
+ @classmethod
37
+ def generate_name_from_id(cls, id: str) -> str:
38
+ return f"{TriggerType.WEBHOOK.value}{NAME_SEPARATOR}{id}"
39
+
29
40
  @classmethod
30
- def from_object(cls, obj: CoreWebhook) -> Self:
41
+ def from_object(cls, obj: CoreWebhook | CoreWebhookNode) -> Self:
31
42
  event_trigger = EventTrigger()
32
43
  if obj.event_type.value == "all":
33
44
  event_trigger.events.add("infrahub.*")
@@ -45,7 +56,11 @@ class WebhookTriggerDefinition(TriggerDefinition):
45
56
  "infrahub.resource.label": f"!{registry.default_branch}",
46
57
  }
47
58
 
59
+ if obj.node_kind.value and obj.event_type.value in get_all_infrahub_node_kind_events():
60
+ event_trigger.match = {"infrahub.node.kind": obj.node_kind.value}
61
+
48
62
  definition = cls(
63
+ id=obj.id,
49
64
  name=obj.name.value,
50
65
  trigger=event_trigger,
51
66
  actions=[
@@ -53,7 +68,9 @@ class WebhookTriggerDefinition(TriggerDefinition):
53
68
  workflow=WEBHOOK_PROCESS,
54
69
  parameters={
55
70
  "webhook_id": obj.id,
71
+ "webhook_name": obj.name.value,
56
72
  "webhook_kind": obj.get_kind(),
73
+ "branch_name": "{{ event.resource['infrahub.branch.name'] }}",
57
74
  "event_id": "{{ event.id }}",
58
75
  "event_type": "{{ event.event }}",
59
76
  "event_occured_at": "{{ event.occurred }}",
@@ -102,7 +119,7 @@ class Webhook(BaseModel):
102
119
  event_type: str = Field(...)
103
120
  validate_certificates: bool = Field(...)
104
121
  _payload: Any = None
105
- _headers: Optional[dict[str, Any]] = None
122
+ _headers: dict[str, Any] | None = None
106
123
 
107
124
  async def _prepare_payload(self, data: dict[str, Any], context: EventContext, service: InfrahubServices) -> None: # noqa: ARG002
108
125
  self._payload = {"data": data, **context.model_dump()}
@@ -189,7 +206,7 @@ class TransformWebhook(Webhook):
189
206
  transform_timeout: int = Field(...)
190
207
 
191
208
  async def _prepare_payload(self, data: dict[str, Any], context: EventContext, service: InfrahubServices) -> None:
192
- repo: Union[InfrahubReadOnlyRepository, InfrahubRepository]
209
+ repo: InfrahubReadOnlyRepository | InfrahubRepository
193
210
  if self.repository_kind == InfrahubKind.READONLYREPOSITORY:
194
211
  repo = await InfrahubReadOnlyRepository.init(
195
212
  id=self.repository_id,
@@ -214,7 +231,7 @@ class TransformWebhook(Webhook):
214
231
  location=f"{self.transform_file}::{self.transform_class}",
215
232
  data={"data": data, **context.model_dump()},
216
233
  client=service.client,
217
- )
234
+ ) # type: ignore[misc]
218
235
 
219
236
  @classmethod
220
237
  def from_object(cls, obj: CoreCustomWebhook, transform: CoreTransformPython) -> Self:
infrahub/webhook/tasks.py CHANGED
@@ -14,9 +14,10 @@ from prefect.logging import get_run_logger
14
14
  from infrahub.message_bus.types import KVTTL
15
15
  from infrahub.services import InfrahubServices # noqa: TC001 needed for prefect flow
16
16
  from infrahub.trigger.models import TriggerType
17
- from infrahub.trigger.tasks import setup_triggers
17
+ from infrahub.trigger.setup import setup_triggers
18
+ from infrahub.workflows.utils import add_tags
18
19
 
19
- from .constants import AUTOMATION_NAME_RUN
20
+ from .gather import gather_trigger_webhook
20
21
  from .models import CustomWebhook, EventContext, StandardWebhook, TransformWebhook, Webhook, WebhookTriggerDefinition
21
22
 
22
23
  if TYPE_CHECKING:
@@ -61,18 +62,23 @@ async def convert_node_to_webhook(webhook_node: CoreWebhook, client: InfrahubCli
61
62
  return CustomWebhook.from_object(obj=webhook_node)
62
63
 
63
64
 
64
- @flow(name="webhook-process", flow_run_name="Send webhook")
65
+ @flow(name="webhook-process", flow_run_name="Send webhook for {webhook_name}")
65
66
  async def webhook_process(
66
67
  webhook_id: str,
68
+ webhook_name: str, # noqa: ARG001
67
69
  webhook_kind: str,
68
70
  event_id: str,
69
71
  event_type: str,
70
72
  event_occured_at: str,
71
73
  event_payload: dict,
72
74
  service: InfrahubServices,
75
+ branch_name: str | None = None,
73
76
  ) -> None:
74
77
  log = get_run_logger()
75
78
 
79
+ if branch_name:
80
+ await add_tags(branches=[branch_name])
81
+
76
82
  webhook_data_str = await service.cache.get(key=f"webhook:{webhook_id}")
77
83
  if not webhook_data_str:
78
84
  log.info(f"Webhook {webhook_id} not found in cache")
@@ -101,33 +107,33 @@ async def webhook_process(
101
107
  log.info(f"Successfully sent webhook to {response.url} with status {response.status_code}")
102
108
 
103
109
 
104
- @flow(name="webhook-setup-automation-all", flow_run_name="Configuration webhook automation for all webhooks")
110
+ @flow(name="webhook-setup-automation-all", flow_run_name="Configure all webhooks")
105
111
  async def configure_webhook_all(service: InfrahubServices) -> None:
106
112
  log = get_run_logger()
107
113
 
108
- webhooks = await service.client.all(kind=CoreWebhook)
109
- triggers = [WebhookTriggerDefinition.from_object(webhook) for webhook in webhooks]
114
+ triggers = await gather_trigger_webhook(db=service.database)
110
115
 
111
116
  async with get_client(sync_client=False) as prefect_client:
112
117
  await setup_triggers(
113
118
  client=prefect_client,
114
119
  triggers=triggers,
115
120
  trigger_type=TriggerType.WEBHOOK,
116
- deprecated_triggers=[AUTOMATION_NAME_RUN],
117
- )
121
+ ) # type: ignore[misc]
118
122
 
119
123
  log.info(f"{len(triggers)} Webhooks automation configuration completed")
120
124
 
121
125
 
122
- @flow(name="webhook-setup-automation-one", flow_run_name="Configuration webhook automation for one webhook")
123
- async def configure_webhook_one(event_type: str, event_data: dict, service: InfrahubServices) -> None:
126
+ @flow(name="webhook-setup-automation-one", flow_run_name="Configurate webhook for {webhook_name}")
127
+ async def configure_webhook_one(
128
+ webhook_name: str, # noqa: ARG001
129
+ event_data: dict,
130
+ service: InfrahubServices,
131
+ ) -> None:
124
132
  log = get_run_logger()
125
133
 
126
134
  webhook = await service.client.get(kind=CoreWebhook, id=event_data["node_id"])
127
135
  trigger = WebhookTriggerDefinition.from_object(webhook)
128
136
 
129
- delete_automation: bool = "infrahub.node.deleted" in event_type
130
-
131
137
  async with get_client(sync_client=False) as prefect_client:
132
138
  # Query the deployment associated with the trigger to have its ID
133
139
  deployment_name = trigger.get_deployment_names()[0]
@@ -135,22 +141,41 @@ async def configure_webhook_one(event_type: str, event_data: dict, service: Infr
135
141
 
136
142
  automation = AutomationCore(
137
143
  name=trigger.generate_name(),
138
- description=trigger.description,
144
+ description=trigger.get_description(),
139
145
  enabled=True,
140
146
  trigger=trigger.trigger.get_prefect(),
141
147
  actions=[action.get(deployment.id) for action in trigger.actions],
142
148
  )
143
149
 
144
150
  existing_automations = await prefect_client.read_automations_by_name(trigger.generate_name())
151
+ existing_automation = existing_automations[0] if existing_automations else None
145
152
 
146
- if existing_automations and not delete_automation:
147
- existing_automation = existing_automations[0]
153
+ if existing_automation:
148
154
  await prefect_client.update_automation(automation_id=existing_automation.id, automation=automation)
149
155
  log.info(f"Automation {trigger.generate_name()} updated")
150
- elif existing_automations and delete_automation:
151
- existing_automation = existing_automations[0]
152
- await prefect_client.delete_automation(automation_id=existing_automation.id)
153
- log.info(f"Automation {trigger.generate_name()} deleted")
154
156
  else:
155
157
  await prefect_client.create_automation(automation=automation)
156
158
  log.info(f"Automation {trigger.generate_name()} created")
159
+
160
+ await service.cache.delete(key=f"webhook:{webhook.id}")
161
+
162
+
163
+ @flow(name="webhook-delete-automation", flow_run_name="Delete webhook automation for {webhook_name}")
164
+ async def delete_webhook_automation(
165
+ webhook_id: str,
166
+ webhook_name: str, # noqa: ARG001
167
+ service: InfrahubServices,
168
+ ) -> None:
169
+ log = get_run_logger()
170
+
171
+ async with get_client(sync_client=False) as prefect_client:
172
+ automation_name = WebhookTriggerDefinition.generate_name_from_id(id=webhook_id)
173
+
174
+ existing_automations = await prefect_client.read_automations_by_name(automation_name)
175
+ existing_automation = existing_automations[0] if existing_automations else None
176
+
177
+ if existing_automation:
178
+ await prefect_client.delete_automation(automation_id=existing_automation.id)
179
+ log.info(f"Automation {automation_name} deleted")
180
+
181
+ await service.cache.delete(key=f"webhook:{webhook_id}")
@@ -1,13 +1,11 @@
1
1
  from infrahub.core.constants import InfrahubKind
2
2
  from infrahub.trigger.models import BuiltinTriggerDefinition, EventTrigger, ExecuteWorkflow
3
- from infrahub.workflows.catalogue import (
4
- WEBHOOK_CONFIGURE_ONE,
5
- )
3
+ from infrahub.workflows.catalogue import WEBHOOK_CONFIGURE_ONE, WEBHOOK_DELETE_AUTOMATION
6
4
 
7
5
  TRIGGER_WEBHOOK_SETUP_UPDATE = BuiltinTriggerDefinition(
8
6
  name="webhook-configure-one",
9
7
  trigger=EventTrigger(
10
- events={"infrahub.node.*"},
8
+ events={"infrahub.node.created", "infrahub.node.updated"},
11
9
  match={
12
10
  "infrahub.node.kind": [InfrahubKind.CUSTOMWEBHOOK, InfrahubKind.STANDARDWEBHOOK],
13
11
  },
@@ -16,7 +14,7 @@ TRIGGER_WEBHOOK_SETUP_UPDATE = BuiltinTriggerDefinition(
16
14
  ExecuteWorkflow(
17
15
  workflow=WEBHOOK_CONFIGURE_ONE,
18
16
  parameters={
19
- "event_type": "{{ event.event }}",
17
+ "webhook_name": "{{ event.payload['data']['changelog']['display_label'] }}",
20
18
  "event_data": {
21
19
  "__prefect_kind": "json",
22
20
  "value": {"__prefect_kind": "jinja", "template": "{{ event.payload['data'] | tojson }}"},
@@ -25,3 +23,22 @@ TRIGGER_WEBHOOK_SETUP_UPDATE = BuiltinTriggerDefinition(
25
23
  ),
26
24
  ],
27
25
  )
26
+
27
+ TRIGGER_WEBHOOK_DELETE = BuiltinTriggerDefinition(
28
+ name="webhook-delete",
29
+ trigger=EventTrigger(
30
+ events={"infrahub.node.deleted"},
31
+ match={
32
+ "infrahub.node.kind": [InfrahubKind.CUSTOMWEBHOOK, InfrahubKind.STANDARDWEBHOOK],
33
+ },
34
+ ),
35
+ actions=[
36
+ ExecuteWorkflow(
37
+ workflow=WEBHOOK_DELETE_AUTOMATION,
38
+ parameters={
39
+ "webhook_id": "{{ event.payload['data']['node_id'] }}",
40
+ "webhook_name": "{{ event.payload['data']['changelog']['display_label'] }}",
41
+ },
42
+ ),
43
+ ],
44
+ )
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
  import os
3
- from typing import Any, Optional
3
+ from typing import Any
4
4
 
5
5
  import typer
6
6
  from anyio.abc import TaskStatus
@@ -138,7 +138,7 @@ class InfrahubWorkerAsync(BaseWorker):
138
138
  self,
139
139
  flow_run: FlowRun,
140
140
  configuration: BaseJobConfiguration,
141
- task_status: Optional[TaskStatus] = None,
141
+ task_status: TaskStatus | None = None,
142
142
  ) -> BaseWorkerResult:
143
143
  flow_run_logger = self.get_flow_run_logger(flow_run)
144
144
 
infrahub/workers/utils.py CHANGED
@@ -24,7 +24,7 @@ def inject_service_parameter(func: Flow, parameters: dict[str, Any], service: In
24
24
 
25
25
  if service_parameter_name := get_parameter_name(func=func, types=[InfrahubServices.__name__, InfrahubServices]):
26
26
  if any(isinstance(param_value, InfrahubServices) for param_value in parameters):
27
- raise ValueError(f"{func} parameters contains an InfrahubServices object while it should be injected")
27
+ raise ValueError(f"{func.name} parameters contains an InfrahubServices object while it should be injected")
28
28
  parameters[service_parameter_name] = service
29
29
  return
30
30
 
@@ -37,7 +37,7 @@ def inject_context_parameter(func: Flow, parameters: dict[str, Any], context: In
37
37
 
38
38
  if service_parameter_name and not context:
39
39
  raise ValueError(
40
- f"{func} has a {service_parameter_name} parameter of type InfrahubContext, while context is not provided"
40
+ f"{func.name} has a {service_parameter_name} parameter of type InfrahubContext, while context is not provided"
41
41
  )
42
42
 
43
43
 
@@ -24,7 +24,7 @@ ANONYMOUS_TELEMETRY_SEND = WorkflowDefinition(
24
24
  name="anonymous_telemetry_send",
25
25
  type=WorkflowType.INTERNAL,
26
26
  cron=f"{random.randint(0, 59)} 2 * * *",
27
- module="infrahub.tasks.telemetry",
27
+ module="infrahub.telemetry.tasks",
28
28
  function="send_telemetry_push",
29
29
  )
30
30
 
@@ -234,8 +234,8 @@ GRAPHQL_QUERY_GROUP_UPDATE = WorkflowDefinition(
234
234
  function="update_graphql_query_group",
235
235
  )
236
236
 
237
- PROCESS_COMPUTED_MACRO = WorkflowDefinition(
238
- name="process_computed_attribute_jinja2",
237
+ COMPUTED_ATTRIBUTE_PROCESS_JINJA2 = WorkflowDefinition(
238
+ name="computed_attribute_process_jinja2",
239
239
  type=WorkflowType.CORE,
240
240
  module="infrahub.computed_attribute.tasks",
241
241
  function="process_jinja2",
@@ -256,11 +256,11 @@ TRIGGER_UPDATE_PYTHON_COMPUTED_ATTRIBUTES = WorkflowDefinition(
256
256
  function="trigger_update_python_computed_attributes",
257
257
  )
258
258
 
259
- COMPUTED_ATTRIBUTE_SETUP = WorkflowDefinition(
260
- name="computed-attribute-setup",
259
+ COMPUTED_ATTRIBUTE_SETUP_JINJA2 = WorkflowDefinition(
260
+ name="computed-attribute-setup-jinja2",
261
261
  type=WorkflowType.CORE,
262
262
  module="infrahub.computed_attribute.tasks",
263
- function="computed_attribute_setup",
263
+ function="computed_attribute_setup_jinja2",
264
264
  )
265
265
 
266
266
  COMPUTED_ATTRIBUTE_SETUP_PYTHON = WorkflowDefinition(
@@ -270,15 +270,8 @@ COMPUTED_ATTRIBUTE_SETUP_PYTHON = WorkflowDefinition(
270
270
  function="computed_attribute_setup_python",
271
271
  )
272
272
 
273
- COMPUTED_ATTRIBUTE_REMOVE_PYTHON = WorkflowDefinition(
274
- name="computed-attribute-remove-python",
275
- type=WorkflowType.CORE,
276
- module="infrahub.computed_attribute.tasks",
277
- function="computed_attribute_remove_python",
278
- )
279
-
280
- UPDATE_COMPUTED_ATTRIBUTE_TRANSFORM = WorkflowDefinition(
281
- name="process_computed_attribute_transform",
273
+ COMPUTED_ATTRIBUTE_PROCESS_TRANSFORM = WorkflowDefinition(
274
+ name="computed_attribute_process_transform",
282
275
  type=WorkflowType.USER,
283
276
  module="infrahub.computed_attribute.tasks",
284
277
  function="process_transform",
@@ -366,12 +359,19 @@ WEBHOOK_CONFIGURE_ONE = WorkflowDefinition(
366
359
 
367
360
  WEBHOOK_CONFIGURE_ALL = WorkflowDefinition(
368
361
  name="webhook-setup-automation-all",
369
- type=WorkflowType.CORE,
362
+ type=WorkflowType.INTERNAL,
370
363
  cron=f"{random.randint(0, 59)} 3 * * *",
371
364
  module="infrahub.webhook.tasks",
372
365
  function="configure_webhook_all",
373
366
  )
374
367
 
368
+ WEBHOOK_DELETE_AUTOMATION = WorkflowDefinition(
369
+ name="webhook-delete-automation",
370
+ type=WorkflowType.CORE,
371
+ module="infrahub.webhook.tasks",
372
+ function="delete_webhook_automation",
373
+ )
374
+
375
375
  GIT_REPOSITORIES_CHECK_ARTIFACT_CREATE = WorkflowDefinition(
376
376
  name="git-repository-check-artifact-create",
377
377
  type=WorkflowType.USER,
@@ -414,6 +414,13 @@ GIT_REPOSITORY_MERGE_CONFLICTS_CHECKS_RUN = WorkflowDefinition(
414
414
  function="run_check_merge_conflicts",
415
415
  )
416
416
 
417
+ TRIGGER_CONFIGURE_ALL = WorkflowDefinition(
418
+ name="trigger-configure-all",
419
+ type=WorkflowType.CORE,
420
+ module="infrahub.trigger.tasks",
421
+ function="trigger_configure_all",
422
+ )
423
+
417
424
 
418
425
  worker_pools = [INFRAHUB_WORKER_POOL]
419
426
 
@@ -426,8 +433,9 @@ workflows = [
426
433
  BRANCH_MERGE_MUTATION,
427
434
  BRANCH_REBASE,
428
435
  BRANCH_VALIDATE,
429
- COMPUTED_ATTRIBUTE_REMOVE_PYTHON,
430
- COMPUTED_ATTRIBUTE_SETUP,
436
+ COMPUTED_ATTRIBUTE_PROCESS_JINJA2,
437
+ COMPUTED_ATTRIBUTE_PROCESS_TRANSFORM,
438
+ COMPUTED_ATTRIBUTE_SETUP_JINJA2,
431
439
  COMPUTED_ATTRIBUTE_SETUP_PYTHON,
432
440
  DIFF_REFRESH,
433
441
  DIFF_REFRESH_ALL,
@@ -448,7 +456,6 @@ workflows = [
448
456
  GIT_REPOSITORY_USER_CHECK_RUN,
449
457
  GRAPHQL_QUERY_GROUP_UPDATE,
450
458
  IPAM_RECONCILIATION,
451
- PROCESS_COMPUTED_MACRO,
452
459
  PROPOSED_CHANGE_MERGE,
453
460
  QUERY_COMPUTED_ATTRIBUTE_TRANSFORM_TARGETS,
454
461
  REQUEST_ARTIFACT_DEFINITION_CHECK,
@@ -466,11 +473,12 @@ workflows = [
466
473
  TRANSFORM_JINJA2_RENDER,
467
474
  TRANSFORM_PYTHON_RENDER,
468
475
  TRIGGER_ARTIFACT_DEFINITION_GENERATE,
476
+ TRIGGER_CONFIGURE_ALL,
469
477
  TRIGGER_GENERATOR_DEFINITION_RUN,
470
478
  TRIGGER_UPDATE_JINJA_COMPUTED_ATTRIBUTES,
471
479
  TRIGGER_UPDATE_PYTHON_COMPUTED_ATTRIBUTES,
472
- UPDATE_COMPUTED_ATTRIBUTE_TRANSFORM,
473
480
  WEBHOOK_CONFIGURE_ALL,
474
481
  WEBHOOK_CONFIGURE_ONE,
482
+ WEBHOOK_DELETE_AUTOMATION,
475
483
  WEBHOOK_PROCESS,
476
484
  ]
@@ -8,9 +8,8 @@ from prefect.logging import get_run_logger
8
8
 
9
9
  from infrahub import config
10
10
  from infrahub.trigger.catalogue import builtin_triggers
11
- from infrahub.trigger.constants import DEPRECATED_STATIC_TRIGGER_NAMES
12
11
  from infrahub.trigger.models import TriggerType
13
- from infrahub.trigger.tasks import setup_triggers
12
+ from infrahub.trigger.setup import setup_triggers
14
13
 
15
14
  from .catalogue import worker_pools, workflows
16
15
  from .models import TASK_RESULT_STORAGE_NAME
@@ -75,5 +74,4 @@ async def setup_task_manager() -> None:
75
74
  client=client,
76
75
  triggers=builtin_triggers,
77
76
  trigger_type=TriggerType.BUILTIN,
78
- deprecated_triggers=DEPRECATED_STATIC_TRIGGER_NAMES,
79
77
  )
@@ -51,7 +51,7 @@ class WorkflowDefinition(BaseModel):
51
51
 
52
52
  @property
53
53
  def entrypoint(self) -> str:
54
- return f'backend/{self.module.replace(".", "/")}:{self.function}'
54
+ return f"backend/{self.module.replace('.', '/')}:{self.function}"
55
55
 
56
56
  @property
57
57
  def full_name(self) -> str:
@@ -6,6 +6,7 @@ from typing import TYPE_CHECKING
6
6
  from prefect import get_client
7
7
  from prefect.runtime import flow_run
8
8
 
9
+ from infrahub.core.constants import GLOBAL_BRANCH_NAME
9
10
  from infrahub.core.registry import registry
10
11
  from infrahub.tasks.registry import refresh_branches
11
12
 
@@ -27,7 +28,15 @@ async def add_tags(
27
28
  client = get_client(sync_client=False)
28
29
  current_flow_run_id = flow_run.id
29
30
  current_tags: list[str] = flow_run.tags
30
- branch_tags = [WorkflowTag.BRANCH.render(identifier=branch_name) for branch_name in branches] if branches else []
31
+ branch_tags = (
32
+ [
33
+ WorkflowTag.BRANCH.render(identifier=branch_name)
34
+ for branch_name in branches
35
+ if branch_name != GLOBAL_BRANCH_NAME
36
+ ]
37
+ if branches
38
+ else []
39
+ )
31
40
  node_tags = [WorkflowTag.RELATED_NODE.render(identifier=node_id) for node_id in nodes] if nodes else []
32
41
  others_tags = others or []
33
42
  new_tags = set(current_tags + branch_tags + node_tags + others_tags)