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,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, AsyncIterator, Optional
3
+ from typing import TYPE_CHECKING, AsyncIterator
4
4
 
5
5
  from fastapi import Depends, Query, Request
6
6
  from fastapi.security import APIKeyHeader, HTTPAuthorizationCredentials, HTTPBearer
@@ -25,7 +25,7 @@ jwt_scheme = HTTPBearer(auto_error=False)
25
25
  api_key_scheme = APIKeyHeader(name="X-INFRAHUB-KEY", auto_error=False)
26
26
 
27
27
 
28
- async def cookie_auth_scheme(request: Request) -> Optional[str]:
28
+ async def cookie_auth_scheme(request: Request) -> str | None:
29
29
  return request.cookies.get("access_token") # Replace with the actual name of your JWT cookie
30
30
 
31
31
 
@@ -62,7 +62,7 @@ async def get_access_token(
62
62
  async def get_refresh_token(
63
63
  request: Request,
64
64
  db: InfrahubDatabase = Depends(get_db),
65
- jwt_header: Optional[HTTPAuthorizationCredentials] = Depends(jwt_scheme),
65
+ jwt_header: HTTPAuthorizationCredentials | None = Depends(jwt_scheme),
66
66
  ) -> RefreshTokenData:
67
67
  token = None
68
68
 
@@ -83,8 +83,8 @@ async def get_refresh_token(
83
83
 
84
84
  async def get_branch_params(
85
85
  db: InfrahubDatabase = Depends(get_db),
86
- branch_name: Optional[str] = Query(None, alias="branch", description="Name of the branch to use for the query"),
87
- at: Optional[str] = Query(None, description="Time to use for the query, in absolute or relative format"),
86
+ branch_name: str | None = Query(None, alias="branch", description="Name of the branch to use for the query"),
87
+ at: str | None = Query(None, description="Time to use for the query, in absolute or relative format"),
88
88
  ) -> BranchParams:
89
89
  branch = await registry.get_branch(db=db, branch=branch_name)
90
90
 
@@ -93,7 +93,7 @@ async def get_branch_params(
93
93
 
94
94
  async def get_branch_dep(
95
95
  db: InfrahubDatabase = Depends(get_db),
96
- branch_name: Optional[str] = Query(None, alias="branch", description="Name of the branch to use for the query"),
96
+ branch_name: str | None = Query(None, alias="branch", description="Name of the branch to use for the query"),
97
97
  ) -> Branch:
98
98
  return await registry.get_branch(db=db, branch=branch_name)
99
99
 
@@ -1,4 +1,4 @@
1
- from typing import Any, Optional
1
+ from typing import Any
2
2
 
3
3
  from pydantic import BaseModel, ConfigDict, field_validator, model_validator
4
4
 
@@ -9,13 +9,13 @@ from infrahub.core.timestamp import Timestamp
9
9
  class DiffQueryValidated(BaseModel):
10
10
  model_config = ConfigDict(arbitrary_types_allowed=True)
11
11
  branch: Branch
12
- time_from: Optional[str] = None
13
- time_to: Optional[str] = None
12
+ time_from: str | None = None
13
+ time_to: str | None = None
14
14
  branch_only: bool
15
15
 
16
16
  @field_validator("time_from", "time_to", mode="before")
17
17
  @classmethod
18
- def validate_time(cls, value: Optional[str]) -> Optional[str]:
18
+ def validate_time(cls, value: str | None) -> str | None:
19
19
  if not value:
20
20
  return None
21
21
  Timestamp(value)
@@ -24,12 +24,12 @@ class DiffQueryValidated(BaseModel):
24
24
  @model_validator(mode="before")
25
25
  @classmethod
26
26
  def validate_time_from_if_required(cls, values: dict[str, Any]) -> dict[str, Any]:
27
- branch: Optional[Branch] = values.get("branch")
28
- time_from: Optional[Timestamp] = values.get("time_from")
27
+ branch: Branch | None = values.get("branch")
28
+ time_from: Timestamp | None = values.get("time_from")
29
29
  if getattr(branch, "is_default", False) and not time_from:
30
30
  branch_name = getattr(branch, "name", "")
31
31
  raise ValueError(f"time_from is mandatory when diffing on the default branch `{branch_name}`.")
32
- time_to: Optional[Timestamp] = values.get("time_to")
32
+ time_to: Timestamp | None = values.get("time_to")
33
33
  if time_to and time_from and time_to < time_from:
34
34
  raise ValueError("time_from and time_to are not a valid time range")
35
35
  return values
infrahub/api/schema.py CHANGED
@@ -68,7 +68,7 @@ class APISchemaMixin:
68
68
  @classmethod
69
69
  def set_kind(cls, values: Any) -> Any:
70
70
  if isinstance(values, dict):
71
- values["kind"] = f'{values["namespace"]}{values["name"]}'
71
+ values["kind"] = f"{values['namespace']}{values['name']}"
72
72
  return values
73
73
 
74
74
 
@@ -1,5 +1,3 @@
1
- from typing import Optional
2
-
3
1
  from pydantic import BaseModel, Field
4
2
 
5
3
  from infrahub.context import InfrahubContext
@@ -21,7 +19,7 @@ class CheckArtifactCreate(BaseModel):
21
19
  target_id: str = Field(..., description="The ID of the target object for this artifact")
22
20
  target_kind: str = Field(..., description="The kind of the target object for this artifact")
23
21
  target_name: str = Field(..., description="Name of the artifact target")
24
- artifact_id: Optional[str] = Field(default=None, description="The id of the artifact if it previously existed")
22
+ artifact_id: str | None = Field(default=None, description="The id of the artifact if it previously existed")
25
23
  query: str = Field(..., description="The name of the query to use when collecting data")
26
24
  timeout: int = Field(..., description="Timeout for requests used to generate this artifact")
27
25
  variables: dict = Field(..., description="Input variables when generating the artifact")
@@ -1,5 +1,3 @@
1
- from typing import Union
2
-
3
1
  from prefect import flow
4
2
 
5
3
  from infrahub.artifacts.models import CheckArtifactCreate
@@ -35,7 +33,7 @@ async def create(model: CheckArtifactCreate, service: InfrahubServices) -> Valid
35
33
  artifact, artifact_created = await define_artifact(model=model, service=service)
36
34
 
37
35
  severity = "info"
38
- artifact_result: dict[str, Union[str, bool, None]] = {
36
+ artifact_result: dict[str, str | bool | None] = {
39
37
  "changed": None,
40
38
  "checksum": None,
41
39
  "artifact_id": None,
infrahub/cli/__init__.py CHANGED
@@ -1,18 +1,21 @@
1
1
  from asyncio import run as aiorun
2
2
 
3
3
  import typer
4
+ from infrahub_sdk.async_typer import AsyncTyper
4
5
 
5
6
  from infrahub import config
6
- from infrahub.cli.context import CliContext
7
- from infrahub.cli.db import app as db_app
8
- from infrahub.cli.events import app as events_app
9
- from infrahub.cli.git_agent import app as git_app
10
- from infrahub.cli.server import app as server_app
11
- from infrahub.cli.tasks import app as tasks_app
12
7
  from infrahub.core.initialization import initialization
13
8
  from infrahub.database import InfrahubDatabase, get_db
14
9
 
15
- app = typer.Typer(name="Infrahub CLI", pretty_exceptions_enable=False)
10
+ from .context import CliContext
11
+ from .db import app as db_app
12
+ from .events import app as events_app
13
+ from .git_agent import app as git_app
14
+ from .server import app as server_app
15
+ from .tasks import app as tasks_app
16
+ from .upgrade import upgrade_cmd
17
+
18
+ app = AsyncTyper(name="Infrahub CLI", pretty_exceptions_enable=False)
16
19
 
17
20
 
18
21
  @app.callback()
@@ -22,10 +25,11 @@ def common(ctx: typer.Context) -> None:
22
25
 
23
26
 
24
27
  app.add_typer(server_app, name="server")
25
- app.add_typer(git_app, name="git-agent")
28
+ app.add_typer(git_app, name="git-agent", hidden=True)
26
29
  app.add_typer(db_app, name="db")
27
- app.add_typer(events_app, name="events", help="Interact with the events system.")
30
+ app.add_typer(events_app, name="events", help="Interact with the events system.", hidden=True)
28
31
  app.add_typer(tasks_app, name="tasks", hidden=True)
32
+ app.command(name="upgrade")(upgrade_cmd)
29
33
 
30
34
 
31
35
  async def _init_shell(config_file: str) -> None:
@@ -0,0 +1,3 @@
1
+ ERROR_BADGE = "[bold red]ERROR[/bold red]"
2
+ SUCCESS_BADGE = "[green]SUCCESS[/green]"
3
+ FAILED_BADGE = "[bold red]FAILED[/bold red]"
infrahub/cli/db.py CHANGED
@@ -4,7 +4,7 @@ import importlib
4
4
  import logging
5
5
  import os
6
6
  from enum import Enum
7
- from typing import TYPE_CHECKING, Optional
7
+ from typing import TYPE_CHECKING
8
8
 
9
9
  import typer
10
10
  from infrahub_sdk.async_typer import AsyncTyper
@@ -16,23 +16,16 @@ from rich.table import Table
16
16
 
17
17
  from infrahub import config
18
18
  from infrahub.core import registry
19
- from infrahub.core.constants import InfrahubKind
20
19
  from infrahub.core.graph import GRAPH_VERSION
21
20
  from infrahub.core.graph.constraints import ConstraintManagerBase, ConstraintManagerMemgraph, ConstraintManagerNeo4j
22
21
  from infrahub.core.graph.index import node_indexes, rel_indexes
23
22
  from infrahub.core.graph.schema import GRAPH_SCHEMA
24
23
  from infrahub.core.initialization import (
25
- create_anonymous_role,
26
- create_default_menu,
27
- create_default_roles,
28
- create_super_administrator_role,
29
- create_super_administrators_group,
30
24
  first_time_initialization,
31
25
  get_root_node,
32
26
  initialization,
33
27
  initialize_registry,
34
28
  )
35
- from infrahub.core.manager import NodeManager
36
29
  from infrahub.core.migrations.graph import get_graph_migrations
37
30
  from infrahub.core.migrations.schema.models import SchemaApplyMigrationData
38
31
  from infrahub.core.migrations.schema.tasks import schema_apply_migrations
@@ -48,6 +41,8 @@ from infrahub.services import InfrahubServices
48
41
  from infrahub.services.adapters.message_bus.local import BusSimulator
49
42
  from infrahub.services.adapters.workflow.local import WorkflowLocalExecution
50
43
 
44
+ from .constants import ERROR_BADGE, FAILED_BADGE, SUCCESS_BADGE
45
+
51
46
  if TYPE_CHECKING:
52
47
  from infrahub.cli.context import CliContext
53
48
  from infrahub.database import InfrahubDatabase
@@ -121,6 +116,7 @@ async def load_test_data(
121
116
 
122
117
  context: CliContext = ctx.obj
123
118
  dbdriver = await context.init_db(retry=1)
119
+
124
120
  async with dbdriver.start_session() as db:
125
121
  await initialization(db=db)
126
122
 
@@ -136,8 +132,8 @@ async def load_test_data(
136
132
  await dbdriver.close()
137
133
 
138
134
 
139
- @app.command()
140
- async def migrate(
135
+ @app.command(name="migrate")
136
+ async def migrate_cmd(
141
137
  ctx: typer.Context,
142
138
  check: bool = typer.Option(False, help="Check the state of the database without applying the migrations."),
143
139
  config_file: str = typer.Argument("infrahub.toml", envvar="INFRAHUB_CONFIG"),
@@ -147,53 +143,18 @@ async def migrate(
147
143
  logging.getLogger("neo4j").setLevel(logging.ERROR)
148
144
  logging.getLogger("prefect").setLevel(logging.ERROR)
149
145
 
150
- log = get_logger()
151
-
152
146
  config.load_and_exit(config_file_name=config_file)
153
147
 
154
148
  context: CliContext = ctx.obj
155
149
  dbdriver = await context.init_db(retry=1)
156
- async with dbdriver.start_session() as db:
157
- rprint("Checking current state of the Database")
158
150
 
159
- await initialize_registry(db=db)
160
- root_node = await get_root_node(db=db)
161
- migrations = await get_graph_migrations(root=root_node)
162
-
163
- if not migrations:
164
- rprint(f"Database up-to-date (v{root_node.graph_version}), no migration to execute.")
165
- else:
166
- rprint(
167
- f"Database needs to be updated (v{root_node.graph_version} -> v{GRAPH_VERSION}), {len(migrations)} migrations pending"
168
- )
169
-
170
- if migrations and not check:
171
- for migration in migrations:
172
- log.debug(f"Execute Migration: {migration.name}")
173
- execution_result = await migration.execute(db=db)
174
- validation_result = None
175
-
176
- if execution_result.success:
177
- validation_result = await migration.validate_migration(db=db)
178
- if validation_result.success:
179
- rprint(f"Migration: {migration.name} [green]SUCCESS[/green]")
180
- root_node.graph_version = migration.minimum_version + 1
181
- await root_node.save(db=db)
182
-
183
- if not execution_result.success or validation_result and not validation_result.success:
184
- rprint(f"Migration: {migration.name} [bold red]FAILED[/bold red]")
185
- for error in execution_result.errors:
186
- rprint(f" {error}")
187
- if validation_result and not validation_result.success:
188
- for error in validation_result.errors:
189
- rprint(f" {error}")
190
- break
151
+ await migrate_database(db=dbdriver, initialize=True, check=check)
191
152
 
192
153
  await dbdriver.close()
193
154
 
194
155
 
195
- @app.command()
196
- async def update_core_schema(
156
+ @app.command(name="update-core-schema")
157
+ async def update_core_schema_cmd(
197
158
  ctx: typer.Context,
198
159
  debug: bool = typer.Option(False, help="Enable advanced logging and troubleshooting"),
199
160
  config_file: str = typer.Argument("infrahub.toml", envvar="INFRAHUB_CONFIG"),
@@ -209,116 +170,14 @@ async def update_core_schema(
209
170
  context: CliContext = ctx.obj
210
171
  dbdriver = await context.init_db(retry=1)
211
172
 
212
- error_badge = "[bold red]ERROR[/bold red]"
173
+ service = await InfrahubServices.new(
174
+ database=dbdriver, message_bus=BusSimulator(), workflow=WorkflowLocalExecution()
175
+ )
213
176
 
214
- async with dbdriver.start_session() as db:
215
- with prefect_test_harness():
216
- # ----------------------------------------------------------
217
- # Initialize Schema and Registry
218
- # ----------------------------------------------------------
219
- service = await InfrahubServices.new(
220
- database=db, message_bus=BusSimulator(), workflow=WorkflowLocalExecution()
221
- )
222
- await initialize_registry(db=db)
223
-
224
- default_branch = registry.get_branch_from_registry(branch=registry.default_branch)
225
-
226
- registry.schema = SchemaManager()
227
- schema = SchemaRoot(**internal_schema)
228
- registry.schema.register_schema(schema=schema)
229
-
230
- # ----------------------------------------------------------
231
- # Load Current Schema from the database
232
- # ----------------------------------------------------------
233
- schema_default_branch = await registry.schema.load_schema_from_db(db=db, branch=default_branch)
234
- registry.schema.set_schema_branch(name=default_branch.name, schema=schema_default_branch)
235
- branch_schema = registry.schema.get_schema_branch(name=registry.default_branch)
236
-
237
- candidate_schema = branch_schema.duplicate()
238
- candidate_schema.load_schema(schema=SchemaRoot(**internal_schema))
239
- candidate_schema.load_schema(schema=SchemaRoot(**core_models))
240
- candidate_schema.load_schema(schema=SchemaRoot(**deprecated_models))
241
- candidate_schema.process()
242
-
243
- schema_diff = branch_schema.diff(other=candidate_schema)
244
- branch_schema.validate_node_deletions(diff=schema_diff)
245
- result = branch_schema.validate_update(
246
- other=candidate_schema, diff=schema_diff, enforce_update_support=False
247
- )
248
- if result.errors:
249
- rprint(f"{error_badge} | Unable to update the schema, due to failed validations")
250
- for error in result.errors:
251
- rprint(error.to_string())
252
- raise typer.Exit(1)
253
-
254
- if not result.diff.all:
255
- await create_defaults(db=db)
256
- rprint("Core Schema Up to date, nothing to update")
257
- raise typer.Exit(0)
258
-
259
- rprint("Core Schema has diff, will need to be updated")
260
- if debug:
261
- result.diff.print()
262
-
263
- # ----------------------------------------------------------
264
- # Validate if the new schema is valid with the content of the database
265
- # ----------------------------------------------------------
266
- validate_migration_data = SchemaValidateMigrationData(
267
- branch=default_branch,
268
- schema_branch=candidate_schema,
269
- constraints=result.constraints,
270
- )
271
- responses = await schema_validate_migrations(message=validate_migration_data, service=service)
272
- error_messages = [violation.message for response in responses for violation in response.violations]
273
- if error_messages:
274
- rprint(f"{error_badge} | Unable to update the schema, due to failed validations")
275
- for message in error_messages:
276
- rprint(message)
277
- raise typer.Exit(1)
278
-
279
- # ----------------------------------------------------------
280
- # Update the schema
281
- # ----------------------------------------------------------
282
- origin_schema = branch_schema.duplicate()
283
-
284
- # Update the internal schema
285
- schema_default_branch.load_schema(schema=SchemaRoot(**internal_schema))
286
- schema_default_branch.process()
287
- registry.schema.set_schema_branch(name=default_branch.name, schema=schema_default_branch)
288
-
289
- async with db.start_transaction() as dbt:
290
- await registry.schema.update_schema_branch(
291
- schema=candidate_schema,
292
- db=dbt,
293
- branch=default_branch.name,
294
- diff=result.diff,
295
- limit=result.diff.all,
296
- update_db=True,
297
- )
298
- default_branch.update_schema_hash()
299
- rprint("The Core Schema has been updated")
300
- if debug:
301
- rprint(f"New schema hash: {default_branch.active_schema_hash.main}")
302
- await default_branch.save(db=dbt)
303
-
304
- # ----------------------------------------------------------
305
- # Run the migrations
306
- # ----------------------------------------------------------
307
- apply_migration_data = SchemaApplyMigrationData(
308
- branch=default_branch,
309
- new_schema=candidate_schema,
310
- previous_schema=origin_schema,
311
- migrations=result.migrations,
312
- )
313
- migration_error_msgs = await schema_apply_migrations(message=apply_migration_data, service=service)
314
-
315
- if migration_error_msgs:
316
- rprint(f"{error_badge} | Some error(s) happened while running the schema migrations")
317
- for message in migration_error_msgs:
318
- rprint(message)
319
- raise typer.Exit(1)
320
-
321
- await create_defaults(db=db)
177
+ with prefect_test_harness():
178
+ await update_core_schema(db=dbdriver, service=service, initialize=True, debug=debug)
179
+
180
+ await dbdriver.close()
322
181
 
323
182
 
324
183
  @app.command()
@@ -333,7 +192,7 @@ async def constraint(
333
192
  context: CliContext = ctx.obj
334
193
  dbdriver = await context.init_db(retry=1)
335
194
 
336
- manager: Optional[ConstraintManagerBase] = None
195
+ manager: ConstraintManagerBase | None = None
337
196
  if dbdriver.db_type == DatabaseType.NEO4J:
338
197
  manager = ConstraintManagerNeo4j.from_graph_schema(db=dbdriver, schema=GRAPH_SCHEMA)
339
198
  elif dbdriver.db_type == DatabaseType.MEMGRAPH:
@@ -405,35 +264,158 @@ async def index(
405
264
  await dbdriver.close()
406
265
 
407
266
 
408
- async def create_defaults(db: InfrahubDatabase) -> None:
409
- """Create and assign default objects."""
410
- existing_permissions = await NodeManager.query(
411
- schema=InfrahubKind.OBJECTPERMISSION,
412
- db=db,
413
- limit=1,
414
- )
415
- if not existing_permissions:
416
- await setup_permissions(db=db)
267
+ async def migrate_database(db: InfrahubDatabase, initialize: bool = False, check: bool = False) -> None:
268
+ """Apply the latest migrations to the database, this function will print the status directly in the console.
269
+
270
+ This function is expected to run on an empty
271
+
272
+ Args:
273
+ db: The database object.
274
+ check: If True, the function will only check the status of the database and not apply the migrations. Defaults to False.
275
+ """
276
+ rprint("Checking current state of the Database")
277
+
278
+ if initialize:
279
+ await initialize_registry(db=db)
280
+
281
+ root_node = await get_root_node(db=db)
282
+ migrations = await get_graph_migrations(root=root_node)
283
+
284
+ if not migrations:
285
+ rprint(f"Database up-to-date (v{root_node.graph_version}), no migration to execute.")
286
+ return
417
287
 
418
- existing_menu_items = await NodeManager.query(
419
- schema=InfrahubKind.MENUITEM,
420
- db=db,
421
- limit=1,
288
+ rprint(
289
+ f"Database needs to be updated (v{root_node.graph_version} -> v{GRAPH_VERSION}), {len(migrations)} migrations pending"
422
290
  )
423
- if not existing_menu_items:
424
- await create_default_menu(db=db)
291
+
292
+ if check:
293
+ return
294
+
295
+ for migration in migrations:
296
+ execution_result = await migration.execute(db=db)
297
+ validation_result = None
298
+
299
+ if execution_result.success:
300
+ validation_result = await migration.validate_migration(db=db)
301
+ if validation_result.success:
302
+ rprint(f"Migration: {migration.name} {SUCCESS_BADGE}")
303
+ root_node.graph_version = migration.minimum_version + 1
304
+ await root_node.save(db=db)
305
+
306
+ if not execution_result.success or validation_result and not validation_result.success:
307
+ rprint(f"Migration: {migration.name} {FAILED_BADGE}")
308
+ for error in execution_result.errors:
309
+ rprint(f" {error}")
310
+ if validation_result and not validation_result.success:
311
+ for error in validation_result.errors:
312
+ rprint(f" {error}")
313
+ break
314
+
315
+
316
+ async def initialize_internal_schema() -> None:
317
+ registry.schema = SchemaManager()
318
+ schema = SchemaRoot(**internal_schema)
319
+ registry.schema.register_schema(schema=schema)
425
320
 
426
321
 
427
- async def setup_permissions(db: InfrahubDatabase) -> None:
428
- existing_accounts = await NodeManager.query(
429
- schema=InfrahubKind.ACCOUNT,
430
- db=db,
431
- limit=1,
322
+ async def update_core_schema(
323
+ db: InfrahubDatabase, service: InfrahubServices, initialize: bool = True, debug: bool = False
324
+ ) -> None:
325
+ """Update the core schema of Infrahub to the latest version"""
326
+ # ----------------------------------------------------------
327
+ # Initialize Schema and Registry
328
+ # ----------------------------------------------------------
329
+ if initialize:
330
+ await initialize_registry(db=db)
331
+ await initialize_internal_schema()
332
+
333
+ default_branch = registry.get_branch_from_registry(branch=registry.default_branch)
334
+
335
+ # ----------------------------------------------------------
336
+ # Load Current Schema from the database
337
+ # ----------------------------------------------------------
338
+ schema_default_branch = await registry.schema.load_schema_from_db(db=db, branch=default_branch)
339
+ registry.schema.set_schema_branch(name=default_branch.name, schema=schema_default_branch)
340
+ branch_schema = registry.schema.get_schema_branch(name=registry.default_branch)
341
+
342
+ candidate_schema = branch_schema.duplicate()
343
+ candidate_schema.load_schema(schema=SchemaRoot(**internal_schema))
344
+ candidate_schema.load_schema(schema=SchemaRoot(**core_models))
345
+ candidate_schema.load_schema(schema=SchemaRoot(**deprecated_models))
346
+ candidate_schema.process()
347
+
348
+ schema_diff = branch_schema.diff(other=candidate_schema)
349
+ branch_schema.validate_node_deletions(diff=schema_diff)
350
+ result = branch_schema.validate_update(other=candidate_schema, diff=schema_diff, enforce_update_support=False)
351
+ if result.errors:
352
+ rprint(f"{ERROR_BADGE} | Unable to update the schema, due to failed validations")
353
+ for error in result.errors:
354
+ rprint(error.to_string())
355
+ raise typer.Exit(1)
356
+
357
+ if not result.diff.all:
358
+ rprint("Core Schema Up to date, nothing to update")
359
+ return
360
+
361
+ rprint("Core Schema has diff, will need to be updated")
362
+ if debug:
363
+ result.diff.print()
364
+
365
+ # ----------------------------------------------------------
366
+ # Validate if the new schema is valid with the content of the database
367
+ # ----------------------------------------------------------
368
+ validate_migration_data = SchemaValidateMigrationData(
369
+ branch=default_branch,
370
+ schema_branch=candidate_schema,
371
+ constraints=result.constraints,
432
372
  )
433
- administrator_role = await create_super_administrator_role(db=db)
434
- await create_super_administrators_group(db=db, role=administrator_role, admin_accounts=existing_accounts)
373
+ responses = await schema_validate_migrations(message=validate_migration_data, service=service)
374
+ error_messages = [violation.message for response in responses for violation in response.violations]
375
+ if error_messages:
376
+ rprint(f"{ERROR_BADGE} | Unable to update the schema, due to failed validations")
377
+ for message in error_messages:
378
+ rprint(message)
379
+ raise typer.Exit(1)
435
380
 
436
- await create_default_roles(db=db)
381
+ # ----------------------------------------------------------
382
+ # Update the schema
383
+ # ----------------------------------------------------------
384
+ origin_schema = branch_schema.duplicate()
385
+
386
+ # Update the internal schema
387
+ schema_default_branch.load_schema(schema=SchemaRoot(**internal_schema))
388
+ schema_default_branch.process()
389
+ registry.schema.set_schema_branch(name=default_branch.name, schema=schema_default_branch)
390
+
391
+ async with db.start_transaction() as dbt:
392
+ await registry.schema.update_schema_branch(
393
+ schema=candidate_schema,
394
+ db=dbt,
395
+ branch=default_branch.name,
396
+ diff=result.diff,
397
+ limit=result.diff.all,
398
+ update_db=True,
399
+ )
400
+ default_branch.update_schema_hash()
401
+ rprint("The Core Schema has been updated")
402
+ if debug:
403
+ rprint(f"New schema hash: {default_branch.active_schema_hash.main}")
404
+ await default_branch.save(db=dbt)
405
+
406
+ # ----------------------------------------------------------
407
+ # Run the migrations
408
+ # ----------------------------------------------------------
409
+ apply_migration_data = SchemaApplyMigrationData(
410
+ branch=default_branch,
411
+ new_schema=candidate_schema,
412
+ previous_schema=origin_schema,
413
+ migrations=result.migrations,
414
+ )
415
+ migration_error_msgs = await schema_apply_migrations(message=apply_migration_data, service=service)
437
416
 
438
- if config.SETTINGS.main.allow_anonymous_access:
439
- await create_anonymous_role(db=db)
417
+ if migration_error_msgs:
418
+ rprint(f"{ERROR_BADGE} | Some error(s) happened while running the schema migrations")
419
+ for message in migration_error_msgs:
420
+ rprint(message)
421
+ raise typer.Exit(1)