Skip to content

Commit edb5991

Browse files
authored
Merge pull request #136 from opsmill/develop
Merge develop into stable
2 parents a5a134b + 4585ce4 commit edb5991

21 files changed

+238
-115
lines changed

changelog/+479a6128.removed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Removed previously deprecated InfrahubTransform.init() method

changelog/+89d1d0b7.deprecated.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Marked InfrahubCheck.init() as deprecated and scheduled to be removed in Infrahub SDK 2.0.0

changelog/27.fixed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix generated GraphQL query when having a relationship to a pool node

changelog/81.fixed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
CTL: Fix support for relative imports for transforms and generators

infrahub_sdk/_importer.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,26 @@
22

33
import importlib
44
import sys
5+
from pathlib import Path
56
from typing import TYPE_CHECKING, Optional
67

78
from .exceptions import ModuleImportError
89

910
if TYPE_CHECKING:
10-
from pathlib import Path
1111
from types import ModuleType
1212

13+
module_mtime_cache: dict[str, float] = {}
14+
1315

1416
def import_module(
1517
module_path: Path, import_root: Optional[str] = None, relative_path: Optional[str] = None
1618
) -> ModuleType:
1719
import_root = import_root or str(module_path.parent)
1820

21+
file_on_disk = module_path
22+
if import_root and relative_path:
23+
file_on_disk = Path(import_root, relative_path, module_path.name)
24+
1925
if import_root not in sys.path:
2026
sys.path.append(import_root)
2127

