Skip to content
Open
22 changes: 20 additions & 2 deletions src/tirith/core/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import yaml
from types import CodeType

from typing import Any, Dict, List, Tuple, Optional
from typing import Any, Dict, List, Tuple, Optional, Union

from tirith.providers.common import ProviderError
from ..providers import PROVIDERS_DICT
Expand Down Expand Up @@ -81,8 +81,26 @@
has_evaluation_passed = None
continue

# Run evaluation on the provider's input value
evaluation_result = evaluator_instance.evaluate(evaluator_input["value"], evaluator_data)
evaluation_result["meta"] = evaluator_input.get("meta")

# Copy metadata and addresses if provided by the provider
if "meta" in evaluator_input:
evaluation_result["meta"] = evaluator_input["meta"]

# Copy addresses directly if provided
if "addresses" in evaluator_input:
# Ensure addresses is a list of strings
addresses = evaluator_input["addresses"]
if addresses is None:
evaluation_result["addresses"] = []

Check warning on line 96 in src/tirith/core/core.py

View check run for this annotation

Codecov / codecov/patch

src/tirith/core/core.py#L96

Added line #L96 was not covered by tests
elif isinstance(addresses, str):
evaluation_result["addresses"] = [addresses]

Check warning on line 98 in src/tirith/core/core.py

View check run for this annotation

Codecov / codecov/patch

src/tirith/core/core.py#L98

Added line #L98 was not covered by tests
elif isinstance(addresses, list):
evaluation_result["addresses"] = [str(item) for item in addresses]
else:
evaluation_result["addresses"] = [str(addresses)]

Check warning on line 102 in src/tirith/core/core.py

View check run for this annotation

Codecov / codecov/patch

src/tirith/core/core.py#L102

Added line #L102 was not covered by tests
Comment thread
refeed marked this conversation as resolved.
Outdated

evaluation_results.append(evaluation_result)
has_valid_evaluation = True

Expand Down
11 changes: 11 additions & 0 deletions src/tirith/prettyprinter.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,17 @@

for result_num, result_dict in enumerate(check_dict["result"]):
result_message = result_dict["message"]

# Include addresses in the message if it exists in the result_dict
if "addresses" in result_dict:
addresses = result_dict["addresses"]

Check warning on line 105 in src/tirith/prettyprinter.py

View check run for this annotation

Codecov / codecov/patch

src/tirith/prettyprinter.py#L105

Added line #L105 was not covered by tests
# Format addresses as a comma-separated string
if isinstance(addresses, list) and addresses:
addresses_str = ", ".join(addresses)
result_message = f"{result_message} - (Addresses: `{addresses_str}`)"

Check warning on line 109 in src/tirith/prettyprinter.py

View check run for this annotation

Codecov / codecov/patch

src/tirith/prettyprinter.py#L108-L109

Added lines #L108 - L109 were not covered by tests
elif addresses: # Fallback for any non-empty non-list value
result_message = f"{result_message} - (Addresses: `{addresses}`)"

Check warning on line 111 in src/tirith/prettyprinter.py

View check run for this annotation

Codecov / codecov/patch

src/tirith/prettyprinter.py#L111

Added line #L111 was not covered by tests
Comment thread
refeed marked this conversation as resolved.
Outdated

if result_dict["passed"]:
print(TermStyle.green(f" {result_num+1}. PASSED: {result_message}"))
elif check_dict["passed"] is None:
Expand Down
8 changes: 7 additions & 1 deletion src/tirith/providers/json/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,13 @@ def get_value(provider_args: Dict, input_data: Dict) -> List[dict]:
)
]

outputs = [create_result_dict(value=value, meta=None, err=None) for value in values]
# Create result dict with addresses as a separate property, not in meta
outputs = []
for value in values:
result = create_result_dict(value=value, meta=None, err=None)
# Simply use a list directly
result["addresses"] = [key_path]
outputs.append(result)

return outputs

Expand Down
134 changes: 88 additions & 46 deletions src/tirith/providers/terraform_plan/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

# input->(list ["a.b","c", "d"],value of resource)
# returns->[any, any, any]
from typing import Iterable, Tuple
from typing import Iterable, Tuple, List, Union, Any
import pydash

from ..common import ProviderError
Expand Down Expand Up @@ -56,6 +56,18 @@
return final_data


def _ensure_list_of_strings(value: Union[str, List[str], None]) -> List[str]:
"""Convert a string or None value to a list of strings"""
if value is None:
return []

Check warning on line 62 in src/tirith/providers/terraform_plan/handler.py

View check run for this annotation

Codecov / codecov/patch

src/tirith/providers/terraform_plan/handler.py#L62

