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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (346) hide show
  1. infrahub/api/artifact.py +16 -4
  2. infrahub/api/dependencies.py +8 -0
  3. infrahub/api/oauth2.py +0 -1
  4. infrahub/api/oidc.py +0 -1
  5. infrahub/api/query.py +18 -7
  6. infrahub/api/schema.py +32 -6
  7. infrahub/api/transformation.py +12 -5
  8. infrahub/{message_bus/messages/check_artifact_create.py → artifacts/models.py} +2 -4
  9. infrahub/{message_bus/operations/check/artifact.py → artifacts/tasks.py} +26 -25
  10. infrahub/cli/__init__.py +0 -2
  11. infrahub/cli/db.py +6 -7
  12. infrahub/cli/events.py +8 -3
  13. infrahub/cli/git_agent.py +9 -7
  14. infrahub/cli/tasks.py +4 -6
  15. infrahub/computed_attribute/tasks.py +63 -17
  16. infrahub/computed_attribute/triggers.py +90 -0
  17. infrahub/config.py +1 -1
  18. infrahub/context.py +39 -0
  19. infrahub/core/account.py +5 -8
  20. infrahub/core/attribute.py +53 -21
  21. infrahub/core/branch/models.py +4 -4
  22. infrahub/core/branch/tasks.py +89 -130
  23. infrahub/core/changelog/__init__.py +0 -0
  24. infrahub/core/changelog/diff.py +232 -0
  25. infrahub/core/changelog/models.py +488 -0
  26. infrahub/core/constants/__init__.py +19 -2
  27. infrahub/core/constants/infrahubkind.py +1 -0
  28. infrahub/core/diff/combiner.py +12 -8
  29. infrahub/core/diff/coordinator.py +49 -70
  30. infrahub/core/diff/data_check_synchronizer.py +86 -7
  31. infrahub/core/diff/enricher/aggregated.py +3 -3
  32. infrahub/core/diff/enricher/cardinality_one.py +2 -7
  33. infrahub/core/diff/enricher/hierarchy.py +5 -3
  34. infrahub/core/diff/enricher/labels.py +14 -4
  35. infrahub/core/diff/enricher/path_identifier.py +3 -9
  36. infrahub/core/diff/enricher/summary_counts.py +3 -1
  37. infrahub/core/diff/merger/merger.py +8 -4
  38. infrahub/core/diff/model/path.py +47 -29
  39. infrahub/core/diff/query/all_conflicts.py +6 -3
  40. infrahub/core/diff/query/artifact.py +1 -1
  41. infrahub/core/diff/query/delete_query.py +1 -1
  42. infrahub/core/diff/query/diff_get.py +3 -2
  43. infrahub/core/diff/query/diff_summary.py +1 -1
  44. infrahub/core/diff/query/field_specifiers.py +3 -1
  45. infrahub/core/diff/query/field_summary.py +3 -2
  46. infrahub/core/diff/query/filters.py +12 -1
  47. infrahub/core/diff/query/get_conflict_query.py +1 -1
  48. infrahub/core/diff/query/has_conflicts_query.py +6 -3
  49. infrahub/core/diff/query/merge.py +3 -3
  50. infrahub/core/diff/query/{drop_tracking_id.py → merge_tracking_id.py} +4 -4
  51. infrahub/core/diff/query/roots_metadata.py +9 -2
  52. infrahub/core/diff/query/save.py +151 -66
  53. infrahub/core/diff/query/summary_counts_enricher.py +220 -0
  54. infrahub/core/diff/query/time_range_query.py +3 -2
  55. infrahub/core/diff/query/update_conflict_query.py +1 -1
  56. infrahub/core/diff/query_parser.py +49 -24
  57. infrahub/core/diff/repository/deserializer.py +24 -25
  58. infrahub/core/diff/repository/repository.py +76 -20
  59. infrahub/core/diff/tasks.py +9 -8
  60. infrahub/core/enums.py +1 -1
  61. infrahub/core/integrity/object_conflict/conflict_recorder.py +1 -1
  62. infrahub/core/ipam/reconciler.py +1 -1
  63. infrahub/core/ipam/tasks.py +2 -3
  64. infrahub/core/manager.py +18 -13
  65. infrahub/core/merge.py +5 -2
  66. infrahub/core/migrations/graph/m001_add_version_to_graph.py +1 -1
  67. infrahub/core/migrations/graph/m002_attribute_is_default.py +2 -2
  68. infrahub/core/migrations/graph/m003_relationship_parent_optional.py +2 -2
  69. infrahub/core/migrations/graph/m004_add_attr_documentation.py +1 -1
  70. infrahub/core/migrations/graph/m005_add_rel_read_only.py +1 -1
  71. infrahub/core/migrations/graph/m006_add_rel_on_delete.py +1 -1
  72. infrahub/core/migrations/graph/m007_add_rel_allow_override.py +1 -1
  73. infrahub/core/migrations/graph/m008_add_human_friendly_id.py +1 -1
  74. infrahub/core/migrations/graph/m009_add_generate_profile_attr.py +1 -1
  75. infrahub/core/migrations/graph/m010_add_generate_profile_attr_generic.py +1 -1
  76. infrahub/core/migrations/graph/m011_remove_profile_relationship_schema.py +2 -2
  77. infrahub/core/migrations/graph/m012_convert_account_generic.py +12 -23
  78. infrahub/core/migrations/graph/m013_convert_git_password_credential.py +7 -11
  79. infrahub/core/migrations/graph/m014_remove_index_attr_value.py +2 -2
  80. infrahub/core/migrations/graph/m015_diff_format_update.py +1 -1
  81. infrahub/core/migrations/graph/m016_diff_delete_bug_fix.py +1 -1
  82. infrahub/core/migrations/graph/m017_add_core_profile.py +1 -1
  83. infrahub/core/migrations/graph/m018_uniqueness_nulls.py +2 -2
  84. infrahub/core/migrations/query/attribute_add.py +1 -1
  85. infrahub/core/migrations/query/attribute_rename.py +1 -1
  86. infrahub/core/migrations/query/delete_element_in_schema.py +1 -1
  87. infrahub/core/migrations/query/node_duplicate.py +1 -1
  88. infrahub/core/migrations/query/relationship_duplicate.py +1 -1
  89. infrahub/core/migrations/query/schema_attribute_update.py +1 -1
  90. infrahub/core/migrations/schema/node_attribute_remove.py +1 -1
  91. infrahub/core/migrations/schema/node_remove.py +1 -1
  92. infrahub/core/migrations/schema/tasks.py +5 -5
  93. infrahub/core/migrations/shared.py +4 -4
  94. infrahub/core/models.py +7 -8
  95. infrahub/core/node/__init__.py +161 -40
  96. infrahub/core/node/base.py +1 -1
  97. infrahub/core/node/constraints/grouped_uniqueness.py +9 -2
  98. infrahub/core/node/delete_validator.py +4 -4
  99. infrahub/core/node/ipam.py +13 -8
  100. infrahub/core/node/permissions.py +4 -0
  101. infrahub/core/node/resource_manager/ip_prefix_pool.py +8 -5
  102. infrahub/core/node/standard.py +3 -5
  103. infrahub/core/property.py +1 -1
  104. infrahub/core/protocols.py +4 -0
  105. infrahub/core/protocols_base.py +4 -2
  106. infrahub/core/query/__init__.py +2 -5
  107. infrahub/core/query/attribute.py +9 -9
  108. infrahub/core/query/branch.py +5 -5
  109. infrahub/core/query/delete.py +1 -1
  110. infrahub/core/query/diff.py +45 -7
  111. infrahub/core/query/ipam.py +4 -4
  112. infrahub/core/query/node.py +19 -14
  113. infrahub/core/query/relationship.py +10 -11
  114. infrahub/core/query/resource_manager.py +13 -11
  115. infrahub/core/query/standard_node.py +6 -6
  116. infrahub/core/query/task.py +3 -3
  117. infrahub/core/query/task_log.py +1 -1
  118. infrahub/core/query/utils.py +5 -5
  119. infrahub/core/registry.py +0 -2
  120. infrahub/core/relationship/constraints/count.py +1 -1
  121. infrahub/core/relationship/constraints/peer_kind.py +1 -1
  122. infrahub/core/relationship/model.py +66 -26
  123. infrahub/core/schema/__init__.py +6 -4
  124. infrahub/core/schema/basenode_schema.py +1 -3
  125. infrahub/core/schema/definitions/core.py +14 -2
  126. infrahub/core/schema/definitions/internal.py +16 -0
  127. infrahub/core/schema/generated/genericnode_schema.py +5 -0
  128. infrahub/core/schema/generated/node_schema.py +5 -0
  129. infrahub/core/schema/generic_schema.py +5 -1
  130. infrahub/core/schema/manager.py +45 -42
  131. infrahub/core/schema/node_schema.py +4 -0
  132. infrahub/core/schema/profile_schema.py +4 -0
  133. infrahub/core/schema/relationship_schema.py +2 -2
  134. infrahub/core/schema/schema_branch.py +248 -14
  135. infrahub/core/schema/template_schema.py +36 -0
  136. infrahub/core/task/user_task.py +7 -5
  137. infrahub/core/timestamp.py +1 -1
  138. infrahub/core/utils.py +3 -2
  139. infrahub/core/validators/attribute/choices.py +1 -1
  140. infrahub/core/validators/attribute/enum.py +1 -1
  141. infrahub/core/validators/attribute/kind.py +1 -1
  142. infrahub/core/validators/attribute/length.py +1 -1
  143. infrahub/core/validators/attribute/optional.py +1 -1
  144. infrahub/core/validators/attribute/regex.py +1 -1
  145. infrahub/core/validators/attribute/unique.py +1 -1
  146. infrahub/core/validators/checks_runner.py +37 -0
  147. infrahub/core/validators/node/generate_profile.py +1 -1
  148. infrahub/core/validators/node/hierarchy.py +1 -1
  149. infrahub/core/validators/query.py +1 -1
  150. infrahub/core/validators/relationship/count.py +1 -1
  151. infrahub/core/validators/relationship/optional.py +1 -1
  152. infrahub/core/validators/relationship/peer.py +1 -1
  153. infrahub/core/validators/tasks.py +8 -6
  154. infrahub/core/validators/uniqueness/query.py +20 -17
  155. infrahub/database/__init__.py +15 -2
  156. infrahub/database/memgraph.py +1 -1
  157. infrahub/dependencies/builder/constraint/grouped/node_runner.py +0 -2
  158. infrahub/dependencies/builder/diff/combiner.py +1 -1
  159. infrahub/dependencies/builder/diff/conflicts_enricher.py +1 -1
  160. infrahub/dependencies/builder/diff/coordinator.py +0 -2
  161. infrahub/dependencies/builder/diff/deserializer.py +1 -1
  162. infrahub/dependencies/builder/diff/enricher/summary_counts.py +1 -1
  163. infrahub/events/branch_action.py +47 -21
  164. infrahub/events/group_action.py +73 -0
  165. infrahub/events/models.py +159 -51
  166. infrahub/events/node_action.py +74 -8
  167. infrahub/events/repository_action.py +8 -8
  168. infrahub/events/schema_action.py +21 -8
  169. infrahub/generators/tasks.py +12 -13
  170. infrahub/git/base.py +3 -5
  171. infrahub/git/constants.py +0 -1
  172. infrahub/git/integrator.py +36 -35
  173. infrahub/git/repository.py +7 -8
  174. infrahub/git/tasks.py +43 -107
  175. infrahub/git_credential/helper.py +2 -3
  176. infrahub/graphql/analyzer.py +572 -11
  177. infrahub/graphql/app.py +34 -26
  178. infrahub/graphql/auth/query_permission_checker/anonymous_checker.py +5 -5
  179. infrahub/graphql/auth/query_permission_checker/default_branch_checker.py +4 -4
  180. infrahub/graphql/auth/query_permission_checker/merge_operation_checker.py +4 -4
  181. infrahub/graphql/auth/query_permission_checker/object_permission_checker.py +28 -35
  182. infrahub/graphql/auth/query_permission_checker/super_admin_checker.py +5 -5
  183. infrahub/graphql/enums.py +1 -1
  184. infrahub/graphql/initialization.py +5 -1
  185. infrahub/graphql/loaders/node.py +2 -2
  186. infrahub/graphql/manager.py +59 -54
  187. infrahub/graphql/mutations/account.py +20 -13
  188. infrahub/graphql/mutations/artifact_definition.py +16 -12
  189. infrahub/graphql/mutations/branch.py +61 -40
  190. infrahub/graphql/mutations/computed_attribute.py +19 -13
  191. infrahub/graphql/mutations/diff.py +37 -9
  192. infrahub/graphql/mutations/diff_conflict.py +9 -8
  193. infrahub/graphql/mutations/graphql_query.py +19 -11
  194. infrahub/graphql/mutations/ipam.py +21 -19
  195. infrahub/graphql/mutations/main.py +197 -44
  196. infrahub/graphql/mutations/menu.py +8 -8
  197. infrahub/graphql/mutations/proposed_change.py +36 -28
  198. infrahub/graphql/mutations/relationship.py +302 -105
  199. infrahub/graphql/mutations/repository.py +41 -35
  200. infrahub/graphql/mutations/resource_manager.py +26 -26
  201. infrahub/graphql/mutations/schema.py +51 -33
  202. infrahub/graphql/mutations/tasks.py +16 -10
  203. infrahub/graphql/parser.py +1 -1
  204. infrahub/graphql/permissions.py +6 -4
  205. infrahub/graphql/queries/account.py +22 -18
  206. infrahub/graphql/queries/branch.py +6 -4
  207. infrahub/graphql/queries/diff/tree.py +48 -42
  208. infrahub/graphql/queries/event.py +112 -0
  209. infrahub/graphql/queries/internal.py +3 -3
  210. infrahub/graphql/queries/ipam.py +23 -18
  211. infrahub/graphql/queries/relationship.py +11 -10
  212. infrahub/graphql/queries/resource_manager.py +43 -27
  213. infrahub/graphql/queries/search.py +9 -8
  214. infrahub/graphql/queries/status.py +12 -9
  215. infrahub/graphql/queries/task.py +11 -9
  216. infrahub/graphql/resolvers/resolver.py +69 -43
  217. infrahub/graphql/resolvers/single_relationship.py +16 -10
  218. infrahub/graphql/schema.py +2 -0
  219. infrahub/graphql/subscription/__init__.py +1 -1
  220. infrahub/graphql/subscription/events.py +1 -1
  221. infrahub/graphql/subscription/graphql_query.py +8 -8
  222. infrahub/graphql/types/branch.py +2 -2
  223. infrahub/graphql/types/common.py +6 -1
  224. infrahub/graphql/types/enums.py +2 -0
  225. infrahub/graphql/types/event.py +100 -0
  226. infrahub/graphql/types/interface.py +2 -2
  227. infrahub/graphql/types/node.py +3 -3
  228. infrahub/graphql/types/permission.py +2 -2
  229. infrahub/graphql/types/relationship.py +3 -3
  230. infrahub/graphql/types/standard_node.py +9 -11
  231. infrahub/graphql/utils.py +28 -182
  232. infrahub/groups/tasks.py +2 -3
  233. infrahub/lock.py +1 -1
  234. infrahub/menu/constants.py +1 -0
  235. infrahub/menu/generator.py +14 -3
  236. infrahub/menu/menu.py +116 -127
  237. infrahub/menu/models.py +4 -4
  238. infrahub/message_bus/messages/__init__.py +0 -4
  239. infrahub/message_bus/messages/event_branch_merge.py +3 -0
  240. infrahub/message_bus/messages/request_proposedchange_pipeline.py +2 -0
  241. infrahub/message_bus/operations/__init__.py +3 -5
  242. infrahub/message_bus/operations/check/__init__.py +2 -2
  243. infrahub/message_bus/operations/check/generator.py +1 -3
  244. infrahub/message_bus/operations/check/repository.py +1 -1
  245. infrahub/message_bus/operations/event/branch.py +7 -3
  246. infrahub/message_bus/operations/event/schema.py +1 -1
  247. infrahub/message_bus/operations/finalize/validator.py +1 -1
  248. infrahub/message_bus/operations/git/file.py +2 -2
  249. infrahub/message_bus/operations/git/repository.py +1 -1
  250. infrahub/message_bus/operations/requests/__init__.py +0 -2
  251. infrahub/message_bus/operations/requests/generator_definition.py +1 -1
  252. infrahub/message_bus/operations/requests/proposed_change.py +26 -11
  253. infrahub/message_bus/operations/requests/repository.py +2 -2
  254. infrahub/message_bus/operations/send/echo.py +1 -1
  255. infrahub/message_bus/types.py +1 -1
  256. infrahub/permissions/__init__.py +2 -1
  257. infrahub/permissions/types.py +26 -0
  258. infrahub/pools/prefix.py +29 -165
  259. infrahub/prefect_server/__init__.py +0 -0
  260. infrahub/prefect_server/app.py +18 -0
  261. infrahub/prefect_server/database.py +20 -0
  262. infrahub/prefect_server/events.py +28 -0
  263. infrahub/prefect_server/models.py +46 -0
  264. infrahub/proposed_change/models.py +15 -1
  265. infrahub/proposed_change/tasks.py +173 -35
  266. infrahub/pytest_plugin.py +4 -4
  267. infrahub/server.py +12 -11
  268. infrahub/services/__init__.py +147 -62
  269. infrahub/services/adapters/cache/__init__.py +7 -5
  270. infrahub/services/adapters/cache/nats.py +40 -22
  271. infrahub/services/adapters/cache/redis.py +0 -4
  272. infrahub/services/adapters/event/__init__.py +10 -18
  273. infrahub/services/adapters/http/__init__.py +0 -5
  274. infrahub/services/adapters/http/httpx.py +22 -15
  275. infrahub/services/adapters/message_bus/__init__.py +23 -6
  276. infrahub/services/adapters/message_bus/local.py +8 -6
  277. infrahub/services/adapters/message_bus/nats.py +12 -6
  278. infrahub/services/adapters/message_bus/rabbitmq.py +22 -9
  279. infrahub/services/adapters/workflow/__init__.py +11 -8
  280. infrahub/services/adapters/workflow/local.py +28 -7
  281. infrahub/services/adapters/workflow/worker.py +23 -7
  282. infrahub/services/component.py +38 -35
  283. infrahub/services/scheduler.py +32 -29
  284. infrahub/storage.py +2 -4
  285. infrahub/task_manager/constants.py +1 -1
  286. infrahub/task_manager/event.py +182 -0
  287. infrahub/task_manager/models.py +125 -1
  288. infrahub/task_manager/task.py +1 -1
  289. infrahub/tasks/artifact.py +14 -16
  290. infrahub/tasks/registry.py +1 -1
  291. infrahub/tasks/telemetry.py +13 -14
  292. infrahub/transformations/tasks.py +3 -5
  293. infrahub/trigger/__init__.py +0 -0
  294. infrahub/trigger/catalogue.py +15 -0
  295. infrahub/trigger/constants.py +9 -0
  296. infrahub/trigger/models.py +69 -0
  297. infrahub/trigger/tasks.py +85 -0
  298. infrahub/types.py +1 -1
  299. infrahub/utils.py +1 -1
  300. infrahub/webhook/constants.py +0 -2
  301. infrahub/webhook/models.py +8 -2
  302. infrahub/webhook/tasks.py +20 -73
  303. infrahub/webhook/triggers.py +20 -0
  304. infrahub/workers/infrahub_async.py +36 -25
  305. infrahub/workers/utils.py +63 -0
  306. infrahub/workflows/catalogue.py +13 -37
  307. infrahub/workflows/initialization.py +6 -8
  308. infrahub/workflows/models.py +3 -5
  309. infrahub/workflows/utils.py +1 -1
  310. infrahub_sdk/ctl/check.py +3 -3
  311. infrahub_sdk/ctl/cli_commands.py +11 -10
  312. infrahub_sdk/ctl/exceptions.py +0 -6
  313. infrahub_sdk/ctl/exporter.py +1 -1
  314. infrahub_sdk/ctl/generator.py +5 -5
  315. infrahub_sdk/ctl/importer.py +3 -2
  316. infrahub_sdk/ctl/menu.py +1 -1
  317. infrahub_sdk/ctl/object.py +1 -1
  318. infrahub_sdk/ctl/repository.py +23 -15
  319. infrahub_sdk/ctl/schema.py +2 -2
  320. infrahub_sdk/ctl/utils.py +4 -3
  321. infrahub_sdk/ctl/validate.py +2 -1
  322. infrahub_sdk/exceptions.py +6 -0
  323. infrahub_sdk/generator.py +3 -0
  324. infrahub_sdk/node.py +2 -2
  325. infrahub_sdk/schema/__init__.py +14 -2
  326. infrahub_sdk/schema/main.py +7 -0
  327. infrahub_sdk/utils.py +11 -1
  328. infrahub_sdk/yaml.py +2 -3
  329. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/METADATA +46 -12
  330. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/RECORD +338 -321
  331. infrahub_testcontainers/container.py +14 -6
  332. infrahub_testcontainers/docker-compose.test.yml +24 -5
  333. infrahub_testcontainers/haproxy.cfg +43 -0
  334. infrahub_testcontainers/helpers.py +85 -1
  335. infrahub/core/branch/constants.py +0 -2
  336. infrahub/graphql/query.py +0 -52
  337. infrahub/message_bus/messages/request_artifactdefinition_check.py +0 -17
  338. infrahub/message_bus/operations/requests/artifact_definition.py +0 -148
  339. infrahub/schema/constants.py +0 -1
  340. infrahub/schema/tasks.py +0 -76
  341. infrahub/services/adapters/database/__init__.py +0 -9
  342. infrahub_sdk/ctl/_file.py +0 -13
  343. /infrahub/{schema → artifacts}/__init__.py +0 -0
  344. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/LICENSE.txt +0 -0
  345. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/WHEEL +0 -0
  346. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/entry_points.txt +0 -0
