Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

setup(
name='stackql-deploy',
version='1.8.6',
version='1.8.7',
description='Model driven resource provisioning and deployment framework using StackQL.',
long_description=readme,
long_description_content_type='text/x-rst',
Expand Down
2 changes: 1 addition & 1 deletion stackql_deploy/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '1.8.6'
__version__ = '1.8.7'
22 changes: 5 additions & 17 deletions stackql_deploy/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
import os
import sys
import subprocess

from . import __version__ as deploy_version
from .lib.bootstrap import logger
from .lib.utils import print_unicode_box, BorderColor
from .cmd.build import StackQLProvisioner
from .cmd.test import StackQLTestRunner
from .cmd.teardown import StackQLDeProvisioner
Expand All @@ -16,20 +18,6 @@
# utility functions
#

def print_unicode_box(message):
border_color = '\033[93m' # Yellow color
reset_color = '\033[0m'

lines = message.split('\n')
max_length = max(len(line) for line in lines)
top_border = border_color + '┌' + '─' * (max_length + 2) + '┐' + reset_color
bottom_border = border_color + '└' + '─' * (max_length + 2) + '┘' + reset_color

click.echo(top_border)
for line in lines:
click.echo(border_color + '│ ' + line.ljust(max_length) + ' │' + reset_color)
click.echo(bottom_border)

def get_stackql_instance(custom_registry=None, download_dir=None):
"""Initializes StackQL with the given options."""
stackql_kwargs = {}
Expand Down Expand Up @@ -190,7 +178,7 @@ def build(ctx, stack_dir, stack_env, log_level, env_file,
)
message = (f"Deploying stack: [{stack_name_display}] "
f"to environment: [{stack_env}]")
print_unicode_box(message)
print_unicode_box(message, BorderColor.YELLOW)

provisioner.run(dry_run, show_queries, on_failure)
click.echo("🎯 dry-run build complete" if dry_run
Expand Down Expand Up @@ -222,7 +210,7 @@ def teardown(ctx, stack_dir, stack_env, log_level, env_file,
)
message = (f"Tearing down stack: [{stack_name_display}] "
f"in environment: [{stack_env}]")
print_unicode_box(message)
print_unicode_box(message, BorderColor.YELLOW)

deprovisioner.run(dry_run, show_queries, on_failure)
click.echo(f"🚧 teardown complete (dry run: {dry_run})")
Expand Down Expand Up @@ -253,7 +241,7 @@ def test(ctx, stack_dir, stack_env, log_level, env_file,
)
message = (f"Testing stack: [{stack_name_display}] "
f"in environment: [{stack_env}]")
print_unicode_box(message)
print_unicode_box(message, BorderColor.YELLOW)

test_runner.run(dry_run, show_queries, on_failure)
click.echo(f"🔍 tests complete (dry run: {dry_run})")
Expand Down
19 changes: 19 additions & 0 deletions stackql_deploy/cmd/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,25 @@ def process_exports(
show_query(True, exports_query, self.logger)
catch_error_and_exit(f"exports query failed for {resource['name']}", self.logger)

# Check if we received an error from the query execution
if (len(exports) >= 1 and isinstance(exports[0], dict)):
# Check for our custom error wrapper
if '_stackql_deploy_error' in exports[0]:
error_msg = exports[0]['_stackql_deploy_error']
show_query(True, exports_query, self.logger)
catch_error_and_exit(
f"exports query failed for {resource['name']}\n\nError details:\n{error_msg}",
self.logger
)
# Check for direct error in result
elif 'error' in exports[0]:
error_msg = exports[0]['error']
show_query(True, exports_query, self.logger)
catch_error_and_exit(
f"exports query failed for {resource['name']}\n\nError details:\n{error_msg}",
self.logger
)

if len(exports) > 1:
catch_error_and_exit(
f"exports should include one row only, received {str(len(exports))} rows",
Expand Down
6 changes: 5 additions & 1 deletion stackql_deploy/cmd/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
catch_error_and_exit,
export_vars,
run_ext_script,
get_type
get_type,
print_unicode_box,
BorderColor
)
from ..lib.config import get_full_context, render_value
from ..lib.templating import get_queries, render_inline_template
Expand Down Expand Up @@ -43,6 +45,8 @@ def run(self, dry_run, show_queries, on_failure):

for resource in self.manifest.get('resources', []):

print_unicode_box(f"Processing resource: [{resource['name']}]", BorderColor.BLUE)

type = get_type(resource, self.logger)

self.logger.info(f"processing resource [{resource['name']}], type: {type}")
Expand Down
7 changes: 6 additions & 1 deletion stackql_deploy/cmd/teardown.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
import datetime
from ..lib.utils import (
catch_error_and_exit,
get_type
get_type,
print_unicode_box,
BorderColor
)
from ..lib.config import get_full_context, render_value
from ..lib.templating import get_queries, render_inline_template
Expand Down Expand Up @@ -70,6 +72,9 @@ def run(self, dry_run, show_queries, on_failure):
self.collect_exports(show_queries, dry_run)

for resource in reversed(self.manifest['resources']):

print_unicode_box(f"Processing resource: [{resource['name']}]", BorderColor.RED)

# process resources in reverse order
type = get_type(resource, self.logger)

Expand Down
6 changes: 5 additions & 1 deletion stackql_deploy/cmd/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
import datetime
from ..lib.utils import (
catch_error_and_exit,
get_type
get_type,
print_unicode_box,
BorderColor
)
from ..lib.config import get_full_context
from ..lib.templating import get_queries, render_inline_template
Expand All @@ -19,6 +21,8 @@ def run(self, dry_run, show_queries, on_failure):

for resource in self.manifest.get('resources', []):

print_unicode_box(f"Processing resource: [{resource['name']}]", BorderColor.BLUE)

type = get_type(resource, self.logger)

if type == 'query':
Expand Down
54 changes: 41 additions & 13 deletions stackql_deploy/lib/utils.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,31 @@
# lib/utils.py
import click
from enum import Enum
import time
import json
import sys
import subprocess
import re

class BorderColor(Enum):
YELLOW = '\033[93m' # Bright yellow
BLUE = '\033[94m' # Bright blue
RED = '\033[91m' # Bright red

def print_unicode_box(message: str, color: BorderColor = BorderColor.YELLOW):
border_color = color.value
reset_color = '\033[0m'

lines = message.split('\n')
max_length = max(len(line) for line in lines)
top_border = border_color + '┌' + '─' * (max_length + 2) + '┐' + reset_color
bottom_border = border_color + '└' + '─' * (max_length + 2) + '┘' + reset_color

click.echo(top_border)
for line in lines:
click.echo(border_color + '│ ' + line.ljust(max_length) + ' │' + reset_color)
click.echo(bottom_border)

def catch_error_and_exit(errmsg, logger):
logger.error(errmsg)
sys.exit("stackql-deploy operation failed 🚫")
Expand All @@ -18,6 +39,7 @@ def get_type(resource, logger):

def run_stackql_query(query, stackql, suppress_errors, logger, custom_auth=None, env_vars=None, retries=0, delay=5):
attempt = 0
last_error = None
while attempt <= retries:
try:
logger.debug(f"(utils.run_stackql_query) executing stackql query on attempt {attempt + 1}:\n\n{query}\n")
Expand All @@ -29,20 +51,22 @@ def run_stackql_query(query, stackql, suppress_errors, logger, custom_auth=None,
if len(result) == 0:
logger.debug("(utils.run_stackql_query) stackql query executed successfully, retrieved 0 items.")
pass
elif not suppress_errors and result and 'error' in result[0]:
elif result and 'error' in result[0]:
error_message = result[0]['error']
if attempt == retries:
# If retries are exhausted, log the error and exit
catch_error_and_exit(
(
f"(utils.run_stackql_query) error occurred during stackql query execution:\n\n"
f"{error_message}\n"
),
logger
)
else:
# Log the error and prepare for another attempt
logger.error(f"attempt {attempt + 1} failed:\n\n{error_message}\n")
last_error = error_message # Store the error for potential return
if not suppress_errors:
if attempt == retries:
# If retries are exhausted, log the error and exit
catch_error_and_exit(
(
f"(utils.run_stackql_query) error occurred during stackql query execution:\n\n"
f"{error_message}\n"
),
logger
)
else:
# Log the error and prepare for another attempt
logger.error(f"attempt {attempt + 1} failed:\n\n{error_message}\n")
elif 'count' in result[0]:
# If the result is a count query, return the count
logger.debug(
Expand Down Expand Up @@ -74,6 +98,7 @@ def run_stackql_query(query, stackql, suppress_errors, logger, custom_auth=None,

except Exception as e:
# Log the exception and check if retry attempts are exhausted
last_error = str(e) # Store the exception for potential return
if attempt == retries:
catch_error_and_exit(
f"(utils.run_stackql_query) an exception occurred during stackql query execution:\n\n{str(e)}\n",
Expand All @@ -87,6 +112,9 @@ def run_stackql_query(query, stackql, suppress_errors, logger, custom_auth=None,
attempt += 1

logger.debug(f"(utils.run_stackql_query) all attempts ({retries + 1}) to execute the query completed.")
# If suppress_errors is True and we have an error, return an empty list with error info as a special dict
if suppress_errors and last_error:
return [{'_stackql_deploy_error': last_error}]
# return None
return []

Expand Down
Loading