infrahub-server 1.2.12__py3-none-any.whl → 1.3.0__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 (205) hide show
  1. infrahub/actions/constants.py +130 -0
  2. infrahub/actions/gather.py +114 -0
  3. infrahub/actions/models.py +243 -0
  4. infrahub/actions/parsers.py +104 -0
  5. infrahub/actions/schema.py +393 -0
  6. infrahub/actions/tasks.py +119 -0
  7. infrahub/actions/triggers.py +21 -0
  8. infrahub/branch/__init__.py +0 -0
  9. infrahub/branch/tasks.py +29 -0
  10. infrahub/branch/triggers.py +22 -0
  11. infrahub/cli/db.py +3 -4
  12. infrahub/computed_attribute/gather.py +3 -1
  13. infrahub/computed_attribute/tasks.py +23 -29
  14. infrahub/core/account.py +24 -47
  15. infrahub/core/attribute.py +13 -15
  16. infrahub/core/constants/__init__.py +10 -0
  17. infrahub/core/constants/infrahubkind.py +9 -0
  18. infrahub/core/constraint/node/runner.py +3 -1
  19. infrahub/core/convert_object_type/__init__.py +0 -0
  20. infrahub/core/convert_object_type/conversion.py +124 -0
  21. infrahub/core/convert_object_type/schema_mapping.py +56 -0
  22. infrahub/core/diff/coordinator.py +8 -1
  23. infrahub/core/diff/query/all_conflicts.py +1 -5
  24. infrahub/core/diff/query/artifact.py +10 -20
  25. infrahub/core/diff/query/delete_query.py +8 -4
  26. infrahub/core/diff/query/diff_get.py +3 -6
  27. infrahub/core/diff/query/field_specifiers.py +1 -1
  28. infrahub/core/diff/query/field_summary.py +2 -4
  29. infrahub/core/diff/query/merge.py +72 -125
  30. infrahub/core/diff/query/save.py +28 -43
  31. infrahub/core/diff/query/summary_counts_enricher.py +34 -54
  32. infrahub/core/diff/query/time_range_query.py +0 -1
  33. infrahub/core/diff/repository/repository.py +4 -0
  34. infrahub/core/manager.py +14 -11
  35. infrahub/core/migrations/graph/m003_relationship_parent_optional.py +1 -2
  36. infrahub/core/migrations/graph/m012_convert_account_generic.py +1 -1
  37. infrahub/core/migrations/graph/m013_convert_git_password_credential.py +2 -6
  38. infrahub/core/migrations/graph/m015_diff_format_update.py +1 -2
  39. infrahub/core/migrations/graph/m016_diff_delete_bug_fix.py +1 -2
  40. infrahub/core/migrations/graph/m019_restore_rels_to_time.py +11 -22
  41. infrahub/core/migrations/graph/m020_duplicate_edges.py +3 -6
  42. infrahub/core/migrations/graph/m021_missing_hierarchy_merge.py +1 -2
  43. infrahub/core/migrations/graph/m023_deduplicate_cardinality_one_relationships.py +2 -2
  44. infrahub/core/migrations/graph/m024_missing_hierarchy_backfill.py +1 -2
  45. infrahub/core/migrations/graph/m028_delete_diffs.py +1 -2
  46. infrahub/core/migrations/graph/m029_duplicates_cleanup.py +30 -48
  47. infrahub/core/migrations/graph/m030_illegal_edges.py +1 -2
  48. infrahub/core/migrations/query/attribute_add.py +1 -2
  49. infrahub/core/migrations/query/attribute_rename.py +6 -11
  50. infrahub/core/migrations/query/delete_element_in_schema.py +19 -17
  51. infrahub/core/migrations/query/node_duplicate.py +19 -21
  52. infrahub/core/migrations/query/relationship_duplicate.py +19 -18
  53. infrahub/core/migrations/schema/node_attribute_remove.py +4 -8
  54. infrahub/core/migrations/schema/node_remove.py +19 -20
  55. infrahub/core/models.py +29 -2
  56. infrahub/core/node/__init__.py +131 -28
  57. infrahub/core/node/base.py +1 -1
  58. infrahub/core/node/create.py +211 -0
  59. infrahub/core/node/resource_manager/number_pool.py +31 -5
  60. infrahub/core/node/standard.py +6 -1
  61. infrahub/core/path.py +15 -1
  62. infrahub/core/protocols.py +57 -0
  63. infrahub/core/protocols_base.py +3 -0
  64. infrahub/core/query/__init__.py +2 -2
  65. infrahub/core/query/delete.py +3 -3
  66. infrahub/core/query/diff.py +19 -32
  67. infrahub/core/query/ipam.py +10 -20
  68. infrahub/core/query/node.py +29 -47
  69. infrahub/core/query/relationship.py +55 -34
  70. infrahub/core/query/resource_manager.py +1 -2
  71. infrahub/core/query/standard_node.py +19 -5
  72. infrahub/core/query/subquery.py +2 -4
  73. infrahub/core/relationship/constraints/count.py +10 -9
  74. infrahub/core/relationship/constraints/interface.py +2 -1
  75. infrahub/core/relationship/constraints/peer_kind.py +2 -1
  76. infrahub/core/relationship/constraints/peer_parent.py +56 -0
  77. infrahub/core/relationship/constraints/peer_relatives.py +72 -0
  78. infrahub/core/relationship/constraints/profiles_kind.py +1 -1
  79. infrahub/core/relationship/model.py +4 -1
  80. infrahub/core/schema/__init__.py +2 -1
  81. infrahub/core/schema/attribute_parameters.py +160 -0
  82. infrahub/core/schema/attribute_schema.py +130 -7
  83. infrahub/core/schema/basenode_schema.py +27 -3
  84. infrahub/core/schema/definitions/core/__init__.py +29 -1
  85. infrahub/core/schema/definitions/core/group.py +45 -0
  86. infrahub/core/schema/definitions/core/resource_pool.py +9 -0
  87. infrahub/core/schema/definitions/internal.py +43 -5
  88. infrahub/core/schema/generated/attribute_schema.py +16 -3
  89. infrahub/core/schema/generated/relationship_schema.py +11 -1
  90. infrahub/core/schema/manager.py +7 -2
  91. infrahub/core/schema/schema_branch.py +104 -9
  92. infrahub/core/validators/__init__.py +15 -2
  93. infrahub/core/validators/attribute/choices.py +1 -3
  94. infrahub/core/validators/attribute/enum.py +1 -3
  95. infrahub/core/validators/attribute/kind.py +1 -3
  96. infrahub/core/validators/attribute/length.py +13 -7
  97. infrahub/core/validators/attribute/min_max.py +118 -0
  98. infrahub/core/validators/attribute/number_pool.py +106 -0
  99. infrahub/core/validators/attribute/optional.py +1 -4
  100. infrahub/core/validators/attribute/regex.py +5 -6
  101. infrahub/core/validators/attribute/unique.py +1 -3
  102. infrahub/core/validators/determiner.py +18 -2
  103. infrahub/core/validators/enum.py +12 -0
  104. infrahub/core/validators/node/hierarchy.py +3 -6
  105. infrahub/core/validators/query.py +1 -3
  106. infrahub/core/validators/relationship/count.py +6 -12
  107. infrahub/core/validators/relationship/optional.py +2 -4
  108. infrahub/core/validators/relationship/peer.py +177 -12
  109. infrahub/core/validators/tasks.py +1 -1
  110. infrahub/core/validators/uniqueness/query.py +5 -9
  111. infrahub/database/__init__.py +12 -4
  112. infrahub/database/validation.py +1 -2
  113. infrahub/dependencies/builder/constraint/grouped/node_runner.py +4 -0
  114. infrahub/dependencies/builder/constraint/relationship_manager/peer_parent.py +8 -0
  115. infrahub/dependencies/builder/constraint/relationship_manager/peer_relatives.py +8 -0
  116. infrahub/dependencies/builder/constraint/schema/aggregated.py +2 -0
  117. infrahub/dependencies/builder/constraint/schema/relationship_peer.py +8 -0
  118. infrahub/dependencies/builder/diff/deserializer.py +1 -1
  119. infrahub/dependencies/registry.py +4 -0
  120. infrahub/events/group_action.py +1 -0
  121. infrahub/events/models.py +1 -1
  122. infrahub/git/base.py +5 -3
  123. infrahub/git/integrator.py +96 -5
  124. infrahub/git/tasks.py +1 -0
  125. infrahub/graphql/analyzer.py +139 -18
  126. infrahub/graphql/manager.py +4 -0
  127. infrahub/graphql/mutations/action.py +164 -0
  128. infrahub/graphql/mutations/convert_object_type.py +71 -0
  129. infrahub/graphql/mutations/main.py +24 -175
  130. infrahub/graphql/mutations/proposed_change.py +20 -17
  131. infrahub/graphql/mutations/relationship.py +32 -0
  132. infrahub/graphql/mutations/resource_manager.py +63 -7
  133. infrahub/graphql/queries/convert_object_type_mapping.py +34 -0
  134. infrahub/graphql/queries/resource_manager.py +7 -1
  135. infrahub/graphql/resolvers/many_relationship.py +1 -1
  136. infrahub/graphql/resolvers/resolver.py +2 -2
  137. infrahub/graphql/resolvers/single_relationship.py +1 -1
  138. infrahub/graphql/schema.py +6 -0
  139. infrahub/menu/menu.py +34 -2
  140. infrahub/message_bus/messages/__init__.py +0 -10
  141. infrahub/message_bus/operations/__init__.py +0 -8
  142. infrahub/message_bus/operations/refresh/registry.py +3 -6
  143. infrahub/patch/queries/delete_duplicated_edges.py +10 -15
  144. infrahub/pools/models.py +14 -0
  145. infrahub/pools/number.py +5 -3
  146. infrahub/pools/registration.py +22 -0
  147. infrahub/pools/tasks.py +126 -0
  148. infrahub/prefect_server/models.py +1 -19
  149. infrahub/proposed_change/models.py +68 -3
  150. infrahub/proposed_change/tasks.py +911 -34
  151. infrahub/schema/__init__.py +0 -0
  152. infrahub/schema/tasks.py +27 -0
  153. infrahub/schema/triggers.py +23 -0
  154. infrahub/task_manager/models.py +10 -6
  155. infrahub/trigger/catalogue.py +6 -0
  156. infrahub/trigger/models.py +23 -6
  157. infrahub/trigger/setup.py +26 -2
  158. infrahub/trigger/tasks.py +4 -2
  159. infrahub/types.py +6 -0
  160. infrahub/webhook/tasks.py +4 -8
  161. infrahub/workflows/catalogue.py +103 -1
  162. infrahub_sdk/client.py +43 -10
  163. infrahub_sdk/ctl/generator.py +4 -4
  164. infrahub_sdk/ctl/repository.py +1 -1
  165. infrahub_sdk/node/__init__.py +39 -0
  166. infrahub_sdk/node/attribute.py +122 -0
  167. infrahub_sdk/node/constants.py +21 -0
  168. infrahub_sdk/{node.py → node/node.py} +158 -803
  169. infrahub_sdk/node/parsers.py +15 -0
  170. infrahub_sdk/node/property.py +24 -0
  171. infrahub_sdk/node/related_node.py +266 -0
  172. infrahub_sdk/node/relationship.py +302 -0
  173. infrahub_sdk/protocols.py +112 -0
  174. infrahub_sdk/protocols_base.py +34 -2
  175. infrahub_sdk/pytest_plugin/items/python_transform.py +2 -1
  176. infrahub_sdk/query_groups.py +17 -5
  177. infrahub_sdk/schema/main.py +1 -0
  178. infrahub_sdk/schema/repository.py +16 -0
  179. infrahub_sdk/spec/object.py +1 -1
  180. infrahub_sdk/store.py +1 -1
  181. infrahub_sdk/testing/schemas/car_person.py +1 -0
  182. infrahub_sdk/utils.py +7 -20
  183. infrahub_sdk/yaml.py +6 -5
  184. {infrahub_server-1.2.12.dist-info → infrahub_server-1.3.0.dist-info}/METADATA +3 -3
  185. {infrahub_server-1.2.12.dist-info → infrahub_server-1.3.0.dist-info}/RECORD +192 -166
  186. infrahub_testcontainers/container.py +0 -1
  187. infrahub_testcontainers/docker-compose.test.yml +1 -1
  188. infrahub_testcontainers/helpers.py +8 -2
  189. infrahub/message_bus/messages/check_generator_run.py +0 -26
  190. infrahub/message_bus/messages/finalize_validator_execution.py +0 -15
  191. infrahub/message_bus/messages/proposed_change/base_with_diff.py +0 -16
  192. infrahub/message_bus/messages/proposed_change/request_proposedchange_refreshartifacts.py +0 -11
  193. infrahub/message_bus/messages/request_generatordefinition_check.py +0 -20
  194. infrahub/message_bus/messages/request_proposedchange_pipeline.py +0 -23
  195. infrahub/message_bus/operations/check/__init__.py +0 -3
  196. infrahub/message_bus/operations/check/generator.py +0 -156
  197. infrahub/message_bus/operations/finalize/__init__.py +0 -3
  198. infrahub/message_bus/operations/finalize/validator.py +0 -133
  199. infrahub/message_bus/operations/requests/__init__.py +0 -9
  200. infrahub/message_bus/operations/requests/generator_definition.py +0 -140
  201. infrahub/message_bus/operations/requests/proposed_change.py +0 -629
  202. /infrahub/{message_bus/messages/proposed_change → actions}/__init__.py +0 -0
  203. {infrahub_server-1.2.12.dist-info → infrahub_server-1.3.0.dist-info}/LICENSE.txt +0 -0
  204. {infrahub_server-1.2.12.dist-info → infrahub_server-1.3.0.dist-info}/WHEEL +0 -0
  205. {infrahub_server-1.2.12.dist-info → infrahub_server-1.3.0.dist-info}/entry_points.txt +0 -0