infrahub_sdk/ctl/check.py CHANGED
@@ -5,7 +5,7 @@ import sys
5
5
  from asyncio import run as aiorun
6
6
  from dataclasses import dataclass
7
7
  from pathlib import Path
8
- from typing import TYPE_CHECKING
8
+ from typing import TYPE_CHECKING, Optional
9
9
 
10
10
  import typer
11
11
  from rich.console import Console
@@ -50,8 +50,8 @@ def run(
50
50
  format_json: bool,
51
51
  list_available: bool,
52
52
  variables: dict[str, str],
53
- name: str | None = None,
54
- branch: str | None = None,
53
+ name: Optional[str] = None,
54
+ branch: Optional[str] = None,
55
55
  ) -> None:
56
56
  """Locate and execute all checks under the defined path."""
57
57
 
@@ -7,7 +7,7 @@ import logging
7
7
  import platform
8
8
  import sys
9
9
  from pathlib import Path
10
- from typing import TYPE_CHECKING, Any, Callable
10
+ from typing import TYPE_CHECKING, Any, Callable, Optional
11
11
 
12
12
  import jinja2
13
13
  import typer
@@ -74,13 +74,13 @@ console = Console()
74
74
  @catch_exception(console=console)
75
75
  def check(
76
76
  check_name: str = typer.Argument(default="", help="Name of the Python check"),
77
- branch: str | None = None,
77
+ branch: Optional[str] = None,
78
78
  path: str = typer.Option(".", help="Root directory"),
79
79
  debug: bool = False,
80
80
  format_json: bool = False,
81
81
  _: str = CONFIG_PARAM,
82
82
  list_available: bool = typer.Option(False, "--list", help="Show available Python checks"),
83
- variables: list[str] | None = typer.Argument(
83
+ variables: Optional[list[str]] = typer.Argument(
84
84
  None, help="Variables to pass along with the query. Format key=value key=value."
85
85
  ),
86
86
  ) -> None:
@@ -102,12 +102,12 @@ def check(
102
102
  @catch_exception(console=console)
103
103
  async def generator(
104
104
  generator_name: str = typer.Argument(default="", help="Name of the Generator"),
105
- branch: str | None = None,
105
+ branch: Optional[str] = None,
106
106
  path: str = typer.Option(".", help="Root directory"),
107
107
  debug: bool = False,
108
108
  _: str = CONFIG_PARAM,
109
109
  list_available: bool = typer.Option(False, "--list", help="Show available Generators"),
110
- variables: list[str] | None = typer.Argument(
110
+ variables: Optional[list[str]] = typer.Argument(
111
111
  None, help="Variables to pass along with the query. Format key=value key=value."
112
112
  ),
113
113
  ) -> None:
@@ -129,14 +129,14 @@ async def run(
129
129
  method: str = "run",
130
130
  debug: bool = False,
131
131
  _: str = CONFIG_PARAM,
132
- branch: str = typer.Option("main", help="Branch on which to run the script."),
133
- concurrent: int | None = typer.Option(
132
+ branch: str = typer.Option(None, help="Branch on which to run the script."),
133
+ concurrent: Optional[int] = typer.Option(
134
134
  None,
135
135
  help="Maximum number of requests to execute at the same time.",
136
136
  envvar="INFRAHUB_MAX_CONCURRENT_EXECUTION",
137
137
  ),
138
138
  timeout: int = typer.Option(60, help="Timeout in sec", envvar="INFRAHUB_TIMEOUT"),
139
- variables: list[str] | None = typer.Argument(
139
+ variables: Optional[list[str]] = typer.Argument(
140
140
  None, help="Variables to pass along with the query. Format key=value key=value."
141
141
  ),
142
142
  ) -> None:
@@ -259,7 +259,7 @@ def _run_transform(
259
259
  @catch_exception(console=console)
260
260
  def render(
261
261
  transform_name: str = typer.Argument(default="", help="Name of the Python transformation", show_default=False),
262
- variables: list[str] | None = typer.Argument(
262
+ variables: Optional[list[str]] = typer.Argument(
263
263
  None, help="Variables to pass along with the query. Format key=value key=value."
264
264
  ),
265
265
  branch: str = typer.Option(None, help="Branch on which to render the transform."),
@@ -309,7 +309,7 @@ def render(
309
309
  @catch_exception(console=console)
310
310
  def transform(
311
311
  transform_name: str = typer.Argument(default="", help="Name of the Python transformation", show_default=False),
312
- variables: list[str] | None = typer.Argument(
312
+ variables: Optional[list[str]] = typer.Argument(
313
313
  None, help="Variables to pass along with the query. Format key=value key=value."
314
314
  ),
315
315
  branch: str = typer.Option(None, help="Branch on which to run the transformation"),
@@ -383,6 +383,7 @@ def protocols(
383
383
 
384
384
  else:
385
385
  client = initialize_client_sync()
386
+ branch = branch or client.default_branch
386
387
  schema.update(client.schema.fetch(branch=branch))
387
388
 
388
389
  code_generator = CodeGenerator(schema=schema)
@@ -6,9 +6,3 @@ class QueryNotFoundError(Error):
6
6
  def __init__(self, name: str, message: str = ""):
7
7
  self.message = message or f"The requested query '{name}' was not found."
8
8
  super().__init__(self.message)
9
-
10
-
11
- class FileNotValidError(Error):
12
- def __init__(self, name: str, message: str = ""):
13
- self.message = message or f"Cannot parse '{name}' content."
14
- super().__init__(self.message)
@@ -22,7 +22,7 @@ def dump(
22
22
  directory: Path = typer.Option(directory_name_with_timestamp, help="Directory path to store export"),
23
23
  quiet: bool = typer.Option(False, help="No console output"),
24
24
  _: str = CONFIG_PARAM,
25
- branch: str = typer.Option("main", help="Branch from which to export"),
25
+ branch: str = typer.Option(None, help="Branch from which to export"),
26
26
  concurrent: int = typer.Option(
27
27
  4,
28
28
  help="Maximum number of requests to execute at the same time.",
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from pathlib import Path
4
- from typing import TYPE_CHECKING
4
+ from typing import TYPE_CHECKING, Optional
5
5
 
6
6
  import typer
7
7
  from rich.console import Console
@@ -9,7 +9,7 @@ from rich.console import Console
9
9
  from ..ctl import config
10
10
  from ..ctl.client import initialize_client
11
11
  from ..ctl.repository import get_repository_config
12
- from ..ctl.utils import execute_graphql_query, parse_cli_vars
12
+ from ..ctl.utils import execute_graphql_query, init_logging, parse_cli_vars
13
13
  from ..exceptions import ModuleImportError
14
14
  from ..node import InfrahubNode
15
15
 
@@ -20,11 +20,12 @@ if TYPE_CHECKING:
20
20
  async def run(
21
21
  generator_name: str,
22
22
  path: str, # noqa: ARG001
23
- debug: bool, # noqa: ARG001
23
+ debug: bool,
24
24
  list_available: bool,
25
25
  branch: str | None = None,
26
- variables: list[str] | None = None,
26
+ variables: Optional[list[str]] = None,
27
27
  ) -> None:
28
+ init_logging(debug=debug)
28
29
  repository_config = get_repository_config(Path(config.INFRAHUB_REPO_CONFIG_FILE))
29
30
 
30
31
  if list_available or not generator_name:
@@ -34,7 +35,6 @@ async def run(
34
35
  generator_config = repository_config.get_generator_definition(name=generator_name)
35
36
 
36
37
  console = Console()
37
-
38
38
  relative_path = str(generator_config.file_path.parent) if generator_config.file_path.parent != Path() else None
39
39
 
40
40
  try:
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from asyncio import run as aiorun
4
4
  from pathlib import Path
5
+ from typing import Optional
5
6
 
6
7
  import typer
7
8
  from rich.console import Console
@@ -25,8 +26,8 @@ def load(
25
26
  ),
26
27
  quiet: bool = typer.Option(False, help="No console output"),
27
28
  _: str = CONFIG_PARAM,
28
- branch: str = typer.Option("main", help="Branch from which to export"),
29
- concurrent: int | None = typer.Option(
29
+ branch: str = typer.Option(None, help="Branch from which to export"),
30
+ concurrent: Optional[int] = typer.Option(
30
31
  None,
31
32
  help="Maximum number of requests to execute at the same time.",
32
33
  envvar="INFRAHUB_MAX_CONCURRENT_EXECUTION",
infrahub_sdk/ctl/menu.py CHANGED
@@ -27,7 +27,7 @@ def callback() -> None:
27
27
  async def load(
28
28
  menus: list[Path],
29
29
  debug: bool = False,
30
- branch: str = typer.Option("main", help="Branch on which to load the menu."),
30
+ branch: str = typer.Option(None, help="Branch on which to load the menu."),
31
31
  _: str = CONFIG_PARAM,
32
32
  ) -> None:
33
33
  """Load one or multiple menu files into Infrahub."""
@@ -27,7 +27,7 @@ def callback() -> None:
27
27
  async def load(
28
28
  paths: list[Path],
29
29
  debug: bool = False,
30
- branch: str = typer.Option("main", help="Branch on which to load the objects."),
30
+ branch: str = typer.Option(None, help="Branch on which to load the objects."),
31
31
  _: str = CONFIG_PARAM,
32
32
  ) -> None:
33
33
  """Load one or multiple objects files into Infrahub."""
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from pathlib import Path
4
+ from typing import Optional
4
5
 
5
6
  import typer
6
7
  import yaml
@@ -8,15 +9,14 @@ from pydantic import ValidationError
8
9
  from rich.console import Console
9
10
  from rich.table import Table
10
11
 
11
- from infrahub_sdk.ctl.client import initialize_client
12
-
13
12
  from ..async_typer import AsyncTyper
14
- from ..ctl.exceptions import FileNotValidError
15
- from ..ctl.utils import init_logging
13
+ from ..exceptions import FileNotValidError
16
14
  from ..graphql import Mutation, Query
17
15
  from ..schema.repository import InfrahubRepositoryConfig
18
- from ._file import read_file
16
+ from ..utils import read_file
17
+ from .client import initialize_client
19
18
  from .parameters import CONFIG_PARAM
19
+ from .utils import init_logging
20
20
 
21
21
  app = AsyncTyper()
22
22
  console = Console()
@@ -69,12 +69,11 @@ async def add(
69
69
  name: str,
70
70
  location: str,
71
71
  description: str = "",
72
- username: str | None = None,
72
+ username: Optional[str] = None,
73
73
  password: str = "",
74
- commit: str = "",
74
+ ref: str = "",
75
75
  read_only: bool = False,
76
76
  debug: bool = False,
77
- branch: str = typer.Option("main", help="Branch on which to add the repository."),
78
77
  _: str = CONFIG_PARAM,
79
78
  ) -> None:
80
79
  """Add a new repository."""
@@ -86,15 +85,24 @@ async def add(
86
85
  "name": {"value": name},
87
86
  "location": {"value": location},
88
87
  "description": {"value": description},
89
- "commit": {"value": commit},
90
88
  },
91
89
  }
90
+ if read_only:
91
+ input_data["data"]["ref"] = {"value": ref}
92
+ else:
93
+ input_data["data"]["default_branch"] = {"value": ref}
92
94
 
93
95
  client = initialize_client()
94
96
 
95
- credential = await client.create(kind="CorePasswordCredential", name=name, username=username, password=password)
96
- await credential.save(allow_upsert=True)
97
- input_data["data"]["credential"] = {"id": credential.id}
97
+ if username or password:
98
+ credential = await client.create(
99
+ kind="CorePasswordCredential",
100
+ name=name,
101
+ username=username,
102
+ password=password,
103
+ )
104
+ await credential.save(allow_upsert=True)
105
+ input_data["data"]["credential"] = {"id": credential.id}
98
106
 
99
107
  query = Mutation(
100
108
  mutation="CoreReadOnlyRepositoryCreate" if read_only else "CoreRepositoryCreate",
@@ -102,18 +110,18 @@ async def add(
102
110
  query={"ok": None},
103
111
  )
104
112
 
105
- await client.execute_graphql(query=query.render(), branch_name=branch, tracker="mutation-repository-create")
113
+ await client.execute_graphql(query=query.render(), tracker="mutation-repository-create")
106
114
 
107
115
 
108
116
  @app.command()
109
117
  async def list(
110
- branch: str | None = None,
118
+ branch: Optional[str] = typer.Option(None, help="Branch on which to list repositories."),
111
119
  debug: bool = False,
112
120
  _: str = CONFIG_PARAM,
113
121
  ) -> None:
114
122
  init_logging(debug=debug)
115
123
 
116
- client = initialize_client(branch=branch)
124
+ client = initialize_client()
117
125
 
118
126
  repo_status_query = {
119
127
  "CoreGenericRepository": {
@@ -108,7 +108,7 @@ def get_node(schemas_data: list[dict], schema_index: int, node_index: int) -> di
108
108
  async def load(
109
109
  schemas: list[Path],
110
110
  debug: bool = False,
111
- branch: str = typer.Option("main", help="Branch on which to load the schema."),
111
+ branch: str = typer.Option(None, help="Branch on which to load the schema."),
112
112
  wait: int = typer.Option(0, help="Time in seconds to wait until the schema has converged across all workers"),
113
113
  _: str = CONFIG_PARAM,
114
114
  ) -> None:
@@ -159,7 +159,7 @@ async def load(
159
159
  async def check(
160
160
  schemas: list[Path],
161
161
  debug: bool = False,
162
- branch: str = typer.Option("main", help="Branch on which to check the schema."),
162
+ branch: str = typer.Option(None, help="Branch on which to check the schema."),
163
163
  _: str = CONFIG_PARAM,
164
164
  ) -> None:
165
165
  """Check if schema files are valid and what would be the impact of loading them with Infrahub."""
infrahub_sdk/ctl/utils.py CHANGED
@@ -6,7 +6,7 @@ import traceback
6
6
  from collections.abc import Coroutine
7
7
  from functools import wraps
8
8
  from pathlib import Path
9
- from typing import TYPE_CHECKING, Any, Callable, NoReturn, TypeVar
9
+ from typing import TYPE_CHECKING, Any, Callable, NoReturn, Optional, TypeVar
10
10
 
11
11
  import pendulum
12
12
  import typer
@@ -17,10 +17,10 @@ from rich.console import Console
17
17
  from rich.logging import RichHandler
18
18
  from rich.markup import escape
19
19
 
20
- from ..ctl.exceptions import FileNotValidError, QueryNotFoundError
21
20
  from ..exceptions import (
22
21
  AuthenticationError,
23
22
  Error,
23
+ FileNotValidError,
24
24
  GraphQLError,
25
25
  NodeNotFoundError,
26
26
  ResourceNotDefinedError,
@@ -30,6 +30,7 @@ from ..exceptions import (
30
30
  )
31
31
  from ..yaml import YamlFile
32
32
  from .client import initialize_client_sync
33
+ from .exceptions import QueryNotFoundError
33
34
 
34
35
  if TYPE_CHECKING:
35
36
  from ..schema.repository import InfrahubRepositoryConfig
@@ -144,7 +145,7 @@ def print_graphql_errors(console: Console, errors: list) -> None:
144
145
  console.print(f"[red]{escape(str(error))}")
145
146
 
146
147
 
147
- def parse_cli_vars(variables: list[str] | None) -> dict[str, str]:
148
+ def parse_cli_vars(variables: Optional[list[str]]) -> dict[str, str]:
148
149
  if not variables:
149
150
  return {}
150
151
 
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import sys
4
4
  from pathlib import Path
5
+ from typing import Optional
5
6
 
6
7
  import typer
7
8
  import ujson
@@ -57,7 +58,7 @@ async def validate_schema(schema: Path, _: str = CONFIG_PARAM) -> None:
57
58
  @catch_exception(console=console)
58
59
  def validate_graphql(
59
60
  query: str,
60
- variables: list[str] | None = typer.Argument(
61
+ variables: Optional[list[str]] = typer.Argument(
61
62
  None, help="Variables to pass along with the query. Format key=value key=value."
62
63
  ),
63
64
  debug: bool = typer.Option(False, help="Display more troubleshooting information."),
@@ -131,3 +131,9 @@ class UninitializedError(Error):
131
131
 
132
132
  class InvalidResponseError(Error):
133
133
  """Raised when an object requires an initialization step before use"""
134
+
135
+
136
+ class FileNotValidError(Error):
137
+ def __init__(self, name: str, message: str = ""):
138
+ self.message = message or f"Cannot parse '{name}' content."
139
+ super().__init__(self.message)
infrahub_sdk/generator.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import logging
3
4
  import os
4
5
  from abc import abstractmethod
5
6
  from typing import TYPE_CHECKING
@@ -27,6 +28,7 @@ class InfrahubGenerator:
27
28
  generator_instance: str = "",
28
29
  params: dict | None = None,
29
30
  convert_query_response: bool = False,
31
+ logger: logging.Logger | None = None,
30
32
  ) -> None:
31
33
  self.query = query
32
34
  self.branch = branch
@@ -41,6 +43,7 @@ class InfrahubGenerator:
41
43
  self._related_nodes: list[InfrahubNode] = []
42
44
  self.infrahub_node = infrahub_node
43
45
  self.convert_query_response = convert_query_response
46
+ self.logger = logger if logger else logging.getLogger("infrahub.tasks")
44
47
 
45
48
  @property
46
49
  def store(self) -> NodeStore:
infrahub_sdk/node.py CHANGED
@@ -1118,14 +1118,14 @@ class InfrahubNode(InfrahubNodeBase):
1118
1118
  async def artifact_generate(self, name: str) -> None:
1119
1119
  self._validate_artifact_support(ARTIFACT_GENERATE_FEATURE_NOT_SUPPORTED_MESSAGE)
1120
1120
 
1121
- artifact = await self._client.get(kind="CoreArtifact", definition__name__value=name, object__ids=[self.id])
1121
+ artifact = await self._client.get(kind="CoreArtifact", name__value=name, object__ids=[self.id])
1122
1122
  await artifact.definition.fetch() # type: ignore[attr-defined]
1123
1123
  await artifact.definition.peer.generate([artifact.id]) # type: ignore[attr-defined]
1124
1124
 
1125
1125
  async def artifact_fetch(self, name: str) -> str | dict[str, Any]:
1126
1126
  self._validate_artifact_support(ARTIFACT_GENERATE_FEATURE_NOT_SUPPORTED_MESSAGE)
1127
1127
 
1128
- artifact = await self._client.get(kind="CoreArtifact", definition__name__value=name, object__ids=[self.id])
1128
+ artifact = await self._client.get(kind="CoreArtifact", name__value=name, object__ids=[self.id])
1129
1129
  content = await self._client.object_store.get(identifier=artifact.storage_id.value) # type: ignore[attr-defined]
1130
1130
  return content
1131
1131
 
@@ -34,6 +34,7 @@ from .main import (
34
34
  RelationshipSchemaAPI,
35
35
  SchemaRoot,
36
36
  SchemaRootAPI,
37
+ TemplateSchemaAPI,
37
38
  )
38
39
 
39
40
  if TYPE_CHECKING:
@@ -58,6 +59,7 @@ __all__ = [
58
59
  "RelationshipSchemaAPI",
59
60
  "SchemaRoot",
60
61
  "SchemaRootAPI",
62
+ "TemplateSchemaAPI",
61
63
  ]
62
64
 
63
65
 
@@ -78,8 +80,10 @@ class EnumMutation(str, Enum):
78
80
 
79
81
 
80
82
  MainSchemaTypes: TypeAlias = Union[NodeSchema, GenericSchema]
81
- MainSchemaTypesAPI: TypeAlias = Union[NodeSchemaAPI, GenericSchemaAPI, ProfileSchemaAPI]
82
- MainSchemaTypesAll: TypeAlias = Union[NodeSchema, GenericSchema, NodeSchemaAPI, GenericSchemaAPI, ProfileSchemaAPI]
83
+ MainSchemaTypesAPI: TypeAlias = Union[NodeSchemaAPI, GenericSchemaAPI, ProfileSchemaAPI, TemplateSchemaAPI]
84
+ MainSchemaTypesAll: TypeAlias = Union[
85
+ NodeSchema, GenericSchema, NodeSchemaAPI, GenericSchemaAPI, ProfileSchemaAPI, TemplateSchemaAPI
86
+ ]
83
87
 
84
88
 
85
89
  class InfrahubSchemaBase:
@@ -417,6 +421,10 @@ class InfrahubSchema(InfrahubSchemaBase):
417
421
  profile = ProfileSchemaAPI(**profile_schema)
418
422
  nodes[profile.kind] = profile
419
423
 
424
+ for template_schema in data.get("templates", []):
425
+ template = TemplateSchemaAPI(**template_schema)
426
+ nodes[template.kind] = template
427
+
420
428
  return nodes
421
429
 
422
430
 
@@ -621,6 +629,10 @@ class InfrahubSchemaSync(InfrahubSchemaBase):
621
629
  profile = ProfileSchemaAPI(**profile_schema)
622
630
  nodes[profile.kind] = profile
623
631
 
632
+ for template_schema in data.get("templates", []):
633
+ template = TemplateSchemaAPI(**template_schema)
634
+ nodes[template.kind] = template
635
+
624
636
  return nodes
625
637
 
626
638
  def load(
@@ -31,6 +31,7 @@ class RelationshipKind(str, Enum):
31
31
  GROUP = "Group"
32
32
  HIERARCHY = "Hierarchy"
33
33
  PROFILE = "Profile"
34
+ TEMPLATE = "Template"
34
35
 
35
36
 
36
37
  class RelationshipDirection(str, Enum):
@@ -290,6 +291,7 @@ class BaseNodeSchema(BaseSchema):
290
291
  branch: BranchSupportType | None = None
291
292
  default_filter: str | None = None
292
293
  generate_profile: bool | None = None
294
+ generate_template: bool | None = None
293
295
  parent: str | None = None
294
296
  children: str | None = None
295
297
 
@@ -308,6 +310,10 @@ class ProfileSchemaAPI(BaseSchema, BaseSchemaAttrRelAPI):
308
310
  inherit_from: list[str] = Field(default_factory=list)
309
311
 
310
312
 
313
+ class TemplateSchemaAPI(BaseSchema, BaseSchemaAttrRelAPI):
314
+ inherit_from: list[str] = Field(default_factory=list)
315
+
316
+
311
317
  class NodeExtensionSchema(BaseModel):
312
318
  model_config = ConfigDict(use_enum_values=True)
313
319
 
@@ -341,3 +347,4 @@ class SchemaRootAPI(BaseModel):
341
347
  generics: list[GenericSchemaAPI] = Field(default_factory=list)
342
348
  nodes: list[NodeSchemaAPI] = Field(default_factory=list)
343
349
  profiles: list[ProfileSchemaAPI] = Field(default_factory=list)
350
+ templates: list[TemplateSchemaAPI] = Field(default_factory=list)
infrahub_sdk/utils.py CHANGED
@@ -17,7 +17,7 @@ from graphql import (
17
17
 
18
18
  from infrahub_sdk.repository import GitRepoManager
19
19
 
20
- from .exceptions import JsonDecodeError
20
+ from .exceptions import FileNotValidError, JsonDecodeError
21
21
 
22
22
  if TYPE_CHECKING:
23
23
  from graphql import GraphQLResolveInfo
@@ -342,6 +342,16 @@ def write_to_file(path: Path, value: Any) -> bool:
342
342
  return written is not None
343
343
 
344
344
 
345
+ def read_file(file_name: Path) -> str:
346
+ if not file_name.is_file():
347
+ raise FileNotValidError(name=str(file_name), message=f"{file_name} is not a valid file")
348
+ try:
349
+ with Path.open(file_name, encoding="utf-8") as fobj:
350
+ return fobj.read()
351
+ except UnicodeDecodeError as exc:
352
+ raise FileNotValidError(name=str(file_name), message=f"Unable to read {file_name} with utf-8 encoding") from exc
353
+
354
+
345
355
  def get_user_permissions(data: list[dict]) -> dict:
346
356
  groups = {}
347
357
  for group in data:
infrahub_sdk/yaml.py CHANGED
@@ -8,9 +8,8 @@ import yaml
8
8
  from pydantic import BaseModel, Field
9
9
  from typing_extensions import Self
10
10
 
11
- from .ctl._file import read_file
12
- from .ctl.exceptions import FileNotValidError
13
- from .utils import find_files
11
+ from .exceptions import FileNotValidError
12
+ from .utils import find_files, read_file
14
13
 
15
14
 
16
15
  class InfrahubFileApiVersion(str, Enum):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: infrahub-server
3
- Version: 1.1.6
3
+ Version: 1.2.0rc0
4
4
  Summary: Infrahub is taking a new approach to Infrastructure Management by providing a new generation of datastore to organize and control all the data that defines how an infrastructure should run.
5
5
  Home-page: https://opsmill.com
6
6
  License: AGPL-3.0-only
@@ -29,8 +29,8 @@ Requires-Dist: graphene (>=3.4,<3.5)
29
29
  Requires-Dist: gunicorn (>=23.0.0,<24.0.0)
30
30
  Requires-Dist: lunr (>=0.7.0.post1,<0.8.0)
31
31
  Requires-Dist: nats-py (>=2.7.2,<3.0.0)
32
- Requires-Dist: neo4j (>=5.24,<5.25)
33
- Requires-Dist: neo4j-rust-ext (>=5.24.0.0,<6.0.0.0)
32
+ Requires-Dist: neo4j (>=5.27,<5.28)
33
+ Requires-Dist: neo4j-rust-ext (>=5.27.0.0,<6.0.0.0)
34
34
  Requires-Dist: netaddr (==1.3.0)
35
35
  Requires-Dist: numpy (>=1.24.2,<2.0.0) ; python_version >= "3.9" and python_version < "3.12"
36
36
  Requires-Dist: numpy (>=1.26.2,<2.0.0) ; python_version >= "3.12"
@@ -38,10 +38,10 @@ Requires-Dist: opentelemetry-exporter-otlp-proto-grpc (==1.28.1)
38
38
  Requires-Dist: opentelemetry-exporter-otlp-proto-http (==1.28.1)
39
39
  Requires-Dist: opentelemetry-instrumentation-aio-pika (==0.49b1)
40
40
  Requires-Dist: opentelemetry-instrumentation-fastapi (==0.49b1)
41
- Requires-Dist: prefect (==3.0.11)
41
+ Requires-Dist: prefect (==3.1.14)
42
42
  Requires-Dist: pyarrow (>=14,<15)
43
- Requires-Dist: pydantic (==2.7.2)
44
- Requires-Dist: pydantic-settings (==2.6.1)
43
+ Requires-Dist: pydantic (>=2.9,<2.10)
44
+ Requires-Dist: pydantic-settings (>=2.7,<2.8)
45
45
  Requires-Dist: pyjwt (>=2.8,<2.9)
46
46
  Requires-Dist: pytest (>=7.4,<7.5)
47
47
  Requires-Dist: python-multipart (==0.0.18)
@@ -58,16 +58,46 @@ Project-URL: Documentation, https://docs.infrahub.app/
58
58
  Project-URL: Repository, https://github.com/opsmill/infrahub
59
59
  Description-Content-Type: text/markdown
60
60
 
61
- <!-- markdownlint-disable -->
62
- ![Infrahub Logo](docs/static/img/infrahub-hori.svg)
63
- <!-- markdownlint-restore -->
61
+ <h1 align="center">
62
+ <a href=""><img src="docs/static/img/infrahub-hori.svg" alt="Infrahub" width="350"></a>
63
+ </h1>
64
+ <h3 align="center">Simplify Infrastructure Automation</h2>
64
65
 
65
- # Infrahub
66
+ <p align="center">
67
+ <a href="https://www.linkedin.com/company/opsmill">
68
+ <img src="https://img.shields.io/badge/linkedin-blue?logo=linkedin"/>
69
+ </a>
70
+ <a href="https://discord.gg/opsmill">
71
+ <img src="https://img.shields.io/badge/Discord-7289DA?&logo=discord&logoColor=white"/>
72
+ </a>
73
+ </p>
66
74
 
67
- Infrahub from [OpsMill](https://opsmill.com) is taking a new approach to Infrastructure Management by providing a new generation of datastore to organize and control all the data that defines how an infrastructure should run. Infrahub offers a central hub to manage the data, templates and playbooks that powers your infrastructure by combining the version control and branch management capabilities of Git with the flexible data model and UI of a graph database.
75
+ Infrahub from [OpsMill](https://opsmill.com) is taking a new approach to Infrastructure Management by providing a new generation of datastore to organize and control all the data that defines how an infrastructure should run. Infrahub offers a central hub to manage the data, templates and playbooks that powers your infrastructure by combining the version control and branch management capabilities similar to Git with the flexible data model and UI of a graph database.
76
+
77
+ If you just want to try Infrahub out, you can use our [Always-On Sandbox](https://demo.infrahub.app/) to get started.
68
78
 
69
79
  ![infrahub screenshot](docs/docs/media/infrahub-readme.gif)
70
80
 
81
+ ## Why Use Infrahub?
82
+
83
+ **Unified Source of Truth** - Infrahub is a single source of truth for all your infrastructure and network data. It provides a unified view of your infrastructure, allowing you to manage your infrastructure in a more efficient and effective way. Infrahub allows unidirectional and bi-directional [data synchronization](https://docs.infrahub.app/sync/sync/) between other internal systems and Infrahub. The data can be accessed via WebUI, API and SDK, along with SSO and RBAC for access control.
84
+
85
+ **Flexible Schema** - Infrahub provides a flexible schema for your infrastructure data and related business information, allowing you to define your own data model and customize it to your needs. Get started quickly with our [schema library](https://github.com/opsmill/schema-library) or build your own.
86
+
87
+ **Version Control** - Infrahub provides a version control system for your infrastructure data, allowing you to track changes and revert to previous versions if needed. Immutable history of all changes to the data and artifacts is maintained, allowing you to audit and review changes to your infrastructure.
88
+
89
+ **CI Pipeline and Validation** - Infrahub provides a CI pipeline and validation system for your infrastructure data, allowing you to ensure that your infrastructure is always in a valid state. Infrahub was designed with infrastructure-as-code workflows in mind, removing fragility and complexity of combining together multiple tools and projects to achieve the same goal.
90
+
91
+ ## Infrahub Use Cases
92
+
93
+ **Service Catalog** - Infrahub acts as the underlying system to provide infrastructure-as-a-service, allowing you to manage your services and lifecycle them as the services evolve.
94
+
95
+ **Infrastructure Automation** - Provide infrastructure and network automation workflows with Infrahub rendering configurations and artifacts via Jinja2 and python,then passing to deployment tools such as [Nornir](https://www.opsmill.com/simplifying-network-automation-workflows-with-infrahub-nornir-and-jinja2/), [Ansible](https://docs.infrahub.app/ansible/ansible/), Terraform, or vendor-specific tools.
96
+
97
+ **Inventory Management** - Infrahub serves as a centralized inventory system for your infrastructure, allowing you to manage your inventory and track changes to your infrastructure. It provides a WebUI and API for other teams to self-service the information needed to allow the organization to operate.
98
+
99
+ **DCIM and IPAM** - Infrahub provides centralized DCIM and IPAM systems for your infrastructure, capable of handling complex cases such as overlapping IP addresses and VLANs, automation-friendly, branch-aware allocation of resources via Infrahub's [Resource Manager](https://docs.infrahub.app/python-sdk/guides/resource-manager), and more.
100
+
71
101
  ## Quick Start
72
102
 
73
103
  [Always-On Sandbox](https://demo.infrahub.app/) - Instantly login to the UI of a demo environment of Infrahub with sample data pre-loaded.
@@ -94,7 +124,11 @@ If you need help, support for the community version of Infrahub is provided on [
94
124
 
95
125
  ## Contributing
96
126
 
97
- [View our CONTRIBUTING](./CONTRIBUTING.md) policy to get started on contributing to Infrahub.
127
+ To help our community with the creation of contributions, please view our [CONTRIBUTING](./CONTRIBUTING.md) page.
128
+
129
+ <a href="https://github.com/opsmill/infrahub/graphs/contributors">
130
+ <img src="https://contrib.rocks/image?repo=opsmill/infrahub" />
131
+ </a>
98
132
 
99
133
  ## Security
100
134