infrahub-server 1.4.0b1__py3-none-any.whl → 1.4.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. infrahub/api/schema.py +3 -7
  2. infrahub/cli/db.py +25 -0
  3. infrahub/cli/db_commands/__init__.py +0 -0
  4. infrahub/cli/db_commands/check_inheritance.py +284 -0
  5. infrahub/cli/upgrade.py +3 -0
  6. infrahub/config.py +4 -4
  7. infrahub/core/attribute.py +6 -0
  8. infrahub/core/constants/__init__.py +1 -0
  9. infrahub/core/diff/model/path.py +0 -39
  10. infrahub/core/graph/__init__.py +1 -1
  11. infrahub/core/initialization.py +26 -21
  12. infrahub/core/manager.py +2 -2
  13. infrahub/core/migrations/__init__.py +2 -0
  14. infrahub/core/migrations/graph/__init__.py +4 -2
  15. infrahub/core/migrations/graph/m033_deduplicate_relationship_vertices.py +1 -1
  16. infrahub/core/migrations/graph/m035_orphan_relationships.py +43 -0
  17. infrahub/core/migrations/graph/{m035_drop_attr_value_index.py → m036_drop_attr_value_index.py} +3 -3
  18. infrahub/core/migrations/graph/{m036_index_attr_vals.py → m037_index_attr_vals.py} +3 -3
  19. infrahub/core/migrations/query/node_duplicate.py +26 -3
  20. infrahub/core/migrations/schema/attribute_kind_update.py +156 -0
  21. infrahub/core/models.py +5 -1
  22. infrahub/core/node/resource_manager/ip_address_pool.py +50 -48
  23. infrahub/core/node/resource_manager/ip_prefix_pool.py +55 -53
  24. infrahub/core/node/resource_manager/number_pool.py +20 -18
  25. infrahub/core/query/branch.py +37 -20
  26. infrahub/core/query/node.py +15 -0
  27. infrahub/core/relationship/model.py +13 -13
  28. infrahub/core/schema/definitions/internal.py +1 -1
  29. infrahub/core/schema/generated/attribute_schema.py +1 -1
  30. infrahub/core/schema/node_schema.py +0 -5
  31. infrahub/core/schema/schema_branch.py +2 -2
  32. infrahub/core/validators/attribute/choices.py +28 -3
  33. infrahub/core/validators/attribute/kind.py +5 -1
  34. infrahub/core/validators/determiner.py +22 -2
  35. infrahub/events/__init__.py +2 -0
  36. infrahub/events/proposed_change_action.py +22 -0
  37. infrahub/graphql/app.py +2 -1
  38. infrahub/graphql/context.py +1 -1
  39. infrahub/graphql/mutations/proposed_change.py +5 -0
  40. infrahub/graphql/mutations/relationship.py +1 -1
  41. infrahub/graphql/mutations/schema.py +14 -1
  42. infrahub/graphql/queries/diff/tree.py +53 -2
  43. infrahub/graphql/schema.py +3 -14
  44. infrahub/graphql/types/event.py +8 -0
  45. infrahub/permissions/__init__.py +3 -0
  46. infrahub/permissions/constants.py +13 -0
  47. infrahub/permissions/globals.py +32 -0
  48. infrahub/task_manager/event.py +5 -1
  49. infrahub_sdk/client.py +8 -8
  50. infrahub_sdk/node/node.py +2 -2
  51. infrahub_sdk/protocols.py +6 -40
  52. infrahub_sdk/pytest_plugin/items/graphql_query.py +1 -1
  53. infrahub_sdk/schema/repository.py +1 -1
  54. infrahub_sdk/testing/docker.py +1 -1
  55. infrahub_sdk/utils.py +11 -7
  56. {infrahub_server-1.4.0b1.dist-info → infrahub_server-1.4.1.dist-info}/METADATA +4 -4
  57. {infrahub_server-1.4.0b1.dist-info → infrahub_server-1.4.1.dist-info}/RECORD +60 -56
  58. {infrahub_server-1.4.0b1.dist-info → infrahub_server-1.4.1.dist-info}/LICENSE.txt +0 -0
  59. {infrahub_server-1.4.0b1.dist-info → infrahub_server-1.4.1.dist-info}/WHEEL +0 -0
  60. {infrahub_server-1.4.0b1.dist-info → infrahub_server-1.4.1.dist-info}/entry_points.txt +0 -0