File without changes
@@ -0,0 +1,27 @@
1
+ from __future__ import annotations
2
+
3
+ from prefect import flow
4
+ from prefect.logging import get_run_logger
5
+
6
+ from infrahub.context import InfrahubContext # noqa: TC001 needed for prefect flow
7
+ from infrahub.pools.tasks import validate_schema_number_pools
8
+ from infrahub.services import InfrahubServices # noqa: TC001 needed for prefect flow
9
+ from infrahub.workflows.utils import wait_for_schema_to_converge
10
+
11
+
12
+ @flow(
13
+ name="schema-updated",
14
+ flow_run_name="Running actions after the schema was updated on '{branch_name}'",
15
+ )
16
+ async def schema_updated(
17
+ branch_name: str,
18
+ schema_hash: str, # noqa: ARG001
19
+ context: InfrahubContext,
20
+ service: InfrahubServices,
21
+ ) -> None:
22
+ log = get_run_logger()
23
+ await wait_for_schema_to_converge(
24
+ branch_name=branch_name, component=service.component, db=service.database, log=log
25
+ )
26
+
27
+ await validate_schema_number_pools(branch_name=branch_name, context=context, service=service)
@@ -0,0 +1,23 @@
1
+ from infrahub.events.schema_action import SchemaUpdatedEvent
2
+ from infrahub.trigger.models import BuiltinTriggerDefinition, EventTrigger, ExecuteWorkflow
3
+ from infrahub.workflows.catalogue import SCHEMA_UPDATED
4
+
5
+ TRIGGER_SCHEMA_UPDATED = BuiltinTriggerDefinition(
6
+ name="schema-updated-trigger",
7
+ trigger=EventTrigger(
8
+ events={SchemaUpdatedEvent.event_name},
9
+ ),
10
+ actions=[
11
+ ExecuteWorkflow(
12
+ workflow=SCHEMA_UPDATED,
13
+ parameters={
14
+ "branch_name": "{{ event.payload['data']['branch_name'] }}",
15
+ "schema_hash": "{{ event.payload['data']['schema_hash'] }}",
16
+ "context": {
17
+ "__prefect_kind": "json",
18
+ "value": {"__prefect_kind": "jinja", "template": "{{ event.payload['context'] | tojson }}"},
19
+ },
20
+ },
21
+ ),
22
+ ],
23
+ )
@@ -84,11 +84,15 @@ class FlowProgress(BaseModel):
84
84
 
