Created IAC reverse generator
This commit is contained in:
197
src/iac_reverse/generator/provider_block.py
Normal file
197
src/iac_reverse/generator/provider_block.py
Normal file
@@ -0,0 +1,197 @@
|
||||
"""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,
|
||||
)
|
||||
Reference in New Issue
Block a user