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
@@ -29,7 +29,7 @@ INFRAHUB_SERVICES: dict[str, ContainerService] = {
29
29
 
30
30
  PROJECT_ENV_VARIABLES: dict[str, str] = {
31
31
  "MESSAGE_QUEUE_DOCKER_IMAGE": "rabbitmq:3.13.7-management",
32
- "CACHE_DOCKER_IMAGE": "redis:7.2.4",
32
+ "CACHE_DOCKER_IMAGE": "redis:7.2.11",
33
33
  "INFRAHUB_TESTING_DOCKER_IMAGE": "registry.opsmill.io/opsmill/infrahub",
34
34
  "INFRAHUB_TESTING_DOCKER_ENTRYPOINT": f"gunicorn --config backend/infrahub/serve/gunicorn_config.py -w {os.environ.get('INFRAHUB_TESTING_WEB_CONCURRENCY', 4)} --logger-class infrahub.serve.log.GunicornLogger infrahub.server:app", # noqa: E501
35
35
  "INFRAHUB_TESTING_IMAGE_VERSION": infrahub_version,
@@ -37,7 +37,7 @@ services:
37
37
  - ${INFRAHUB_TESTING_MESSAGE_QUEUE_PORT:-0}:15692
38
38
 
39
39
  cache:
40
- image: ${CACHE_DOCKER_IMAGE:-redis:7.2.4}
40
+ image: ${CACHE_DOCKER_IMAGE:-redis:7.2.11}
41
41
  restart: unless-stopped
42
42
  healthcheck:
43
43
  test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
@@ -20,7 +20,7 @@ services:
20
20
  - ${INFRAHUB_TESTING_MESSAGE_QUEUE_PORT:-0}:15692
21
21
 
22
22
  cache:
23
- image: ${CACHE_DOCKER_IMAGE:-redis:7.2.4}
23
+ image: ${CACHE_DOCKER_IMAGE:-redis:7.2.11}
24
24
  restart: unless-stopped
25
25
  healthcheck:
26
26
  test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
@@ -1,4 +1,4 @@
1
- from datetime import datetime, timezone
1
+ from datetime import UTC, datetime
2
2
  from enum import Enum
3
3
  from typing import Any
4
4
 
@@ -27,7 +27,7 @@ class InfrahubResultContext(BaseModel):
27
27
 
28
28
  class InfrahubActiveMeasurementItem(BaseModel):
29
29
  definition: MeasurementDefinition
30
- start_time: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
30
+ start_time: datetime = Field(default_factory=lambda: datetime.now(UTC))
31
31
  context: dict[str, Any] = Field(default_factory=dict)
32
32
 
33
33
 
@@ -1,6 +1,6 @@
1
1
  import hashlib
2
2
  import json
3
- from datetime import datetime, timezone
3
+ from datetime import UTC, datetime
4
4
  from types import TracebackType
5
5
  from typing import Any
6
6
 
@@ -35,7 +35,7 @@ class InfrahubPerformanceTest:
35
35
  self.env_vars = {}
36
36
  self.project_name = ""
37
37
  self.test_info = {}
38
- self.start_time = datetime.now(timezone.utc)
38
+ self.start_time = datetime.now(UTC)
39
39
  self.end_time: datetime | None = None
40
40
  self.results_url = results_url
41
41
  self.scraper_endpoint = ""
@@ -57,7 +57,7 @@ class InfrahubPerformanceTest:
57
57
 
58
58
  def finalize(self, session: pytest.Session) -> None:
59
59
  if self.initialized:
60
- self.end_time = datetime.now(timezone.utc)
60
+ self.end_time = datetime.now(UTC)
61
61
  self.extract_test_session_information(session)
62
62
  self.send_results()
63
63
 
@@ -129,7 +129,7 @@ class InfrahubPerformanceTest:
129
129
  if not exc_type and self.active_measurements:
130
130
  self.add_measurement(
131
131
  definition=self.active_measurements.definition,
132
- value=(datetime.now(timezone.utc) - self.active_measurements.start_time).total_seconds() * 1000,
132
+ value=(datetime.now(UTC) - self.active_measurements.start_time).total_seconds() * 1000,
133
133
  context=self.active_measurements.context,
134
134
  )
135
135
 
@@ -1,134 +0,0 @@
1
- from typing import Any
2
-
3
- from pydantic import BaseModel
4
-
5
- from infrahub.core.attribute import BaseAttribute
6
- from infrahub.core.branch import Branch
7
- from infrahub.core.constants import RelationshipCardinality
8
- from infrahub.core.manager import NodeManager
9
- from infrahub.core.node import Node
10
- from infrahub.core.node.create import create_node
11
- from infrahub.core.query.relationship import GetAllPeersIds
12
- from infrahub.core.query.resource_manager import PoolChangeReserved
13
- from infrahub.core.relationship import RelationshipManager
14
- from infrahub.core.schema import NodeSchema
15
- from infrahub.database import InfrahubDatabase
16
-
17
-
18
- class InputDataForDestField(BaseModel): # Only one of these fields can be not None
19
- attribute_value: Any | None = None
20
- peer_id: str | None = None
21
- peers_ids: list[str] | None = None
22
-
23
- @property
24
- def value(self) -> Any:
25
- fields = [self.attribute_value, self.peer_id, self.peers_ids]
26
- set_fields = [f for f in fields if f is not None]
27
- if len(set_fields) != 1:
28
- raise ValueError("Exactly one of attribute_value, peer_id, or peers_ids must be set")
29
- return set_fields[0]
30
-
31
-
32
- class InputForDestField(BaseModel): # Only one of these fields can be not None
33
- source_field: str | None = None
34
- data: InputDataForDestField | None = None
35
-
36
- @property
37
- def value(self) -> Any:
38
- if self.source_field is not None and self.data is not None:
39
- raise ValueError("Only one of source_field or data can be set")
40
- if self.source_field is None and self.data is None:
41
- raise ValueError("Either source_field or data must be set")
42
- return self.source_field if self.source_field is not None else self.data
43
-
44
-
45
- async def get_out_rels_peers_ids(node: Node, db: InfrahubDatabase) -> list[str]:
46
- all_peers: list[Node] = []
47
- for name in node._relationships:
48
- relm: RelationshipManager = getattr(node, name)
49
- peers = await relm.get_peers(db=db)
50
- all_peers.extend(peers.values())
51
- return [peer.id for peer in all_peers]
52
-
53
-
54
- async def build_data_new_node(db: InfrahubDatabase, mapping: dict[str, InputForDestField], node: Node) -> dict:
55
- """Value of a given field on the target kind to convert is either an input source attribute/relationship of the source node,
56
- or a raw value."""
57
-
58
- data = {}
59
- for dest_field_name, input_for_dest_field in mapping.items():
60
- value = input_for_dest_field.value
61
- if isinstance(value, str): # source_field
62
- item = getattr(node, value)
63
- if isinstance(item, BaseAttribute):
64
- data[dest_field_name] = item.value
65
- elif isinstance(item, RelationshipManager):
66
- if item.schema.cardinality == RelationshipCardinality.ONE:
67
- peer = await item.get_peer(db=db)
68
- if peer is not None:
69
- data[dest_field_name] = {"id": peer.id}
70
- # else, relationship is optional, and if the target relationship is mandatory an error will be raised during creation
71
- elif item.schema.cardinality == RelationshipCardinality.MANY:
72
- data[dest_field_name] = [{"id": peer.id} for _, peer in (await item.get_peers(db=db)).items()]
73
- else:
74
- raise ValueError(f"Unknown cardinality {item.schema.cardinality=}")
75
- else: # user input data
76
- data[dest_field_name] = value.value
77
- return data
78
-
79
-
80
- async def get_unidirectional_rels_peers_ids(node: Node, branch: Branch, db: InfrahubDatabase) -> list[str]:
81
- """
82
- Returns peers ids of nodes connected to input `node` through an incoming unidirectional relationship.
83
- """
84
-
85
- out_rels_identifier = [rel.identifier for rel in node.get_schema().relationships]
86
- query = await GetAllPeersIds.init(db=db, node_id=node.id, branch=branch, exclude_identifiers=out_rels_identifier)
87
- await query.execute(db=db)
88
- return query.get_peers_uuids()
89
-
90
-
91
- async def convert_object_type(
92
- node: Node, target_schema: NodeSchema, mapping: dict[str, InputForDestField], branch: Branch, db: InfrahubDatabase
93
- ) -> Node:
94
- """Delete the node and return the new created one. If creation fails, the node is not deleted, and raise an error.
95
- An extra check is performed on input node peers relationships to make sure they are still valid."""
96
-
97
- node_schema = node.get_schema()
98
- if not isinstance(node_schema, NodeSchema):
99
- raise ValueError(f"Only a node with a NodeSchema can be converted, got {type(node_schema)}")
100
-
101
- async with db.start_transaction() as dbt:
102
- deleted_node_out_rels_peer_ids = await get_out_rels_peers_ids(node=node, db=dbt)
103
- deleted_node_unidir_rels_peer_ids = await get_unidirectional_rels_peers_ids(node=node, db=dbt, branch=branch)
104
-
105
- # Delete the node, so we delete relationships with peers as well, which might temporarily break cardinality constraints
106
- # but they should be restored when creating the new node.
107
- deleted_nodes = await NodeManager.delete(db=dbt, branch=branch, nodes=[node], cascade_delete=False)
108
- if len(deleted_nodes) != 1:
109
- raise ValueError(f"Deleted {len(deleted_nodes)} nodes instead of 1")
110
-
111
- data_new_node = await build_data_new_node(dbt, mapping, node)
112
- node_created = await create_node(
113
- data=data_new_node,
114
- db=dbt,
115
- branch=branch,
116
- schema=target_schema,
117
- )
118
-
119
- # Make sure relationships with constraints are not broken by retrieving them
120
- peers_ids = deleted_node_out_rels_peer_ids + deleted_node_unidir_rels_peer_ids
121
- peers = await NodeManager.get_many(ids=peers_ids, db=dbt, prefetch_relationships=True, branch=branch)
122
- for peer in peers.values():
123
- peer.validate_relationships()
124
-
125
- # If the node had some value reserved in any Pools / Resource Manager, we need to change the identifier of the reservation(s)
126
- query = await PoolChangeReserved.init(
127
- db=dbt,
128
- existing_identifier=node.get_id(),
129
- new_identifier=node_created.get_id(),
130
- branch=branch,
131
- )
132
- await query.execute(db=dbt)
133
-
134
- return node_created