85
85
 
86
86
  class InfrahubEventFilter(EventFilter):
87
- matching_related: list[EventRelatedFilter] = Field(default_factory=list)
87
+ def add_related_filter(self, related: EventRelatedFilter) -> None:
88
+ if not isinstance(self.related, list):
89
+ self.related = []
90
+
91
+ self.related.append(related)
88
92
 
89
93
  def add_account_filter(self, account__ids: list[str] | None) -> None:
90
94
  if account__ids:
91
- self.matching_related.append(
95
+ self.add_related_filter(
92
96
  EventRelatedFilter(
93
97
  labels=ResourceSpecification(
94
98
  {"prefect.resource.role": "infrahub.account", "infrahub.resource.id": account__ids}
@@ -98,7 +102,7 @@ class InfrahubEventFilter(EventFilter):
98
102
 
99
103
  def add_branch_filter(self, branches: list[str] | None = None) -> None:
100
104
  if branches:
101
- self.matching_related.append(
105
+ self.add_related_filter(
102
106
  EventRelatedFilter(
103
107
  labels=ResourceSpecification(
104
108
  {"prefect.resource.role": "infrahub.branch", "infrahub.resource.label": branches}
@@ -116,7 +120,7 @@ class InfrahubEventFilter(EventFilter):
116
120
 
117
121
  if event_filter:
118
122
  event_filter["prefect.resource.role"] = "infrahub.event"
119
- self.matching_related.append(EventRelatedFilter(labels=ResourceSpecification(event_filter)))
123
+ self.add_related_filter(EventRelatedFilter(labels=ResourceSpecification(event_filter)))
120
124
 
121
125
  def add_event_id_filter(self, ids: list[str] | None = None) -> None:
122
126
  if ids:
@@ -151,7 +155,7 @@ class InfrahubEventFilter(EventFilter):
151
155
 
152
156
  def add_parent_filter(self, parent__ids: list[str] | None) -> None:
153
157
  if parent__ids:
154
- self.matching_related.append(
158
+ self.add_related_filter(
155
159
  EventRelatedFilter(
156
160
  labels=ResourceSpecification(
157
161
  {"prefect.resource.role": "infrahub.child_event", "infrahub.event_parent.id": parent__ids}
@@ -161,7 +165,7 @@ class InfrahubEventFilter(EventFilter):
161
165
 
162
166
  def add_related_node_filter(self, related_node__ids: list[str] | None) -> None:
163
167
  if related_node__ids:
164
- self.matching_related.append(
168
+ self.add_related_filter(
165
169
  EventRelatedFilter(
166
170
  labels=ResourceSpecification(
167
171
  {"prefect.resource.role": "infrahub.related.node", "prefect.resource.id": related_node__ids}
@@ -1,13 +1,19 @@
1
+ from infrahub.actions.triggers import TRIGGER_ACTION_RULE_UPDATE
2
+ from infrahub.branch.triggers import TRIGGER_BRANCH_MERGED
1
3
  from infrahub.computed_attribute.triggers import (
2
4
  TRIGGER_COMPUTED_ATTRIBUTE_ALL_SCHEMA,
3
5
  TRIGGER_COMPUTED_ATTRIBUTE_PYTHON_SETUP_COMMIT,
4
6
  )
7
+ from infrahub.schema.triggers import TRIGGER_SCHEMA_UPDATED
5
8
  from infrahub.trigger.models import TriggerDefinition
6
9
  from infrahub.webhook.triggers import TRIGGER_WEBHOOK_DELETE, TRIGGER_WEBHOOK_SETUP_UPDATE
7
10
 
8
11
  builtin_triggers: list[TriggerDefinition] = [
12
+ TRIGGER_ACTION_RULE_UPDATE,
13
+ TRIGGER_BRANCH_MERGED,
9
14
  TRIGGER_COMPUTED_ATTRIBUTE_ALL_SCHEMA,
10
15
  TRIGGER_COMPUTED_ATTRIBUTE_PYTHON_SETUP_COMMIT,
16
+ TRIGGER_SCHEMA_UPDATED,
11
17
  TRIGGER_WEBHOOK_DELETE,
12
18
  TRIGGER_WEBHOOK_SETUP_UPDATE,
13
19
  ]
@@ -5,10 +5,7 @@ from enum import Enum
5
5
  from typing import TYPE_CHECKING, Any
6
6
 
7
7
  from prefect.events.actions import RunDeployment
8
- from prefect.events.schemas.automations import (
9
- Automation, # noqa: TC002
10
- Posture,
11
- )
8
+ from prefect.events.schemas.automations import Automation, Posture
12
9
  from prefect.events.schemas.automations import EventTrigger as PrefectEventTrigger
13
10
  from prefect.events.schemas.events import ResourceSpecification
14
11
  from pydantic import BaseModel, Field
@@ -28,8 +25,13 @@ class TriggerSetupReport(BaseModel):
28
25
  deleted: list[Automation] = Field(default_factory=list)
29
26
  unchanged: list[TriggerDefinition] = Field(default_factory=list)
30
27
 
28
+ @property
29
+ def in_use_count(self) -> int:
30
+ return len(self.created + self.updated + self.unchanged)
31
+
31
32
 
32
33
  class TriggerType(str, Enum):
34
+ ACTION_TRIGGER_RULE = "action_trigger_rule"
33
35
  BUILTIN = "builtin"
34
36
  WEBHOOK = "webhook"
35
37
  COMPUTED_ATTR_JINJA2 = "computed_attr_jinja2"
@@ -38,10 +40,15 @@ class TriggerType(str, Enum):
38
40
  # OBJECT = "object"
39
41
 
40
42
 
43
+ def _match_related_dict() -> dict:
44
+ # Make Mypy happy as match related is a dict[str, Any] | list[dict[str, Any]]
45
+ return {}
46
+
47
+
41
48
  class EventTrigger(BaseModel):
42
49
  events: set = Field(default_factory=set)
43
50
  match: dict[str, Any] = Field(default_factory=dict)
44
- match_related: dict[str, Any] = Field(default_factory=dict)
51
+ match_related: dict[str, Any] | list[dict[str, Any]] = Field(default_factory=_match_related_dict)
45
52
 
46
53
  def get_prefect(self) -> PrefectEventTrigger:
47
54
  return PrefectEventTrigger(
@@ -49,10 +56,20 @@ class EventTrigger(BaseModel):
49
56
  expect=self.events,
50
57
  within=timedelta(0),
51
58
  match=ResourceSpecification(self.match),
52
- match_related=ResourceSpecification(self.match_related),
59
+ match_related=self.related_resource_specification,
53
60
  threshold=1,
54
61
  )
55
62
 
63
+ @property
64
+ def related_resource_specification(self) -> ResourceSpecification | list[ResourceSpecification]:
65
+ if isinstance(self.match_related, dict):
66
+ return ResourceSpecification(self.match_related)
67
+
68
+ if len(self.match_related) == 1:
69
+ return ResourceSpecification(self.match_related[0])
70
+
71
+ return [ResourceSpecification(related_match) for related_match in self.match_related]
72
+
56
73
 
57
74
  class ExecuteWorkflow(BaseModel):
58
75
  workflow: WorkflowDefinition
infrahub/trigger/setup.py CHANGED
@@ -1,12 +1,14 @@
1
- from typing import TYPE_CHECKING
1
+ from typing import TYPE_CHECKING, Awaitable, Callable
2
2
 
3
3
  from prefect import get_run_logger, task
4
4
  from prefect.automations import AutomationCore
5
5
  from prefect.cache_policies import NONE
6
- from prefect.client.orchestration import PrefectClient
6
+ from prefect.client.orchestration import PrefectClient, get_client
7
7
  from prefect.client.schemas.filters import DeploymentFilter, DeploymentFilterName
8
8
  from prefect.events.schemas.automations import Automation
9
9
 
10
+ from infrahub import lock
11
+ from infrahub.database import InfrahubDatabase
10
12
  from infrahub.trigger.models import TriggerDefinition
11
13
 
12
14
  from .models import TriggerSetupReport, TriggerType
@@ -27,6 +29,28 @@ def compare_automations(target: AutomationCore, existing: Automation) -> bool:
27
29
  return target_dump == existing_dump
28
30
 
29
31
 
32
+ @task(name="trigger-setup-specific", task_run_name="Setup triggers of a specific kind", cache_policy=NONE) # type: ignore[arg-type]
33
+ async def setup_triggers_specific(
34
+ gatherer: Callable[[InfrahubDatabase | None], Awaitable[list[TriggerDefinition]]],
35
+ trigger_type: TriggerType,
36
+ db: InfrahubDatabase | None = None,
37
+ ) -> TriggerSetupReport:
38
+ async with lock.registry.get(
39
+ name=f"configure-action-rules-{trigger_type.value}", namespace="trigger-rules", local=False
40
+ ):
41
+ if db:
42
+ async with db.start_session(read_only=True) as dbs:
43
+ triggers = await gatherer(dbs)
44
+ else:
45
+ triggers = await gatherer(db)
46
+ async with get_client(sync_client=False) as prefect_client:
47
+ return await setup_triggers(
48
+ client=prefect_client,
49
+ triggers=triggers,
50
+ trigger_type=trigger_type,
51
+ ) # type: ignore[misc]
52
+
53
+
30
54
  @task(name="trigger-setup", task_run_name="Setup triggers", cache_policy=NONE) # type: ignore[arg-type]
31
55
  async def setup_triggers(
32
56
  client: PrefectClient,
infrahub/trigger/tasks.py CHANGED
@@ -1,11 +1,12 @@
1
1
  from prefect import flow
2
2
  from prefect.client.orchestration import get_client
3
3
 
4
+ from infrahub.actions.gather import gather_trigger_action_rules
4
5
  from infrahub.computed_attribute.gather import (
5
6
  gather_trigger_computed_attribute_jinja2,
6
7
  gather_trigger_computed_attribute_python,
7
8
  )
8
- from infrahub.services import InfrahubServices # noqa: TC001 needed for prefect flow
9
+ from infrahub.services import InfrahubServices
9
10
  from infrahub.trigger.catalogue import builtin_triggers
10
11
  from infrahub.webhook.gather import gather_trigger_webhook
11
12
 
@@ -21,13 +22,14 @@ async def trigger_configure_all(service: InfrahubServices) -> None:
21
22
  computed_attribute_python_triggers,
22
23
  computed_attribute_python_query_triggers,
23
24
  ) = await gather_trigger_computed_attribute_python(db=db)
24
-
25
+ action_rules = await gather_trigger_action_rules(db=db)
25
26
  triggers = (
26
27
  computed_attribute_j2_triggers
27
28
  + computed_attribute_python_triggers
28
29
  + computed_attribute_python_query_triggers
29
30
  + builtin_triggers
30
31
  + webhook_trigger
32
+ + action_rules
31
33
  )
32
34
 
33
35
  async with get_client(sync_client=False) as prefect_client:
infrahub/types.py CHANGED
@@ -235,6 +235,10 @@ class Number(InfrahubDataType):
235
235
  infrahub = "Integer"
236
236
 
237
237
 
238
+ class NumberPool(Number):
239
+ label: str = "Number Pool"
240
+
241
+
238
242
  class Bandwidth(InfrahubDataType):
239
243
  label: str = "Bandwidth"
240
244
  graphql = graphene.Int
@@ -329,6 +333,7 @@ ATTRIBUTE_TYPES: dict[str, type[InfrahubDataType]] = {
329
333
  "MacAddress": MacAddress,
330
334
  "Color": Color,
331
335
  "Number": Number,
336
+ "NumberPool": NumberPool,
332
337
  "Bandwidth": Bandwidth,
333
338
  "IPHost": IPHost,
334
339
  "IPNetwork": IPNetwork,
@@ -353,6 +358,7 @@ ATTRIBUTE_PYTHON_TYPES: dict[str, type] = {
353
358
  "MacAddress": str, # MAC addresses can be straightforward strings
354
359
  "Color": str, # Colors often represented as hex strings
355
360
  "Number": float, # Numbers can be floats for general use
361
+ "NumberPool": float, # Numbers can be floats for general use
356
362
  "Bandwidth": float, # Bandwidth in some units, represented as a float
357
363
  "IPHost": IPvAnyAddress, # type: ignore[dict-item]
358
364
  "IPNetwork": str,
infrahub/webhook/tasks.py CHANGED
@@ -14,7 +14,7 @@ from prefect.logging import get_run_logger
14
14
  from infrahub.message_bus.types import KVTTL
15
15
  from infrahub.services import InfrahubServices # noqa: TC001 needed for prefect flow
16
16
  from infrahub.trigger.models import TriggerType
17
- from infrahub.trigger.setup import setup_triggers
17
+ from infrahub.trigger.setup import setup_triggers_specific
18
18
  from infrahub.workflows.utils import add_tags
19
19
 
20
20
  from .gather import gather_trigger_webhook
@@ -114,14 +114,10 @@ async def configure_webhook_all(service: InfrahubServices) -> None:
114
114
  async with service.database.start_session(read_only=True) as db:
115
115
  triggers = await gather_trigger_webhook(db=db)
116
116
 
117
- async with get_client(sync_client=False) as prefect_client:
118
- await setup_triggers(
119
- client=prefect_client,
120
- triggers=triggers,
121
- trigger_type=TriggerType.WEBHOOK,
122
- ) # type: ignore[misc]
123
-
124
117
  log.info(f"{len(triggers)} Webhooks automation configuration completed")
118
+ await setup_triggers_specific(
119
+ gatherer=gather_trigger_webhook, db=service.database, trigger_type=TriggerType.WEBHOOK
120
+ ) # type: ignore[misc]
125
121
 
126
122
 
127
123
  @flow(name="webhook-setup-automation-one", flow_run_name="Configurate webhook for {webhook_name}")
@@ -6,6 +6,35 @@ from .models import WorkerPoolDefinition, WorkflowDefinition
6
6
  INFRAHUB_WORKER_POOL = WorkerPoolDefinition(name="infrahub-worker", description="Default Pool for internal tasks")
7
7
 
8
8
 
9
+ ACTION_ADD_NODE_TO_GROUP = WorkflowDefinition(
10
+ name="action-add-node-to-group",
11
+ type=WorkflowType.CORE,
12
+ module="infrahub.actions.tasks",
13
+ function="add_node_to_group",
14
+ )
15
+
16
+ ACTION_RUN_GENERATOR = WorkflowDefinition(
17
+ name="action-run-generator",
18
+ type=WorkflowType.CORE,
19
+ module="infrahub.actions.tasks",
20
+ function="run_generator",
21
+ )
22
+
23
+ ACTION_RUN_GENERATOR_GROUP_EVENT = WorkflowDefinition(
24
+ name="action-run-generator-group-event",
25
+ type=WorkflowType.CORE,
26
+ module="infrahub.actions.tasks",
27
+ function="run_generator_group_event",
28
+ )
29
+
30
+
31
+ CONFIGURE_ACTION_RULES = WorkflowDefinition(
32
+ name="configure-action-rules",
33
+ type=WorkflowType.CORE,
34
+ module="infrahub.actions.tasks",
35
+ function="configure_action_rules",
36
+ )
37
+
9
38
  TRANSFORM_JINJA2_RENDER = WorkflowDefinition(
10
39
  name="transform_render_jinja2_template",
11
40
  type=WorkflowType.USER,
@@ -66,6 +95,13 @@ IPAM_RECONCILIATION = WorkflowDefinition(
66
95
  tags=[WorkflowTag.DATABASE_CHANGE],
67
96
  )
68
97
 
98
+ REMOVE_ADD_NODE_FROM_GROUP = WorkflowDefinition(
99
+ name="action-remove-node-from-group",
100
+ type=WorkflowType.CORE,
101
+ module="infrahub.actions.tasks",
102
+ function="remove_node_from_group",
103
+ )
104
+
69
105
  REQUEST_GENERATOR_RUN = WorkflowDefinition(
70
106
  name="generator-run",
71
107
  type=WorkflowType.USER,
@@ -74,6 +110,14 @@ REQUEST_GENERATOR_RUN = WorkflowDefinition(
74
110
  tags=[WorkflowTag.DATABASE_CHANGE],
75
111
  )
76
112
 
113
+ RUN_GENERATOR_AS_CHECK = WorkflowDefinition(
114
+ name="run-generator-as-check",
115
+ type=WorkflowType.USER,
116
+ module="infrahub.proposed_change.tasks",
117
+ function="run_generator_as_check",
118
+ tags=[WorkflowTag.DATABASE_CHANGE],
119
+ )
120
+
77
121
  REQUEST_GENERATOR_DEFINITION_RUN = WorkflowDefinition(
78
122
  name="request-generator-definition-run",
79
123
  type=WorkflowType.CORE,
@@ -82,6 +126,14 @@ REQUEST_GENERATOR_DEFINITION_RUN = WorkflowDefinition(
82
126
  tags=[WorkflowTag.DATABASE_CHANGE],
83
127
  )
84
128
 
129
+ REQUEST_GENERATOR_DEFINITION_CHECK = WorkflowDefinition(
130
+ name="request-generator-definition-check",
131
+ type=WorkflowType.CORE,
132
+ module="infrahub.proposed_change.tasks",
133
+ function="request_generator_definition_check",
134
+ tags=[WorkflowTag.DATABASE_CHANGE],
135
+ )
136
+
85
137
  REQUEST_ARTIFACT_GENERATE = WorkflowDefinition(
86
138
  name="artifact-generate",
87
139
  type=WorkflowType.CORE, # NOTE need to check
@@ -190,6 +242,14 @@ BRANCH_MERGE = WorkflowDefinition(
190
242
  tags=[WorkflowTag.DATABASE_CHANGE],
191
243
  )
192
244
 
245
+ BRANCH_MERGED = WorkflowDefinition(
246
+ name="branch-merged",
247
+ type=WorkflowType.CORE,
248
+ module="infrahub.branch.tasks",
249
+ function="branch_merged",
250
+ tags=[WorkflowTag.DATABASE_CHANGE],
251
+ )
252
+
193
253
  BRANCH_MERGE_POST_PROCESS = WorkflowDefinition(
194
254
  name="branch-merge-post-process",
195
255
  type=WorkflowType.CORE,
@@ -198,7 +258,6 @@ BRANCH_MERGE_POST_PROCESS = WorkflowDefinition(
198
258
  tags=[WorkflowTag.DATABASE_CHANGE],
199
259
  )
200
260
 
201
-
202
261
  BRANCH_MERGE_MUTATION = WorkflowDefinition(
203
262
  name="merge-branch-mutation",
204
263
  type=WorkflowType.CORE,
@@ -338,6 +397,21 @@ GIT_REPOSITORIES_IMPORT_OBJECTS = WorkflowDefinition(
338
397
  tags=[WorkflowTag.DATABASE_CHANGE],
339
398
  )
340
399
 
400
+ REQUEST_PROPOSED_CHANGE_PIPELINE = WorkflowDefinition(
401
+ name="proposed-changed-pipeline",
402
+ type=WorkflowType.INTERNAL,
403
+ module="infrahub.proposed_change.tasks",
404
+ function="run_proposed_change_pipeline",
405
+ tags=[WorkflowTag.DATABASE_CHANGE],
406
+ )
407
+
408
+ REQUEST_PROPOSED_CHANGE_REFRESH_ARTIFACTS = WorkflowDefinition(
409
+ name="proposed-changed-refresh-artifacts",
410
+ type=WorkflowType.INTERNAL,
411
+ module="infrahub.proposed_change.tasks",
412
+ function="refresh_artifacts",
413
+ )
414
+
341
415
  REQUEST_PROPOSED_CHANGE_RUN_GENERATORS = WorkflowDefinition(
342
416
  name="proposed-changed-run-generator",
343
417
  type=WorkflowType.INTERNAL,
@@ -431,6 +505,14 @@ GIT_REPOSITORY_MERGE_CONFLICTS_CHECKS_RUN = WorkflowDefinition(
431
505
  function="run_check_merge_conflicts",
432
506
  )
433
507
 
508
+ SCHEMA_UPDATED = WorkflowDefinition(
509
+ name="schema-updated",
510
+ type=WorkflowType.CORE,
511
+ module="infrahub.schema.tasks",
512
+ function="schema_updated",
513
+ )
514
+
515
+
434
516
  TRIGGER_CONFIGURE_ALL = WorkflowDefinition(
435
517
  name="trigger-configure-all",
436
518
  type=WorkflowType.CORE,
@@ -439,14 +521,26 @@ TRIGGER_CONFIGURE_ALL = WorkflowDefinition(
439
521
  )
440
522
 
441
523
 
524
+ VALIDATE_SCHEMA_NUMBER_POOLS = WorkflowDefinition(
525
+ name="validate-schema-number-pools",
526
+ type=WorkflowType.CORE,
527
+ module="infrahub.pools.tasks",
528
+ function="validate_schema_number_pools",
529
+ )
530
+
531
+
442
532
  worker_pools = [INFRAHUB_WORKER_POOL]
443
533
 
444
534
  workflows = [
535
+ ACTION_ADD_NODE_TO_GROUP,
536
+ ACTION_RUN_GENERATOR,
537
+ ACTION_RUN_GENERATOR_GROUP_EVENT,
445
538
  ANONYMOUS_TELEMETRY_SEND,
446
539
  BRANCH_CANCEL_PROPOSED_CHANGES,
447
540
  BRANCH_CREATE,
448
541
  BRANCH_DELETE,
449
542
  BRANCH_MERGE,
543
+ BRANCH_MERGED,
450
544
  BRANCH_MERGE_MUTATION,
451
545
  BRANCH_MERGE_POST_PROCESS,
452
546
  BRANCH_REBASE,
@@ -456,6 +550,7 @@ workflows = [
456
550
  COMPUTED_ATTRIBUTE_PROCESS_TRANSFORM,
457
551
  COMPUTED_ATTRIBUTE_SETUP_JINJA2,
458
552
  COMPUTED_ATTRIBUTE_SETUP_PYTHON,
553
+ CONFIGURE_ACTION_RULES,
459
554
  DIFF_REFRESH,
460
555
  DIFF_REFRESH_ALL,
461
556
  DIFF_UPDATE,
@@ -477,17 +572,23 @@ workflows = [
477
572
  IPAM_RECONCILIATION,
478
573
  PROPOSED_CHANGE_MERGE,
479
574
  QUERY_COMPUTED_ATTRIBUTE_TRANSFORM_TARGETS,
575
+ REMOVE_ADD_NODE_FROM_GROUP,
480
576
  REQUEST_ARTIFACT_DEFINITION_CHECK,
481
577
  REQUEST_ARTIFACT_DEFINITION_GENERATE,
482
578
  REQUEST_ARTIFACT_GENERATE,
579
+ REQUEST_GENERATOR_DEFINITION_CHECK,
483
580
  REQUEST_GENERATOR_DEFINITION_RUN,
484
581
  REQUEST_GENERATOR_RUN,
485
582
  REQUEST_PROPOSED_CHANGE_DATA_INTEGRITY,
583
+ REQUEST_PROPOSED_CHANGE_PIPELINE,
584
+ REQUEST_PROPOSED_CHANGE_REFRESH_ARTIFACTS,
486
585
  REQUEST_PROPOSED_CHANGE_REPOSITORY_CHECKS,
487
586
  REQUEST_PROPOSED_CHANGE_RUN_GENERATORS,
488
587
  REQUEST_PROPOSED_CHANGE_SCHEMA_INTEGRITY,
489
588
  REQUEST_PROPOSED_CHANGE_USER_TESTS,
589
+ RUN_GENERATOR_AS_CHECK,
490
590
  SCHEMA_APPLY_MIGRATION,
591
+ SCHEMA_UPDATED,
491
592
  SCHEMA_VALIDATE_MIGRATION,
492
593
  TRANSFORM_JINJA2_RENDER,
493
594
  TRANSFORM_PYTHON_RENDER,
@@ -496,6 +597,7 @@ workflows = [
496
597
  TRIGGER_GENERATOR_DEFINITION_RUN,
497
598
  TRIGGER_UPDATE_JINJA_COMPUTED_ATTRIBUTES,
498
599
  TRIGGER_UPDATE_PYTHON_COMPUTED_ATTRIBUTES,
600
+ VALIDATE_SCHEMA_NUMBER_POOLS,
499
601
  WEBHOOK_CONFIGURE_ALL,
500
602
  WEBHOOK_CONFIGURE_ONE,
501
603
  WEBHOOK_DELETE_AUTOMATION,
infrahub_sdk/client.py CHANGED
@@ -172,11 +172,18 @@ class BaseClient:
172
172
  params: dict[str, Any] | None = None,
173
173
  delete_unused_nodes: bool = False,
174
174
  group_type: str | None = None,
175
+ group_params: dict[str, Any] | None = None,
176
+ branch: str | None = None,
175
177
  ) -> Self:
176
178
  self.mode = InfrahubClientMode.TRACKING
177
179
  identifier = identifier or self.identifier or "python-sdk"
178
180
  self.set_context_properties(
179
- identifier=identifier, params=params, delete_unused_nodes=delete_unused_nodes, group_type=group_type
181
+ identifier=identifier,
182
+ params=params,
183
+ delete_unused_nodes=delete_unused_nodes,
184
+ group_type=group_type,
185
+ group_params=group_params,
186
+ branch=branch,
180
187
  )
181
188
  return self
182
189
 
@@ -187,14 +194,22 @@ class BaseClient:
187
194
  delete_unused_nodes: bool = True,
188
195
  reset: bool = True,
189
196
  group_type: str | None = None,
197
+ group_params: dict[str, Any] | None = None,
198
+ branch: str | None = None,
190
199
  ) -> None:
191
200
  if reset:
192
201
  if isinstance(self, InfrahubClient):
193
202
  self.group_context = InfrahubGroupContext(self)
194
203
  elif isinstance(self, InfrahubClientSync):
195
204
  self.group_context = InfrahubGroupContextSync(self)
205
+
196
206
  self.group_context.set_properties(
197
- identifier=identifier, params=params, delete_unused_nodes=delete_unused_nodes, group_type=group_type
207
+ identifier=identifier,
208
+ params=params,
209
+ delete_unused_nodes=delete_unused_nodes,
210
+ group_type=group_type,
211
+ group_params=group_params,
212
+ branch=branch,
198
213
  )
199
214
 
200
215
  def _graphql_url(
@@ -562,18 +577,27 @@ class InfrahubClient(BaseClient):
562
577
  at: Timestamp | None = None,
563
578
  branch: str | None = None,
564
579
  timeout: int | None = None,
580
+ partial_match: bool = False,
565
581
  **kwargs: Any,
566
582
  ) -> int:
567
583
  """Return the number of nodes of a given kind."""
568
- filters = kwargs
569
- schema = await self.schema.get(kind=kind, branch=branch)
584
+ filters: dict[str, Any] = dict(kwargs)
570
585
 
586
+ if partial_match:
587
+ filters["partial_match"] = True
588
+
589
+ schema = await self.schema.get(kind=kind, branch=branch)
571
590
  branch = branch or self.default_branch
572
591
  if at:
573
592
  at = Timestamp(at)
574
593
 
594
+ data: dict[str, Any] = {
595
+ "count": None,
596
+ "@filters": filters,
597
+ }
598
+
575
599
  response = await self.execute_graphql(
576
- query=Query(query={schema.kind: {"count": None, "@filters": filters}}).render(),
600
+ query=Query(query={schema.kind: data}).render(),
577
601
  branch_name=branch,
578
602
  at=at,
579
603
  timeout=timeout,
@@ -801,7 +825,7 @@ class InfrahubClient(BaseClient):
801
825
  nodes = []
802
826
  related_nodes = []
803
827
  batch_process = await self.create_batch()
804
- count = await self.count(kind=schema.kind, **filters)
828
+ count = await self.count(kind=schema.kind, partial_match=partial_match, **filters)
805
829
  total_pages = (count + pagination_size - 1) // pagination_size
806
830
 
807
831
  for page_number in range(1, total_pages + 1):
@@ -1683,18 +1707,27 @@ class InfrahubClientSync(BaseClient):
1683
1707
  at: Timestamp | None = None,
1684
1708
  branch: str | None = None,
1685
1709
  timeout: int | None = None,
1710
+ partial_match: bool = False,
1686
1711
  **kwargs: Any,
1687
1712
  ) -> int:
1688
1713
  """Return the number of nodes of a given kind."""
1689
- filters = kwargs
1690
- schema = self.schema.get(kind=kind, branch=branch)
1714
+ filters: dict[str, Any] = dict(kwargs)
1715
+
1716
+ if partial_match:
1717
+ filters["partial_match"] = True
1691
1718
 
1719
+ schema = self.schema.get(kind=kind, branch=branch)
1692
1720
  branch = branch or self.default_branch
1693
1721
  if at:
1694
1722
  at = Timestamp(at)
1695
1723
 
1724
+ data: dict[str, Any] = {
1725
+ "count": None,
1726
+ "@filters": filters,
1727
+ }
1728
+
1696
1729
  response = self.execute_graphql(
1697
- query=Query(query={schema.kind: {"count": None, "@filters": filters}}).render(),
1730
+ query=Query(query={schema.kind: data}).render(),
1698
1731
  branch_name=branch,
1699
1732
  at=at,
1700
1733
  timeout=timeout,
@@ -1957,7 +1990,7 @@ class InfrahubClientSync(BaseClient):
1957
1990
  related_nodes = []
1958
1991
  batch_process = self.create_batch()
1959
1992
 
1960
- count = self.count(kind=schema.kind, **filters)
1993
+ count = self.count(kind=schema.kind, partial_match=partial_match, **filters)
1961
1994
  total_pages = (count + pagination_size - 1) // pagination_size
1962
1995
 
1963
1996
  for page_number in range(1, total_pages + 1):
@@ -74,20 +74,20 @@ async def run(
74
74
  targets = await client.get(
75
75
  kind="CoreGroup", branch=branch, include=["members"], name__value=generator_config.targets
76
76
  )
77
- await targets.members.fetch()
77
+ await targets._get_relationship_many(name="members").fetch()
78
78
 
79
- if not targets.members.peers:
79
+ if not targets._get_relationship_many(name="members").peers:
80
80
  console.print(
81
81
  f"[red]No members found within '{generator_config.targets}', not running generator '{generator_name}'"
82
82
  )
83
83
  return
84
84
 
85
- for member in targets.members.peers:
85
+ for member in targets._get_relationship_many(name="members").peers:
86
86
  check_parameter = {}
87
87
  if identifier:
88
88
  attribute = getattr(member.peer, identifier)
89
89
  check_parameter = {identifier: attribute.value}
90
- params = {"name": member.peer.name.value}
90
+ params = {"name": member.peer._get_attribute(name="name").value}
91
91
  generator = generator_class(
92
92
  query=generator_config.query,
93
93
  client=client,