60 lines
2.3 KiB
Python
60 lines
2.3 KiB
Python
"""Multi-provider resource merging with conflict resolution.
|
|
|
|
Merges resources from multiple scan profiles into a unified inventory,
|
|
resolving naming conflicts by prefixing with the provider identifier.
|
|
"""
|
|
|
|
from dataclasses import replace
|
|
from collections import defaultdict
|
|
|
|
from iac_reverse.models import DiscoveredResource, ScanResult
|
|
|
|
|
|
class ResourceMerger:
|
|
"""Merges resources from multiple ScanResult objects into a unified list.
|
|
|
|
When resources from different providers share the same name, the merger
|
|
resolves the conflict by prefixing each conflicting resource's name with
|
|
its provider identifier (e.g., "kubernetes_nginx", "docker_swarm_nginx").
|
|
|
|
Provider-specific attributes are preserved unchanged.
|
|
"""
|
|
|
|
def merge(self, scan_results: list[ScanResult]) -> list[DiscoveredResource]:
|
|
"""Merge resources from multiple scan results into a unified list.
|
|
|
|
Args:
|
|
scan_results: List of ScanResult objects, one per provider/scan profile.
|
|
|
|
Returns:
|
|
A unified list of DiscoveredResource with naming conflicts resolved
|
|
by prefixing conflicting names with the provider identifier.
|
|
"""
|
|
# Collect all resources from all scan results
|
|
all_resources: list[DiscoveredResource] = []
|
|
for result in scan_results:
|
|
all_resources.extend(result.resources)
|
|
|
|
# Group resources by name to detect conflicts
|
|
resources_by_name: dict[str, list[DiscoveredResource]] = defaultdict(list)
|
|
for resource in all_resources:
|
|
resources_by_name[resource.name].append(resource)
|
|
|
|
# Identify conflicting names: same name from different providers
|
|
conflicting_names: set[str] = set()
|
|
for name, resources in resources_by_name.items():
|
|
providers = {r.provider for r in resources}
|
|
if len(providers) > 1:
|
|
conflicting_names.add(name)
|
|
|
|
# Build the merged list, resolving conflicts
|
|
merged: list[DiscoveredResource] = []
|
|
for resource in all_resources:
|
|
if resource.name in conflicting_names:
|
|
prefixed_name = f"{resource.provider.value}_{resource.name}"
|
|
merged.append(replace(resource, name=prefixed_name))
|
|
else:
|
|
merged.append(resource)
|
|
|
|
return merged
|