@@ -1,7 +1,19 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
1
5
  from infrahub.core.account import GlobalPermission
2
6
  from infrahub.core.constants import GLOBAL_BRANCH_NAME, GlobalPermissions, PermissionDecision
7
+ from infrahub.core.manager import NodeManager
8
+ from infrahub.core.node import Node
9
+ from infrahub.core.protocols import CoreGlobalPermission
3
10
  from infrahub.core.registry import registry
4
11
 
12
+ from .constants import GLOBAL_PERMISSION_DESCRIPTION
13
+
14
+ if TYPE_CHECKING:
15
+ from infrahub.database import InfrahubDatabase
16
+
5
17
 
6
18
  def define_global_permission_from_branch(permission: GlobalPermissions, branch_name: str) -> GlobalPermission:
7
19
  if branch_name in (GLOBAL_BRANCH_NAME, registry.default_branch):
@@ -10,3 +22,23 @@ def define_global_permission_from_branch(permission: GlobalPermissions, branch_n
10
22
  decision = PermissionDecision.ALLOW_OTHER
11
23
 
12
24
  return GlobalPermission(action=permission.value, decision=decision.value)
25
+
26
+
27
+ async def get_or_create_global_permission(db: InfrahubDatabase, permission: GlobalPermissions) -> CoreGlobalPermission:
28
+ permissions = await NodeManager.query(
29
+ db=db, schema=CoreGlobalPermission, filters={"action__value": permission.value}, limit=1
30
+ )
31
+
32
+ if permissions:
33
+ return permissions[0]
34
+
35
+ p = await Node.init(db=db, schema=CoreGlobalPermission)
36
+ await p.new(
37
+ db=db,
38
+ action=permission.value,
39
+ decision=PermissionDecision.ALLOW_ALL.value,
40
+ description=GLOBAL_PERMISSION_DESCRIPTION[permission],
41
+ )
42
+ await p.save(db=db)
43
+
44
+ return p
@@ -242,7 +242,11 @@ class PrefectEventData(PrefectEventModel):
242
242
  **self._return_proposed_change_event(),
243
243
  **self._return_proposed_change_reviewer_former_decision(),
244
244
  }
245
- case "infrahub.proposed_change.review_requested" | "infrahub.proposed_change.merged":
245
+ case (
246
+ "infrahub.proposed_change.approvals_revoked"
247
+ | "infrahub.proposed_change.review_requested"
248
+ | "infrahub.proposed_change.merged"
249
+ ):
246
250
  event_specifics = self._return_proposed_change_event()
247
251
 
248
252
  return event_specifics
infrahub_sdk/client.py CHANGED
@@ -250,7 +250,7 @@ class BaseClient:
250
250
 
251
251
  return Mutation(
252
252
  name="AllocateIPAddress",
253
- mutation="InfrahubIPAddressPoolGetResource",
253
+ mutation="IPAddressPoolGetResource",
254
254
  query={"ok": None, "node": {"id": None, "kind": None, "identifier": None, "display_label": None}},
255
255
  input_data={"data": input_data},
256
256
  )
@@ -281,7 +281,7 @@ class BaseClient:
281
281
 
282
282
  return Mutation(
283
283
  name="AllocateIPPrefix",
284
- mutation="InfrahubIPPrefixPoolGetResource",
284
+ mutation="IPPrefixPoolGetResource",
285
285
  query={"ok": None, "node": {"id": None, "kind": None, "identifier": None, "display_label": None}},
286
286
  input_data={"data": input_data},
287
287
  )
