Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add parameter to disable env replacement in module params #730

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: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ This project adheres to [Semantic Versioning](http://semver.org/) and [Keep a Ch

### Changes

- Added ability to disable env replacement in module parameters

### Fixes

## v5.0.0 (2024-08-16)
Expand Down
8 changes: 7 additions & 1 deletion docs/source/manifests.md
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ When using this feature, any change to these file(s) (modifying, add to manifest
## Universal Environment Variable Replacement in Manifests
As of the release of `seed-farmer==3.5.0`, we have added support for dynamic replacement of values with environment variables in manifests. This does not replace any pre-existing functionality. This also is limited to only manifests (`deployment_manifest` and `module_manifest`). Things like the `deployspec` and the `modulestack` are NOT included in this functionality. We strongly recommend using hard-coded values in manifests or leveraging the facilities already in place, but we have added this feature based on feedback from experienced users.

Any string within your manifests that has a designated pattern will automatically be resolved. If you have an environment variable named `SOMEKEY` that is defined, you can reference it in your manifests via wrapping it in `${}` --> for example `${SOMEKEY}`.
Any string within your manifests that has a designated pattern will automatically be resolved. If you have an environment variable named `SOMEKEY` that is defined, you can reference it in your manifests via wrapping it in `${}` --> for example `${SOMEKEY}`. Additionally, it is possible to disable environment variable replacement in module input parameters using `disableEnvVarResolution: True` for cases such as when input parameter is a script.

The following is a valid manifest:

Expand All @@ -356,6 +356,12 @@ parameters:
- name: vpc-id
valueFrom:
secretsManager: ${SOMEKEY}
- name: param-no-env-resolution
disableEnvVarResolution: True
value:
- |
export VAR=test
echo "${VAR}"
```
This can be applied to all values in the manifest. We do not recommend using this in the `name` field of manifests as any value that is referenced by downstream manifests MUST align. For example, in the following:

Expand Down
1 change: 1 addition & 0 deletions seedfarmer/models/manifests/_module_manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class ModuleParameter(ValueFromRef):
name: str
value: Optional[Any] = None
version: Optional[Any] = None
disableEnvVarResolution: Optional[bool] = None
resolved_value: Optional[Any] = None

def __init__(self, **data: Any) -> None:
Expand Down
15 changes: 8 additions & 7 deletions seedfarmer/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,13 +237,14 @@ def recurse_list(working_list: List[Any]) -> List[Any]:
return working_list

def recurse_dict(working_element: Dict[str, Any]) -> Dict[str, Any]:
for key, value in working_element.items():
if isinstance(value, str):
working_element[key] = replace_str(value)
elif isinstance(value, list):
recurse_list(value)
elif isinstance(value, dict) and key not in ["deploy_spec"]:
recurse_dict(value)
if not working_element.get("disableEnvVarResolution"):
for key, value in working_element.items():
if isinstance(value, str):
working_element[key] = replace_str(value)
elif isinstance(value, list):
recurse_list(value)
elif isinstance(value, dict) and key not in ["deploy_spec"]:
recurse_dict(value)
return working_element

payload = recurse_dict(payload)
Expand Down
75 changes: 75 additions & 0 deletions test/unit-test/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -637,3 +637,78 @@ def test_module_manifest_with_env_var_resolution_in_path_error():
):
with pytest.raises(InvalidManifestError, match="The environment variable is not available: 'GIT_URL'"):
ModuleManifest(**module_yaml)


@pytest.mark.models
@pytest.mark.models_module_manifest
def test_module_manifest_with_env_var_resolution_in_script():
module_yaml = yaml.safe_load(
"""
name: test-module-1
path: modules/test-module
targetAccount: primary
targetRegion: us-west-2
parameters:
- name: param1
value:
- |
echo "${VAR}"
"""
)

with mock.patch.dict(
os.environ,
{
"VAR": "test",
},
clear=True,
):
ModuleManifest(**module_yaml)


@pytest.mark.models
@pytest.mark.models_module_manifest
def test_module_manifest_with_env_var_resolution_in_script_disabled():
module_yaml = yaml.safe_load(
"""
name: test-module-1
path: modules/test-module
targetAccount: primary
targetRegion: us-west-2
parameters:
- name: param1
disableEnvVarResolution: True
value:
- |
export VAR=test
echo "${VAR}"
- name: param2_nested
disableEnvVarResolution: True
value:
nested_value:
- |
export VAR=test
echo "${VAR}"
"""
)
ModuleManifest(**module_yaml)


@pytest.mark.models
@pytest.mark.models_module_manifest
def test_module_manifest_with_env_var_resolution_in_script_error():
module_yaml = yaml.safe_load(
"""
name: test-module-1
path: modules/test-module
targetAccount: primary
targetRegion: us-west-2
parameters:
- name: param1
value:
- |
echo "${VAR}"
"""
)
with pytest.raises(InvalidManifestError, match="The environment variable is not available: 'VAR'"):
ModuleManifest(**module_yaml)