diff --git a/Dockerfile b/Dockerfile index 17546c6..5c7c4f2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -90,6 +90,7 @@ ENV BUILDAH_ISOLATION=chroot ENV VIRTUAL_ENV=/venv ENV PATH="$VIRTUAL_ENV/bin:$PATH" +ENV PULUMI_SKIP_UPDATE_CHECK=true USER $USER diff --git a/cloudbender/cli.py b/cloudbender/cli.py index 4136db0..54edfc9 100644 --- a/cloudbender/cli.py +++ b/cloudbender/cli.py @@ -8,8 +8,9 @@ from concurrent.futures import ThreadPoolExecutor, as_completed from . import __version__ from .core import CloudBender -from .utils import setup_logging +from .utils import setup_logging, get_docker_version from .exceptions import InvalidProjectDir +from .pulumi import get_pulumi_version import logging @@ -17,7 +18,6 @@ logger = logging.getLogger(__name__) @click.group() -@click.version_option(version=__version__, prog_name="CloudBender") @click.option("--debug", is_flag=True, help="Turn on debug logging.") @click.option("--dir", "directory", help="Specify cloudbender project directory.") @click.pass_context @@ -46,6 +46,30 @@ def cli(ctx, debug, directory): ctx.obj = cb +@click.command() +@click.pass_obj +def version(cb): + """Displays own version and all dependencies""" + logger.error(f"CloudBender: {__version__}") + + # Pulumi + # import pdb;pdb.set_trace() + pulumi_version = get_pulumi_version() + if not pulumi_version: + logger.error( + "Pulumi: Error calling pulumi, see https://www.pulumi.com/docs/get-started/install/" + ) + else: + logger.error(f"Pulumi: {pulumi_version}") + + # Docker / podman + docker_version = get_docker_version() + if not docker_version: + logger.error("Podman/Docker: Cannot call podman nor docker") + else: + logger.error(f"Podman/Docker: {docker_version}") + + @click.command() @click.argument("stack_names", nargs=-1) @click.option("--multi", is_flag=True, help="Allow more than one stack to match") @@ -153,7 +177,7 @@ def refresh(cb, stack_name): @click.command() @click.argument("stack_name") @click.argument("function", default="") -@click.argument('args', nargs=-1) +@click.argument("args", nargs=-1) @click.option( "--listall", is_flag=True, @@ -168,7 +192,9 @@ def execute(cb, stack_name, function, args, listall=False): if s.mode == "pulumi": s.execute(function, args, listall) else: - logger.info("{} uses Cloudformation, no exec feature available.".format(s.stackname)) + logger.info( + "{} uses Cloudformation, no exec feature available.".format(s.stackname) + ) @click.command() @@ -415,6 +441,7 @@ def _provision(cb, stacks): future.result() +cli.add_command(version) cli.add_command(render) cli.add_command(sync) cli.add_command(validate) diff --git a/cloudbender/jinja.py b/cloudbender/jinja.py index 9035b9e..097e36a 100644 --- a/cloudbender/jinja.py +++ b/cloudbender/jinja.py @@ -150,7 +150,7 @@ def gz_pack(source): out = "" # Preserve shebangs (don't care about encodings for this) first_line = source.split("\n")[0] - if re.compile('^#!.*$').match(first_line): + if re.compile("^#!.*$").match(first_line): if first_line.rstrip().endswith("python"): first_line = first_line.rstrip() first_line += "3" diff --git a/cloudbender/pulumi.py b/cloudbender/pulumi.py index 0552fc6..c05b2c4 100644 --- a/cloudbender/pulumi.py +++ b/cloudbender/pulumi.py @@ -5,17 +5,30 @@ import shutil import tempfile import importlib import pulumi +import subprocess + from functools import wraps import logging logger = logging.getLogger(__name__) -# Fail early if pulumi binaries are not available -if not shutil.which("pulumi"): - raise FileNotFoundError( - "Cannot find pulumi binary, see https://www.pulumi.com/docs/get-started/install/" +# Disable Pulumis version check globally +os.environ["PULUMI_SKIP_UPDATE_CHECK"] = "true" + + +def get_pulumi_version(): + p = shutil.which("pulumi") + if not p: + return None + + proc = subprocess.Popen( + [p, "version"], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL ) + if not proc.returncode: + return proc.communicate()[0].decode().strip() + else: + return None def pulumi_ws(func): @@ -36,7 +49,9 @@ def pulumi_ws(func): _found = False try: _stack = importlib.import_module( - "config.{}.{}".format(self.rel_path, self.template).replace("/", ".") + "config.{}.{}".format(self.rel_path, self.template).replace( + "/", "." + ) ) _found = True @@ -45,7 +60,9 @@ def pulumi_ws(func): try: spec = importlib.util.spec_from_file_location( "_stack", - "{}/pulumi/{}.py".format(artifacts_path.resolve(), self.template), + "{}/pulumi/{}.py".format( + artifacts_path.resolve(), self.template + ), ) _stack = importlib.util.module_from_spec(spec) spec.loader.exec_module(_stack) @@ -66,7 +83,9 @@ def pulumi_ws(func): project_name = self.parameters["Conglomerate"] # Remove stacknameprefix if equals Conglomerate as Pulumi implicitly prefixes project_name - self.pulumi_stackname = re.sub(r"^" + project_name + "-?", "", self.stackname) + self.pulumi_stackname = re.sub( + r"^" + project_name + "-?", "", self.stackname + ) try: pulumi_backend = "{}/{}/{}".format( self.pulumi["backend"], project_name, self.region @@ -111,7 +130,9 @@ def pulumi_ws(func): secrets_provider == "passphrase" and "PULUMI_CONFIG_PASSPHRASE" not in os.environ ): - raise ValueError("Missing PULUMI_CONFIG_PASSPHRASE environment variable!") + raise ValueError( + "Missing PULUMI_CONFIG_PASSPHRASE environment variable!" + ) else: try: @@ -136,20 +157,22 @@ def pulumi_ws(func): ) _tags["zdt:cloudbender.owner"] = f"{project_name}.{self.pulumi_stackname}" - self.pulumi_config.update({ - "aws:region": self.region, - "aws:defaultTags": {"tags": _tags}, - "zdt:region": self.region, - "zdt:awsAccountId": account_id, - "zdt:projectName": project_name, - "zdt:stackName": self.pulumi_stackname - }) + self.pulumi_config.update( + { + "aws:region": self.region, + "aws:defaultTags": {"tags": _tags}, + "zdt:region": self.region, + "zdt:awsAccountId": account_id, + "zdt:projectName": project_name, + "zdt:stackName": self.pulumi_stackname, + } + ) # inject all parameters as config in the namespace for p in self.parameters: - self.pulumi_config["{}:{}".format(self.parameters["Conglomerate"], p)] = self.parameters[ - p - ] + self.pulumi_config[ + "{}:{}".format(self.parameters["Conglomerate"], p) + ] = self.parameters[p] stack_settings = pulumi.automation.StackSettings( config=self.pulumi_config, diff --git a/cloudbender/stack.py b/cloudbender/stack.py index 20bd8e3..af91d5f 100644 --- a/cloudbender/stack.py +++ b/cloudbender/stack.py @@ -877,7 +877,9 @@ class Stack(object): if exec_function in vars(self._pulumi_code): pulumi_stack = self._get_pulumi_stack() vars(self._pulumi_code)[exec_function]( - config=pulumi_stack.get_all_config(), outputs=pulumi_stack.outputs(), args=args + config=pulumi_stack.get_all_config(), + outputs=pulumi_stack.outputs(), + args=args, ) else: diff --git a/cloudbender/utils.py b/cloudbender/utils.py index 5bdff59..f30d880 100644 --- a/cloudbender/utils.py +++ b/cloudbender/utils.py @@ -2,6 +2,24 @@ import os import copy import logging import re +import shutil +import subprocess + + +def get_docker_version(): + p = shutil.which("podman") + if not p: + p = shutil.which("docker") + if not p: + return None + + proc = subprocess.Popen( + [p, "--version"], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL + ) + if not proc.returncode: + return proc.communicate()[0].decode().strip() + else: + return None def dict_merge(a, b):