@@ -790,7 +790,7 @@ class InfrahubClient(BaseClient):
790
790
  async def process_page(page_offset: int, page_number: int) -> tuple[dict, ProcessRelationsNode]:
791
791
  """Process a single page of results."""
792
792
  query_data = await InfrahubNode(client=self, schema=schema, branch=branch).generate_query_data(
793
- offset=offset or page_offset,
793
+ offset=page_offset if offset is None else offset,
794
794
  limit=limit or pagination_size,
795
795
  filters=filters,
796
796
  include=include,
@@ -1300,7 +1300,7 @@ class InfrahubClient(BaseClient):
1300
1300
  raise ValueError("resource_pool is not an IP address pool")
1301
1301
 
1302
1302
  branch = branch or self.default_branch
1303
- mutation_name = "InfrahubIPAddressPoolGetResource"
1303
+ mutation_name = "IPAddressPoolGetResource"
1304
1304
 
1305
1305
  query = self._build_ip_address_allocation_query(
1306
1306
  resource_pool_id=resource_pool.id,
@@ -1452,7 +1452,7 @@ class InfrahubClient(BaseClient):
1452
1452
  raise ValueError("resource_pool is not an IP prefix pool")
1453
1453
 
1454
1454
  branch = branch or self.default_branch
1455
- mutation_name = "InfrahubIPPrefixPoolGetResource"
1455
+ mutation_name = "IPPrefixPoolGetResource"
1456
1456
 
1457
1457
  query = self._build_ip_prefix_allocation_query(
1458
1458
  resource_pool_id=resource_pool.id,
@@ -1954,7 +1954,7 @@ class InfrahubClientSync(BaseClient):
1954
1954
  def process_page(page_offset: int, page_number: int) -> tuple[dict, ProcessRelationsNodeSync]:
1955
1955
  """Process a single page of results."""
1956
1956
  query_data = InfrahubNodeSync(client=self, schema=schema, branch=branch).generate_query_data(
1957
- offset=offset or page_offset,
1957
+ offset=page_offset if offset is None else offset,
1958
1958
  limit=limit or pagination_size,
1959
1959
  filters=filters,
1960
1960
  include=include,
@@ -2438,7 +2438,7 @@ class InfrahubClientSync(BaseClient):
2438
2438
  raise ValueError("resource_pool is not an IP address pool")
2439
2439
 
2440
2440
  branch = branch or self.default_branch
2441
- mutation_name = "InfrahubIPAddressPoolGetResource"
2441
+ mutation_name = "IPAddressPoolGetResource"
2442
2442
 
2443
2443
  query = self._build_ip_address_allocation_query(
2444
2444
  resource_pool_id=resource_pool.id,
@@ -2586,7 +2586,7 @@ class InfrahubClientSync(BaseClient):
2586
2586
  raise ValueError("resource_pool is not an IP prefix pool")
2587
2587
 
2588
2588
  branch = branch or self.default_branch
2589
- mutation_name = "InfrahubIPPrefixPoolGetResource"
2589
+ mutation_name = "IPPrefixPoolGetResource"
2590
2590
 
2591
2591
  query = self._build_ip_prefix_allocation_query(
2592
2592
  resource_pool_id=resource_pool.id,
infrahub_sdk/node/node.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from collections.abc import Iterable
4
- from copy import copy
4
+ from copy import copy, deepcopy
5
5
  from typing import TYPE_CHECKING, Any
6
6
 
7
7
  from ..constants import InfrahubClientMode
@@ -397,7 +397,7 @@ class InfrahubNodeBase:
397
397
  "edges": {"node": {"id": None, "hfid": None, "display_label": None, "__typename": None}},
398
398
  }
399
399
 
400
- data["@filters"] = filters or {}
400
+ data["@filters"] = deepcopy(filters) if filters is not None else {}
401
401
 
402
402
  if order:
403
403
  data["@filters"]["order"] = order
infrahub_sdk/protocols.py CHANGED
@@ -233,10 +233,6 @@ class CoreWebhook(CoreNode):
233
233
  validate_certificates: BooleanOptional
234
234
 
235
235
 
236
- class CoreWeightedPoolResource(CoreNode):
237
- allocation_weight: IntegerOptional
238
-
239
-
240
236
  class LineageOwner(CoreNode):
241
237
  pass
242
238
 
@@ -325,7 +321,6 @@ class CoreCheckDefinition(CoreTaskTarget):
325
321
 
326
322
 
327
323
  class CoreCustomWebhook(CoreWebhook, CoreTaskTarget):
328
- shared_key: StringOptional
329
324
  transformation: RelatedNode
330
325
 
331
326
 
@@ -410,12 +405,12 @@ class CoreGraphQLQueryGroup(CoreGroup):
410
405
 
411
406
 
412
407
  class CoreGroupAction(CoreAction):
413
- member_action: Dropdown
408
+ add_members: Boolean
414
409
  group: RelatedNode
415
410
 
416
411
 
417
412
  class CoreGroupTriggerRule(CoreTriggerRule):
418
- member_update: Dropdown
413
+ members_added: Boolean
419
414
  group: RelatedNode
420
415
 
421
416
 
@@ -447,7 +442,7 @@ class CoreNodeTriggerAttributeMatch(CoreNodeTriggerMatch):
447
442
 
448
443
  class CoreNodeTriggerRelationshipMatch(CoreNodeTriggerMatch):
449
444
  relationship_name: String
450
- modification_type: Dropdown
445
+ added: Boolean
451
446
  peer: StringOptional
452
447
 
453
448
 
@@ -462,7 +457,6 @@ class CoreNumberPool(CoreResourcePool, LineageSource):
462
457
  node_attribute: String
463
458
  start_range: Integer
464
459
  end_range: Integer
465
- pool_type: Enum
466
460
 
467
461
 
468
462
  class CoreObjectPermission(CoreBasePermission):
@@ -487,10 +481,7 @@ class CoreProposedChange(CoreTaskTarget):
487
481
  source_branch: String
488
482
  destination_branch: String
489
483
  state: Enum
490
- is_draft: Boolean
491
- total_comments: IntegerOptional
492
484
  approved_by: RelationshipManager
493
- rejected_by: RelationshipManager
494
485
  reviewers: RelationshipManager
495
486
  created_by: RelatedNode
496
487
  comments: RelationshipManager
@@ -564,14 +555,6 @@ class InternalAccountToken(CoreNode):
564
555
  account: RelatedNode
565
556
 
566
557
 
567
- class InternalIPPrefixAvailable(BuiltinIPPrefix):
568
- pass
569
-
570
-
571
- class InternalIPRangeAvailable(BuiltinIPAddress):
572
- last_address: IPHost
573
-
574
-
575
558
  class InternalRefreshToken(CoreNode):
576
559
  expiration: DateTime
577
560
  account: RelatedNode
@@ -783,10 +766,6 @@ class CoreWebhookSync(CoreNodeSync):
783
766
  validate_certificates: BooleanOptional
784
767
 
785
768
 
786
- class CoreWeightedPoolResourceSync(CoreNodeSync):
787
- allocation_weight: IntegerOptional
788
-
789
-
790
769
  class LineageOwnerSync(CoreNodeSync):
791
770
  pass
792
771
 
@@ -875,7 +854,6 @@ class CoreCheckDefinitionSync(CoreTaskTargetSync):
875
854
 
876
855
 
877
856
  class CoreCustomWebhookSync(CoreWebhookSync, CoreTaskTargetSync):
878
- shared_key: StringOptional
879
857
  transformation: RelatedNodeSync
880
858
 
881
859
 
@@ -960,12 +938,12 @@ class CoreGraphQLQueryGroupSync(CoreGroupSync):
960
938
 
961
939
 
962
940
  class CoreGroupActionSync(CoreActionSync):
963
- member_action: Dropdown
941
+ add_members: Boolean
964
942
  group: RelatedNodeSync
965
943
 
966
944
 
967
945
  class CoreGroupTriggerRuleSync(CoreTriggerRuleSync):
968
- member_update: Dropdown
946
+ members_added: Boolean
969
947
  group: RelatedNodeSync
970
948
 
971
949
 
@@ -997,7 +975,7 @@ class CoreNodeTriggerAttributeMatchSync(CoreNodeTriggerMatchSync):
997
975
 
998
976
  class CoreNodeTriggerRelationshipMatchSync(CoreNodeTriggerMatchSync):
999
977
  relationship_name: String
1000
- modification_type: Dropdown
978
+ added: Boolean
1001
979
  peer: StringOptional
1002
980
 
1003
981
 
@@ -1012,7 +990,6 @@ class CoreNumberPoolSync(CoreResourcePoolSync, LineageSourceSync):
1012
990
  node_attribute: String
1013
991
  start_range: Integer
1014
992
  end_range: Integer
1015
- pool_type: Enum
1016
993
 
1017
994
 
1018
995
  class CoreObjectPermissionSync(CoreBasePermissionSync):
@@ -1037,10 +1014,7 @@ class CoreProposedChangeSync(CoreTaskTargetSync):
1037
1014
  source_branch: String
1038
1015
  destination_branch: String
1039
1016
  state: Enum
1040
- is_draft: Boolean
1041
- total_comments: IntegerOptional
1042
1017
  approved_by: RelationshipManagerSync
1043
- rejected_by: RelationshipManagerSync
1044
1018
  reviewers: RelationshipManagerSync
1045
1019
  created_by: RelatedNodeSync
1046
1020
  comments: RelationshipManagerSync
@@ -1114,14 +1088,6 @@ class InternalAccountTokenSync(CoreNodeSync):
1114
1088
  account: RelatedNodeSync
1115
1089
 
1116
1090
 
1117
- class InternalIPPrefixAvailableSync(BuiltinIPPrefixSync):
1118
- pass
1119
-
1120
-
1121
- class InternalIPRangeAvailableSync(BuiltinIPAddressSync):
1122
- last_address: IPHost
1123
-
1124
-
1125
1091
  class InternalRefreshTokenSync(CoreNodeSync):
1126
1092
  expiration: DateTime
1127
1093
  account: RelatedNodeSync
@@ -16,7 +16,7 @@ if TYPE_CHECKING:
16
16
 
17
17
  class InfrahubGraphQLQueryItem(InfrahubItem):
18
18
  def validate_resource_config(self) -> None:
19
- # Resource name does not need to match against infrahub repo config
19
+ # Resource name does not need to match against Infrahub repository configuration
20
20
  return
21
21
 
22
22
  def execute_query(self) -> Any:
@@ -24,7 +24,7 @@ ResourceClass = TypeVar("ResourceClass")
24
24
 
25
25
 
26
26
  class InfrahubRepositoryConfigElement(BaseModel):
27
- """Class to regroup all elements of the infrahub configuration for a repository for typing purpose."""
27
+ """Class to regroup all elements of the Infrahub configuration for a repository for typing purpose."""
28
28
 
29
29
 
30
30
  class InfrahubRepositoryArtifactDefinitionConfig(InfrahubRepositoryConfigElement):
@@ -13,7 +13,7 @@ INFRAHUB_VERSION = os.getenv("INFRAHUB_TESTING_IMAGE_VER")
13
13
 
14
14
  def skip_version(min_infrahub_version: str | None = None, max_infrahub_version: str | None = None) -> bool:
15
15
  """
16
- Check if a test should be skipped depending on infrahub version.
16
+ Check if a test should be skipped depending on Infrahub version.
17
17
  """
18
18
  if INFRAHUB_VERSION is None:
19
19
  return True
infrahub_sdk/utils.py CHANGED
@@ -95,7 +95,7 @@ def decode_json(response: httpx.Response) -> dict:
95
95
  try:
96
96
  return response.json()
97
97
  except json.decoder.JSONDecodeError as exc:
98
- raise JsonDecodeError(content=response.text, url=response.url) from exc
98
+ raise JsonDecodeError(content=response.text, url=str(response.url)) from exc
99
99
 
100
100
 
101
101
  def generate_uuid() -> str:
@@ -142,14 +142,18 @@ def deep_merge_dict(dicta: dict, dictb: dict, path: list | None = None) -> dict:
142
142
  if path is None:
143
143
  path = []
144
144
  for key in dictb:
145
+ b_val = dictb[key]
145
146
  if key in dicta:
146
- if isinstance(dicta[key], dict) and isinstance(dictb[key], dict):
147
- deep_merge_dict(dicta[key], dictb[key], path + [str(key)])
148
- elif isinstance(dicta[key], list) and isinstance(dictb[key], list):
147
+ a_val = dicta[key]
148
+ if isinstance(a_val, dict) and isinstance(b_val, dict):
149
+ deep_merge_dict(a_val, b_val, path + [str(key)])
150
+ elif isinstance(a_val, list) and isinstance(b_val, list):
149
151
  # Merge lists
150
152
  # Cannot use compare_list because list of dicts won't work (dict not hashable)
151
- dicta[key] = [i for i in dicta[key] if i not in dictb[key]] + dictb[key]
152
- elif dicta[key] == dictb[key]:
153
+ dicta[key] = [i for i in a_val if i not in b_val] + b_val
154
+ elif a_val is None and b_val is not None:
155
+ dicta[key] = b_val
156
+ elif a_val == b_val or (a_val is not None and b_val is None):
153
157
  continue
154
158
  else:
155
159
  raise ValueError("Conflict at %s" % ".".join(path + [str(key)]))
@@ -228,7 +232,7 @@ def get_branch(branch: str | None = None, directory: str | Path = ".") -> str:
228
232
  if branch:
229
233
  return branch
230
234
 
231
- repo = GitRepoManager(directory)
235
+ repo = GitRepoManager(root_directory=str(directory))
232
236
  return str(repo.active_branch)
233
237
 
234
238
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: infrahub-server
3
- Version: 1.4.0b1
3
+ Version: 1.4.1
4
4
  Summary: Infrahub is taking a new approach to Infrastructure Management by providing a new generation of datastore to organize and control all the data that defines how an infrastructure should run.
5
5
  License: Apache-2.0
6
6
  Author: OpsMill
@@ -23,7 +23,7 @@ Requires-Dist: copier (>=9.8.0,<10.0.0)
23
23
  Requires-Dist: dulwich (>=0.22.7,<0.23.0)
24
24
  Requires-Dist: email-validator (>=2.1,<2.2)
25
25
  Requires-Dist: fast-depends (>=2.4.12,<3.0.0)
26
- Requires-Dist: fastapi (>=0.115,<0.116)
26
+ Requires-Dist: fastapi (==0.116.1)
27
27
  Requires-Dist: fastapi-storages (>=0.3,<0.4)
28
28
  Requires-Dist: gitpython (>=3,<4)
29
29
  Requires-Dist: graphene (>=3.4,<3.5)
@@ -40,8 +40,8 @@ Requires-Dist: opentelemetry-exporter-otlp-proto-grpc (==1.28.1)
40
40
  Requires-Dist: opentelemetry-exporter-otlp-proto-http (==1.28.1)
41
41
  Requires-Dist: opentelemetry-instrumentation-aio-pika (==0.49b1)
42
42
  Requires-Dist: opentelemetry-instrumentation-fastapi (==0.49b1)
43
- Requires-Dist: prefect (==3.4.11)
44
- Requires-Dist: prefect-redis (==0.2.3)
43
+ Requires-Dist: prefect (==3.4.13)
44
+ Requires-Dist: prefect-redis (==0.2.4)
45
45
  Requires-Dist: pyarrow (>=14,<15)
46
46
  Requires-Dist: pydantic (>=2.10,<2.11)
47
47
  Requires-Dist: pydantic-settings (>=2.8,<2.9)