Skip to content

Commit

Permalink
Merge pull request #94 from DontShaveTheYak/develop
Browse files Browse the repository at this point in the history
Release v0.4.1
  • Loading branch information
shadycuz authored Nov 9, 2022
2 parents 46d86fa + 896aead commit a0ad1ea
Show file tree
Hide file tree
Showing 13 changed files with 4,766 additions and 63 deletions.
65 changes: 36 additions & 29 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ flake8 = "^5.0.4"
flake8-black = "^0.3.3"
coverage = {extras = ["toml"], version = "^6.4.2"}
pytest-cov = "^4.0.0"
mypy = "^0.982"
mypy = "^0.990"
typing-extensions = { version = "^4.3.0", python = "~3.7" }

[build-system]
Expand Down
52 changes: 33 additions & 19 deletions src/cf2tf/conversion/expressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,14 @@ def get_att(template: "TemplateConverter", values: Any):
log.debug(f"Fn::GetAtt - Parsing attributes for {docs_path}")
valid_arguments, valid_attributes = doc_file.parse_attributes(docs_path)

tf_name = cf2tf.convert.pascal_to_snake(cf_name)
tf_type = cf2tf.convert.create_resource_type(docs_path)

if nested_prop:
prop = f"{cf_property}.{'.'.join(nested_prop)}"
log.debug(f"Looking up nested attr {prop} for {tf_type}")
return nested_attr(tf_name, tf_type, cf_property, nested_prop)

result = cf2tf.convert.matcher(cf_property, valid_arguments + valid_attributes, 50)

if not result:
Expand All @@ -416,37 +424,43 @@ def get_att(template: "TemplateConverter", values: Any):

attribute_name, _ = result

tf_name = cf2tf.convert.pascal_to_snake(cf_name)
tf_type = cf2tf.convert.create_resource_type(docs_path)
return LiteralType(f"{tf_type}.{tf_name}.{attribute_name}")

if nested_prop:

prop = f"{cf_property}.{'.'.join(nested_prop)}"
log.debug(
f"Looking up nested attr {attribute_name} from {cf_property} for {tf_type}"
def nested_attr(tf_name: str, tf_type: str, cf_attr: str, nested_attr: List[str]):

if len(nested_attr) != 1:
raise ValueError(
f"Error parsing nested stack output for {cf_attr}.{'.'.join(nested_attr)}"
)
return nested_attr(tf_name, tf_type, prop, attribute_name)

return LiteralType(f"{tf_type}.{tf_name}.{attribute_name}")
nested_attr_value = nested_attr[0]

if tf_type == "aws_cloudformation_stack":
return get_attr_nested_stack(tf_name, tf_type, cf_attr, nested_attr_value)

if tf_type == "aws_db_instance":
return get_attr_db_instance(tf_name, tf_type, cf_attr, nested_attr_value)

raise ValueError(
f"Unable to solve nested GetAttr {cf_attr} for {tf_name} and {tf_type}"
)

def nested_attr(tf_name: str, tf_type: str, cf_prop: str, tf_attr: str):

if tf_type == "aws_cloudformation_stack" and tf_attr == "outputs":
return get_attr_nested_stack(tf_name, tf_type, cf_prop, tf_attr)
def get_attr_db_instance(tf_name: str, tf_type: str, cf_attr: str, nested_attr: str):

raise ValueError(f"Unable to solve nested GetAttr {cf_prop}")
if cf_attr != "Endpoint":
raise ValueError(f"Unable to solve nested GetAttr {tf_name}")

return LiteralType(f"{tf_type}.{tf_name}.{nested_attr.lower()}")

def get_attr_nested_stack(tf_name: str, tf_type: str, cf_property, tf_attr):
items = cf_property.split(".")

if len(items) > 2:
raise ValueError(f"Error parsing nested stack output for {cf_property}")
def get_attr_nested_stack(tf_name: str, tf_type: str, cf_attr: str, nested_attr: str):

_, stack_output_name = items
if cf_attr != "Outputs":
raise ValueError(f"Unable to solve nested GetAttr {cf_attr}")

return LiteralType(f"{tf_type}.{tf_name}.{tf_attr}.{stack_output_name}")
return LiteralType(f"{tf_type}.{tf_name}.outputs.{nested_attr}")


def get_azs(template: "TemplateConverter", region: Any):
Expand Down Expand Up @@ -851,7 +865,7 @@ def stack_name_pseduo(template: "TemplateConverter"):
local_block = hcl2.Locals({})
template.add_post_block(local_block)

local_block.arguments["stack_name"] = template.name
local_block.arguments["stack_name"] = StringType(template.name)

return LiteralType("local.stack_name")

Expand Down
4 changes: 2 additions & 2 deletions src/cf2tf/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,9 @@ def convert(self) -> config.Configuration:

def parse_template(self):

for section in self.cf_template:
for section in self.valid_sections:

if section not in self.valid_sections:
if section not in self.cf_template:
log.debug(
f"Ignoring section {section} not found in {self.valid_sections}"
)
Expand Down
36 changes: 28 additions & 8 deletions src/cf2tf/terraform/code.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import logging
import re
from pathlib import Path
from tempfile import gettempdir
from typing import Optional
import re

import click
from click._termui_impl import ProgressBar
from git import RemoteProgress
from git.repo.base import Repo
from thefuzz import fuzz, process # type: ignore

import cf2tf.convert

from click._termui_impl import ProgressBar

log = logging.getLogger("cf2tf")


Expand All @@ -22,11 +21,9 @@ def __init__(self, docs_path: Path) -> None:
self.resources = list(docs_path.joinpath("r").glob("*.markdown"))
self.datas = list(docs_path.joinpath("d").glob("*.markdown"))

def find(self, name: str) -> Path:

name = name.replace("::", " ").replace("AWS", "")
def find(self, resource_type: str) -> Path:

name = cf2tf.convert.camel_case_split(name).lower().strip()
name = resource_type_to_name(resource_type)

log.debug(f"Searcing for {name} in terraform docs...")

Expand All @@ -38,7 +35,7 @@ def find(self, name: str) -> Path:
ranking: int
doc_path: Path
resource_name, ranking, doc_path = process.extractOne(
name.lower(), files, scorer=fuzz.token_sort_ratio
name.lower(), files, scorer=fuzz.UWRatio
)

log.debug(
Expand Down Expand Up @@ -88,6 +85,29 @@ def get_code():
return repo


def resource_type_to_name(resource_type: str) -> str:
"""Converts a Cloudformation Resource Type into something more search friendly.
Args:
resource_type (str): The Cloudformation resource type.
Returns:
str: A search term that can be used to match resources in the TF docs.
"""

search_tokens = resource_type.replace("::", " ").replace("AWS", " ").split(" ")

for i, token in enumerate(search_tokens):
if len(token) >= 4:
search_tokens[i] = cf2tf.convert.camel_case_split(token)

search_term = " ".join(search_tokens).lower().strip()

log.debug(f"Converted CF type {resource_type} to search term {search_term}.")

return search_term


class CloneProgress(RemoteProgress):
def __init__(self):
super().__init__()
Expand Down
Loading

0 comments on commit a0ad1ea

Please sign in to comment.