198 lines
6.9 KiB
Python
198 lines
6.9 KiB
Python
"""Provider block generator for Terraform HCL output.
|
|
|
|
Generates a providers.tf file containing:
|
|
- A terraform { required_providers { ... } } block listing all providers used
|
|
- Individual provider configuration blocks with platform-specific settings
|
|
(endpoints, certificates, credentials) for each distinct provider type.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from iac_reverse.models import ProviderType, ScanProfile, GeneratedFile
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Provider metadata: maps ProviderType to Terraform provider details
|
|
# ---------------------------------------------------------------------------
|
|
|
|
# Each entry: (terraform_provider_name, source, version_constraint)
|
|
_PROVIDER_METADATA: dict[ProviderType, tuple[str, str, str]] = {
|
|
ProviderType.KUBERNETES: (
|
|
"kubernetes",
|
|
"hashicorp/kubernetes",
|
|
"~> 2.0",
|
|
),
|
|
ProviderType.DOCKER_SWARM: (
|
|
"docker",
|
|
"kreuzwerker/docker",
|
|
"~> 3.0",
|
|
),
|
|
ProviderType.SYNOLOGY: (
|
|
"synology",
|
|
"synology-community/synology",
|
|
"~> 0.2",
|
|
),
|
|
ProviderType.HARVESTER: (
|
|
"harvester",
|
|
"harvester/harvester",
|
|
"~> 0.6",
|
|
),
|
|
ProviderType.BARE_METAL: (
|
|
"redfish",
|
|
"dell/redfish",
|
|
"~> 1.0",
|
|
),
|
|
ProviderType.WINDOWS: (
|
|
"windows",
|
|
"hashicorp/windows",
|
|
"~> 0.1",
|
|
),
|
|
}
|
|
|
|
|
|
def _generate_provider_config(
|
|
provider_type: ProviderType, profile: ScanProfile
|
|
) -> str:
|
|
"""Generate the provider configuration block for a given provider type.
|
|
|
|
Uses credentials and endpoints from the ScanProfile to populate
|
|
platform-specific configuration attributes.
|
|
"""
|
|
tf_name = _PROVIDER_METADATA[provider_type][0]
|
|
lines: list[str] = []
|
|
lines.append(f'provider "{tf_name}" {{')
|
|
|
|
if provider_type == ProviderType.KUBERNETES:
|
|
host = profile.credentials.get("host", "")
|
|
cluster_ca = profile.credentials.get("cluster_ca_certificate", "")
|
|
token = profile.credentials.get("token", "")
|
|
lines.append(f' host = "{host}"')
|
|
lines.append(f' cluster_ca_certificate = "{cluster_ca}"')
|
|
lines.append(f' token = "{token}"')
|
|
|
|
elif provider_type == ProviderType.DOCKER_SWARM:
|
|
host = profile.credentials.get("host", "")
|
|
cert_path = profile.credentials.get("cert_path", "")
|
|
lines.append(f' host = "{host}"')
|
|
lines.append(f' cert_path = "{cert_path}"')
|
|
|
|
elif provider_type == ProviderType.SYNOLOGY:
|
|
url = profile.credentials.get("url", "")
|
|
username = profile.credentials.get("username", "")
|
|
password = profile.credentials.get("password", "")
|
|
lines.append(f' url = "{url}"')
|
|
lines.append(f' username = "{username}"')
|
|
lines.append(f' password = "{password}"')
|
|
|
|
elif provider_type == ProviderType.HARVESTER:
|
|
kubeconfig = profile.credentials.get("kubeconfig", "")
|
|
lines.append(f' kubeconfig = "{kubeconfig}"')
|
|
|
|
elif provider_type == ProviderType.BARE_METAL:
|
|
endpoint = profile.credentials.get("endpoint", "")
|
|
username = profile.credentials.get("username", "")
|
|
password = profile.credentials.get("password", "")
|
|
lines.append(f' endpoint = "{endpoint}"')
|
|
lines.append(f' username = "{username}"')
|
|
lines.append(f' password = "{password}"')
|
|
|
|
elif provider_type == ProviderType.WINDOWS:
|
|
host = profile.credentials.get("host", "")
|
|
username = profile.credentials.get("username", "")
|
|
password = profile.credentials.get("password", "")
|
|
lines.append(f' host = "{host}"')
|
|
lines.append(f' username = "{username}"')
|
|
lines.append(f' password = "{password}"')
|
|
lines.append("")
|
|
lines.append(" winrm {")
|
|
winrm_port = profile.credentials.get("winrm_port", "5985")
|
|
winrm_use_ssl = profile.credentials.get("winrm_use_ssl", "false")
|
|
lines.append(f" port = {winrm_port}")
|
|
lines.append(f" use_ssl = {winrm_use_ssl}")
|
|
lines.append(" }")
|
|
|
|
lines.append("}")
|
|
return "\n".join(lines)
|
|
|
|
|
|
def _generate_required_providers_block(
|
|
provider_types: set[ProviderType],
|
|
) -> str:
|
|
"""Generate the terraform { required_providers { ... } } block."""
|
|
lines: list[str] = []
|
|
lines.append("terraform {")
|
|
lines.append(" required_providers {")
|
|
|
|
for provider_type in sorted(provider_types, key=lambda p: p.value):
|
|
tf_name, source, version = _PROVIDER_METADATA[provider_type]
|
|
lines.append(f" {tf_name} = {{")
|
|
lines.append(f' source = "{source}"')
|
|
lines.append(f' version = "{version}"')
|
|
lines.append(" }")
|
|
|
|
lines.append(" }")
|
|
lines.append("}")
|
|
return "\n".join(lines)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# ProviderBlockGenerator
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class ProviderBlockGenerator:
|
|
"""Generates Terraform provider configuration blocks.
|
|
|
|
Accepts a list of ScanProfiles and a set of ProviderTypes used in the
|
|
generated code, and produces a providers.tf file containing:
|
|
- A terraform { required_providers { ... } } block
|
|
- Individual provider blocks with platform-specific configuration
|
|
"""
|
|
|
|
def generate(
|
|
self,
|
|
profiles: list[ScanProfile],
|
|
provider_types: set[ProviderType],
|
|
) -> GeneratedFile:
|
|
"""Generate the providers.tf file content.
|
|
|
|
Args:
|
|
profiles: List of ScanProfiles providing credentials/endpoints.
|
|
provider_types: Set of distinct ProviderTypes used in the code.
|
|
|
|
Returns:
|
|
A GeneratedFile with filename "providers.tf" and the HCL content.
|
|
"""
|
|
# Build a map from ProviderType -> first matching profile
|
|
profile_map: dict[ProviderType, ScanProfile] = {}
|
|
for profile in profiles:
|
|
if profile.provider not in profile_map:
|
|
profile_map[profile.provider] = profile
|
|
|
|
sections: list[str] = []
|
|
|
|
# 1. required_providers block
|
|
sections.append(_generate_required_providers_block(provider_types))
|
|
|
|
# 2. Individual provider configuration blocks
|
|
for provider_type in sorted(provider_types, key=lambda p: p.value):
|
|
profile = profile_map.get(provider_type)
|
|
if profile is not None:
|
|
sections.append(
|
|
_generate_provider_config(provider_type, profile)
|
|
)
|
|
else:
|
|
# Generate a placeholder block if no profile matches
|
|
tf_name = _PROVIDER_METADATA[provider_type][0]
|
|
sections.append(
|
|
f'provider "{tf_name}" {{\n # No profile provided\n}}'
|
|
)
|
|
|
|
content = "\n\n".join(sections) + "\n"
|
|
|
|
return GeneratedFile(
|
|
filename="providers.tf",
|
|
content=content,
|
|
resource_count=0,
|
|
)
|