@@ -25,7 +31,21 @@ def import_module(
2531
module_name = relative_path.replace("/", ".") + f".{module_name}"
2632

2733
try:
28-
module = importlib.import_module(module_name)
34+
if module_name in sys.modules:
35+
module = sys.modules[module_name]
36+
current_mtime = file_on_disk.stat().st_mtime
37+
38+
if module_name in module_mtime_cache:
39+
last_mtime = module_mtime_cache[module_name]
40+
if current_mtime == last_mtime:
41+
return module
42+
43+
module_mtime_cache[module_name] = current_mtime
44+
module = importlib.reload(module)
45+
else:
46+
module = importlib.import_module(module_name)
47+
module_mtime_cache[module_name] = file_on_disk.stat().st_mtime
48+
2949
except ModuleNotFoundError as exc:
3050
raise ModuleImportError(message=f"{exc!s} ({module_path})") from exc
3151
except SyntaxError as exc:

infrahub_sdk/checks.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,26 @@
33
import asyncio
44
import importlib
55
import os
6+
import warnings
67
from abc import abstractmethod
78
from typing import TYPE_CHECKING, Any, Optional
89

910
import ujson
1011
from git.repo import Repo
1112
from pydantic import BaseModel, Field
1213

13-
from . import InfrahubClient
1414
from .exceptions import InfrahubCheckNotFoundError, UninitializedError
1515

1616
if TYPE_CHECKING:
1717
from pathlib import Path
1818

19+
from . import InfrahubClient
1920
from .schema import InfrahubCheckDefinitionConfig
2021

2122
INFRAHUB_CHECK_VARIABLE_TO_IMPORT = "INFRAHUB_CHECKS"
2223

24+
_client_class = "InfrahubClient"
25+
2326

2427
class InfrahubCheckInitializer(BaseModel):
2528
"""Information about the originator of the check."""
@@ -81,11 +84,17 @@ def client(self, value: InfrahubClient) -> None:
8184
@classmethod
8285
async def init(cls, client: Optional[InfrahubClient] = None, *args: Any, **kwargs: Any) -> InfrahubCheck:
8386
"""Async init method, If an existing InfrahubClient client hasn't been provided, one will be created automatically."""
84-
85-
instance = cls(*args, **kwargs)
86-
instance.client = client or InfrahubClient()
87-
88-
return instance
87+
warnings.warn(
88+
"InfrahubCheck.init has been deprecated and will be removed in the version in Infrahub SDK 2.0.0",
89+
DeprecationWarning,
90+
stacklevel=1,
91+
)
92+
if not client:
93+
client_module = importlib.import_module("infrahub_sdk.client")
94+
client_class = getattr(client_module, _client_class)
95+
client = client_class()
96+
kwargs["client"] = client
97+
return cls(*args, **kwargs)
8998

9099
@property
91100
def errors(self) -> list[dict[str, Any]]:

infrahub_sdk/ctl/check.py

Lines changed: 13 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
import importlib
21
import logging
32
import sys
43
from asyncio import run as aiorun
54
from dataclasses import dataclass
65
from pathlib import Path
7-
from types import ModuleType
8-
from typing import Optional
6+
from typing import Optional, Type
97

108
import typer
119
from rich.console import Console
@@ -18,6 +16,7 @@
1816
from ..ctl.exceptions import QueryNotFoundError
1917
from ..ctl.repository import get_repository_config
2018
from ..ctl.utils import catch_exception, execute_graphql_query
19+
from ..exceptions import ModuleImportError
2120
from ..schema import InfrahubCheckDefinitionConfig, InfrahubRepositoryConfig
2221

2322
app = typer.Typer()
@@ -27,12 +26,9 @@
2726
@dataclass
2827
class CheckModule:
2928
name: str
30-
module: ModuleType
29+
check_class: Type[InfrahubCheck]
3130
definition: InfrahubCheckDefinitionConfig
3231

33-
def get_check(self) -> InfrahubCheck:
34-
return getattr(self.module, self.definition.class_name)
35-
3632

3733
@app.callback()
3834
def callback() -> None:
@@ -67,11 +63,7 @@ def run(
6763

6864
check_definitions = repository_config.check_definitions
6965
if name:
70-
check_definitions = [check for check in repository_config.check_definitions if check.name == name] # pylint: disable=not-an-iterable
71-
if not check_definitions:
72-
console.print(f"[red]Unable to find requested transform: {name}")
73-
list_checks(repository_config=repository_config)
74-
return
66+
check_definitions = [repository_config.get_check_definition(name=name)]
7567

7668
check_modules = get_modules(check_definitions=check_definitions)
7769
aiorun(
@@ -99,8 +91,8 @@ async def run_check(
9991
output = "stdout" if format_json else None
10092
log = logging.getLogger("infrahub")
10193
passed = True
102-
check_class = check_module.get_check()
103-
check = await check_class.init(client=client, params=params, output=output, root_directory=path, branch=branch)
94+
check_class = check_module.check_class
95+
check = check_class(client=client, params=params, output=output, root_directory=path, branch=branch)
10496
param_log = f" - {params}" if params else ""
10597
try:
10698
data = execute_graphql_query(
@@ -231,25 +223,19 @@ async def run_checks(
231223

232224

233225
def get_modules(check_definitions: list[InfrahubCheckDefinitionConfig]) -> list[CheckModule]:
234-
log = logging.getLogger("infrahub")
235226
modules = []
236227
for check_definition in check_definitions:
237-
directory_name = str(check_definition.file_path.parent)
238228
module_name = check_definition.file_path.stem
239229

240-
if directory_name not in sys.path:
241-
sys.path.append(directory_name)
230+
relative_path = str(check_definition.file_path.parent) if check_definition.file_path.parent != Path() else None
242231

243232
try:
244-
module = importlib.import_module(module_name)
245-
except ModuleNotFoundError:
246-
log.error(f"Unable to load {check_definition.file_path}")
247-
continue
248-
249-
if check_definition.class_name not in dir(module):
250-
log.error(f"{check_definition.class_name} class not found in {check_definition.file_path}")
251-
continue
252-
modules.append(CheckModule(name=module_name, module=module, definition=check_definition))
233+
check_class = check_definition.load_class(import_root=str(Path.cwd()), relative_path=relative_path)
234+
except ModuleImportError as exc:
235+
console.print(f"[red]{exc.message}")
236+
raise typer.Exit(1) from exc
237+
238+
modules.append(CheckModule(name=module_name, check_class=check_class, definition=check_definition))
253239

254240
return modules
255241

infrahub_sdk/ctl/cli_commands.py

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,13 @@
3636
parse_cli_vars,
3737
)
3838
from ..ctl.validate import app as validate_app
39-
from ..exceptions import GraphQLError, InfrahubTransformNotFoundError
39+
from ..exceptions import GraphQLError, ModuleImportError
4040
from ..jinja2 import identify_faulty_jinja_code
4141
from ..schema import (
4242
InfrahubRepositoryConfig,
4343
MainSchemaTypes,
4444
SchemaRoot,
4545
)
46-
from ..transforms import get_transform_class_instance
4746
from ..utils import get_branch, write_to_file
4847
from ..yaml import SchemaFile
4948
from .exporter import dump
@@ -322,32 +321,22 @@ def transform(
322321
list_transforms(config=repository_config)
323322
return
324323

325-
# Load transform config
326-
try:
327-
matched = [transform for transform in repository_config.python_transforms if transform.name == transform_name] # pylint: disable=not-an-iterable
328-
if not matched:
329-
raise ValueError(f"{transform_name} does not exist")
330-
except ValueError as exc:
331-
console.print(f"[red]Unable to find requested transform: {transform_name}")
332-
list_transforms(config=repository_config)
333-
raise typer.Exit(1) from exc
334-
335-
transform_config = matched[0]
324+
transform_config = repository_config.get_python_transform(name=transform_name)
336325

337326
# Get client
338327
client = initialize_client(branch=branch)
339328

340329
# Get python transform class instance
330+
331+
relative_path = str(transform_config.file_path.parent) if transform_config.file_path.parent != Path() else None
332+
341333
try:
342-
transform = get_transform_class_instance(
343-
transform_config=transform_config,
344-
branch=branch,
345-
client=client,
346-
)
347-
except InfrahubTransformNotFoundError as exc:
348-
console.print(f"Unable to load {transform_name} from python_transforms")
334+
transform_class = transform_config.load_class(import_root=str(Path.cwd()), relative_path=relative_path)
335+
except ModuleImportError as exc:
336+
console.print(f"[red]{exc.message}")
349337
raise typer.Exit(1) from exc
350338

339+
transform = transform_class(client=client, branch=branch)
351340
# Get data
352341
query_str = repository_config.get_query(name=transform.query).load_query()
353342
data = asyncio.run(

infrahub_sdk/ctl/generator.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
from pathlib import Path
22
from typing import Optional
33

4+
import typer
45
from rich.console import Console
56

67
from ..ctl import config
78
from ..ctl.client import initialize_client
89
from ..ctl.repository import get_repository_config
910
from ..ctl.utils import execute_graphql_query, parse_cli_vars
11+
from ..exceptions import ModuleImportError
1012
from ..node import InfrahubNode
1113
from ..schema import InfrahubRepositoryConfig
1214

@@ -18,24 +20,25 @@ async def run(
1820
list_available: bool,
1921
branch: Optional[str] = None,
2022
variables: Optional[list[str]] = None,
21-
): # pylint: disable=unused-argument
23+
) -> None: # pylint: disable=unused-argument
2224
repository_config = get_repository_config(Path(config.INFRAHUB_REPO_CONFIG_FILE))
2325

2426
if list_available:
2527
list_generators(repository_config=repository_config)
2628
return
2729

28-
matched = [generator for generator in repository_config.generator_definitions if generator.name == generator_name] # pylint: disable=not-an-iterable
30+
generator_config = repository_config.get_generator_definition(name=generator_name)
2931

3032
console = Console()
3133

32-
if not matched:
33-
console.print(f"[red]Unable to find requested generator: {generator_name}")
34-
list_generators(repository_config=repository_config)
35-
return
34+
relative_path = str(generator_config.file_path.parent) if generator_config.file_path.parent != Path() else None
35+
36+
try:
37+
generator_class = generator_config.load_class(import_root=str(Path.cwd()), relative_path=relative_path)
38+
except ModuleImportError as exc:
39+
console.print(f"[red]{exc.message}")
40+
raise typer.Exit(1) from exc
3641

37-
generator_config = matched[0]
38-
generator_class = generator_config.load_class()
3942
variables_dict = parse_cli_vars(variables)
4043

4144
param_key = list(generator_config.parameters.keys())
@@ -69,6 +72,13 @@ async def run(
6972
kind="CoreGroup", branch=branch, include=["members"], name__value=generator_config.targets
7073
)
7174
await targets.members.fetch()
75+
76+
if not targets.members.peers:
77+
console.print(
78+
f"[red]No members found within '{generator_config.targets}', not running generator '{generator_name}'"
79+
)
80+
return
81+
7282
for member in targets.members.peers:
7383
check_parameter = {}
7484
if identifier:

infrahub_sdk/ctl/utils.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
Error,
2121
GraphQLError,
2222
NodeNotFoundError,
23+
ResourceNotDefinedError,
2324
SchemaNotFoundError,
2425
ServerNotReachableError,
2526
ServerNotResponsiveError,
@@ -59,7 +60,7 @@ def handle_exception(exc: Exception, console: Console, exit_code: int) -> NoRetu
5960
if isinstance(exc, GraphQLError):
6061
print_graphql_errors(console=console, errors=exc.errors)
6162
raise typer.Exit(code=exit_code)
62-
if isinstance(exc, (SchemaNotFoundError, NodeNotFoundError)):
63+
if isinstance(exc, (SchemaNotFoundError, NodeNotFoundError, ResourceNotDefinedError)):
6364
console.print(f"[red]Error: {exc!s}")
6465
raise typer.Exit(code=exit_code)
6566

0 commit comments

Comments
 (0)