infrahub-server 1.4.12__py3-none-any.whl → 1.5.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.
- infrahub/actions/tasks.py +208 -16
- infrahub/api/artifact.py +3 -0
- infrahub/api/diff/diff.py +1 -1
- infrahub/api/internal.py +2 -0
- infrahub/api/query.py +2 -0
- infrahub/api/schema.py +27 -3
- infrahub/auth.py +5 -5
- infrahub/cli/__init__.py +2 -0
- infrahub/cli/db.py +160 -157
- infrahub/cli/dev.py +118 -0
- infrahub/cli/tasks.py +46 -0
- infrahub/cli/upgrade.py +56 -9
- infrahub/computed_attribute/tasks.py +19 -7
- infrahub/config.py +7 -2
- infrahub/core/attribute.py +35 -24
- infrahub/core/branch/enums.py +1 -1
- infrahub/core/branch/models.py +9 -5
- infrahub/core/branch/needs_rebase_status.py +11 -0
- infrahub/core/branch/tasks.py +72 -10
- infrahub/core/changelog/models.py +2 -10
- infrahub/core/constants/__init__.py +4 -0
- infrahub/core/constants/infrahubkind.py +1 -0
- infrahub/core/convert_object_type/object_conversion.py +201 -0
- infrahub/core/convert_object_type/repository_conversion.py +89 -0
- infrahub/core/convert_object_type/schema_mapping.py +27 -3
- infrahub/core/diff/calculator.py +2 -2
- infrahub/core/diff/model/path.py +4 -0
- infrahub/core/diff/payload_builder.py +1 -1
- infrahub/core/diff/query/artifact.py +1 -0
- infrahub/core/diff/query/delete_query.py +9 -5
- infrahub/core/diff/query/field_summary.py +1 -0
- infrahub/core/diff/query/merge.py +39 -23
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/initialization.py +7 -4
- infrahub/core/manager.py +3 -81
- infrahub/core/migrations/__init__.py +3 -0
- infrahub/core/migrations/exceptions.py +4 -0
- infrahub/core/migrations/graph/__init__.py +13 -10
- infrahub/core/migrations/graph/load_schema_branch.py +21 -0
- infrahub/core/migrations/graph/m013_convert_git_password_credential.py +1 -1
- infrahub/core/migrations/graph/m037_index_attr_vals.py +11 -30
- infrahub/core/migrations/graph/m039_ipam_reconcile.py +9 -7
- infrahub/core/migrations/graph/m041_deleted_dup_edges.py +149 -0
- infrahub/core/migrations/graph/m042_profile_attrs_in_db.py +147 -0
- infrahub/core/migrations/graph/m043_create_hfid_display_label_in_db.py +164 -0
- infrahub/core/migrations/graph/m044_backfill_hfid_display_label_in_db.py +864 -0
- infrahub/core/migrations/query/__init__.py +7 -8
- infrahub/core/migrations/query/attribute_add.py +8 -6
- infrahub/core/migrations/query/attribute_remove.py +134 -0
- infrahub/core/migrations/runner.py +54 -0
- infrahub/core/migrations/schema/attribute_kind_update.py +9 -3
- infrahub/core/migrations/schema/attribute_supports_profile.py +90 -0
- infrahub/core/migrations/schema/node_attribute_add.py +26 -5
- infrahub/core/migrations/schema/node_attribute_remove.py +13 -109
- infrahub/core/migrations/schema/node_kind_update.py +2 -1
- infrahub/core/migrations/schema/node_remove.py +2 -1
- infrahub/core/migrations/schema/placeholder_dummy.py +3 -2
- infrahub/core/migrations/shared.py +66 -19
- infrahub/core/models.py +2 -2
- infrahub/core/node/__init__.py +207 -54
- infrahub/core/node/create.py +53 -49
- infrahub/core/node/lock_utils.py +124 -0
- infrahub/core/node/node_property_attribute.py +230 -0
- infrahub/core/node/resource_manager/ip_address_pool.py +2 -1
- infrahub/core/node/resource_manager/ip_prefix_pool.py +2 -1
- infrahub/core/node/resource_manager/number_pool.py +2 -1
- infrahub/core/node/standard.py +1 -1
- infrahub/core/property.py +11 -0
- infrahub/core/protocols.py +8 -1
- infrahub/core/query/attribute.py +82 -15
- infrahub/core/query/diff.py +61 -16
- infrahub/core/query/ipam.py +16 -4
- infrahub/core/query/node.py +92 -212
- infrahub/core/query/relationship.py +44 -26
- infrahub/core/query/subquery.py +0 -8
- infrahub/core/relationship/model.py +69 -24
- infrahub/core/schema/__init__.py +56 -0
- infrahub/core/schema/attribute_schema.py +4 -2
- infrahub/core/schema/basenode_schema.py +42 -2
- infrahub/core/schema/definitions/core/__init__.py +2 -0
- infrahub/core/schema/definitions/core/check.py +1 -1
- infrahub/core/schema/definitions/core/generator.py +2 -0
- infrahub/core/schema/definitions/core/group.py +16 -2
- infrahub/core/schema/definitions/core/repository.py +7 -0
- infrahub/core/schema/definitions/core/transform.py +1 -1
- infrahub/core/schema/definitions/internal.py +12 -3
- infrahub/core/schema/generated/attribute_schema.py +2 -2
- infrahub/core/schema/generated/base_node_schema.py +6 -1
- infrahub/core/schema/manager.py +3 -0
- infrahub/core/schema/node_schema.py +1 -0
- infrahub/core/schema/relationship_schema.py +0 -1
- infrahub/core/schema/schema_branch.py +295 -10
- infrahub/core/schema/schema_branch_display.py +135 -0
- infrahub/core/schema/schema_branch_hfid.py +120 -0
- infrahub/core/validators/aggregated_checker.py +1 -1
- infrahub/database/graph.py +21 -0
- infrahub/display_labels/__init__.py +0 -0
- infrahub/display_labels/gather.py +48 -0
- infrahub/display_labels/models.py +240 -0
- infrahub/display_labels/tasks.py +192 -0
- infrahub/display_labels/triggers.py +22 -0
- infrahub/events/branch_action.py +27 -1
- infrahub/events/group_action.py +1 -1
- infrahub/events/node_action.py +1 -1
- infrahub/generators/constants.py +7 -0
- infrahub/generators/models.py +38 -12
- infrahub/generators/tasks.py +34 -16
- infrahub/git/base.py +42 -2
- infrahub/git/integrator.py +22 -14
- infrahub/git/tasks.py +52 -2
- infrahub/graphql/analyzer.py +9 -0
- infrahub/graphql/api/dependencies.py +2 -4
- infrahub/graphql/api/endpoints.py +16 -6
- infrahub/graphql/app.py +2 -4
- infrahub/graphql/initialization.py +2 -3
- infrahub/graphql/manager.py +213 -137
- infrahub/graphql/middleware.py +12 -0
- infrahub/graphql/mutations/branch.py +16 -0
- infrahub/graphql/mutations/computed_attribute.py +110 -3
- infrahub/graphql/mutations/convert_object_type.py +44 -13
- infrahub/graphql/mutations/display_label.py +118 -0
- infrahub/graphql/mutations/generator.py +25 -7
- infrahub/graphql/mutations/hfid.py +125 -0
- infrahub/graphql/mutations/ipam.py +73 -41
- infrahub/graphql/mutations/main.py +61 -178
- infrahub/graphql/mutations/profile.py +195 -0
- infrahub/graphql/mutations/proposed_change.py +8 -1
- infrahub/graphql/mutations/relationship.py +2 -2
- infrahub/graphql/mutations/repository.py +22 -83
- infrahub/graphql/mutations/resource_manager.py +2 -2
- infrahub/graphql/mutations/webhook.py +1 -1
- infrahub/graphql/queries/resource_manager.py +1 -1
- infrahub/graphql/registry.py +173 -0
- infrahub/graphql/resolvers/resolver.py +2 -0
- infrahub/graphql/schema.py +8 -1
- infrahub/graphql/schema_sort.py +170 -0
- infrahub/graphql/types/branch.py +4 -1
- infrahub/graphql/types/enums.py +3 -0
- infrahub/groups/tasks.py +1 -1
- infrahub/hfid/__init__.py +0 -0
- infrahub/hfid/gather.py +48 -0
- infrahub/hfid/models.py +240 -0
- infrahub/hfid/tasks.py +191 -0
- infrahub/hfid/triggers.py +22 -0
- infrahub/lock.py +119 -42
- infrahub/locks/__init__.py +0 -0
- infrahub/locks/tasks.py +37 -0
- infrahub/message_bus/types.py +1 -0
- infrahub/patch/plan_writer.py +2 -2
- infrahub/permissions/constants.py +2 -0
- infrahub/profiles/__init__.py +0 -0
- infrahub/profiles/node_applier.py +101 -0
- infrahub/profiles/queries/__init__.py +0 -0
- infrahub/profiles/queries/get_profile_data.py +98 -0
- infrahub/profiles/tasks.py +63 -0
- infrahub/proposed_change/tasks.py +67 -14
- infrahub/repositories/__init__.py +0 -0
- infrahub/repositories/create_repository.py +113 -0
- infrahub/server.py +9 -1
- infrahub/services/__init__.py +8 -5
- infrahub/services/adapters/http/__init__.py +5 -0
- infrahub/services/adapters/workflow/worker.py +14 -3
- infrahub/task_manager/event.py +5 -0
- infrahub/task_manager/models.py +7 -0
- infrahub/task_manager/task.py +73 -0
- infrahub/tasks/registry.py +6 -4
- infrahub/trigger/catalogue.py +4 -0
- infrahub/trigger/models.py +2 -0
- infrahub/trigger/setup.py +13 -4
- infrahub/trigger/tasks.py +6 -0
- infrahub/webhook/models.py +1 -1
- infrahub/workers/dependencies.py +3 -1
- infrahub/workers/infrahub_async.py +10 -2
- infrahub/workflows/catalogue.py +118 -3
- infrahub/workflows/initialization.py +21 -0
- infrahub/workflows/models.py +17 -2
- infrahub/workflows/utils.py +2 -1
- infrahub_sdk/branch.py +17 -8
- infrahub_sdk/checks.py +1 -1
- infrahub_sdk/client.py +376 -95
- infrahub_sdk/config.py +29 -2
- infrahub_sdk/convert_object_type.py +61 -0
- infrahub_sdk/ctl/branch.py +3 -0
- infrahub_sdk/ctl/check.py +2 -3
- infrahub_sdk/ctl/cli_commands.py +20 -12
- infrahub_sdk/ctl/config.py +8 -2
- infrahub_sdk/ctl/generator.py +6 -3
- infrahub_sdk/ctl/graphql.py +184 -0
- infrahub_sdk/ctl/repository.py +39 -1
- infrahub_sdk/ctl/schema.py +40 -10
- infrahub_sdk/ctl/task.py +110 -0
- infrahub_sdk/ctl/utils.py +4 -0
- infrahub_sdk/ctl/validate.py +5 -3
- infrahub_sdk/diff.py +4 -5
- infrahub_sdk/exceptions.py +2 -0
- infrahub_sdk/generator.py +7 -1
- infrahub_sdk/graphql/__init__.py +12 -0
- infrahub_sdk/graphql/constants.py +1 -0
- infrahub_sdk/graphql/plugin.py +85 -0
- infrahub_sdk/graphql/query.py +77 -0
- infrahub_sdk/{graphql.py → graphql/renderers.py} +88 -75
- infrahub_sdk/graphql/utils.py +40 -0
- infrahub_sdk/node/attribute.py +2 -0
- infrahub_sdk/node/node.py +28 -20
- infrahub_sdk/node/relationship.py +1 -3
- infrahub_sdk/playback.py +1 -2
- infrahub_sdk/protocols.py +54 -6
- infrahub_sdk/pytest_plugin/plugin.py +7 -4
- infrahub_sdk/pytest_plugin/utils.py +40 -0
- infrahub_sdk/repository.py +1 -2
- infrahub_sdk/schema/__init__.py +70 -4
- infrahub_sdk/schema/main.py +1 -0
- infrahub_sdk/schema/repository.py +8 -0
- infrahub_sdk/spec/models.py +7 -0
- infrahub_sdk/spec/object.py +54 -6
- infrahub_sdk/spec/processors/__init__.py +0 -0
- infrahub_sdk/spec/processors/data_processor.py +10 -0
- infrahub_sdk/spec/processors/factory.py +34 -0
- infrahub_sdk/spec/processors/range_expand_processor.py +56 -0
- infrahub_sdk/spec/range_expansion.py +118 -0
- infrahub_sdk/task/models.py +6 -4
- infrahub_sdk/timestamp.py +18 -6
- infrahub_sdk/transforms.py +1 -1
- {infrahub_server-1.4.12.dist-info → infrahub_server-1.5.0.dist-info}/METADATA +9 -10
- {infrahub_server-1.4.12.dist-info → infrahub_server-1.5.0.dist-info}/RECORD +233 -176
- infrahub_testcontainers/container.py +114 -2
- infrahub_testcontainers/docker-compose-cluster.test.yml +5 -0
- infrahub_testcontainers/docker-compose.test.yml +5 -0
- infrahub_testcontainers/models.py +2 -2
- infrahub_testcontainers/performance_test.py +4 -4
- infrahub/core/convert_object_type/conversion.py +0 -134
- {infrahub_server-1.4.12.dist-info → infrahub_server-1.5.0.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.4.12.dist-info → infrahub_server-1.5.0.dist-info}/WHEEL +0 -0
- {infrahub_server-1.4.12.dist-info → infrahub_server-1.5.0.dist-info}/entry_points.txt +0 -0
infrahub_sdk/node/node.py
CHANGED
|
@@ -234,15 +234,10 @@ class InfrahubNodeBase:
|
|
|
234
234
|
|
|
235
235
|
rel: RelatedNodeBase | RelationshipManagerBase = getattr(self, item_name)
|
|
236
236
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
# and self._schema.get_relationship(item_name).cardinality == "one"
|
|
242
|
-
# ):
|
|
243
|
-
# data[item_name] = None
|
|
244
|
-
# continue
|
|
245
|
-
# el
|
|
237
|
+
if rel_schema.cardinality == RelationshipCardinality.ONE and rel_schema.optional and not rel.initialized:
|
|
238
|
+
data[item_name] = None
|
|
239
|
+
continue
|
|
240
|
+
|
|
246
241
|
if rel is None or not rel.initialized:
|
|
247
242
|
continue
|
|
248
243
|
|
|
@@ -315,7 +310,16 @@ class InfrahubNodeBase:
|
|
|
315
310
|
variables.pop(variable_key)
|
|
316
311
|
|
|
317
312
|
# TODO: I do not feel _great_ about this
|
|
318
|
-
|
|
313
|
+
# -> I don't even know who you are (but this is not great indeed) -- gmazoyer (quoting Thanos)
|
|
314
|
+
original_data_item = original_data.get(item)
|
|
315
|
+
original_data_item_is_none = original_data_item is None
|
|
316
|
+
if isinstance(original_data_item, dict):
|
|
317
|
+
if "node" in original_data_item:
|
|
318
|
+
original_data_item_is_none = original_data_item["node"] is None
|
|
319
|
+
elif "id" not in original_data_item:
|
|
320
|
+
original_data_item_is_none = True
|
|
321
|
+
|
|
322
|
+
if item in data and (data_item in ({}, []) or (data_item is None and original_data_item_is_none)):
|
|
319
323
|
data.pop(item)
|
|
320
324
|
|
|
321
325
|
def _strip_unmodified(self, data: dict, variables: dict) -> tuple[dict, dict]:
|
|
@@ -324,7 +328,9 @@ class InfrahubNodeBase:
|
|
|
324
328
|
relationship_property = getattr(self, relationship)
|
|
325
329
|
if not relationship_property or relationship not in data:
|
|
326
330
|
continue
|
|
327
|
-
if not relationship_property.initialized
|
|
331
|
+
if not relationship_property.initialized and (
|
|
332
|
+
not isinstance(relationship_property, RelatedNodeBase) or not relationship_property.schema.optional
|
|
333
|
+
):
|
|
328
334
|
data.pop(relationship)
|
|
329
335
|
elif isinstance(relationship_property, RelationshipManagerBase) and not relationship_property.has_update:
|
|
330
336
|
data.pop(relationship)
|
|
@@ -573,8 +579,7 @@ class InfrahubNode(InfrahubNodeBase):
|
|
|
573
579
|
self._validate_artifact_support(ARTIFACT_GENERATE_FEATURE_NOT_SUPPORTED_MESSAGE)
|
|
574
580
|
|
|
575
581
|
artifact = await self._client.get(kind="CoreArtifact", name__value=name, object__ids=[self.id])
|
|
576
|
-
|
|
577
|
-
return content
|
|
582
|
+
return await self._client.object_store.get(identifier=artifact._get_attribute(name="storage_id").value)
|
|
578
583
|
|
|
579
584
|
async def delete(self, timeout: int | None = None, request_context: RequestContext | None = None) -> None:
|
|
580
585
|
input_data = {"data": {"id": self.id}}
|
|
@@ -742,12 +747,11 @@ class InfrahubNode(InfrahubNodeBase):
|
|
|
742
747
|
continue
|
|
743
748
|
|
|
744
749
|
peer_data: dict[str, Any] = {}
|
|
745
|
-
|
|
750
|
+
should_fetch_relationship = prefetch_relationships or (include is not None and rel_name in include)
|
|
751
|
+
if rel_schema and should_fetch_relationship:
|
|
746
752
|
peer_schema = await self._client.schema.get(kind=rel_schema.peer, branch=self._branch)
|
|
747
753
|
peer_node = InfrahubNode(client=self._client, schema=peer_schema, branch=self._branch)
|
|
748
754
|
peer_data = await peer_node.generate_query_data_node(
|
|
749
|
-
include=include,
|
|
750
|
-
exclude=exclude,
|
|
751
755
|
property=property,
|
|
752
756
|
)
|
|
753
757
|
|
|
@@ -886,7 +890,11 @@ class InfrahubNode(InfrahubNodeBase):
|
|
|
886
890
|
await self._process_mutation_result(mutation_name=mutation_name, response=response, timeout=timeout)
|
|
887
891
|
|
|
888
892
|
async def _process_relationships(
|
|
889
|
-
self,
|
|
893
|
+
self,
|
|
894
|
+
node_data: dict[str, Any],
|
|
895
|
+
branch: str,
|
|
896
|
+
related_nodes: list[InfrahubNode],
|
|
897
|
+
timeout: int | None = None,
|
|
890
898
|
) -> None:
|
|
891
899
|
"""Processes the Relationships of a InfrahubNode and add Related Nodes to a list.
|
|
892
900
|
|
|
@@ -1199,8 +1207,7 @@ class InfrahubNodeSync(InfrahubNodeBase):
|
|
|
1199
1207
|
def artifact_fetch(self, name: str) -> str | dict[str, Any]:
|
|
1200
1208
|
self._validate_artifact_support(ARTIFACT_FETCH_FEATURE_NOT_SUPPORTED_MESSAGE)
|
|
1201
1209
|
artifact = self._client.get(kind="CoreArtifact", name__value=name, object__ids=[self.id])
|
|
1202
|
-
|
|
1203
|
-
return content
|
|
1210
|
+
return self._client.object_store.get(identifier=artifact._get_attribute(name="storage_id").value)
|
|
1204
1211
|
|
|
1205
1212
|
def delete(self, timeout: int | None = None, request_context: RequestContext | None = None) -> None:
|
|
1206
1213
|
input_data = {"data": {"id": self.id}}
|
|
@@ -1363,7 +1370,8 @@ class InfrahubNodeSync(InfrahubNodeBase):
|
|
|
1363
1370
|
continue
|
|
1364
1371
|
|
|
1365
1372
|
peer_data: dict[str, Any] = {}
|
|
1366
|
-
|
|
1373
|
+
should_fetch_relationship = prefetch_relationships or (include is not None and rel_name in include)
|
|
1374
|
+
if rel_schema and should_fetch_relationship:
|
|
1367
1375
|
peer_schema = self._client.schema.get(kind=rel_schema.peer, branch=self._branch)
|
|
1368
1376
|
peer_node = InfrahubNodeSync(client=self._client, schema=peer_schema, branch=self._branch)
|
|
1369
1377
|
peer_data = peer_node.generate_query_data_node(include=include, exclude=exclude, property=property)
|
|
@@ -4,7 +4,6 @@ from collections import defaultdict
|
|
|
4
4
|
from collections.abc import Iterable
|
|
5
5
|
from typing import TYPE_CHECKING, Any
|
|
6
6
|
|
|
7
|
-
from ..batch import InfrahubBatch
|
|
8
7
|
from ..exceptions import (
|
|
9
8
|
Error,
|
|
10
9
|
UninitializedError,
|
|
@@ -166,7 +165,7 @@ class RelationshipManager(RelationshipManagerBase):
|
|
|
166
165
|
raise Error("Unable to fetch the peer, id and/or typename are not defined")
|
|
167
166
|
ids_per_kind_map[peer.typename].append(peer.id)
|
|
168
167
|
|
|
169
|
-
batch =
|
|
168
|
+
batch = await self.client.create_batch()
|
|
170
169
|
for kind, ids in ids_per_kind_map.items():
|
|
171
170
|
batch.add(
|
|
172
171
|
task=self.client.filters,
|
|
@@ -289,7 +288,6 @@ class RelationshipManagerSync(RelationshipManagerBase):
|
|
|
289
288
|
raise Error("Unable to fetch the peer, id and/or typename are not defined")
|
|
290
289
|
ids_per_kind_map[peer.typename].append(peer.id)
|
|
291
290
|
|
|
292
|
-
# Unlike Async, no need to create a new batch from scratch because we are not using a semaphore
|
|
293
291
|
batch = self.client.create_batch()
|
|
294
292
|
for kind, ids in ids_per_kind_map.items():
|
|
295
293
|
batch.add(
|
infrahub_sdk/playback.py
CHANGED
|
@@ -56,5 +56,4 @@ class JSONPlayback(BaseSettings):
|
|
|
56
56
|
with Path(f"{self.directory}/{filename}.json").open(encoding="utf-8") as fobj:
|
|
57
57
|
data = ujson.load(fobj)
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
return response
|
|
59
|
+
return httpx.Response(status_code=data["status_code"], content=data["response_content"], request=request)
|
infrahub_sdk/protocols.py
CHANGED
|
@@ -131,6 +131,7 @@ class CoreGenericRepository(CoreNode):
|
|
|
131
131
|
queries: RelationshipManager
|
|
132
132
|
checks: RelationshipManager
|
|
133
133
|
generators: RelationshipManager
|
|
134
|
+
groups_objects: RelationshipManager
|
|
134
135
|
|
|
135
136
|
|
|
136
137
|
class CoreGroup(CoreNode):
|
|
@@ -233,6 +234,10 @@ class CoreWebhook(CoreNode):
|
|
|
233
234
|
validate_certificates: BooleanOptional
|
|
234
235
|
|
|
235
236
|
|
|
237
|
+
class CoreWeightedPoolResource(CoreNode):
|
|
238
|
+
allocation_weight: IntegerOptional
|
|
239
|
+
|
|
240
|
+
|
|
236
241
|
class LineageOwner(CoreNode):
|
|
237
242
|
pass
|
|
238
243
|
|
|
@@ -321,6 +326,7 @@ class CoreCheckDefinition(CoreTaskTarget):
|
|
|
321
326
|
|
|
322
327
|
|
|
323
328
|
class CoreCustomWebhook(CoreWebhook, CoreTaskTarget):
|
|
329
|
+
shared_key: StringOptional
|
|
324
330
|
transformation: RelatedNode
|
|
325
331
|
|
|
326
332
|
|
|
@@ -350,6 +356,10 @@ class CoreGeneratorAction(CoreAction):
|
|
|
350
356
|
generator: RelatedNode
|
|
351
357
|
|
|
352
358
|
|
|
359
|
+
class CoreGeneratorAwareGroup(CoreGroup):
|
|
360
|
+
pass
|
|
361
|
+
|
|
362
|
+
|
|
353
363
|
class CoreGeneratorCheck(CoreCheck):
|
|
354
364
|
instance: String
|
|
355
365
|
|
|
@@ -361,6 +371,8 @@ class CoreGeneratorDefinition(CoreTaskTarget):
|
|
|
361
371
|
file_path: String
|
|
362
372
|
class_name: String
|
|
363
373
|
convert_query_response: BooleanOptional
|
|
374
|
+
execute_in_proposed_change: BooleanOptional
|
|
375
|
+
execute_after_merge: BooleanOptional
|
|
364
376
|
query: RelatedNode
|
|
365
377
|
repository: RelatedNode
|
|
366
378
|
targets: RelatedNode
|
|
@@ -405,12 +417,12 @@ class CoreGraphQLQueryGroup(CoreGroup):
|
|
|
405
417
|
|
|
406
418
|
|
|
407
419
|
class CoreGroupAction(CoreAction):
|
|
408
|
-
|
|
420
|
+
member_action: Dropdown
|
|
409
421
|
group: RelatedNode
|
|
410
422
|
|
|
411
423
|
|
|
412
424
|
class CoreGroupTriggerRule(CoreTriggerRule):
|
|
413
|
-
|
|
425
|
+
member_update: Dropdown
|
|
414
426
|
group: RelatedNode
|
|
415
427
|
|
|
416
428
|
|
|
@@ -442,7 +454,7 @@ class CoreNodeTriggerAttributeMatch(CoreNodeTriggerMatch):
|
|
|
442
454
|
|
|
443
455
|
class CoreNodeTriggerRelationshipMatch(CoreNodeTriggerMatch):
|
|
444
456
|
relationship_name: String
|
|
445
|
-
|
|
457
|
+
modification_type: Dropdown
|
|
446
458
|
peer: StringOptional
|
|
447
459
|
|
|
448
460
|
|
|
@@ -457,6 +469,7 @@ class CoreNumberPool(CoreResourcePool, LineageSource):
|
|
|
457
469
|
node_attribute: String
|
|
458
470
|
start_range: Integer
|
|
459
471
|
end_range: Integer
|
|
472
|
+
pool_type: Enum
|
|
460
473
|
|
|
461
474
|
|
|
462
475
|
class CoreObjectPermission(CoreBasePermission):
|
|
@@ -481,7 +494,10 @@ class CoreProposedChange(CoreTaskTarget):
|
|
|
481
494
|
source_branch: String
|
|
482
495
|
destination_branch: String
|
|
483
496
|
state: Enum
|
|
497
|
+
is_draft: Boolean
|
|
498
|
+
total_comments: IntegerOptional
|
|
484
499
|
approved_by: RelationshipManager
|
|
500
|
+
rejected_by: RelationshipManager
|
|
485
501
|
reviewers: RelationshipManager
|
|
486
502
|
created_by: RelatedNode
|
|
487
503
|
comments: RelationshipManager
|
|
@@ -555,6 +571,14 @@ class InternalAccountToken(CoreNode):
|
|
|
555
571
|
account: RelatedNode
|
|
556
572
|
|
|
557
573
|
|
|
574
|
+
class InternalIPPrefixAvailable(BuiltinIPPrefix):
|
|
575
|
+
pass
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
class InternalIPRangeAvailable(BuiltinIPAddress):
|
|
579
|
+
last_address: IPHost
|
|
580
|
+
|
|
581
|
+
|
|
558
582
|
class InternalRefreshToken(CoreNode):
|
|
559
583
|
expiration: DateTime
|
|
560
584
|
account: RelatedNode
|
|
@@ -664,6 +688,7 @@ class CoreGenericRepositorySync(CoreNodeSync):
|
|
|
664
688
|
queries: RelationshipManagerSync
|
|
665
689
|
checks: RelationshipManagerSync
|
|
666
690
|
generators: RelationshipManagerSync
|
|
691
|
+
groups_objects: RelationshipManagerSync
|
|
667
692
|
|
|
668
693
|
|
|
669
694
|
class CoreGroupSync(CoreNodeSync):
|
|
@@ -766,6 +791,10 @@ class CoreWebhookSync(CoreNodeSync):
|
|
|
766
791
|
validate_certificates: BooleanOptional
|
|
767
792
|
|
|
768
793
|
|
|
794
|
+
class CoreWeightedPoolResourceSync(CoreNodeSync):
|
|
795
|
+
allocation_weight: IntegerOptional
|
|
796
|
+
|
|
797
|
+
|
|
769
798
|
class LineageOwnerSync(CoreNodeSync):
|
|
770
799
|
pass
|
|
771
800
|
|
|
@@ -854,6 +883,7 @@ class CoreCheckDefinitionSync(CoreTaskTargetSync):
|
|
|
854
883
|
|
|
855
884
|
|
|
856
885
|
class CoreCustomWebhookSync(CoreWebhookSync, CoreTaskTargetSync):
|
|
886
|
+
shared_key: StringOptional
|
|
857
887
|
transformation: RelatedNodeSync
|
|
858
888
|
|
|
859
889
|
|
|
@@ -883,6 +913,10 @@ class CoreGeneratorActionSync(CoreActionSync):
|
|
|
883
913
|
generator: RelatedNodeSync
|
|
884
914
|
|
|
885
915
|
|
|
916
|
+
class CoreGeneratorAwareGroupSync(CoreGroupSync):
|
|
917
|
+
pass
|
|
918
|
+
|
|
919
|
+
|
|
886
920
|
class CoreGeneratorCheckSync(CoreCheckSync):
|
|
887
921
|
instance: String
|
|
888
922
|
|
|
@@ -894,6 +928,8 @@ class CoreGeneratorDefinitionSync(CoreTaskTargetSync):
|
|
|
894
928
|
file_path: String
|
|
895
929
|
class_name: String
|
|
896
930
|
convert_query_response: BooleanOptional
|
|
931
|
+
execute_in_proposed_change: BooleanOptional
|
|
932
|
+
execute_after_merge: BooleanOptional
|
|
897
933
|
query: RelatedNodeSync
|
|
898
934
|
repository: RelatedNodeSync
|
|
899
935
|
targets: RelatedNodeSync
|
|
@@ -938,12 +974,12 @@ class CoreGraphQLQueryGroupSync(CoreGroupSync):
|
|
|
938
974
|
|
|
939
975
|
|
|
940
976
|
class CoreGroupActionSync(CoreActionSync):
|
|
941
|
-
|
|
977
|
+
member_action: Dropdown
|
|
942
978
|
group: RelatedNodeSync
|
|
943
979
|
|
|
944
980
|
|
|
945
981
|
class CoreGroupTriggerRuleSync(CoreTriggerRuleSync):
|
|
946
|
-
|
|
982
|
+
member_update: Dropdown
|
|
947
983
|
group: RelatedNodeSync
|
|
948
984
|
|
|
949
985
|
|
|
@@ -975,7 +1011,7 @@ class CoreNodeTriggerAttributeMatchSync(CoreNodeTriggerMatchSync):
|
|
|
975
1011
|
|
|
976
1012
|
class CoreNodeTriggerRelationshipMatchSync(CoreNodeTriggerMatchSync):
|
|
977
1013
|
relationship_name: String
|
|
978
|
-
|
|
1014
|
+
modification_type: Dropdown
|
|
979
1015
|
peer: StringOptional
|
|
980
1016
|
|
|
981
1017
|
|
|
@@ -990,6 +1026,7 @@ class CoreNumberPoolSync(CoreResourcePoolSync, LineageSourceSync):
|
|
|
990
1026
|
node_attribute: String
|
|
991
1027
|
start_range: Integer
|
|
992
1028
|
end_range: Integer
|
|
1029
|
+
pool_type: Enum
|
|
993
1030
|
|
|
994
1031
|
|
|
995
1032
|
class CoreObjectPermissionSync(CoreBasePermissionSync):
|
|
@@ -1014,7 +1051,10 @@ class CoreProposedChangeSync(CoreTaskTargetSync):
|
|
|
1014
1051
|
source_branch: String
|
|
1015
1052
|
destination_branch: String
|
|
1016
1053
|
state: Enum
|
|
1054
|
+
is_draft: Boolean
|
|
1055
|
+
total_comments: IntegerOptional
|
|
1017
1056
|
approved_by: RelationshipManagerSync
|
|
1057
|
+
rejected_by: RelationshipManagerSync
|
|
1018
1058
|
reviewers: RelationshipManagerSync
|
|
1019
1059
|
created_by: RelatedNodeSync
|
|
1020
1060
|
comments: RelationshipManagerSync
|
|
@@ -1088,6 +1128,14 @@ class InternalAccountTokenSync(CoreNodeSync):
|
|
|
1088
1128
|
account: RelatedNodeSync
|
|
1089
1129
|
|
|
1090
1130
|
|
|
1131
|
+
class InternalIPPrefixAvailableSync(BuiltinIPPrefixSync):
|
|
1132
|
+
pass
|
|
1133
|
+
|
|
1134
|
+
|
|
1135
|
+
class InternalIPRangeAvailableSync(BuiltinIPAddressSync):
|
|
1136
|
+
last_address: IPHost
|
|
1137
|
+
|
|
1138
|
+
|
|
1091
1139
|
class InternalRefreshTokenSync(CoreNodeSync):
|
|
1092
1140
|
expiration: DateTime
|
|
1093
1141
|
account: RelatedNodeSync
|
|
@@ -9,7 +9,7 @@ from pytest import exit as exit_test
|
|
|
9
9
|
from .. import InfrahubClientSync
|
|
10
10
|
from ..utils import is_valid_url
|
|
11
11
|
from .loader import InfrahubYamlFile
|
|
12
|
-
from .utils import load_repository_config
|
|
12
|
+
from .utils import find_repository_config_file, load_repository_config
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
def pytest_addoption(parser: Parser) -> None:
|
|
@@ -18,9 +18,9 @@ def pytest_addoption(parser: Parser) -> None:
|
|
|
18
18
|
"--infrahub-repo-config",
|
|
19
19
|
action="store",
|
|
20
20
|
dest="infrahub_repo_config",
|
|
21
|
-
default=
|
|
21
|
+
default=None,
|
|
22
22
|
metavar="INFRAHUB_REPO_CONFIG_FILE",
|
|
23
|
-
help="Infrahub configuration file for the repository (
|
|
23
|
+
help="Infrahub configuration file for the repository (.infrahub.yml or .infrahub.yaml)",
|
|
24
24
|
)
|
|
25
25
|
group.addoption(
|
|
26
26
|
"--infrahub-address",
|
|
@@ -63,7 +63,10 @@ def pytest_addoption(parser: Parser) -> None:
|
|
|
63
63
|
|
|
64
64
|
|
|
65
65
|
def pytest_sessionstart(session: Session) -> None:
|
|
66
|
-
|
|
66
|
+
if session.config.option.infrahub_repo_config:
|
|
67
|
+
session.infrahub_config_path = Path(session.config.option.infrahub_repo_config) # type: ignore[attr-defined]
|
|
68
|
+
else:
|
|
69
|
+
session.infrahub_config_path = find_repository_config_file() # type: ignore[attr-defined]
|
|
67
70
|
|
|
68
71
|
if session.infrahub_config_path.is_file(): # type: ignore[attr-defined]
|
|
69
72
|
session.infrahub_repo_config = load_repository_config(repo_config_file=session.infrahub_config_path) # type: ignore[attr-defined]
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from pathlib import Path
|
|
2
4
|
|
|
3
5
|
import yaml
|
|
@@ -6,7 +8,45 @@ from ..schema.repository import InfrahubRepositoryConfig
|
|
|
6
8
|
from .exceptions import FileNotValidError
|
|
7
9
|
|
|
8
10
|
|
|
11
|
+
def find_repository_config_file(base_path: Path | None = None) -> Path:
|
|
12
|
+
"""Find the repository config file, checking for both .yml and .yaml extensions.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
base_path: Base directory to search in. If None, uses current directory.
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
Path to the config file.
|
|
19
|
+
|
|
20
|
+
Raises:
|
|
21
|
+
FileNotFoundError: If neither .infrahub.yml nor .infrahub.yaml exists.
|
|
22
|
+
"""
|
|
23
|
+
if base_path is None:
|
|
24
|
+
base_path = Path()
|
|
25
|
+
|
|
26
|
+
yml_path = base_path / ".infrahub.yml"
|
|
27
|
+
yaml_path = base_path / ".infrahub.yaml"
|
|
28
|
+
|
|
29
|
+
# Prefer .yml if both exist
|
|
30
|
+
if yml_path.exists():
|
|
31
|
+
return yml_path
|
|
32
|
+
if yaml_path.exists():
|
|
33
|
+
return yaml_path
|
|
34
|
+
# For backward compatibility, return .yml path for error messages
|
|
35
|
+
return yml_path
|
|
36
|
+
|
|
37
|
+
|
|
9
38
|
def load_repository_config(repo_config_file: Path) -> InfrahubRepositoryConfig:
|
|
39
|
+
# If the file doesn't exist, try to find it with alternate extension
|
|
40
|
+
if not repo_config_file.exists():
|
|
41
|
+
if repo_config_file.name == ".infrahub.yml":
|
|
42
|
+
alt_path = repo_config_file.parent / ".infrahub.yaml"
|
|
43
|
+
if alt_path.exists():
|
|
44
|
+
repo_config_file = alt_path
|
|
45
|
+
elif repo_config_file.name == ".infrahub.yaml":
|
|
46
|
+
alt_path = repo_config_file.parent / ".infrahub.yml"
|
|
47
|
+
if alt_path.exists():
|
|
48
|
+
repo_config_file = alt_path
|
|
49
|
+
|
|
10
50
|
if not repo_config_file.is_file():
|
|
11
51
|
raise FileNotFoundError(repo_config_file)
|
|
12
52
|
|
infrahub_sdk/repository.py
CHANGED
infrahub_sdk/schema/__init__.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
+
import inspect
|
|
4
5
|
import json
|
|
5
6
|
import warnings
|
|
6
7
|
from collections.abc import MutableMapping
|
|
@@ -90,6 +91,26 @@ MainSchemaTypesAll: TypeAlias = Union[
|
|
|
90
91
|
]
|
|
91
92
|
|
|
92
93
|
|
|
94
|
+
class SchemaWarningType(Enum):
|
|
95
|
+
DEPRECATION = "deprecation"
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class SchemaWarningKind(BaseModel):
|
|
99
|
+
kind: str = Field(..., description="The kind impacted by the warning")
|
|
100
|
+
field: str | None = Field(default=None, description="The attribute or relationship impacted by the warning")
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def display(self) -> str:
|
|
104
|
+
suffix = f".{self.field}" if self.field else ""
|
|
105
|
+
return f"{self.kind}{suffix}"
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class SchemaWarning(BaseModel):
|
|
109
|
+
type: SchemaWarningType = Field(..., description="The type of warning")
|
|
110
|
+
kinds: list[SchemaWarningKind] = Field(default_factory=list, description="The kinds impacted by the warning")
|
|
111
|
+
message: str = Field(..., description="The message that describes the warning")
|
|
112
|
+
|
|
113
|
+
|
|
93
114
|
class InfrahubSchemaBase:
|
|
94
115
|
client: InfrahubClient | InfrahubClientSync
|
|
95
116
|
cache: dict[str, BranchSchema]
|
|
@@ -169,7 +190,9 @@ class InfrahubSchemaBase:
|
|
|
169
190
|
def _validate_load_schema_response(response: httpx.Response) -> SchemaLoadResponse:
|
|
170
191
|
if response.status_code == httpx.codes.OK:
|
|
171
192
|
status = response.json()
|
|
172
|
-
return SchemaLoadResponse(
|
|
193
|
+
return SchemaLoadResponse(
|
|
194
|
+
hash=status["hash"], previous_hash=status["previous_hash"], warnings=status.get("warnings") or []
|
|
195
|
+
)
|
|
173
196
|
|
|
174
197
|
if response.status_code in [
|
|
175
198
|
httpx.codes.BAD_REQUEST,
|
|
@@ -185,12 +208,16 @@ class InfrahubSchemaBase:
|
|
|
185
208
|
|
|
186
209
|
@staticmethod
|
|
187
210
|
def _get_schema_name(schema: type[SchemaType | SchemaTypeSync] | str) -> str:
|
|
188
|
-
if hasattr(schema, "_is_runtime_protocol") and schema._is_runtime_protocol: # type: ignore[union-attr]
|
|
189
|
-
return schema.__name__ # type: ignore[union-attr]
|
|
190
|
-
|
|
191
211
|
if isinstance(schema, str):
|
|
192
212
|
return schema
|
|
193
213
|
|
|
214
|
+
if hasattr(schema, "_is_runtime_protocol") and getattr(schema, "_is_runtime_protocol", None):
|
|
215
|
+
if inspect.iscoroutinefunction(schema.save):
|
|
216
|
+
return schema.__name__
|
|
217
|
+
if schema.__name__[-4:] == "Sync":
|
|
218
|
+
return schema.__name__[:-4]
|
|
219
|
+
return schema.__name__
|
|
220
|
+
|
|
194
221
|
raise ValueError("schema must be a protocol or a string")
|
|
195
222
|
|
|
196
223
|
@staticmethod
|
|
@@ -474,6 +501,25 @@ class InfrahubSchema(InfrahubSchemaBase):
|
|
|
474
501
|
|
|
475
502
|
return branch_schema.nodes
|
|
476
503
|
|
|
504
|
+
async def get_graphql_schema(self, branch: str | None = None) -> str:
|
|
505
|
+
"""Get the GraphQL schema as a string.
|
|
506
|
+
|
|
507
|
+
Args:
|
|
508
|
+
branch: The branch to get the schema for. Defaults to default_branch.
|
|
509
|
+
|
|
510
|
+
Returns:
|
|
511
|
+
The GraphQL schema as a string.
|
|
512
|
+
"""
|
|
513
|
+
branch = branch or self.client.default_branch
|
|
514
|
+
url = f"{self.client.address}/schema.graphql?branch={branch}"
|
|
515
|
+
|
|
516
|
+
response = await self.client._get(url=url)
|
|
517
|
+
|
|
518
|
+
if response.status_code != 200:
|
|
519
|
+
raise ValueError(f"Failed to fetch GraphQL schema: HTTP {response.status_code} - {response.text}")
|
|
520
|
+
|
|
521
|
+
return response.text
|
|
522
|
+
|
|
477
523
|
async def _fetch(self, branch: str, namespaces: list[str] | None = None) -> BranchSchema:
|
|
478
524
|
url_parts = [("branch", branch)]
|
|
479
525
|
if namespaces:
|
|
@@ -697,6 +743,25 @@ class InfrahubSchemaSync(InfrahubSchemaBase):
|
|
|
697
743
|
|
|
698
744
|
return branch_schema.nodes
|
|
699
745
|
|
|
746
|
+
def get_graphql_schema(self, branch: str | None = None) -> str:
|
|
747
|
+
"""Get the GraphQL schema as a string.
|
|
748
|
+
|
|
749
|
+
Args:
|
|
750
|
+
branch: The branch to get the schema for. Defaults to default_branch.
|
|
751
|
+
|
|
752
|
+
Returns:
|
|
753
|
+
The GraphQL schema as a string.
|
|
754
|
+
"""
|
|
755
|
+
branch = branch or self.client.default_branch
|
|
756
|
+
url = f"{self.client.address}/schema.graphql?branch={branch}"
|
|
757
|
+
|
|
758
|
+
response = self.client._get(url=url)
|
|
759
|
+
|
|
760
|
+
if response.status_code != 200:
|
|
761
|
+
raise ValueError(f"Failed to fetch GraphQL schema: HTTP {response.status_code} - {response.text}")
|
|
762
|
+
|
|
763
|
+
return response.text
|
|
764
|
+
|
|
700
765
|
def _fetch(self, branch: str, namespaces: list[str] | None = None) -> BranchSchema:
|
|
701
766
|
url_parts = [("branch", branch)]
|
|
702
767
|
if namespaces:
|
|
@@ -764,6 +829,7 @@ class SchemaLoadResponse(BaseModel):
|
|
|
764
829
|
hash: str = Field(default="", description="The new hash for the entire schema")
|
|
765
830
|
previous_hash: str = Field(default="", description="The previous hash for the entire schema")
|
|
766
831
|
errors: dict = Field(default_factory=dict, description="Errors reported by the server")
|
|
832
|
+
warnings: list[SchemaWarning] = Field(default_factory=list, description="Warnings reported by the server")
|
|
767
833
|
|
|
768
834
|
@property
|
|
769
835
|
def schema_updated(self) -> bool:
|
infrahub_sdk/schema/main.py
CHANGED
|
@@ -267,6 +267,7 @@ class BaseSchema(BaseModel):
|
|
|
267
267
|
description: str | None = None
|
|
268
268
|
include_in_menu: bool | None = None
|
|
269
269
|
menu_placement: str | None = None
|
|
270
|
+
display_label: str | None = None
|
|
270
271
|
display_labels: list[str] | None = None
|
|
271
272
|
human_friendly_id: list[str] | None = None
|
|
272
273
|
icon: str | None = None
|
|
@@ -96,6 +96,14 @@ class InfrahubGeneratorDefinitionConfig(InfrahubRepositoryConfigElement):
|
|
|
96
96
|
default=False,
|
|
97
97
|
description="Decide if the generator should convert the result of the GraphQL query to SDK InfrahubNode objects.",
|
|
98
98
|
)
|
|
99
|
+
execute_in_proposed_change: bool = Field(
|
|
100
|
+
default=True,
|
|
101
|
+
description="Decide if the generator should execute in a proposed change.",
|
|
102
|
+
)
|
|
103
|
+
execute_after_merge: bool = Field(
|
|
104
|
+
default=True,
|
|
105
|
+
description="Decide if the generator should execute after a merge.",
|
|
106
|
+
)
|
|
99
107
|
|
|
100
108
|
def load_class(self, import_root: str | None = None, relative_path: str | None = None) -> type[InfrahubGenerator]:
|
|
101
109
|
module = import_module(module_path=self.file_path, import_root=import_root, relative_path=relative_path)
|