From 4a3417847e30e3c7177e2fbe31bde6799b3ec2d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20G=C3=B6ransson?= Date: Fri, 27 Jun 2025 10:55:03 +0200 Subject: [PATCH 1/3] check for empty configuration file easiest mistake to reproduce would be to misspell a key filter (`-k`). copy original environment file for import also, before rewriting the specified file with imported configuration. --- grizzly_cli/keyvault.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/grizzly_cli/keyvault.py b/grizzly_cli/keyvault.py index 51ddebf..b7dea4c 100644 --- a/grizzly_cli/keyvault.py +++ b/grizzly_cli/keyvault.py @@ -300,18 +300,25 @@ def keyvault_import(client: SecretClient, environment: str, args: Arguments, roo configuration_branch = unflatten(conf_key, conf_value) configuration_unflatten = merge_dicts(configuration_branch, configuration_unflatten) + if len(configuration_unflatten) < 1: + logger.error('environment file %s did not contain any configuration', args.env_file) + return 1 + configuration = configuration_unflatten keyvault_configuration, imported_secrets = load_configuration_keyvault(client, environment, root, filter_keys=args.keys) configuration = merge_dicts(keyvault_configuration, configuration) - env_file = Path(args.env_file) + environment_file = Path(args.env_file) if not args.dry_run: # do not rewrite environment file on dry-run - _dict_to_yaml(env_file, {'configuration': configuration}, indentation=env_file) + unsafe_environment_file = environment_file.rename(environment_file.with_suffix(f'.unsafe{environment_file.suffix}')) + _dict_to_yaml(environment_file, {'configuration': configuration}, indentation=environment_file) + else: + unsafe_environment_file = environment_file.with_suffix(f'.unsafe{environment_file.suffix}') - logger.info('\nimported %d secrets from %s to %s', imported_secrets, client.vault_url, env_file.as_posix()) + logger.info('\nimported %d secrets from %s to %s, saved original in %s', imported_secrets, client.vault_url, environment_file.as_posix(), unsafe_environment_file.as_posix()) return 0 From b7451aaaa5ad720fa210a798eb16b9e4dcb8ecff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20G=C3=B6ransson?= Date: Fri, 27 Jun 2025 10:55:33 +0200 Subject: [PATCH 2/3] show all files that was written for mqm certificates, not only the kdb file. --- grizzly_cli/utils/configuration.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/grizzly_cli/utils/configuration.py b/grizzly_cli/utils/configuration.py index 51b394a..0d1b92e 100644 --- a/grizzly_cli/utils/configuration.py +++ b/grizzly_cli/utils/configuration.py @@ -420,7 +420,9 @@ def _write_mqm_cert( p12_file.unlink() cms_file.with_suffix('.crl').unlink(missing_ok=True) - logger.info('wrote %s', relative_file) + for file in cms_file.parent.glob(f'{cms_file.stem}.*'): + relative_cms_file = _create_relative_path(root, file) + logger.info('wrote %s', relative_cms_file) return _create_relative_path(root, cms_file, no_suffix=True) From 6fb680429f916496abea92d2ece95c7f818d2abd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20G=C3=B6ransson?= Date: Fri, 27 Jun 2025 12:50:48 +0200 Subject: [PATCH 3/3] improvements fixed that `-k` filters on a partial path name to a configuration key (e.g. startswith). fixed that, when merging dicts, if source is None, but merged is a dict, do not overwrite that with the None value, just keep it as it is. --- grizzly_cli/keyvault.py | 23 ++++++++++++++++++----- grizzly_cli/utils/__init__.py | 4 ++-- grizzly_cli/utils/configuration.py | 6 +++++- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/grizzly_cli/keyvault.py b/grizzly_cli/keyvault.py index b7dea4c..9910f63 100644 --- a/grizzly_cli/keyvault.py +++ b/grizzly_cli/keyvault.py @@ -255,7 +255,11 @@ def _extract_metadata(env_file: str) -> tuple[str, str | None, dict[str, Any]]: keyvault = configuration.get('keyvault', None) - return (configuration.get('env', None) or file.stem, keyvault, flatten(configuration)) + configuration = flatten(configuration) + + logger.debug('flatten configuration: %r', configuration) + + return (configuration.get('env', None) or file.stem, keyvault, configuration) def diff(left_file_name: str, right_file_name: str) -> int: @@ -293,10 +297,9 @@ def keyvault_import(client: SecretClient, environment: str, args: Arguments, roo # unflatten existing configuration configuration_unflatten: dict[str, Any] = {} - for conf_key, conf_value in configuration.items(): - if args.keys is not None and conf_key not in args.keys: - continue + original_key_count = len(configuration) + for conf_key, conf_value in configuration.items(): configuration_branch = unflatten(conf_key, conf_value) configuration_unflatten = merge_dicts(configuration_branch, configuration_unflatten) @@ -306,19 +309,29 @@ def keyvault_import(client: SecretClient, environment: str, args: Arguments, roo configuration = configuration_unflatten + logger.debug('exploded configuration: %r', configuration) + keyvault_configuration, imported_secrets = load_configuration_keyvault(client, environment, root, filter_keys=args.keys) configuration = merge_dicts(keyvault_configuration, configuration) + logger.debug('merged configuration: %r', configuration) + environment_file = Path(args.env_file) if not args.dry_run: # do not rewrite environment file on dry-run + flatten_configuration = flatten(configuration) + written_keys = len(flatten_configuration) - original_key_count unsafe_environment_file = environment_file.rename(environment_file.with_suffix(f'.unsafe{environment_file.suffix}')) _dict_to_yaml(environment_file, {'configuration': configuration}, indentation=environment_file) else: + written_keys = 0 unsafe_environment_file = environment_file.with_suffix(f'.unsafe{environment_file.suffix}') - logger.info('\nimported %d secrets from %s to %s, saved original in %s', imported_secrets, client.vault_url, environment_file.as_posix(), unsafe_environment_file.as_posix()) + logger.info( + '\nimported %d secrets from %s and wrote %d new keys to %s, saved original in %s', + imported_secrets, client.vault_url, written_keys, environment_file.as_posix(), unsafe_environment_file.as_posix(), + ) return 0 diff --git a/grizzly_cli/utils/__init__.py b/grizzly_cli/utils/__init__.py index 8dd22f9..dad47dd 100644 --- a/grizzly_cli/utils/__init__.py +++ b/grizzly_cli/utils/__init__.py @@ -1028,9 +1028,9 @@ def merge_dicts(merged: dict[str, Any], source: dict[str, Any]) -> dict[str, Any if ( key in merged and isinstance(merged[key], dict) - and isinstance(source[key], Mapping) + and (isinstance(source[key], Mapping) or source[key] is None) ): - merged[key] = merge_dicts(merged[key], source[key]) + merged[key] = merge_dicts(merged[key], source[key] or {}) else: value = source[key] if isinstance(value, str) and value.lower() == 'none': # pragma: no cover diff --git a/grizzly_cli/utils/configuration.py b/grizzly_cli/utils/configuration.py index 0d1b92e..2fb35eb 100644 --- a/grizzly_cli/utils/configuration.py +++ b/grizzly_cli/utils/configuration.py @@ -581,6 +581,8 @@ def load_configuration_file(file: Path) -> dict[str, Any]: for yaml_configuration in yaml_configurations: configuration = merge_dicts(configuration, yaml_configuration) + logger.debug('configuration: %r', configuration) + return configuration @@ -621,7 +623,7 @@ def load_configuration_keyvault(client: SecretClient, environment: str, root: Pa for secret_key, conf_key in keys.items(): secret = client.get_secret(secret_key) - if filter_keys is not None and conf_key not in filter_keys: + if filter_keys is not None and not any(conf_key.startswith(filter_key) for filter_key in filter_keys): continue content_type = secret.properties.content_type @@ -715,4 +717,6 @@ def load_configuration_keyvault(client: SecretClient, environment: str, root: Pa configuration = merge_dicts(configuration_branch, configuration) imported_secrets += 1 + logger.debug('keyvault configuration: %r', configuration) + return configuration, imported_secrets