terranova/commands/helpers.py (71 lines of code) (raw):

# # Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one # or more contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright # ownership. Elasticsearch B.V. licenses this file to you under # the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # import os from pathlib import Path import click from click import Parameter from click.exceptions import Exit from overrides import override from sh import ErrorReturnCode from terranova.binds import Terraform from terranova.exceptions import InvalidResourcesError, ManifestError from terranova.resources import Resource, ResourcesFinder, ResourcesManifest, Selector from terranova.utils import Constants, Log, SharedContext class SelectorType(click.ParamType): """Selector param typing for click.""" name = "selector" @override def convert( self, value, param: Parameter | None, ctx: click.Context | None ) -> Selector: if not isinstance(value, str): self.fail(f"{value!r} isn't a valid selector", param, ctx) data = value.split("=", maxsplit=1) return Selector(name=data[0], value=None if len(data) == 1 else data[1]) def read_manifest(path: Path) -> "ResourcesManifest": """ Read the resources manifest if possible. This function handle errors by logging and exiting. Args: path: path to manifest directory. Returns: the manifest. """ try: return ResourcesManifest.from_file(path / Constants.MANIFEST_FILE_NAME) except ManifestError as err: Log.fatal("read manifest", err) def discover_resources( path: Path, selectors: list[Selector] | None = None ) -> list[Resource]: """ Discover resources in every terraform configuration files. This function handle errors by logging and exiting. Args: path: path to resources directory. selectors: list of selectors. Returns: list of resources. """ try: return ResourcesFinder.find_in_dir(path, selectors) except InvalidResourcesError as err: Log.fatal( f"discover resources at `{path.as_posix()}`", err, ) def find_all_resource_dirs(resources_dir: Path) -> list[tuple[Path, str]]: """ Find all path where there is a resource manifest. Returns: list of all path. """ paths: list[tuple[Path, str]] = [] resources_dir_path = SharedContext.resources_dir().as_posix() resources_dir_prefix_len = len(resources_dir_path) + 1 for path, _, files in os.walk(resources_dir): for file in files: if os.path.basename(file) == Constants.MANIFEST_FILE_NAME: paths.append((Path(path), path[resources_dir_prefix_len:])) return paths def resource_dirs(path: str | None) -> list[tuple[Path, str]]: """ List of all resource dirs to interact with. Args: path: use a specific path. Returns: list of all resource dirs. """ resources_dir = SharedContext.resources_dir() if path: resources_dir = resources_dir.joinpath(path) return find_all_resource_dirs(resources_dir) def mount_context( full_path: Path, manifest: ResourcesManifest | None = None, import_vars: bool = False, ) -> Terraform: """Mount the terraform context by importing variables if needed.""" # Ensure manifest exists and can be read if not manifest: manifest = read_manifest(full_path) # Import variables variables: dict[str, str] | None = None if manifest.imports and import_vars: variables = {} for importer in manifest.imports: target = importer.target if importer.target else importer.resource variables[target] = extract_output_var(importer.source, importer.resource) return Terraform(full_path, variables) def extract_output_var(path: str, name: str) -> str: """Show output values from your root module.""" # Construct resources path full_path = SharedContext.resources_dir().joinpath(path) # Mount terraform context terraform = mount_context(full_path) # Execute output command try: return terraform.output(name) except ErrorReturnCode as err: raise Exit(code=err.exit_code) from err