diff --git a/.github/workflows/test-cally.yaml b/.github/workflows/test-cally.yaml index 338ddc7..479ff99 100644 --- a/.github/workflows/test-cally.yaml +++ b/.github/workflows/test-cally.yaml @@ -13,6 +13,8 @@ jobs: python: ["3.11"] steps: - uses: actions/checkout@v4 + + # Cally Testing - name: Setup Python uses: actions/setup-python@v5 with: @@ -20,5 +22,28 @@ jobs: cache: pip - name: Install Cally test dependencies run: pip install .[test] + + # Provider Packages + - name: Restore Provider Packages + id: cache-providers + uses: actions/cache@v4 + with: + path: build/random/dist/CallyProvidersRandom-3.6.0.tar.gz + key: cally-provider-random-3.6.0 + - uses: actions/setup-node@v4 + if: steps.cache-providers.outputs.cache-hit != 'true' + with: + node-version: "20" + - name: Install cdktf-cli and build + if: steps.cache-providers.outputs.cache-hit != 'true' + run: | + npm install cdktf-cli + echo "$(pwd)/node_modules/.bin/" >> $GITHUB_PATH + cally provider build --provider random --version 3.6.0 + (cd build/random && python -m build) + - name: Install Provider Pacakge + run: pip install build/random/dist/CallyProvidersRandom-3.6.0.tar.gz + + # Run Tests - name: Run Pytest run: pytest -v diff --git a/pyproject.toml b/pyproject.toml index bc1c7d2..96236fd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,6 +20,7 @@ license = {file = "LICENSE.md"} [project.optional-dependencies] development = [ "black", + "build", "coverage", "isort", "mypy", @@ -32,6 +33,7 @@ development = [ ] test = [ "black", + "build", "coverage", "mypy", "pytest", diff --git a/src/cally/cdk/__init__.py b/src/cally/cdk/__init__.py index 858146a..8abab21 100644 --- a/src/cally/cdk/__init__.py +++ b/src/cally/cdk/__init__.py @@ -2,9 +2,7 @@ from copy import deepcopy from dataclasses import dataclass, make_dataclass from importlib import import_module -from typing import Any, List - -from constructs import Construct +from typing import TYPE_CHECKING, Any, List from cdktf import ( App, @@ -13,6 +11,10 @@ TerraformResource, TerraformStack, ) +from constructs import Construct + +if TYPE_CHECKING: + from cally.cli.config.types import CallyStackService @dataclass @@ -68,10 +70,10 @@ def construct_resource(self, scope: Construct, provider: TerraformProvider) -> N class CallyStack: _resources: List[CallyResource] - name: str + service: 'CallyStackService' - def __init__(self, name: str) -> None: - self.name = name + def __init__(self, service: 'CallyStackService') -> None: + self.service = service def add_resource(self, resource: CallyResource) -> None: self.resources.append(resource) @@ -79,6 +81,14 @@ def add_resource(self, resource: CallyResource) -> None: def add_resources(self, resources: List[CallyResource]) -> None: self.resources.extend(resources) + @property + def name(self) -> str: + return self.service.name + + @property + def environment(self) -> str: + return self.service.environment + @property def resources(self) -> List[CallyResource]: if getattr(self, '_resources', None) is None: diff --git a/src/cally/cli/commands/tf.py b/src/cally/cli/commands/tf.py index 43b9556..18fe5a8 100644 --- a/src/cally/cli/commands/tf.py +++ b/src/cally/cli/commands/tf.py @@ -1,5 +1,6 @@ import click +from ..config.types import CallyStackService from ..tools import terraform @@ -9,10 +10,12 @@ def tf() -> None: @click.command(name='print') +# TODO: Load this by default from config @click.option('--stack-name') @click.option('--stack-type') def print_template(stack_name: str, stack_type: str): - with terraform.Action(stack_name=stack_name, stack_type=stack_type) as action: + service = CallyStackService(name=stack_name, environment='test') + with terraform.Action(stack_type=stack_type, service=service) as action: click.secho(action.print()) diff --git a/src/cally/cli/config/__init__.py b/src/cally/cli/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/cally/cli/config/types.py b/src/cally/cli/config/types.py new file mode 100644 index 0000000..a683270 --- /dev/null +++ b/src/cally/cli/config/types.py @@ -0,0 +1,20 @@ +from dataclasses import dataclass, field +from typing import Any, Optional + + +@dataclass +class CallyService: + name: str + environment: str + + +@dataclass +class CallyStackService(CallyService): + providers: dict = field(default_factory=dict) + stack_vars: dict = field(default_factory=dict) + + def get_provider_vars(self, provider: str) -> dict: + return self.providers.get(provider, {}) + + def get_stack_var(self, var: str, default: Optional[Any] = None) -> Any: + return self.stack_vars.get(var, default) diff --git a/src/cally/cli/tools/terraform.py b/src/cally/cli/tools/terraform.py index e93509a..a94327c 100644 --- a/src/cally/cli/tools/terraform.py +++ b/src/cally/cli/tools/terraform.py @@ -6,16 +6,18 @@ from cally.cdk import stacks +from ..config.types import CallyStackService + class Action: _cwd: Path _tmp_dir: TemporaryDirectory - stack_name: str stack_type: str + service: CallyStackService - def __init__(self, stack_name: str, stack_type: str) -> None: - self.stack_name = stack_name + def __init__(self, stack_type: str, service: CallyStackService) -> None: self.stack_type = stack_type + self.service = service def __enter__(self) -> 'Action': self._tmp_dir = TemporaryDirectory() @@ -37,7 +39,7 @@ def tmp_dir(self) -> str: @property def output_path(self) -> Path: - return Path(self.tmp_dir, 'stacks', self.stack_name) + return Path(self.tmp_dir, 'stacks', self.service.name) @property def output_file(self) -> Path: @@ -48,7 +50,7 @@ def synth_stack( ) -> None: # TODO: fix typing here cls = getattr(stacks, self.stack_type) - cls(self.stack_name).synth_stack(self.tmp_dir) + cls(self.service).synth_stack(self.tmp_dir) def print(self) -> str: self.synth_stack() diff --git a/tests/__init__.py b/tests/__init__.py index 5657498..38361df 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -5,6 +5,7 @@ from unittest import TestCase, mock from cally.cdk import CallyStack +from cally.cli.config.types import CallyStackService class CallyTestHarness(TestCase): @@ -43,6 +44,10 @@ def load_json_file(self, filename) -> dict: class CallyTfTestHarness(CallyTestHarness): + def setUp(self): + super().setUp() + self.empty_service = CallyStackService(name='test', environment='cally') + def synth_stack(self, stack: CallyStack) -> dict: stack.synth_stack(self.working.name) output_file = Path(self.working.name, 'stacks', stack.name, 'cdk.tf.json') diff --git a/tests/cli/config/__init__.py b/tests/cli/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/cli/config/test_types.py b/tests/cli/config/test_types.py new file mode 100644 index 0000000..199290b --- /dev/null +++ b/tests/cli/config/test_types.py @@ -0,0 +1,36 @@ +from cally.cli.config.types import CallyService, CallyStackService + +from .. import CallyTestHarness + + +class TestCallyService(CallyTestHarness): + def test_name(self): + service = CallyService(name='snoopy', environment='yard') + self.assertEqual(service.name, 'snoopy') + + def test_environment(self): + service = CallyService(name='snoopy', environment='yard') + self.assertEqual(service.name, 'snoopy') + + +class TestCallyStackService(CallyTestHarness): + def test_provider_empty(self): + service = CallyStackService(name='snoopy', environment='yard') + self.assertDictEqual(service.get_provider_vars('test'), {}) + + def test_get_provider_vars(self): + providers = {'test': {'foo': 'bar', 'bar': 'foo'}, 'another': {'bar': 'foo'}} + service = CallyStackService( + name='snoopy', environment='yard', providers=providers + ) + self.assertDictEqual( + service.get_provider_vars('test'), {'foo': 'bar', 'bar': 'foo'} + ) + + def test_stack_var_empty(self): + service = CallyStackService(name='snoopy', environment='yard') + self.assertIsNone(service.get_stack_var('test')) + + def test_stack_var_default(self): + service = CallyStackService(name='snoopy', environment='yard') + self.assertEqual(service.get_stack_var('test', 'charlie'), 'charlie') diff --git a/tests/cli/test_tf.py b/tests/cli/test_tf.py index d78e131..f54f55a 100644 --- a/tests/cli/test_tf.py +++ b/tests/cli/test_tf.py @@ -1,7 +1,9 @@ import json -from cally.cli.commands.tf import tf from click.testing import CliRunner + +from cally.cli.commands.tf import tf + from .. import CallyTestHarness diff --git a/tests/stacks/test_cally.py b/tests/stacks/test_cally.py index 43d6414..d876560 100644 --- a/tests/stacks/test_cally.py +++ b/tests/stacks/test_cally.py @@ -6,6 +6,6 @@ class CallyStackTests(CallyTfTestHarness): def test_empty_synth(self): - stack = CallyStack('test') + stack = CallyStack(service=self.empty_service) result = self.synth_stack(stack) self.assertDictEqual(result, self.load_json_file('cdktf/empty_synth.json')) diff --git a/tests/stacks/test_idp.py b/tests/stacks/test_idp.py index 998a6d2..430d4ec 100644 --- a/tests/stacks/test_idp.py +++ b/tests/stacks/test_idp.py @@ -18,6 +18,6 @@ def test_class_load(self): self.assertEqual(stacks.MinimalStack.__name__, 'MinimalStack') def test_empty_synth(self): - stack = stacks.MinimalStack('test') + stack = stacks.MinimalStack(service=self.empty_service) result = self.synth_stack(stack) self.assertDictEqual(result, self.load_json_file('cdktf/empty_synth.json'))