Added line #L62 was not covered by tests
elif isinstance(value, str):
return [value]

Check warning on line 64 in src/tirith/providers/terraform_plan/handler.py

View check run for this annotation

Codecov / codecov/patch

src/tirith/providers/terraform_plan/handler.py#L64

Added line #L64 was not covered by tests
elif isinstance(value, list):
return [str(item) for item in value]

Check warning on line 66 in src/tirith/providers/terraform_plan/handler.py

View check run for this annotation

Codecov / codecov/patch

src/tirith/providers/terraform_plan/handler.py#L66

Added line #L66 was not covered by tests
else:
return [str(value)]

Check warning on line 68 in src/tirith/providers/terraform_plan/handler.py

View check run for this annotation

Codecov / codecov/patch

src/tirith/providers/terraform_plan/handler.py#L68

Added line #L68 was not covered by tests


Comment thread
refeed marked this conversation as resolved.
Outdated
def provide(provider_inputs, input_data):
# """Provides the value of the attribute from the input_data"""
outputs = []
Expand Down Expand Up @@ -97,24 +109,39 @@
is_attribute_found = True
local_is_found_attribute = True
attribute_value = input_resource_change_attrs[attribute]
outputs.append(
{
"value": attribute_value,
"meta": resource_change,
"err": None,
}
)
address = resource_change.get("address")
Comment thread
refeed marked this conversation as resolved.
Outdated
result = {
"value": attribute_value,
"meta": resource_change,
"err": None,
"addresses": [address] if address is not None else [],
}
outputs.append(result)
elif "." in attribute or "*" in attribute:
evaluated_outputs = _wrapper_get_exp_attribute(attribute, input_resource_change_attrs)
if evaluated_outputs:
is_attribute_found = True
local_is_found_attribute = True
for evaluated_output in evaluated_outputs:
outputs.append({"value": evaluated_output, "meta": resource_change, "err": None})
address = resource_change.get("address")
result = {
"value": evaluated_output,
"meta": resource_change,
"err": None,
"addresses": [address] if address is not None else [],
}
outputs.append(result)

