infrahub-server 1.4.10__py3-none-any.whl → 1.5.0b1__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 (178) hide show
  1. infrahub/actions/tasks.py +208 -16
  2. infrahub/api/artifact.py +3 -0
  3. infrahub/api/diff/diff.py +1 -1
  4. infrahub/api/query.py +2 -0
  5. infrahub/api/schema.py +3 -0
  6. infrahub/auth.py +5 -5
  7. infrahub/cli/db.py +26 -2
  8. infrahub/cli/db_commands/clean_duplicate_schema_fields.py +212 -0
  9. infrahub/config.py +7 -2
  10. infrahub/core/attribute.py +25 -22
  11. infrahub/core/branch/models.py +2 -2
  12. infrahub/core/branch/needs_rebase_status.py +11 -0
  13. infrahub/core/branch/tasks.py +4 -3
  14. infrahub/core/changelog/models.py +4 -12
  15. infrahub/core/constants/__init__.py +1 -0
  16. infrahub/core/constants/infrahubkind.py +1 -0
  17. infrahub/core/convert_object_type/object_conversion.py +201 -0
  18. infrahub/core/convert_object_type/repository_conversion.py +89 -0
  19. infrahub/core/convert_object_type/schema_mapping.py +27 -3
  20. infrahub/core/diff/model/path.py +4 -0
  21. infrahub/core/diff/payload_builder.py +1 -1
  22. infrahub/core/diff/query/artifact.py +1 -1
  23. infrahub/core/graph/__init__.py +1 -1
  24. infrahub/core/initialization.py +2 -2
  25. infrahub/core/ipam/utilization.py +1 -1
  26. infrahub/core/manager.py +9 -84
  27. infrahub/core/migrations/graph/__init__.py +6 -0
  28. infrahub/core/migrations/graph/m040_profile_attrs_in_db.py +166 -0
  29. infrahub/core/migrations/graph/m041_create_hfid_display_label_in_db.py +97 -0
  30. infrahub/core/migrations/graph/m042_backfill_hfid_display_label_in_db.py +86 -0
  31. infrahub/core/migrations/schema/node_attribute_add.py +5 -2
  32. infrahub/core/migrations/shared.py +5 -6
  33. infrahub/core/node/__init__.py +165 -42
  34. infrahub/core/node/constraints/attribute_uniqueness.py +3 -1
  35. infrahub/core/node/create.py +67 -35
  36. infrahub/core/node/lock_utils.py +98 -0
  37. infrahub/core/node/node_property_attribute.py +230 -0
  38. infrahub/core/node/standard.py +1 -1
  39. infrahub/core/property.py +11 -0
  40. infrahub/core/protocols.py +8 -1
  41. infrahub/core/query/attribute.py +27 -15
  42. infrahub/core/query/node.py +61 -185
  43. infrahub/core/query/relationship.py +43 -26
  44. infrahub/core/query/subquery.py +0 -8
  45. infrahub/core/registry.py +2 -2
  46. infrahub/core/relationship/constraints/count.py +1 -1
  47. infrahub/core/relationship/model.py +60 -20
  48. infrahub/core/schema/attribute_schema.py +0 -2
  49. infrahub/core/schema/basenode_schema.py +42 -2
  50. infrahub/core/schema/definitions/core/__init__.py +2 -0
  51. infrahub/core/schema/definitions/core/generator.py +2 -0
  52. infrahub/core/schema/definitions/core/group.py +16 -2
  53. infrahub/core/schema/definitions/core/repository.py +7 -0
  54. infrahub/core/schema/definitions/internal.py +14 -1
  55. infrahub/core/schema/generated/base_node_schema.py +6 -1
  56. infrahub/core/schema/node_schema.py +5 -2
  57. infrahub/core/schema/relationship_schema.py +0 -1
  58. infrahub/core/schema/schema_branch.py +137 -2
  59. infrahub/core/schema/schema_branch_display.py +123 -0
  60. infrahub/core/schema/schema_branch_hfid.py +114 -0
  61. infrahub/core/validators/aggregated_checker.py +1 -1
  62. infrahub/core/validators/determiner.py +12 -1
  63. infrahub/core/validators/relationship/peer.py +1 -1
  64. infrahub/core/validators/tasks.py +1 -1
  65. infrahub/display_labels/__init__.py +0 -0
  66. infrahub/display_labels/gather.py +48 -0
  67. infrahub/display_labels/models.py +240 -0
  68. infrahub/display_labels/tasks.py +186 -0
  69. infrahub/display_labels/triggers.py +22 -0
  70. infrahub/events/group_action.py +1 -1
  71. infrahub/events/node_action.py +1 -1
  72. infrahub/generators/constants.py +7 -0
  73. infrahub/generators/models.py +38 -12
  74. infrahub/generators/tasks.py +34 -16
  75. infrahub/git/base.py +38 -1
  76. infrahub/git/integrator.py +22 -14
  77. infrahub/graphql/analyzer.py +1 -1
  78. infrahub/graphql/api/dependencies.py +2 -4
  79. infrahub/graphql/api/endpoints.py +2 -2
  80. infrahub/graphql/app.py +2 -4
  81. infrahub/graphql/initialization.py +2 -3
  82. infrahub/graphql/manager.py +212 -137
  83. infrahub/graphql/middleware.py +12 -0
  84. infrahub/graphql/mutations/branch.py +11 -0
  85. infrahub/graphql/mutations/computed_attribute.py +110 -3
  86. infrahub/graphql/mutations/convert_object_type.py +34 -13
  87. infrahub/graphql/mutations/display_label.py +111 -0
  88. infrahub/graphql/mutations/generator.py +25 -7
  89. infrahub/graphql/mutations/hfid.py +118 -0
  90. infrahub/graphql/mutations/ipam.py +21 -8
  91. infrahub/graphql/mutations/main.py +37 -153
  92. infrahub/graphql/mutations/profile.py +195 -0
  93. infrahub/graphql/mutations/proposed_change.py +2 -1
  94. infrahub/graphql/mutations/relationship.py +2 -2
  95. infrahub/graphql/mutations/repository.py +22 -83
  96. infrahub/graphql/mutations/resource_manager.py +2 -2
  97. infrahub/graphql/mutations/schema.py +5 -5
  98. infrahub/graphql/mutations/webhook.py +1 -1
  99. infrahub/graphql/queries/resource_manager.py +1 -1
  100. infrahub/graphql/registry.py +173 -0
  101. infrahub/graphql/resolvers/resolver.py +2 -0
  102. infrahub/graphql/schema.py +8 -1
  103. infrahub/groups/tasks.py +1 -1
  104. infrahub/hfid/__init__.py +0 -0
  105. infrahub/hfid/gather.py +48 -0
  106. infrahub/hfid/models.py +240 -0
  107. infrahub/hfid/tasks.py +185 -0
  108. infrahub/hfid/triggers.py +22 -0
  109. infrahub/lock.py +67 -30
  110. infrahub/locks/__init__.py +0 -0
  111. infrahub/locks/tasks.py +37 -0
  112. infrahub/middleware.py +26 -1
  113. infrahub/patch/plan_writer.py +2 -2
  114. infrahub/profiles/__init__.py +0 -0
  115. infrahub/profiles/node_applier.py +101 -0
  116. infrahub/profiles/queries/__init__.py +0 -0
  117. infrahub/profiles/queries/get_profile_data.py +99 -0
  118. infrahub/profiles/tasks.py +63 -0
  119. infrahub/proposed_change/tasks.py +10 -1
  120. infrahub/repositories/__init__.py +0 -0
  121. infrahub/repositories/create_repository.py +113 -0
  122. infrahub/server.py +16 -3
  123. infrahub/services/__init__.py +8 -5
  124. infrahub/tasks/registry.py +6 -4
  125. infrahub/trigger/catalogue.py +4 -0
  126. infrahub/trigger/models.py +2 -0
  127. infrahub/trigger/tasks.py +3 -0
  128. infrahub/webhook/models.py +1 -1
  129. infrahub/workflows/catalogue.py +110 -3
  130. infrahub/workflows/initialization.py +16 -0
  131. infrahub/workflows/models.py +17 -2
  132. infrahub_sdk/branch.py +5 -8
  133. infrahub_sdk/checks.py +1 -1
  134. infrahub_sdk/client.py +364 -84
  135. infrahub_sdk/convert_object_type.py +61 -0
  136. infrahub_sdk/ctl/check.py +2 -3
  137. infrahub_sdk/ctl/cli_commands.py +18 -12
  138. infrahub_sdk/ctl/config.py +8 -2
  139. infrahub_sdk/ctl/generator.py +6 -3
  140. infrahub_sdk/ctl/graphql.py +184 -0
  141. infrahub_sdk/ctl/repository.py +39 -1
  142. infrahub_sdk/ctl/schema.py +18 -3
  143. infrahub_sdk/ctl/utils.py +4 -0
  144. infrahub_sdk/ctl/validate.py +5 -3
  145. infrahub_sdk/diff.py +4 -5
  146. infrahub_sdk/exceptions.py +2 -0
  147. infrahub_sdk/generator.py +7 -1
  148. infrahub_sdk/graphql/__init__.py +12 -0
  149. infrahub_sdk/graphql/constants.py +1 -0
  150. infrahub_sdk/graphql/plugin.py +85 -0
  151. infrahub_sdk/graphql/query.py +77 -0
  152. infrahub_sdk/{graphql.py → graphql/renderers.py} +88 -75
  153. infrahub_sdk/graphql/utils.py +40 -0
  154. infrahub_sdk/node/attribute.py +2 -0
  155. infrahub_sdk/node/node.py +28 -20
  156. infrahub_sdk/playback.py +1 -2
  157. infrahub_sdk/protocols.py +54 -6
  158. infrahub_sdk/pytest_plugin/plugin.py +7 -4
  159. infrahub_sdk/pytest_plugin/utils.py +40 -0
  160. infrahub_sdk/repository.py +1 -2
  161. infrahub_sdk/schema/__init__.py +38 -0
  162. infrahub_sdk/schema/main.py +1 -0
  163. infrahub_sdk/schema/repository.py +8 -0
  164. infrahub_sdk/spec/object.py +120 -7
  165. infrahub_sdk/spec/range_expansion.py +118 -0
  166. infrahub_sdk/timestamp.py +18 -6
  167. infrahub_sdk/transforms.py +1 -1
  168. {infrahub_server-1.4.10.dist-info → infrahub_server-1.5.0b1.dist-info}/METADATA +9 -11
  169. {infrahub_server-1.4.10.dist-info → infrahub_server-1.5.0b1.dist-info}/RECORD +177 -134
  170. infrahub_testcontainers/container.py +1 -1
  171. infrahub_testcontainers/docker-compose-cluster.test.yml +1 -1
  172. infrahub_testcontainers/docker-compose.test.yml +1 -1
  173. infrahub_testcontainers/models.py +2 -2
  174. infrahub_testcontainers/performance_test.py +4 -4
  175. infrahub/core/convert_object_type/conversion.py +0 -134
  176. {infrahub_server-1.4.10.dist-info → infrahub_server-1.5.0b1.dist-info}/LICENSE.txt +0 -0
  177. {infrahub_server-1.4.10.dist-info → infrahub_server-1.5.0b1.dist-info}/WHEEL +0 -0
  178. {infrahub_server-1.4.10.dist-info → infrahub_server-1.5.0b1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,63 @@
1
+ from __future__ import annotations
2
+
3
+ from prefect import flow
4
+ from prefect.logging import get_run_logger
5
+
6
+ from infrahub.workers.dependencies import get_client, get_workflow
7
+ from infrahub.workflows.catalogue import PROFILE_REFRESH
8
+ from infrahub.workflows.utils import add_tags
9
+
10
+ REFRESH_PROFILES_MUTATION = """
11
+ mutation RefreshProfiles(
12
+ $id: String!,
13
+ ) {
14
+ InfrahubProfilesRefresh(
15
+ data: {id: $id}
16
+ ) {
17
+ ok
18
+ }
19
+ }
20
+ """
21
+
22
+
23
+ @flow(
24
+ name="object-profiles-refresh",
25
+ flow_run_name="Refresh profiles for {node_id}",
26
+ )
27
+ async def object_profiles_refresh(
28
+ branch_name: str,
29
+ node_id: str,
30
+ ) -> None:
31
+ log = get_run_logger()
32
+ client = get_client()
33
+
34
+ await add_tags(branches=[branch_name], nodes=[node_id], db_change=True)
35
+ await client.execute_graphql(
36
+ query=REFRESH_PROFILES_MUTATION,
37
+ variables={"id": node_id},
38
+ branch_name=branch_name,
39
+ )
40
+ log.info(f"Profiles refreshed for {node_id}")
41
+
42
+
43
+ @flow(
44
+ name="objects-profiles-refresh-multiple",
45
+ flow_run_name="Refresh profiles for multiple objects",
46
+ )
47
+ async def objects_profiles_refresh_multiple(
48
+ branch_name: str,
49
+ node_ids: list[str],
50
+ ) -> None:
51
+ log = get_run_logger()
52
+
53
+ await add_tags(branches=[branch_name])
54
+
55
+ for node_id in node_ids:
56
+ log.info(f"Requesting profile refresh for {node_id}")
57
+ await get_workflow().submit_workflow(
58
+ workflow=PROFILE_REFRESH,
59
+ parameters={
60
+ "branch_name": branch_name,
61
+ "node_id": node_id,
62
+ },
63
+ )
@@ -307,6 +307,7 @@ async def run_generators(model: RequestProposedChangeRunGenerators, context: Inf
307
307
  populate_store=True,
308
308
  branch=model.source_branch,
309
309
  )
310
+
310
311
  generator_definitions = [
311
312
  ProposedChangeGeneratorDefinition(
312
313
  definition_id=generator.id,
@@ -319,8 +320,11 @@ async def run_generators(model: RequestProposedChangeRunGenerators, context: Inf
319
320
  parameters=generator.parameters.value,
320
321
  group_id=generator.targets.peer.id,
321
322
  convert_query_response=generator.convert_query_response.value,
323
+ execute_in_proposed_change=generator.execute_in_proposed_change.value,
324
+ execute_after_merge=generator.execute_after_merge.value,
322
325
  )
323
326
  for generator in generators
327
+ if generator.execute_in_proposed_change.value
324
328
  ]
325
329
 
326
330
  diff_summary = await get_diff_summary_cache(pipeline_id=model.branch_diff.pipeline_id)
@@ -760,6 +764,8 @@ async def run_generator_as_check(model: RunGeneratorAsCheckModel, context: Infra
760
764
  query=model.generator_definition.query_name,
761
765
  targets=model.generator_definition.group_id,
762
766
  convert_query_response=model.generator_definition.convert_query_response,
767
+ execute_in_proposed_change=model.generator_definition.execute_in_proposed_change,
768
+ execute_after_merge=model.generator_definition.execute_after_merge,
763
769
  )
764
770
 
765
771
  commit_worktree = repository.get_commit_worktree(commit=model.commit)
@@ -786,6 +792,8 @@ async def run_generator_as_check(model: RunGeneratorAsCheckModel, context: Infra
786
792
  params=model.variables,
787
793
  generator_instance=generator_instance.id,
788
794
  convert_query_response=generator_definition.convert_query_response,
795
+ execute_after_merge=generator_definition.execute_after_merge,
796
+ execute_in_proposed_change=generator_definition.execute_in_proposed_change,
789
797
  infrahub_node=InfrahubNode,
790
798
  )
791
799
  generator._init_client.request_context = context.to_request_context()
@@ -934,7 +942,7 @@ async def request_generator_definition_check(model: RequestGeneratorDefinitionCh
934
942
  requested_instances = 0
935
943
  impacted_instances = model.branch_diff.get_subscribers_ids(kind=InfrahubKind.GENERATORINSTANCE)
936
944
 
937
- check_generator_run_models = []
945
+ check_generator_run_models: list[RunGeneratorAsCheckModel] = []
938
946
  for relationship in group.members.peers:
939
947
  member = relationship.peer
940
948
  generator_instance = instance_by_member.get(member.id)
@@ -970,6 +978,7 @@ async def request_generator_definition_check(model: RequestGeneratorDefinitionCh
970
978
  context=context,
971
979
  )
972
980
  for check_generator_run_model in check_generator_run_models
981
+ if check_generator_run_model.generator_definition.execute_in_proposed_change
973
982
  ]
974
983
 
975
984
  await run_checks_and_update_validator(
File without changes
@@ -0,0 +1,113 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, cast
4
+
5
+ from infrahub.core.constants import RepositoryInternalStatus
6
+ from infrahub.core.constants.infrahubkind import READONLYREPOSITORY, REPOSITORY
7
+ from infrahub.core.protocols import CoreGenericRepository, CoreReadOnlyRepository, CoreRepository
8
+ from infrahub.exceptions import ValidationError
9
+ from infrahub.git.models import GitRepositoryAdd, GitRepositoryAddReadOnly
10
+ from infrahub.log import get_logger
11
+ from infrahub.message_bus import messages
12
+ from infrahub.message_bus.messages.git_repository_connectivity import GitRepositoryConnectivityResponse
13
+ from infrahub.workflows.catalogue import GIT_REPOSITORY_ADD, GIT_REPOSITORY_ADD_READ_ONLY
14
+
15
+ if TYPE_CHECKING:
16
+ from infrahub.auth import AccountSession
17
+ from infrahub.context import InfrahubContext
18
+ from infrahub.core.branch import Branch
19
+ from infrahub.database import InfrahubDatabase
20
+ from infrahub.services import InfrahubServices
21
+
22
+ log = get_logger()
23
+
24
+
25
+ class RepositoryFinalizer:
26
+ def __init__(
27
+ self,
28
+ account_session: AccountSession,
29
+ services: InfrahubServices,
30
+ context: InfrahubContext,
31
+ ) -> None:
32
+ self.account_session = account_session
33
+ self.services = services
34
+ self.context = context
35
+
36
+ async def post_create(
37
+ self,
38
+ obj: CoreGenericRepository,
39
+ branch: Branch,
40
+ db: InfrahubDatabase,
41
+ delete_on_connectivity_failure: bool = True,
42
+ ) -> None:
43
+ """
44
+ Method meant to be called after a repository has been created in the database.
45
+ It mainly checks the connectivity to the remote repository and submit the workflow to create the repository in the local filesystem.
46
+ """
47
+
48
+ # If the connectivity is not good, we remove the repository to allow the user to add a new one
49
+ if delete_on_connectivity_failure:
50
+ message = messages.GitRepositoryConnectivity(
51
+ repository_name=obj.name.value,
52
+ repository_location=obj.location.value,
53
+ )
54
+ response = await self.services.message_bus.rpc(
55
+ message=message, response_class=GitRepositoryConnectivityResponse
56
+ )
57
+
58
+ if response.data.success is False:
59
+ await obj.delete(db=db)
60
+ raise ValidationError(response.data.message)
61
+
62
+ # If we are in the default branch, we set the sync status to Active
63
+ # If we are in another branch, we set the sync status to Staging
64
+ if branch.is_default:
65
+ obj.internal_status.value = RepositoryInternalStatus.ACTIVE.value
66
+ else:
67
+ obj.internal_status.value = RepositoryInternalStatus.STAGING.value
68
+ await obj.save(db=db)
69
+
70
+ # Create the new repository in the filesystem.
71
+ log.info("create_repository", name=obj.name.value)
72
+ authenticated_user = None
73
+ if self.account_session and self.account_session.authenticated:
74
+ authenticated_user = self.account_session.account_id
75
+
76
+ if obj.get_kind() == READONLYREPOSITORY:
77
+ obj = cast(CoreReadOnlyRepository, obj)
78
+ model = GitRepositoryAddReadOnly(
79
+ repository_id=obj.id,
80
+ repository_name=obj.name.value,
81
+ location=obj.location.value,
82
+ ref=obj.ref.value,
83
+ infrahub_branch_name=branch.name,
84
+ infrahub_branch_id=str(branch.get_uuid()),
85
+ internal_status=obj.internal_status.value,
86
+ created_by=authenticated_user,
87
+ )
88
+ await self.services.workflow.submit_workflow(
89
+ workflow=GIT_REPOSITORY_ADD_READ_ONLY,
90
+ context=self.context,
91
+ parameters={"model": model},
92
+ )
93
+
94
+ elif obj.get_kind() == REPOSITORY:
95
+ obj = cast(CoreRepository, obj)
96
+ git_repo_add_model = GitRepositoryAdd(
97
+ repository_id=obj.id,
98
+ repository_name=obj.name.value,
99
+ location=obj.location.value,
100
+ default_branch_name=obj.default_branch.value,
101
+ infrahub_branch_name=branch.name,
102
+ infrahub_branch_id=str(branch.get_uuid()),
103
+ internal_status=obj.internal_status.value,
104
+ created_by=authenticated_user,
105
+ )
106
+
107
+ await self.services.workflow.submit_workflow(
108
+ workflow=GIT_REPOSITORY_ADD,
109
+ context=self.context,
110
+ parameters={"model": git_repo_add_model},
111
+ )
112
+ else:
113
+ raise ValueError(f"Unknown repository kind: {obj.get_kind()}")
infrahub/server.py CHANGED
@@ -10,7 +10,6 @@ from asgi_correlation_id import CorrelationIdMiddleware
10
10
  from asgi_correlation_id.context import correlation_id
11
11
  from fastapi import FastAPI, Request, Response
12
12
  from fastapi.logger import logger
13
- from fastapi.middleware.gzip import GZipMiddleware
14
13
  from fastapi.responses import RedirectResponse
15
14
  from fastapi.staticfiles import StaticFiles
16
15
  from fastapi.templating import Jinja2Templates
@@ -30,7 +29,7 @@ from infrahub.exceptions import Error, ValidationError
30
29
  from infrahub.graphql.api.endpoints import router as graphql_router
31
30
  from infrahub.lock import initialize_lock
32
31
  from infrahub.log import clear_log_context, get_logger, set_log_data
33
- from infrahub.middleware import InfrahubCORSMiddleware
32
+ from infrahub.middleware import ConditionalGZipMiddleware, InfrahubCORSMiddleware
34
33
  from infrahub.services import InfrahubServices
35
34
  from infrahub.trace import add_span_exception, configure_trace, get_traceid
36
35
  from infrahub.worker import WORKER_IDENTITY
@@ -86,8 +85,12 @@ async def app_initialization(application: FastAPI, enable_scheduler: bool = True
86
85
  async with application.state.db.start_session() as db:
87
86
  await initialization(db=db, add_database_indexes=True)
88
87
 
88
+ # Initialize the workflow after the registry has been setup
89
+ await service.initialize_workflow()
90
+
89
91
  application.state.service = service
90
92
  application.state.response_delay = config.SETTINGS.miscellaneous.response_delay
93
+
91
94
  if enable_scheduler:
92
95
  await service.scheduler.start_schedule()
93
96
 
@@ -184,7 +187,17 @@ app.add_middleware(
184
187
  skip_paths=["/health"],
185
188
  )
186
189
  app.add_middleware(InfrahubCORSMiddleware)
187
- app.add_middleware(GZipMiddleware, minimum_size=100_000)
190
+ app.add_middleware(
191
+ ConditionalGZipMiddleware,
192
+ minimum_size=100_000,
193
+ compresslevel=1,
194
+ include_paths=(
195
+ "/assets",
196
+ "/favicons",
197
+ "/docs",
198
+ "/api/schema",
199
+ ),
200
+ )
188
201
 
189
202
  app.add_exception_handler(Error, generic_api_exception_handler)
190
203
  app.add_exception_handler(TimestampFormatError, partial(generic_api_exception_handler, http_code=400))
@@ -110,14 +110,17 @@ class InfrahubServices:
110
110
  # This circular dependency could be removed if InfrahubScheduler only depends on what it needs.
111
111
  scheduler.service = service
112
112
 
113
- if workflow is not None and isinstance(workflow, WorkflowWorkerExecution):
114
- assert service.component is not None
113
+ return service
114
+
115
+ async def initialize_workflow(self) -> None:
116
+ if self.workflow is not None and isinstance(self.workflow, WorkflowWorkerExecution):
117
+ assert self.component is not None
115
118
  # Ideally `WorkflowWorkerExecution.initialize` would be directly part of WorkflowWorkerExecution
116
119
  # constructor but this requires some redesign as it depends on InfrahubComponent which is instantiated
117
120
  # after workflow instantiation.
118
- await workflow.initialize(component_is_primary_server=await service.component.is_primary_gunicorn_worker())
119
-
120
- return service
121
+ await self.component.refresh_heartbeat()
122
+ is_primary = await self.component.is_primary_gunicorn_worker()
123
+ await self.workflow.initialize(component_is_primary_server=is_primary)
121
124
 
122
125
  @property
123
126
  def component(self) -> InfrahubComponent:
@@ -5,6 +5,7 @@ from typing import TYPE_CHECKING
5
5
  from infrahub import lock
6
6
  from infrahub.core import registry
7
7
  from infrahub.core.constants import GLOBAL_BRANCH_NAME
8
+ from infrahub.graphql.registry import registry as graphql_registry
8
9
  from infrahub.log import get_logger
9
10
  from infrahub.worker import WORKER_IDENTITY
10
11
 
@@ -20,9 +21,8 @@ def update_graphql_schema(branch: Branch, schema_branch: SchemaBranch) -> None:
20
21
  """
21
22
  Update the GraphQL schema for the given branch.
22
23
  """
23
- from infrahub.graphql.manager import GraphQLSchemaManager
24
24
 
25
- gqlm = GraphQLSchemaManager.get_manager_for_branch(branch=branch, schema_branch=schema_branch)
25
+ gqlm = graphql_registry.get_manager_for_branch(branch=branch, schema_branch=schema_branch)
26
26
  gqlm.get_graphql_schema(
27
27
  include_query=True,
28
28
  include_mutation=True,
@@ -67,6 +67,9 @@ async def update_branch_registry(db: InfrahubDatabase, branch: Branch) -> None:
67
67
  worker=WORKER_IDENTITY,
68
68
  )
69
69
  registry.branch[branch.name] = branch
70
+ elif existing_branch.status != branch.status:
71
+ log.info(f"Updating registry branch cache for {branch.name=}")
72
+ registry.branch[branch.name] = branch
70
73
  return
71
74
 
72
75
  log.info(
@@ -89,7 +92,6 @@ async def refresh_branches(db: InfrahubDatabase) -> None:
89
92
  If a branch is already present with a different value for the hash
90
93
  We pull the new schema from the database and we update the registry.
91
94
  """
92
- from infrahub.graphql.manager import GraphQLSchemaManager
93
95
 
94
96
  async with lock.registry.local_schema_lock():
95
97
  active_branches = await registry.branch_object.get_list(db=db)
@@ -106,7 +108,7 @@ async def refresh_branches(db: InfrahubDatabase) -> None:
106
108
 
107
109
  purged_branches = await registry.purge_inactive_branches(db=db, active_branches=active_branches)
108
110
  purged_branches.update(
109
- GraphQLSchemaManager.purge_inactive(active_branches=[branch.name for branch in active_branches])
111
+ graphql_registry.purge_inactive(active_branches=[branch.name for branch in active_branches])
110
112
  )
111
113
  for branch_name in sorted(purged_branches):
112
114
  log.info(f"Removed branch {branch_name!r} from the registry", branch=branch_name, worker=WORKER_IDENTITY)
@@ -4,6 +4,8 @@ from infrahub.computed_attribute.triggers import (
4
4
  TRIGGER_COMPUTED_ATTRIBUTE_ALL_SCHEMA,
5
5
  TRIGGER_COMPUTED_ATTRIBUTE_PYTHON_SETUP_COMMIT,
6
6
  )
7
+ from infrahub.display_labels.triggers import TRIGGER_DISPLAY_LABELS_ALL_SCHEMA
8
+ from infrahub.hfid.triggers import TRIGGER_HFID_ALL_SCHEMA
7
9
  from infrahub.schema.triggers import TRIGGER_SCHEMA_UPDATED
8
10
  from infrahub.trigger.models import TriggerDefinition
9
11
  from infrahub.webhook.triggers import TRIGGER_WEBHOOK_DELETE, TRIGGER_WEBHOOK_SETUP_UPDATE
@@ -13,6 +15,8 @@ builtin_triggers: list[TriggerDefinition] = [
13
15
  TRIGGER_BRANCH_MERGED,
14
16
  TRIGGER_COMPUTED_ATTRIBUTE_ALL_SCHEMA,
15
17
  TRIGGER_COMPUTED_ATTRIBUTE_PYTHON_SETUP_COMMIT,
18
+ TRIGGER_DISPLAY_LABELS_ALL_SCHEMA,
19
+ TRIGGER_HFID_ALL_SCHEMA,
16
20
  TRIGGER_SCHEMA_UPDATED,
17
21
  TRIGGER_WEBHOOK_DELETE,
18
22
  TRIGGER_WEBHOOK_SETUP_UPDATE,
@@ -37,6 +37,8 @@ class TriggerType(str, Enum):
37
37
  COMPUTED_ATTR_JINJA2 = "computed_attr_jinja2"
38
38
  COMPUTED_ATTR_PYTHON = "computed_attr_python"
39
39
  COMPUTED_ATTR_PYTHON_QUERY = "computed_attr_python_query"
40
+ DISPLAY_LABEL_JINJA2 = "display_label_jinja2"
41
+ HUMAN_FRIENDLY_ID = "human_friendly_id"
40
42
  # OBJECT = "object"
41
43
 
42
44
 
infrahub/trigger/tasks.py CHANGED
@@ -6,6 +6,7 @@ from infrahub.computed_attribute.gather import (
6
6
  gather_trigger_computed_attribute_jinja2,
7
7
  gather_trigger_computed_attribute_python,
8
8
  )
9
+ from infrahub.display_labels.gather import gather_trigger_display_labels_jinja2
9
10
  from infrahub.trigger.catalogue import builtin_triggers
10
11
  from infrahub.webhook.gather import gather_trigger_webhook
11
12
  from infrahub.workers.dependencies import get_database
@@ -18,6 +19,7 @@ async def trigger_configure_all() -> None:
18
19
  database = await get_database()
19
20
  async with database.start_session() as db:
20
21
  webhook_trigger = await gather_trigger_webhook(db=db)
22
+ display_label_triggers = await gather_trigger_display_labels_jinja2()
21
23
  computed_attribute_j2_triggers = await gather_trigger_computed_attribute_jinja2()
22
24
  (
23
25
  computed_attribute_python_triggers,
@@ -28,6 +30,7 @@ async def trigger_configure_all() -> None:
28
30
  computed_attribute_j2_triggers
29
31
  + computed_attribute_python_triggers
30
32
  + computed_attribute_python_query_triggers
33
+ + display_label_triggers
31
34
  + builtin_triggers
32
35
  + webhook_trigger
33
36
  + action_rules
@@ -231,7 +231,7 @@ class TransformWebhook(Webhook):
231
231
  commit=commit,
232
232
  location=f"{self.transform_file}::{self.transform_class}",
233
233
  convert_query_response=self.convert_query_response,
234
- data={"data": data, **context.model_dump()},
234
+ data={"data": {"data": data, **context.model_dump()}},
235
235
  client=client,
236
236
  ) # type: ignore[misc]
237
237
 
@@ -1,6 +1,7 @@
1
1
  import random
2
2
 
3
3
  from fast_depends import Depends, inject
4
+ from prefect.client.schemas.objects import ConcurrencyLimitStrategy
4
5
 
5
6
  from .constants import WorkflowTag, WorkflowType
6
7
  from .models import WorkerPoolDefinition, WorkflowDefinition
@@ -17,14 +18,14 @@ ACTION_ADD_NODE_TO_GROUP = WorkflowDefinition(
17
18
 
18
19
  ACTION_RUN_GENERATOR = WorkflowDefinition(
19
20
  name="action-run-generator",
20
- type=WorkflowType.CORE,
21
+ type=WorkflowType.INTERNAL,
21
22
  module="infrahub.actions.tasks",
22
23
  function="run_generator",
23
24
  )
24
25
 
25
26
  ACTION_RUN_GENERATOR_GROUP_EVENT = WorkflowDefinition(
26
27
  name="action-run-generator-group-event",
27
- type=WorkflowType.CORE,
28
+ type=WorkflowType.INTERNAL,
28
29
  module="infrahub.actions.tasks",
29
30
  function="run_generator_group_event",
30
31
  )
@@ -83,7 +84,7 @@ TRIGGER_ARTIFACT_DEFINITION_GENERATE = WorkflowDefinition(
83
84
 
84
85
  TRIGGER_GENERATOR_DEFINITION_RUN = WorkflowDefinition(
85
86
  name="generator-definition-run",
86
- type=WorkflowType.CORE,
87
+ type=WorkflowType.INTERNAL,
87
88
  module="infrahub.generators.tasks",
88
89
  function="run_generator_definition",
89
90
  tags=[WorkflowTag.DATABASE_CHANGE],
@@ -179,6 +180,8 @@ GIT_REPOSITORIES_SYNC = WorkflowDefinition(
179
180
  cron="* * * * *",
180
181
  module="infrahub.git.tasks",
181
182
  function="sync_remote_repositories",
183
+ concurrency_limit=1,
184
+ concurrency_limit_strategy=ConcurrencyLimitStrategy.CANCEL_NEW,
182
185
  )
183
186
 
184
187
  GIT_REPOSITORIES_CREATE_BRANCH = WorkflowDefinition(
@@ -320,6 +323,62 @@ COMPUTED_ATTRIBUTE_JINJA2_UPDATE_VALUE = WorkflowDefinition(
320
323
  tags=[WorkflowTag.DATABASE_CHANGE],
321
324
  )
322
325
 
326
+ DISPLAY_LABELS_PROCESS_JINJA2 = WorkflowDefinition(
327
+ name="display-label-process-jinja2",
328
+ type=WorkflowType.CORE,
329
+ module="infrahub.display_labels.tasks",
330
+ function="process_display_label",
331
+ tags=[WorkflowTag.DATABASE_CHANGE],
332
+ )
333
+
334
+ DISPLAY_LABEL_JINJA2_UPDATE_VALUE = WorkflowDefinition(
335
+ name="display-label-jinja2-update-value",
336
+ type=WorkflowType.CORE,
337
+ module="infrahub.display_labels.tasks",
338
+ function="display_label_jinja2_update_value",
339
+ tags=[WorkflowTag.DATABASE_CHANGE],
340
+ )
341
+
342
+ HFID_PROCESS = WorkflowDefinition(
343
+ name="hfid-process",
344
+ type=WorkflowType.CORE,
345
+ module="infrahub.hfid.tasks",
346
+ function="process_hfid",
347
+ tags=[WorkflowTag.DATABASE_CHANGE],
348
+ )
349
+
350
+ HFID_SETUP = WorkflowDefinition(
351
+ name="hfid-setup",
352
+ type=WorkflowType.CORE,
353
+ module="infrahub.hfid.tasks",
354
+ function="hfid_setup",
355
+ )
356
+
357
+
358
+ HFID_UPDATE_VALUE = WorkflowDefinition(
359
+ name="hfid-update-value",
360
+ type=WorkflowType.CORE,
361
+ module="infrahub.hfid.tasks",
362
+ function="hfid_update_value",
363
+ tags=[WorkflowTag.DATABASE_CHANGE],
364
+ )
365
+
366
+ TRIGGER_UPDATE_DISPLAY_LABELS = WorkflowDefinition(
367
+ name="trigger-update-display-labels",
368
+ type=WorkflowType.CORE,
369
+ module="infrahub.display_labels.tasks",
370
+ function="trigger_update_display_labels",
371
+ tags=[WorkflowTag.DATABASE_CHANGE],
372
+ )
373
+
374
+ TRIGGER_UPDATE_HFID = WorkflowDefinition(
375
+ name="trigger-update-hfid",
376
+ type=WorkflowType.CORE,
377
+ module="infrahub.hfid.tasks",
378
+ function="trigger_update_hfid",
379
+ tags=[WorkflowTag.DATABASE_CHANGE],
380
+ )
381
+
323
382
  TRIGGER_UPDATE_JINJA_COMPUTED_ATTRIBUTES = WorkflowDefinition(
324
383
  name="trigger_update_jinja2_computed_attributes",
325
384
  type=WorkflowType.CORE,
@@ -356,6 +415,14 @@ COMPUTED_ATTRIBUTE_PROCESS_TRANSFORM = WorkflowDefinition(
356
415
  tags=[WorkflowTag.DATABASE_CHANGE],
357
416
  )
358
417
 
418
+ DISPLAY_LABELS_SETUP_JINJA2 = WorkflowDefinition(
419
+ name="display-labels-setup-jinja2",
420
+ type=WorkflowType.CORE,
421
+ module="infrahub.display_labels.tasks",
422
+ function="display_labels_setup_jinja2",
423
+ )
424
+
425
+
359
426
  QUERY_COMPUTED_ATTRIBUTE_TRANSFORM_TARGETS = WorkflowDefinition(
360
427
  name="query-computed-attribute-transform-targets",
361
428
  type=WorkflowType.CORE,
@@ -531,6 +598,35 @@ VALIDATE_SCHEMA_NUMBER_POOLS = WorkflowDefinition(
531
598
  )
532
599
 
533
600
 
601
+ PROFILE_REFRESH_MULTIPLE = WorkflowDefinition(
602
+ name="objects-profiles-refresh-multiple",
603
+ type=WorkflowType.CORE,
604
+ module="infrahub.profiles.tasks",
605
+ function="objects_profiles_refresh_multiple",
606
+ tags=[WorkflowTag.DATABASE_CHANGE],
607
+ )
608
+
609
+
610
+ PROFILE_REFRESH = WorkflowDefinition(
611
+ name="object-profiles-refresh",
612
+ type=WorkflowType.CORE,
613
+ module="infrahub.profiles.tasks",
614
+ function="object_profiles_refresh",
615
+ tags=[WorkflowTag.DATABASE_CHANGE],
616
+ )
617
+
618
+
619
+ CLEAN_UP_DEADLOCKS = WorkflowDefinition(
620
+ name="clean-up-deadlocks",
621
+ type=WorkflowType.INTERNAL,
622
+ cron="* * * * *",
623
+ module="infrahub.locks.tasks",
624
+ function="clean_up_deadlocks",
625
+ concurrency_limit=1,
626
+ concurrency_limit_strategy=ConcurrencyLimitStrategy.CANCEL_NEW,
627
+ )
628
+
629
+
534
630
  WORKER_POOLS = [INFRAHUB_WORKER_POOL]
535
631
 
536
632
  WORKFLOWS = [
@@ -547,6 +643,7 @@ WORKFLOWS = [
547
643
  BRANCH_MERGE_POST_PROCESS,
548
644
  BRANCH_REBASE,
549
645
  BRANCH_VALIDATE,
646
+ CLEAN_UP_DEADLOCKS,
550
647
  COMPUTED_ATTRIBUTE_JINJA2_UPDATE_VALUE,
551
648
  COMPUTED_ATTRIBUTE_PROCESS_JINJA2,
552
649
  COMPUTED_ATTRIBUTE_PROCESS_TRANSFORM,
@@ -556,6 +653,9 @@ WORKFLOWS = [
556
653
  DIFF_REFRESH,
557
654
  DIFF_REFRESH_ALL,
558
655
  DIFF_UPDATE,
656
+ DISPLAY_LABELS_PROCESS_JINJA2,
657
+ DISPLAY_LABELS_SETUP_JINJA2,
658
+ DISPLAY_LABEL_JINJA2_UPDATE_VALUE,
559
659
  GIT_REPOSITORIES_CHECK_ARTIFACT_CREATE,
560
660
  GIT_REPOSITORIES_CREATE_BRANCH,
561
661
  GIT_REPOSITORIES_DIFF_NAMES_ONLY,
@@ -571,7 +671,12 @@ WORKFLOWS = [
571
671
  GIT_REPOSITORY_USER_CHECKS_TRIGGER,
572
672
  GIT_REPOSITORY_USER_CHECK_RUN,
573
673
  GRAPHQL_QUERY_GROUP_UPDATE,
674
+ HFID_PROCESS,
675
+ HFID_SETUP,
676
+ HFID_UPDATE_VALUE,
574
677
  IPAM_RECONCILIATION,
678
+ PROFILE_REFRESH,
679
+ PROFILE_REFRESH_MULTIPLE,
575
680
  PROPOSED_CHANGE_MERGE,
576
681
  QUERY_COMPUTED_ATTRIBUTE_TRANSFORM_TARGETS,
577
682
  REMOVE_ADD_NODE_FROM_GROUP,
@@ -597,6 +702,8 @@ WORKFLOWS = [
597
702
  TRIGGER_ARTIFACT_DEFINITION_GENERATE,
598
703
  TRIGGER_CONFIGURE_ALL,
599
704
  TRIGGER_GENERATOR_DEFINITION_RUN,
705
+ TRIGGER_UPDATE_DISPLAY_LABELS,
706
+ TRIGGER_UPDATE_HFID,
600
707
  TRIGGER_UPDATE_JINJA_COMPUTED_ATTRIBUTES,
601
708
  TRIGGER_UPDATE_PYTHON_COMPUTED_ATTRIBUTES,
602
709
  VALIDATE_SCHEMA_NUMBER_POOLS,
@@ -7,6 +7,8 @@ from prefect.exceptions import ObjectAlreadyExists
7
7
  from prefect.logging import get_run_logger
8
8
 
9
9
  from infrahub import config
10
+ from infrahub.display_labels.gather import gather_trigger_display_labels_jinja2
11
+ from infrahub.hfid.gather import gather_trigger_hfid
10
12
  from infrahub.trigger.catalogue import builtin_triggers
11
13
  from infrahub.trigger.models import TriggerType
12
14
  from infrahub.trigger.setup import setup_triggers
@@ -74,3 +76,17 @@ async def setup_task_manager() -> None:
74
76
  await setup_triggers(
75
77
  client=client, triggers=builtin_triggers, trigger_type=TriggerType.BUILTIN, force_update=True
76
78
  )
79
+ display_label_triggers = await gather_trigger_display_labels_jinja2()
80
+ await setup_triggers(
81
+ client=client,
82
+ triggers=display_label_triggers,
83
+ trigger_type=TriggerType.DISPLAY_LABEL_JINJA2,
84
+ force_update=True,
85
+ ) # type: ignore[misc]
86
+ hfid_triggers = await gather_trigger_hfid()
87
+ await setup_triggers(
88
+ client=client,
89
+ triggers=hfid_triggers,
90
+ trigger_type=TriggerType.HUMAN_FRIENDLY_ID,
91
+ force_update=True,
92
+ ) # type: ignore[misc]
@@ -6,7 +6,7 @@ from uuid import UUID
6
6
  from prefect import Flow
7
7
  from prefect.client.orchestration import PrefectClient
8
8
  from prefect.client.schemas.actions import DeploymentScheduleCreate
9
- from prefect.client.schemas.objects import FlowRun
9
+ from prefect.client.schemas.objects import ConcurrencyLimitStrategy, FlowRun
10
10
  from prefect.client.schemas.schedules import CronSchedule
11
11
  from pydantic import BaseModel, Field
12
12
  from typing_extensions import Self
@@ -48,6 +48,14 @@ class WorkflowDefinition(BaseModel):
48
48
  function: str
49
49
  cron: str | None = None
50
50
  tags: list[WorkflowTag] = Field(default_factory=list)
51
+ concurrency_limit: int | None = Field(
52
+ default=None,
53
+ description="The concurrency limit for the deployment.",
54
+ )
55
+ concurrency_limit_strategy: ConcurrencyLimitStrategy | None = Field(
56
+ default=None,
57
+ description="The concurrency options for the deployment.",
58
+ )
51
59
 
52
60
  @property
53
61
  def entrypoint(self) -> str:
@@ -60,7 +68,14 @@ class WorkflowDefinition(BaseModel):
60
68
  return f"{self.name}/{self.name}"
61
69
 
62
70
  def to_deployment(self) -> dict[str, Any]:
63
- payload: dict[str, Any] = {"name": self.name, "entrypoint": self.entrypoint, "tags": self.get_tags()}
71
+ payload: dict[str, Any] = {
72
+ "name": self.name,
73
+ "entrypoint": self.entrypoint,
74
+ "tags": self.get_tags(),
75
+ "concurrency_limit": self.concurrency_limit,
76
+ }
77
+ if self.concurrency_limit_strategy:
78
+ payload["concurrency_options"] = {"collision_strategy": self.concurrency_limit_strategy}
64
79
  if self.type == WorkflowType.CORE:
65
80
  payload["version"] = __version__
66
81
  if self.cron: