infrahub-server 1.1.6__py3-none-any.whl → 1.1.8__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 (97) hide show
  1. infrahub/core/attribute.py +4 -1
  2. infrahub/core/branch/tasks.py +7 -4
  3. infrahub/core/diff/combiner.py +11 -7
  4. infrahub/core/diff/coordinator.py +49 -70
  5. infrahub/core/diff/data_check_synchronizer.py +86 -7
  6. infrahub/core/diff/enricher/aggregated.py +3 -3
  7. infrahub/core/diff/enricher/cardinality_one.py +6 -6
  8. infrahub/core/diff/enricher/hierarchy.py +17 -4
  9. infrahub/core/diff/enricher/labels.py +18 -3
  10. infrahub/core/diff/enricher/path_identifier.py +7 -8
  11. infrahub/core/diff/merger/merger.py +5 -3
  12. infrahub/core/diff/model/path.py +66 -25
  13. infrahub/core/diff/parent_node_adder.py +78 -0
  14. infrahub/core/diff/payload_builder.py +13 -2
  15. infrahub/core/diff/query/all_conflicts.py +5 -2
  16. infrahub/core/diff/query/diff_get.py +2 -1
  17. infrahub/core/diff/query/field_specifiers.py +2 -0
  18. infrahub/core/diff/query/field_summary.py +2 -1
  19. infrahub/core/diff/query/filters.py +12 -1
  20. infrahub/core/diff/query/has_conflicts_query.py +5 -2
  21. infrahub/core/diff/query/{drop_tracking_id.py → merge_tracking_id.py} +3 -3
  22. infrahub/core/diff/query/roots_metadata.py +8 -1
  23. infrahub/core/diff/query/save.py +230 -139
  24. infrahub/core/diff/query/summary_counts_enricher.py +267 -0
  25. infrahub/core/diff/query/time_range_query.py +2 -1
  26. infrahub/core/diff/query_parser.py +49 -24
  27. infrahub/core/diff/repository/deserializer.py +31 -27
  28. infrahub/core/diff/repository/repository.py +215 -41
  29. infrahub/core/diff/tasks.py +4 -4
  30. infrahub/core/graph/__init__.py +1 -1
  31. infrahub/core/graph/index.py +3 -0
  32. infrahub/core/migrations/graph/__init__.py +4 -0
  33. infrahub/core/migrations/graph/m019_restore_rels_to_time.py +256 -0
  34. infrahub/core/migrations/graph/m020_duplicate_edges.py +160 -0
  35. infrahub/core/migrations/query/node_duplicate.py +38 -18
  36. infrahub/core/migrations/schema/node_remove.py +26 -12
  37. infrahub/core/migrations/shared.py +10 -8
  38. infrahub/core/node/__init__.py +19 -9
  39. infrahub/core/node/constraints/grouped_uniqueness.py +25 -5
  40. infrahub/core/node/ipam.py +6 -1
  41. infrahub/core/node/permissions.py +4 -0
  42. infrahub/core/query/attribute.py +2 -0
  43. infrahub/core/query/diff.py +41 -3
  44. infrahub/core/query/node.py +74 -21
  45. infrahub/core/query/relationship.py +107 -17
  46. infrahub/core/query/resource_manager.py +5 -1
  47. infrahub/core/relationship/model.py +8 -12
  48. infrahub/core/schema/definitions/core.py +1 -0
  49. infrahub/core/utils.py +1 -0
  50. infrahub/core/validators/uniqueness/query.py +20 -17
  51. infrahub/database/__init__.py +14 -0
  52. infrahub/dependencies/builder/constraint/grouped/node_runner.py +0 -2
  53. infrahub/dependencies/builder/diff/coordinator.py +0 -2
  54. infrahub/dependencies/builder/diff/deserializer.py +3 -1
  55. infrahub/dependencies/builder/diff/enricher/hierarchy.py +3 -1
  56. infrahub/dependencies/builder/diff/parent_node_adder.py +8 -0
  57. infrahub/graphql/mutations/computed_attribute.py +3 -1
  58. infrahub/graphql/mutations/diff.py +41 -10
  59. infrahub/graphql/mutations/main.py +11 -6
  60. infrahub/graphql/mutations/relationship.py +29 -1
  61. infrahub/graphql/mutations/resource_manager.py +3 -3
  62. infrahub/graphql/mutations/tasks.py +6 -3
  63. infrahub/graphql/queries/resource_manager.py +7 -3
  64. infrahub/permissions/__init__.py +2 -1
  65. infrahub/permissions/types.py +26 -0
  66. infrahub_sdk/client.py +10 -2
  67. infrahub_sdk/config.py +3 -0
  68. infrahub_sdk/ctl/check.py +3 -3
  69. infrahub_sdk/ctl/cli_commands.py +16 -11
  70. infrahub_sdk/ctl/exceptions.py +0 -6
  71. infrahub_sdk/ctl/exporter.py +1 -1
  72. infrahub_sdk/ctl/generator.py +5 -5
  73. infrahub_sdk/ctl/importer.py +3 -2
  74. infrahub_sdk/ctl/menu.py +1 -1
  75. infrahub_sdk/ctl/object.py +1 -1
  76. infrahub_sdk/ctl/repository.py +23 -15
  77. infrahub_sdk/ctl/schema.py +2 -2
  78. infrahub_sdk/ctl/utils.py +4 -3
  79. infrahub_sdk/ctl/validate.py +2 -1
  80. infrahub_sdk/exceptions.py +12 -0
  81. infrahub_sdk/generator.py +3 -0
  82. infrahub_sdk/node.py +7 -4
  83. infrahub_sdk/testing/schemas/animal.py +9 -0
  84. infrahub_sdk/utils.py +11 -1
  85. infrahub_sdk/yaml.py +2 -3
  86. {infrahub_server-1.1.6.dist-info → infrahub_server-1.1.8.dist-info}/METADATA +41 -7
  87. {infrahub_server-1.1.6.dist-info → infrahub_server-1.1.8.dist-info}/RECORD +94 -91
  88. infrahub_testcontainers/container.py +12 -3
  89. infrahub_testcontainers/docker-compose.test.yml +22 -3
  90. infrahub_testcontainers/haproxy.cfg +43 -0
  91. infrahub_testcontainers/helpers.py +85 -1
  92. infrahub/core/diff/enricher/summary_counts.py +0 -105
  93. infrahub/dependencies/builder/diff/enricher/summary_counts.py +0 -8
  94. infrahub_sdk/ctl/_file.py +0 -13
  95. {infrahub_server-1.1.6.dist-info → infrahub_server-1.1.8.dist-info}/LICENSE.txt +0 -0
  96. {infrahub_server-1.1.6.dist-info → infrahub_server-1.1.8.dist-info}/WHEEL +0 -0
  97. {infrahub_server-1.1.6.dist-info → infrahub_server-1.1.8.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,43 @@
1
+ global
2
+ log stdout local0
3
+ log stdout local1 notice
4
+ #chroot /var/lib/haproxy
5
+ #stats socket /run/haproxy/admin.sock mode 660 level admin
6
+ stats timeout 30s
7
+ user haproxy
8
+ group haproxy
9
+ daemon
10
+
11
+ # Default SSL material locations
12
+ ca-base /etc/ssl/certs
13
+ crt-base /etc/ssl/private
14
+
15
+ # See: https://ssl-config.mozilla.org/#server=haproxy&server-version=2.0.3&config=intermediate
16
+ ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
17
+ ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
18
+ ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
19
+
20
+ defaults
21
+ log global
22
+ mode http
23
+ option httplog
24
+ option dontlognull
25
+ timeout connect 5000
26
+ timeout client 50000
27
+ timeout server 50000
28
+
29
+ resolvers docker
30
+ parse-resolv-conf
31
+ accepted_payload_size 8192
32
+
33
+ frontend fe
34
+ mode http
35
+ bind *:8000
36
+ option forwardfor
37
+
38
+ default_backend be
39
+
40
+ backend be
41
+ mode http
42
+ balance roundrobin
43
+ server-template api 100 infrahub-server:8000 check resolvers docker init-addr none
@@ -1,4 +1,5 @@
1
1
  import os
2
+ import shutil
2
3
  import subprocess # noqa: S404
3
4
  from pathlib import Path
4
5
 
@@ -36,12 +37,25 @@ class TestInfrahubDocker:
36
37
 
37
38
  return directory
38
39
 
40
+ @pytest.fixture(scope="class")
41
+ def remote_backups_dir(self, tmp_directory: Path) -> Path:
42
+ directory = tmp_directory / PROJECT_ENV_VARIABLES["INFRAHUB_TESTING_LOCAL_DB_BACKUP_DIRECTORY"]
43
+ directory.mkdir(exist_ok=True)
44
+
45
+ return directory
46
+
39
47
  @pytest.fixture(scope="class")
40
48
  def default_branch(self) -> str:
41
49
  return "main"
42
50
 
43
51
  @pytest.fixture(scope="class")
44
- def infrahub_compose(self, tmp_directory: Path, infrahub_version: str) -> InfrahubDockerCompose:
52
+ def infrahub_compose(
53
+ self,
54
+ tmp_directory: Path,
55
+ remote_repos_dir: Path, # initialize repository before running docker compose to fix permissions issues # noqa: ARG002
56
+ remote_backups_dir: Path, # noqa: ARG002
57
+ infrahub_version: str,
58
+ ) -> InfrahubDockerCompose:
45
59
  return InfrahubDockerCompose.init(directory=tmp_directory, version=infrahub_version)
46
60
 
47
61
  @pytest.fixture(scope="class")
@@ -62,3 +76,73 @@ class TestInfrahubDocker:
62
76
  @pytest.fixture(scope="class")
63
77
  def task_manager_port(self, infrahub_app: dict[str, int]) -> int:
64
78
  return infrahub_app["task-manager"]
79
+
80
+ def backup_database(self, request: pytest.FixtureRequest, dest_dir: Path | None = None) -> None:
81
+ assert "enterprise" in os.environ.get("NEO4J_DOCKER_IMAGE", "")
82
+
83
+ backup_dir: Path = request.getfixturevalue("remote_backups_dir")
84
+ infrahub_compose: InfrahubDockerCompose = request.getfixturevalue("infrahub_compose")
85
+
86
+ infrahub_compose.exec_in_container(
87
+ command=[
88
+ "neo4j-admin",
89
+ "database",
90
+ "backup",
91
+ "--to-path",
92
+ os.environ.get(
93
+ "INFRAHUB_TESTING_INTERNAL_DB_BACKUP_DIRECTORY",
94
+ PROJECT_ENV_VARIABLES["INFRAHUB_TESTING_INTERNAL_DB_BACKUP_DIRECTORY"],
95
+ ),
96
+ ],
97
+ service_name="database",
98
+ )
99
+
100
+ if dest_dir:
101
+ shutil.copytree(
102
+ str(backup_dir),
103
+ str(dest_dir),
104
+ )
105
+
106
+ def restore_database(self, request: pytest.FixtureRequest, backup_file: Path) -> None:
107
+ assert "enterprise" in os.environ.get("NEO4J_DOCKER_IMAGE", "")
108
+
109
+ backup_dir: Path = request.getfixturevalue("remote_backups_dir")
110
+ infrahub_compose: InfrahubDockerCompose = request.getfixturevalue("infrahub_compose")
111
+
112
+ shutil.copy(
113
+ str(backup_file),
114
+ str(backup_dir / backup_file.name),
115
+ )
116
+
117
+ infrahub_compose.exec_in_container(
118
+ command=["cypher-shell", "-u", "neo4j", "-p", "admin", "STOP DATABASE neo4j;"],
119
+ service_name="database",
120
+ )
121
+
122
+ infrahub_compose.exec_in_container(
123
+ command=[
124
+ "neo4j-admin",
125
+ "database",
126
+ "restore",
127
+ "--overwrite-destination",
128
+ "--from-path",
129
+ str(
130
+ Path(
131
+ os.environ.get(
132
+ "INFRAHUB_TESTING_INTERNAL_DB_BACKUP_DIRECTORY",
133
+ PROJECT_ENV_VARIABLES["INFRAHUB_TESTING_INTERNAL_DB_BACKUP_DIRECTORY"],
134
+ )
135
+ )
136
+ / backup_file.name
137
+ ),
138
+ ],
139
+ service_name="database",
140
+ )
141
+
142
+ infrahub_compose.exec_in_container(
143
+ command=["cypher-shell", "-d", "system", "-u", "neo4j", "-p", "admin", "START DATABASE neo4j;"],
144
+ service_name="database",
145
+ )
146
+
147
+ infrahub_compose.stop(down=False)
148
+ infrahub_compose.start()
@@ -1,105 +0,0 @@
1
- from collections import Counter
2
- from typing import Iterable
3
-
4
- from infrahub.core.constants import DiffAction
5
-
6
- from ..model.path import (
7
- BaseSummary,
8
- CalculatedDiffs,
9
- EnrichedDiffAttribute,
10
- EnrichedDiffNode,
11
- EnrichedDiffRelationship,
12
- EnrichedDiffRoot,
13
- EnrichedDiffSingleRelationship,
14
- )
15
- from .interface import DiffEnricherInterface
16
-
17
-
18
- class DiffSummaryCountsEnricher(DiffEnricherInterface):
19
- async def enrich(
20
- self, enriched_diff_root: EnrichedDiffRoot, calculated_diffs: CalculatedDiffs | None = None
21
- ) -> None:
22
- self._add_root_summaries(diff_root=enriched_diff_root)
23
-
24
- def _add_summary(self, summary_node: BaseSummary, actions: Iterable[DiffAction]) -> None:
25
- summary_count = Counter(actions)
26
- summary_node.num_added = summary_count.get(DiffAction.ADDED, 0)
27
- summary_node.num_updated = summary_count.get(DiffAction.UPDATED, 0)
28
- summary_node.num_removed = summary_count.get(DiffAction.REMOVED, 0)
29
-
30
- def _add_root_summaries(self, diff_root: EnrichedDiffRoot) -> None:
31
- contains_conflict = False
32
- num_conflicts = 0
33
- for diff_node in diff_root.nodes:
34
- contains_conflict |= self._add_node_summaries(diff_node=diff_node)
35
- if diff_node.conflict or diff_node.contains_conflict:
36
- num_conflicts += 1
37
- self._add_summary(summary_node=diff_root, actions=(n.action for n in diff_root.nodes))
38
- diff_root.contains_conflict = contains_conflict
39
- diff_root.num_conflicts = num_conflicts
40
- self._add_child_nodes_to_summaries(diff_root=diff_root)
41
-
42
- def _add_node_summaries(self, diff_node: EnrichedDiffNode) -> bool:
43
- contains_conflict = False
44
- num_conflicts = 0
45
- for diff_attr in diff_node.attributes:
46
- contains_conflict |= self._add_attribute_summaries(diff_attribute=diff_attr)
47
- if diff_attr.contains_conflict:
48
- num_conflicts += 1
49
- for diff_rel in diff_node.relationships:
50
- contains_conflict |= self._add_relationship_summaries(diff_relationship=diff_rel)
51
- if diff_rel.contains_conflict:
52
- num_conflicts += 1
53
- self._add_summary(
54
- summary_node=diff_node, actions=(field.action for field in diff_node.relationships | diff_node.attributes)
55
- )
56
- diff_node.contains_conflict = contains_conflict
57
- diff_node.num_conflicts = num_conflicts
58
- return contains_conflict
59
-
60
- def _add_attribute_summaries(self, diff_attribute: EnrichedDiffAttribute) -> bool:
61
- contains_conflict = False
62
- num_conflicts = 0
63
- for diff_prop in diff_attribute.properties:
64
- if diff_prop.conflict:
65
- num_conflicts += 1
66
- contains_conflict = True
67
- self._add_summary(summary_node=diff_attribute, actions=(p.action for p in diff_attribute.properties))
68
- diff_attribute.contains_conflict = contains_conflict
69
- diff_attribute.num_conflicts = num_conflicts
70
- return contains_conflict
71
-
72
- def _add_relationship_summaries(self, diff_relationship: EnrichedDiffRelationship) -> bool:
73
- contains_conflict = False
74
- num_conflicts = 0
75
- for diff_element in diff_relationship.relationships:
76
- contains_conflict |= self._add_element_summaries(diff_element=diff_element)
77
- if diff_element.conflict:
78
- num_conflicts += 1
79
- self._add_summary(summary_node=diff_relationship, actions=(e.action for e in diff_relationship.relationships))
80
- diff_relationship.contains_conflict = contains_conflict
81
- diff_relationship.num_conflicts = num_conflicts
82
- return contains_conflict
83
-
84
- def _add_element_summaries(self, diff_element: EnrichedDiffSingleRelationship) -> bool:
85
- if diff_element.conflict is None:
86
- contains_conflict = False
87
- num_conflicts = 0
88
- else:
89
- contains_conflict = True
90
- num_conflicts = 1
91
- for diff_prop in diff_element.properties:
92
- if diff_prop.conflict:
93
- num_conflicts += 1
94
- contains_conflict = True
95
- self._add_summary(summary_node=diff_element, actions=(p.action for p in diff_element.properties))
96
- diff_element.contains_conflict = contains_conflict
97
- diff_element.num_conflicts = num_conflicts
98
- return contains_conflict
99
-
100
- def _add_child_nodes_to_summaries(self, diff_root: EnrichedDiffRoot) -> None:
101
- for diff_node in diff_root.nodes:
102
- for diff_rel in diff_node.relationships:
103
- if not diff_rel.contains_conflict:
104
- diff_rel.contains_conflict = any(n.contains_conflict for n in diff_rel.nodes)
105
- diff_rel.num_conflicts += sum(bool(n.contains_conflict or n.conflict) for n in diff_rel.nodes)
@@ -1,8 +0,0 @@
1
- from infrahub.core.diff.enricher.summary_counts import DiffSummaryCountsEnricher
2
- from infrahub.dependencies.interface import DependencyBuilder, DependencyBuilderContext
3
-
4
-
5
- class DiffSummaryCountsEnricherDependency(DependencyBuilder[DiffSummaryCountsEnricher]):
6
- @classmethod
7
- def build(cls, context: DependencyBuilderContext) -> DiffSummaryCountsEnricher:
8
- return DiffSummaryCountsEnricher()
infrahub_sdk/ctl/_file.py DELETED
@@ -1,13 +0,0 @@
1
- from pathlib import Path
2
-
3
- from .exceptions import FileNotValidError
4
-
5
-
6
- def read_file(file_name: Path) -> str:
7
- if not file_name.is_file():
8
- raise FileNotValidError(name=str(file_name), message=f"{file_name} is not a valid file")
9
- try:
10
- with Path.open(file_name, encoding="utf-8") as fobj:
11
- return fobj.read()
12
- except UnicodeDecodeError as exc:
13
- raise FileNotValidError(name=str(file_name), message=f"Unable to read {file_name} with utf-8 encoding") from exc