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
@@ -1,11 +1,22 @@
1
+ from infrahub_sdk.protocols import (
2
+ CoreArtifactValidator,
3
+ CoreDataValidator,
4
+ CoreGeneratorValidator,
5
+ CoreRepositoryValidator,
6
+ CoreSchemaValidator,
7
+ CoreUserValidator,
8
+ CoreValidator,
9
+ )
1
10
  from prefect import flow
2
11
 
3
12
  from infrahub import config
13
+ from infrahub.core.constants import InfrahubKind, ValidatorConclusion
4
14
  from infrahub.core.timestamp import Timestamp
5
15
  from infrahub.log import get_logger
6
16
  from infrahub.message_bus import messages
7
17
  from infrahub.message_bus.types import KVTTL, MessageTTL
8
18
  from infrahub.services import InfrahubServices
19
+ from infrahub.validators.events import send_failed_validator, send_passed_validator
9
20
 
10
21
  log = get_logger()
11
22
 
@@ -20,7 +31,8 @@ async def execution(message: messages.FinalizeValidatorExecution, service: Infra
20
31
 
21
32
  The message will get rescheduled until the timeout has exceeded or until all checks are accounted for.
22
33
  """
23
- validator = await service.client.get(kind=message.validator_type, id=message.validator_id)
34
+ validator_type = get_validator_type(validator_type=message.validator_type)
35
+ validator = await service.client.get(kind=validator_type, id=message.validator_id)
24
36
  checks_key = f"validator_execution_id:{message.validator_execution_id}:checks"
25
37
  current_conclusion = validator.conclusion.value
26
38
  if validator.state.value != "in_progress":
@@ -81,3 +93,41 @@ async def execution(message: messages.FinalizeValidatorExecution, service: Infra
81
93
  validator.completed_at.value = Timestamp().to_string()
82
94
  validator.conclusion.value = conclusion
83
95
  await validator.save()
96
+ if validator.conclusion.value == ValidatorConclusion.SUCCESS.value:
97
+ await send_passed_validator(
98
+ service=service, validator=validator, proposed_change_id=message.proposed_change, context=message.context
99
+ )
100
+ else:
101
+ await send_failed_validator(
102
+ service=service, validator=validator, proposed_change_id=message.proposed_change, context=message.context
103
+ )
104
+
105
+
106
+ def get_validator_type(
107
+ validator_type: str,
108
+ ) -> (
109
+ type[CoreArtifactValidator]
110
+ | type[CoreDataValidator]
111
+ | type[CoreGeneratorValidator]
112
+ | type[CoreRepositoryValidator]
113
+ | type[CoreSchemaValidator]
114
+ | type[CoreUserValidator]
115
+ | type[CoreValidator]
116
+ ):
117
+ match validator_type:
118
+ case InfrahubKind.USERVALIDATOR:
119
+ validator_kind = CoreUserValidator
120
+ case InfrahubKind.SCHEMAVALIDATOR:
121
+ validator_kind = CoreSchemaValidator
122
+ case InfrahubKind.GENERATORVALIDATOR:
123
+ validator_kind = CoreGeneratorValidator
124
+ case InfrahubKind.REPOSITORYVALIDATOR:
125
+ validator_kind = CoreRepositoryValidator
126
+ case InfrahubKind.DATAVALIDATOR:
127
+ validator_kind = CoreDataValidator
128
+ case InfrahubKind.ARTIFACTVALIDATOR:
129
+ validator_kind = CoreArtifactValidator
130
+ case _:
131
+ validator_kind = CoreValidator
132
+
133
+ return validator_kind
@@ -1,12 +1,14 @@
1
+ from infrahub_sdk.protocols import CoreGeneratorValidator
1
2
  from infrahub_sdk.uuidt import UUIDT
2
3
  from prefect import flow
3
4
  from prefect.logging import get_run_logger
4
5
 
5
- from infrahub.core.constants import InfrahubKind, ValidatorConclusion, ValidatorState
6
+ from infrahub.core.constants import InfrahubKind
6
7
  from infrahub.core.timestamp import Timestamp
7
8
  from infrahub.message_bus import InfrahubMessage, Meta, messages
8
9
  from infrahub.message_bus.types import KVTTL
9
10
  from infrahub.services import InfrahubServices
11
+ from infrahub.validators.tasks import start_validator
10
12
  from infrahub.workflows.utils import add_tags
11
13
 
12
14
 
@@ -27,31 +29,26 @@ async def check(message: messages.RequestGeneratorDefinitionCheck, service: Infr
27
29
 
28
30
  await proposed_change.validations.fetch()
29
31
 
30
- validator = None
32
+ previous_validator: CoreGeneratorValidator | None = None
31
33
  for relationship in proposed_change.validations.peers:
32
34
  existing_validator = relationship.peer
33
35
  if (
34
36
  existing_validator.typename == InfrahubKind.GENERATORVALIDATOR
35
37
  and existing_validator.definition.id == message.generator_definition.definition_id
36
38
  ):
37
- validator = existing_validator
39
+ previous_validator = existing_validator
38
40
 
39
- if validator:
40
- validator.conclusion.value = ValidatorConclusion.UNKNOWN.value
41
- validator.state.value = ValidatorState.QUEUED.value
42
- validator.started_at.value = ""
43
- validator.completed_at.value = ""
44
- await validator.save()
45
- else:
46
- validator = await service.client.create(
47
- kind=InfrahubKind.GENERATORVALIDATOR,
48
- data={
49
- "label": validator_name,
50
- "proposed_change": message.proposed_change,
51
- "definition": message.generator_definition.definition_id,
52
- },
53
- )
54
- await validator.save()
41
+ validator = await start_validator(
42
+ service=service,
43
+ validator=previous_validator,
44
+ validator_type=CoreGeneratorValidator,
45
+ proposed_change=message.proposed_change,
46
+ data={
47
+ "label": validator_name,
48
+ "definition": message.generator_definition.definition_id,
49
+ },
50
+ context=message.context,
51
+ )
55
52
 
56
53
  group = await service.client.get(
57
54
  kind=InfrahubKind.GENERICGROUP,
@@ -90,6 +87,7 @@ async def check(message: messages.RequestGeneratorDefinitionCheck, service: Infr
90
87
  log.info(f"Trigger execution of {message.generator_definition.definition_name} for {member.display_label}")
91
88
  events.append(
92
89
  messages.CheckGeneratorRun(
90
+ context=message.context,
93
91
  generator_definition=message.generator_definition,
94
92
  generator_instance=generator_instance,
95
93
  commit=repository.source_commit,
@@ -119,6 +117,8 @@ async def check(message: messages.RequestGeneratorDefinitionCheck, service: Infr
119
117
  validator_id=validator.id,
120
118
  validator_execution_id=validator_execution_id,
121
119
  validator_type=InfrahubKind.GENERATORVALIDATOR,
120
+ context=message.context,
121
+ proposed_change=message.proposed_change,
122
122
  )
123
123
  )
124
124
  for event in events:
@@ -85,7 +85,9 @@ async def pipeline(message: messages.RequestProposedChangePipeline, service: Inf
85
85
  target_branch=repo.destination_branch,
86
86
  )
87
87
  await service.workflow.submit_workflow(
88
- workflow=GIT_REPOSITORY_INTERNAL_CHECKS_TRIGGER, parameters={"model": model}
88
+ workflow=GIT_REPOSITORY_INTERNAL_CHECKS_TRIGGER,
89
+ context=message.context,
90
+ parameters={"model": model},
89
91
  )
90
92
  return
91
93
 
infrahub/pools/number.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass
4
- from typing import TYPE_CHECKING, Optional, Union
4
+ from typing import TYPE_CHECKING
5
5
 
6
6
  from infrahub.core.query.resource_manager import NumberPoolGetAllocated
7
7
  from infrahub.core.registry import registry
@@ -20,9 +20,7 @@ class UsedNumber:
20
20
 
21
21
 
22
22
  class NumberUtilizationGetter:
23
- def __init__(
24
- self, db: InfrahubDatabase, pool: CoreNode, branch: Branch, at: Optional[Union[Timestamp, str]] = None
25
- ) -> None:
23
+ def __init__(self, db: InfrahubDatabase, pool: CoreNode, branch: Branch, at: Timestamp | str | None = None) -> None:
26
24
  self.db = db
27
25
  self.at = at
28
26
  self.pool = pool
@@ -7,7 +7,7 @@ from pathlib import Path
7
7
  from typing import TYPE_CHECKING
8
8
 
9
9
  import pytest
10
- from infrahub_sdk.protocols import CoreGeneratorDefinition, CoreProposedChange
10
+ from infrahub_sdk.protocols import CoreArtifactValidator, CoreGeneratorDefinition, CoreProposedChange
11
11
  from prefect import flow, task
12
12
  from prefect.cache_policies import NONE
13
13
  from prefect.client.schemas.objects import (
@@ -22,7 +22,7 @@ from infrahub.context import InfrahubContext # noqa: TC001 needed for prefect
22
22
  from infrahub.core import registry
23
23
  from infrahub.core.branch import Branch
24
24
  from infrahub.core.branch.tasks import merge_branch
25
- from infrahub.core.constants import InfrahubKind, RepositoryInternalStatus, ValidatorConclusion, ValidatorState
25
+ from infrahub.core.constants import InfrahubKind, RepositoryInternalStatus, ValidatorConclusion
26
26
  from infrahub.core.diff.coordinator import DiffCoordinator
27
27
  from infrahub.core.diff.model.diff import DiffElementType, SchemaConflict
28
28
  from infrahub.core.diff.model.path import NodeDiffFieldSummary
@@ -52,8 +52,8 @@ from infrahub.proposed_change.models import (
52
52
  )
53
53
  from infrahub.pytest_plugin import InfrahubBackendPlugin
54
54
  from infrahub.services import InfrahubServices # noqa: TC001 needed for prefect flow
55
+ from infrahub.validators.tasks import start_validator
55
56
  from infrahub.workflows.catalogue import (
56
- COMPUTED_ATTRIBUTE_SETUP_PYTHON,
57
57
  GIT_REPOSITORIES_CHECK_ARTIFACT_CREATE,
58
58
  GIT_REPOSITORY_INTERNAL_CHECKS_TRIGGER,
59
59
  GIT_REPOSITORY_USER_CHECKS_TRIGGER,
@@ -150,7 +150,9 @@ async def merge_proposed_change(
150
150
 
151
151
  log.info("Proposed change is eligible to be merged")
152
152
  try:
153
- await merge_branch(branch=source_branch.name, context=context, service=service)
153
+ await merge_branch(
154
+ branch=source_branch.name, context=context, service=service, proposed_change_id=proposed_change_id
155
+ )
154
156
  except MergeFailedError as exc:
155
157
  await _proposed_change_transition_state(
156
158
  proposed_change=proposed_change, state=ProposedChangeState.OPEN, service=service
@@ -162,7 +164,6 @@ async def merge_proposed_change(
162
164
  await _proposed_change_transition_state(
163
165
  proposed_change=proposed_change, state=ProposedChangeState.MERGED, service=service
164
166
  )
165
- await service.workflow.submit_workflow(workflow=COMPUTED_ATTRIBUTE_SETUP_PYTHON, context=context)
166
167
  return Completed(message="proposed change merged successfully")
167
168
 
168
169
 
@@ -275,6 +276,7 @@ async def run_generators(
275
276
 
276
277
  if select:
277
278
  msg = messages.RequestGeneratorDefinitionCheck(
279
+ context=context,
278
280
  generator_definition=generator_definition,
279
281
  branch_diff=model.branch_diff,
280
282
  proposed_change=model.proposed_change,
@@ -415,7 +417,9 @@ async def _get_proposed_change_schema_integrity_constraints(
415
417
  name="proposed-changed-repository-checks",
416
418
  flow_run_name="Process user defined checks",
417
419
  )
418
- async def repository_checks(model: RequestProposedChangeRepositoryChecks, service: InfrahubServices) -> None:
420
+ async def repository_checks(
421
+ model: RequestProposedChangeRepositoryChecks, service: InfrahubServices, context: InfrahubContext
422
+ ) -> None:
419
423
  await add_tags(branches=[model.source_branch], nodes=[model.proposed_change])
420
424
 
421
425
  for repository in model.branch_diff.repositories:
@@ -431,7 +435,9 @@ async def repository_checks(model: RequestProposedChangeRepositoryChecks, servic
431
435
  target_branch=model.destination_branch,
432
436
  )
433
437
  await service.workflow.submit_workflow(
434
- workflow=GIT_REPOSITORY_INTERNAL_CHECKS_TRIGGER, parameters={"model": trigger_internal_checks_model}
438
+ workflow=GIT_REPOSITORY_INTERNAL_CHECKS_TRIGGER,
439
+ context=context,
440
+ parameters={"model": trigger_internal_checks_model},
435
441
  )
436
442
 
437
443
  trigger_user_checks_model = TriggerRepositoryUserChecks(
@@ -444,7 +450,9 @@ async def repository_checks(model: RequestProposedChangeRepositoryChecks, servic
444
450
  branch_diff=model.branch_diff,
445
451
  )
446
452
  await service.workflow.submit_workflow(
447
- workflow=GIT_REPOSITORY_USER_CHECKS_TRIGGER, parameters={"model": trigger_user_checks_model}
453
+ workflow=GIT_REPOSITORY_USER_CHECKS_TRIGGER,
454
+ context=context,
455
+ parameters={"model": trigger_user_checks_model},
448
456
  )
449
457
 
450
458
 
@@ -532,31 +540,26 @@ async def validate_artifacts_generation(model: RequestArtifactDefinitionCheck, s
532
540
 
533
541
  await proposed_change.validations.fetch()
534
542
 
535
- validator = None
543
+ previous_validator: CoreArtifactValidator | None = None
536
544
  for relationship in proposed_change.validations.peers:
537
545
  existing_validator = relationship.peer
538
546
  if (
539
547
  existing_validator.typename == InfrahubKind.ARTIFACTVALIDATOR
540
548
  and existing_validator.definition.id == model.artifact_definition.definition_id
541
549
  ):
542
- validator = existing_validator
543
-
544
- if validator:
545
- validator.conclusion.value = ValidatorConclusion.UNKNOWN.value
546
- validator.state.value = ValidatorState.QUEUED.value
547
- validator.started_at.value = ""
548
- validator.completed_at.value = ""
549
- await validator.save()
550
- else:
551
- validator = await service.client.create(
552
- kind=InfrahubKind.ARTIFACTVALIDATOR,
553
- data={
554
- "label": validator_name,
555
- "proposed_change": model.proposed_change,
556
- "definition": model.artifact_definition.definition_id,
557
- },
558
- )
559
- await validator.save()
550
+ previous_validator = existing_validator
551
+
552
+ validator = await start_validator(
553
+ service=service,
554
+ validator=previous_validator,
555
+ validator_type=CoreArtifactValidator,
556
+ proposed_change=model.proposed_change,
557
+ data={
558
+ "label": validator_name,
559
+ "definition": model.artifact_definition.definition_id,
560
+ },
561
+ context=model.context,
562
+ )
560
563
 
561
564
  await artifact_definition.targets.fetch()
562
565
  group = artifact_definition.targets.peer
@@ -617,7 +620,13 @@ async def validate_artifacts_generation(model: RequestArtifactDefinitionCheck, s
617
620
  )
618
621
  )
619
622
 
620
- await run_checks_and_update_validator(checks, validator)
623
+ await run_checks_and_update_validator(
624
+ checks=checks,
625
+ validator=validator,
626
+ proposed_change_id=model.proposed_change,
627
+ context=model.context,
628
+ service=service,
629
+ )
621
630
 
622
631
 
623
632
  def _should_render_artifact(artifact_id: str | None, managed_branch: bool, impacted_artifacts: list[str]) -> bool: # noqa: ARG001
infrahub/pytest_plugin.py CHANGED
@@ -1,9 +1,7 @@
1
- from typing import Optional
2
-
1
+ import pytest
3
2
  from infrahub_sdk.client import Config as InfrahubClientConfig
4
3
  from infrahub_sdk.client import InfrahubClientSync
5
4
  from infrahub_sdk.node import InfrahubNodeSync
6
- from pytest import Config, Item, Session, TestReport
7
5
 
8
6
  from infrahub.core.constants import InfrahubKind
9
7
  from infrahub.core.timestamp import Timestamp
@@ -52,7 +50,12 @@ class InfrahubBackendPlugin:
52
50
 
53
51
  return validator, True
54
52
 
55
- def pytest_collection_modifyitems(self, session: Session, config: Config, items: list[Item]) -> None: # noqa: ARG002
53
+ def pytest_collection_modifyitems(
54
+ self,
55
+ session: pytest.Session, # noqa: ARG002
56
+ config: pytest.Config, # noqa: ARG002
57
+ items: list[pytest.Item],
58
+ ) -> None:
56
59
  """This function is called after item collection and gives the opportunity to work on the collection before sending the items for testing.
57
60
 
58
61
  All items without an "infrahub" marker will be discarded. Items will also be re-ordered to be run in a specific order:
@@ -64,7 +67,7 @@ class InfrahubBackendPlugin:
64
67
  """
65
68
  filtered_items = [i for i in items if i.get_closest_marker("infrahub")]
66
69
 
67
- def sort_key(item: Item) -> tuple[int, int]:
70
+ def sort_key(item: pytest.Item) -> tuple[int, int]:
68
71
  type_cost = 99
69
72
  for marker_name, priority in ORDER_TYPE_MAP.items():
70
73
  if item.get_closest_marker(marker_name):
@@ -82,7 +85,7 @@ class InfrahubBackendPlugin:
82
85
  filtered_items.sort(key=sort_key)
83
86
  items[:] = filtered_items
84
87
 
85
- def pytest_collection_finish(self, session: Session) -> None: # noqa: ARG002
88
+ def pytest_collection_finish(self, session: pytest.Session) -> None: # noqa: ARG002
86
89
  """This function is called when tests have been collected and modified, meaning they are ready to be run."""
87
90
  self.proposed_change = self.client.get(kind=InfrahubKind.PROPOSEDCHANGE, id=self.proposed_change_id)
88
91
  self.proposed_change.validations.fetch()
@@ -95,7 +98,7 @@ class InfrahubBackendPlugin:
95
98
  check = relationship.peer
96
99
  self.checks[check.origin.value] = check
97
100
 
98
- def pytest_runtestloop(self, session: Session) -> Optional[object]: # noqa: ARG002
101
+ def pytest_runtestloop(self, session: pytest.Session) -> object | None: # noqa: ARG002
99
102
  """This function is called when the test loop is being run."""
100
103
  self.validator.conclusion.value = "unknown"
101
104
  self.validator.state.value = "in_progress"
@@ -104,7 +107,7 @@ class InfrahubBackendPlugin:
104
107
 
105
108
  return None
106
109
 
107
- def pytest_runtest_setup(self, item: Item) -> None:
110
+ def pytest_runtest_setup(self, item: pytest.Item) -> None:
108
111
  """Create a StandardCheck for each test item to later record its details.
109
112
 
110
113
  If a check already exists, reset it to its default values.
@@ -132,7 +135,7 @@ class InfrahubBackendPlugin:
132
135
 
133
136
  check.save()
134
137
 
135
- def pytest_runtest_logreport(self, report: TestReport) -> None:
138
+ def pytest_runtest_logreport(self, report: pytest.TestReport) -> None:
136
139
  """This function is called 3 times per test: setup, call, teardown."""
137
140
  if report.when != "call":
138
141
  return
@@ -144,7 +147,7 @@ class InfrahubBackendPlugin:
144
147
  # Workaround for https://github.com/opsmill/infrahub/issues/2184
145
148
  check.update(do_full_update=True)
146
149
 
147
- def pytest_sessionfinish(self, session: Session) -> None: # noqa: ARG002
150
+ def pytest_sessionfinish(self, session: pytest.Session) -> None: # noqa: ARG002
148
151
  """Set the final RepositoryValidator details after completing the test session."""
149
152
  conclusion = "success"
150
153
 
infrahub/server.py CHANGED
@@ -17,7 +17,6 @@ from fastapi.templating import Jinja2Templates
17
17
  from infrahub_sdk.exceptions import TimestampFormatError
18
18
  from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
19
19
  from opentelemetry.trace import Span
20
- from pydantic import ValidationError
21
20
  from starlette_exporter import PrometheusMiddleware, handle_metrics
22
21
 
23
22
  from infrahub import __version__, config
@@ -28,7 +27,7 @@ from infrahub.core.graph.index import node_indexes, rel_indexes
28
27
  from infrahub.core.initialization import initialization
29
28
  from infrahub.database import InfrahubDatabase, InfrahubDatabaseMode, get_db
30
29
  from infrahub.dependencies.registry import build_component_registry
31
- from infrahub.exceptions import Error
30
+ from infrahub.exceptions import Error, ValidationError
32
31
  from infrahub.graphql.api.endpoints import router as graphql_router
33
32
  from infrahub.lock import initialize_lock
34
33
  from infrahub.log import clear_log_context, get_logger, set_log_data
@@ -31,7 +31,7 @@ class InfrahubEventService:
31
31
  async def _send_prefect(self, event: InfrahubEvent) -> None:
32
32
  emit_event(
33
33
  id=event.meta.id,
34
- event=event.get_name(),
34
+ event=event.event_name,
35
35
  resource=event.get_resource(),
36
36
  related=event.get_related(),
37
37
  payload=event.get_event_payload(),
@@ -5,6 +5,7 @@ from typing import TYPE_CHECKING, Any
5
5
 
6
6
  from prefect.client.orchestration import PrefectClient, get_client
7
7
  from prefect.events.schemas.events import Event as PrefectEventModel
8
+ from prefect.exceptions import PrefectHTTPStatusError
8
9
  from pydantic import BaseModel, Field, TypeAdapter
9
10
 
10
11
  from infrahub.core.constants import GLOBAL_BRANCH_NAME
@@ -91,11 +92,10 @@ class PrefectEventData(PrefectEventModel):
91
92
 
92
93
  def _return_node_mutation(self) -> dict[str, Any]:
93
94
  attributes = []
95
+ relationships = []
94
96
 
95
97
  for resource in self.related:
96
- if resource.get("prefect.resource.role") == "infrahub.node.field_update" and resource.get(
97
- "infrahub.attribute.name"
98
- ):
98
+ if resource.role == "infrahub.node.attribute_update" and resource.get("infrahub.attribute.name"):
99
99
  attributes.append(
100
100
  {
101
101
  "name": resource.get("infrahub.attribute.name", ""),
@@ -109,8 +109,19 @@ class PrefectEventData(PrefectEventModel):
109
109
  "action": resource.get("infrahub.attribute.action", "unchanged"),
110
110
  }
111
111
  )
112
+ elif resource.role == "infrahub.node.relationship_update":
113
+ relationships.append(
114
+ {
115
+ "name": resource.get("infrahub.relationship.name"),
116
+ "action": resource.get("infrahub.relationship.peer_status"),
117
+ "peer": {
118
+ "id": resource.get("infrahub.relationship.peer_id"),
119
+ "kind": resource.get("infrahub.relationship.peer_kind"),
120
+ },
121
+ }
122
+ )
112
123
 
113
- return {"attributes": attributes}
124
+ return {"attributes": attributes, "relationships": relationships}
114
125
 
115
126
  def _get_branch_name_from_resource(self) -> str:
116
127
  return self.resource.get("infrahub.branch.name") or ""
@@ -220,14 +231,17 @@ class PrefectEvent:
220
231
 
221
232
  # Retry due to https://github.com/PrefectHQ/prefect/issues/16299
222
233
  for _ in range(1, 5):
223
- response = await client._client.post("/infrahub/events/filter", json=body)
224
- if response.status_code == 200:
234
+ prefect_error: PrefectHTTPStatusError | None = None
235
+ try:
236
+ response = await client._client.post("/infrahub/events/filter", json=body)
225
237
  break
226
- await asyncio.sleep(0.1)
238
+ except PrefectHTTPStatusError as exc:
239
+ prefect_error = exc
240
+ await asyncio.sleep(0.1)
227
241
 
228
- if response.status_code != 200:
242
+ if prefect_error:
229
243
  raise ServiceUnavailableError(
230
- message=f"Unable to query prefect due to invalid response from the server (status_code={response.status_code})"
244
+ message=f"Unable to query prefect due to invalid response from the server (status_code={prefect_error.response.status_code})"
231
245
  )
232
246
  data: dict[str, Any] = response.json()
233
247
 
@@ -1,5 +1,3 @@
1
- from typing import Union
2
-
3
1
  from infrahub_sdk.node import InfrahubNode
4
2
  from prefect import task
5
3
  from prefect.cache_policies import NONE
@@ -13,7 +11,7 @@ from infrahub.services import InfrahubServices
13
11
 
14
12
  @task(name="define-artifact", task_run_name="Define Artifact", cache_policy=NONE) # type: ignore[arg-type]
15
13
  async def define_artifact(
16
- model: Union[CheckArtifactCreate, RequestArtifactGenerate], service: InfrahubServices
14
+ model: CheckArtifactCreate | RequestArtifactGenerate, service: InfrahubServices
17
15
  ) -> tuple[InfrahubNode, bool]:
18
16
  """Return an artifact together with a flag to indicate if the artifact is created now or already existed."""
19
17
  created = False
@@ -41,6 +39,6 @@ async def define_artifact(
41
39
  "content_type": model.content_type,
42
40
  },
43
41
  )
44
- await artifact.save()
42
+ await artifact.save(request_context=model.context.to_request_context())
45
43
  created = True
46
44
  return artifact, created
File without changes
@@ -0,0 +1,9 @@
1
+ from enum import Enum
2
+
3
+ TELEMETRY_KIND: str = "community"
4
+ TELEMETRY_VERSION: str = "20250318"
5
+
6
+
7
+ class InfrahubType(str, Enum):
8
+ COMMUNITY = "community"
9
+ ENTERPRISE = "enterprise"
@@ -0,0 +1,86 @@
1
+ from neo4j.exceptions import Neo4jError
2
+ from prefect import task
3
+ from prefect.cache_policies import NONE
4
+
5
+ from infrahub.core import utils
6
+ from infrahub.core.graph.schema import GRAPH_SCHEMA
7
+ from infrahub.core.query import QueryType
8
+ from infrahub.database import DatabaseType, InfrahubDatabase
9
+
10
+ from .models import TelemetryDatabaseData, TelemetryDatabaseServerData, TelemetryDatabaseSystemInfoData
11
+
12
+
13
+ async def get_server_info(db: InfrahubDatabase) -> list[TelemetryDatabaseServerData]:
14
+ data: list[TelemetryDatabaseServerData] = []
15
+
16
+ try:
17
+ results = await db.execute_query(query="SHOW SERVERS YIELD *", name="get_server_info", type=QueryType.READ)
18
+ except Neo4jError:
19
+ return []
20
+
21
+ for result in results:
22
+ data.append(
23
+ TelemetryDatabaseServerData(
24
+ name=result["name"],
25
+ version=result["version"],
26
+ )
27
+ )
28
+
29
+ return data
30
+
31
+
32
+ async def get_system_info(db: InfrahubDatabase) -> TelemetryDatabaseSystemInfoData:
33
+ query = """
34
+ CALL dbms.queryJmx("java.lang:type=OperatingSystem")
35
+ YIELD attributes
36
+ RETURN
37
+ attributes.AvailableProcessors as processor_available,
38
+ attributes.TotalMemorySize as memory_total,
39
+ attributes.FreeMemorySize as memory_available
40
+ """
41
+ results = await db.execute_query(query=query, name="get_system_info", type=QueryType.READ)
42
+
43
+ return TelemetryDatabaseSystemInfoData(
44
+ memory_total=results[0]["memory_total"]["value"],
45
+ memory_available=results[0]["memory_available"]["value"],
46
+ processor_available=results[0]["processor_available"]["value"],
47
+ )
48
+
49
+
50
+ @task(name="telemetry-gather-db", task_run_name="Gather Database Information", cache_policy=NONE)
51
+ async def gather_database_information(db: InfrahubDatabase) -> TelemetryDatabaseData:
52
+ async with db.start_session() as dbs:
53
+ server_info = []
54
+ system_info = None
55
+ database_type = db.db_type.value
56
+
57
+ if db.db_type == DatabaseType.NEO4J:
58
+ server_info = await get_server_info(db=dbs)
59
+ system_info = await get_system_info(db=dbs)
60
+
61
+ # server_info is only available on Neo4j Enterprise
62
+ # so if it's not empty, we can assume the database is of type Enterprise
63
+ if len(server_info) == 0:
64
+ database_type = f"{database_type}-community"
65
+ else:
66
+ database_type = f"{database_type}-enterprise"
67
+
68
+ data = TelemetryDatabaseData(
69
+ database_type=database_type,
70
+ relationship_count={
71
+ "total": await utils.count_relationships(db=dbs),
72
+ },
73
+ node_count={
74
+ "total": await utils.count_nodes(db=dbs),
75
+ },
76
+ servers=server_info,
77
+ system_info=system_info,
78
+ )
79
+
80
+ for name in GRAPH_SCHEMA["relationships"]:
81
+ data.relationship_count[name] = await utils.count_relationships(db=dbs, label=name)
82
+
83
+ for name in GRAPH_SCHEMA["nodes"]:
84
+ data.node_count[name] = await utils.count_nodes(db=dbs, label=name)
85
+
86
+ return data