# If we didn't find the attribute in this resource, add a None value so it still gets evaluated
if not local_is_found_attribute:
outputs.append({"value": None, "meta": resource_change, "err": None})
address = resource_change.get("address")
result = {
"value": None,
"meta": resource_change,
"err": None,
"addresses": [address] if address is not None else [],
}
outputs.append(result)
else:
outputs.append(
{
Expand Down Expand Up @@ -154,13 +181,14 @@
if resource_type in (resource_change["type"], "*"):
is_resource_type_found = True
for action in resource_change["change"]["actions"]:
outputs.append(
{
"value": action,
"meta": resource_change,
"err": None,
}
)
address = resource_change.get("address")
result = {
"value": action,
"meta": resource_change,
"err": None,
"addresses": [address] if address is not None else [],
}
outputs.append(result)
if not is_resource_type_found:
outputs.append(
{
Expand All @@ -175,6 +203,7 @@
elif input_type == "count":
count = 0
resource_meta = {}
addresses = []
resource_type = provider_inputs["terraform_resource_type"]
for resource_change in resource_changes:
# Skip if resource type is in exclude_resource_types when using wildcard
Expand All @@ -184,15 +213,13 @@
# No need to check if the resource is not found
# because the count of a resource can be zero
resource_meta = resource_change
# Add the address to our list of addresses if available
if "address" in resource_change:
addresses.append(resource_change["address"])
count += 1

outputs.append(
{
"value": count,
"meta": resource_meta,
"err": None,
}
)
result = {"value": count, "meta": resource_meta, "err": None, "addresses": addresses}
outputs.append(result)
return outputs
# CASE 4
elif input_type == "direct_dependencies":
Expand Down Expand Up @@ -252,7 +279,6 @@
continue

is_provider_full_name_found = True

attribute_value = None

if attribute_to_get == "version_constraint":
Expand All @@ -271,12 +297,9 @@
}
)
return
outputs.append(
{
"value": attribute_value,
"meta": provider_config_dict,
}
)

result = {"value": attribute_value, "meta": provider_config_dict, "addresses": [terraform_provider_full_name]}
outputs.append(result)

if not is_provider_full_name_found:
outputs.append(
Expand All @@ -297,7 +320,10 @@
:param provider_inputs: The provider inputs
:param outputs: The outputs
"""
outputs.append({"value": input_data.get("terraform_version"), "meta": input_data})
# For terraform_version, there's no specific address as it applies to the entire plan
outputs.append(
{"value": input_data.get("terraform_version"), "meta": input_data, "addresses": ["terraform_version"]}
)


def direct_dependencies_operator(input_data: dict, provider_inputs: dict, outputs: list):
Expand All @@ -316,12 +342,16 @@
is_resource_found = False

for resource in config_resources:

if resource.get("type") != resource_type:
continue
is_resource_found = True
deps_resource_type = {resource_id.split(".")[0] for resource_id in resource.get("depends_on", [])}
outputs.append({"value": list(deps_resource_type), "meta": config_resources})
result = {"value": list(deps_resource_type), "meta": config_resources}
# Add addresses if available
address = resource.get("address")
if address:
result["addresses"] = [address]
outputs.append(result)

if not is_resource_found:
outputs.append(
Expand All @@ -347,7 +377,7 @@
resource_changes = input_data.get("resource_changes", [])
referenced_by = provider_inputs.get("referenced_by")

reference_target_addresses = set()
reference_target_address = set()
Comment thread
refeed marked this conversation as resolved.
Outdated
is_resource_found = False

# Loop for adding reference_target
Expand All @@ -356,7 +386,7 @@
"destroy"
]:
continue
reference_target_addresses.add(resource_change.get("address"))
reference_target_address.add(resource_change.get("address"))
is_resource_found = True

if not is_resource_found:
Expand Down Expand Up @@ -384,15 +414,19 @@
reference_address = relative_reference_address
else:
reference_address = f"{module_path}.{relative_reference_address}"
if reference_address in reference_target_addresses:
reference_target_addresses.remove(reference_address)
outputs.append(
{"value": True, "meta": {"address": reference_address, "referenced_by": resource_config}}
)
if reference_address in reference_target_address:
reference_target_address.remove(reference_address)
result = {"value": True, "meta": {"referenced_by": resource_config}}

Check warning on line 419 in src/tirith/providers/terraform_plan/handler.py

View check run for this annotation

Codecov / codecov/patch

src/tirith/providers/terraform_plan/handler.py#L418-L419

Added lines #L418 - L419 were not covered by tests
# Add addresses to the output as a simple list
result["addresses"] = [reference_address]
Comment thread
refeed marked this conversation as resolved.
Outdated
outputs.append(result)

Check warning on line 422 in src/tirith/providers/terraform_plan/handler.py

View check run for this annotation

Codecov / codecov/patch

src/tirith/providers/terraform_plan/handler.py#L421-L422

Added lines #L421 - L422 were not covered by tests

# For all of the reference_target_addresses that don't have a reference
for reference_target_address in reference_target_addresses:
outputs.append({"value": False, "meta": {"address": reference_target_address, "referenced_by": {}}})
# For all of the reference_target_address that don't have a reference
for reference_target_address_item in reference_target_address:
result = {"value": False, "meta": {"referenced_by": {}}}
# Add addresses as a simple list
result["addresses"] = [reference_target_address_item]
outputs.append(result)


def get_module_resources_by_type_recursive(module: dict, resource_type: str, current_module_path: str = "") -> iter:
Expand Down Expand Up @@ -478,7 +512,10 @@
return

is_all_resource_type_references_to = resource_type_count == reference_count
outputs.append({"value": is_all_resource_type_references_to, "meta": config_resources})
result = {"value": is_all_resource_type_references_to, "meta": config_resources}

Check warning on line 515 in src/tirith/providers/terraform_plan/handler.py

View check run for this annotation

Codecov / codecov/patch

src/tirith/providers/terraform_plan/handler.py#L515

Added line #L515 was not covered by tests
# Simple list with resource type
result["addresses"] = [resource_type]
outputs.append(result)

Check warning on line 518 in src/tirith/providers/terraform_plan/handler.py

View check run for this annotation

Codecov / codecov/patch

src/tirith/providers/terraform_plan/handler.py#L517-L518

Added lines #L517 - L518 were not covered by tests
Comment thread
refeed marked this conversation as resolved.


def direct_references_operator(input_data: dict, provider_inputs: dict, outputs: list):
Expand Down Expand Up @@ -530,7 +567,12 @@
# Only get the resource type
resource_references.add(reference.split(".")[0])
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use the address from the reference

addresses.append(reference)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@refeed I am unable to understand why do we need to do this?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because that's the real addresses the optype is processing. The resource var that's placed in the meta key was actually not placed correctly, it only contains one resource even though this optype is processing more than one resources


outputs.append({"value": list(resource_references), "meta": resource})
result = {"value": list(resource_references), "meta": resource}
# Add addresses if available as a simple list
address = resource.get("address")
if address:
result["addresses"] = [address]
outputs.append(result)

if not is_resource_found:
outputs.append(
Expand Down