Skip to content

Commit

Permalink
c7n-left: handle reference list declared in __tfmeta
Browse files Browse the repository at this point in the history
Make use of the changes from
cloud-custodian/tfparse#219 to track all references to
other blocks declared for a block.
  • Loading branch information
albertodonato committed Oct 15, 2024
1 parent 6ba8855 commit 7027dee
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 29 deletions.
3 changes: 1 addition & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@ endif
install:
@if [[ -z "$(VIRTUAL_ENV)" ]]; then echo "Create and Activate VirtualEnv First, ie. python3 -m venv .venv && source .venv/bin/activate"; exit 1; fi
poetry install
for pkg in $(PKG_SET); do echo "Install $$pkg" && cd $$pkg && poetry install --all-extras && cd ../..; done

for pkg in $(PKG_SET); do echo "Install $$pkg" && env -C $$pkg poetry install --all-extras; done
.PHONY: test

test:
Expand Down
52 changes: 31 additions & 21 deletions tools/c7n_left/c7n_left/providers/terraform/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
# SPDX-License-Identifier: Apache-2.0
#

import uuid

from ...core import ResourceGraph
from .resource import TerraformResource

Expand All @@ -18,20 +20,23 @@ def get_resources_by_type(self, types=()):
for type_name, type_items in self.resource_data.items():
if types and (type_name not in types and f"data.{type_name}" not in types):
continue
elif type_name == "module":
yield type_name, [self.as_resource(type_name, d, "module") for d in type_items]
elif type_name == "moved":
yield type_name, [self.as_resource(type_name, d, "moved") for d in type_items]
elif type_name == "locals":
yield type_name, [self.as_resource(type_name, d, "local") for d in type_items]
elif type_name == "terraform":
yield type_name, [self.as_resource(type_name, d, "terraform") for d in type_items]
elif type_name == "provider":
yield type_name, [self.as_resource(type_name, d, "provider") for d in type_items]
elif type_name == "variable":
yield type_name, [self.as_resource(type_name, d, "variable") for d in type_items]
elif type_name == "output":
yield type_name, [self.as_resource(type_name, d, "output") for d in type_items]
elif type_name in (
"module",
"moved",
"locals",
"terraform",
"provider",
"variable",
"output"
):
if type_name == "locals":
name = "local"
else:
name = type_name
yield type_name, [
self.as_resource(type_name, d, type_name=name)
for d in type_items
]
else:
data_resources = []
resources = []
Expand Down Expand Up @@ -76,10 +81,11 @@ def __init__(self):

@staticmethod
def is_id_ref(v):
if len(v) != 36:
return False
if v.count("-") != 4:
try:
uuid.UUID(hex=v)
except ValueError:
return False

return True

def resolve_refs(self, block, types=None):
Expand All @@ -95,7 +101,7 @@ def resolve_refs(self, block, types=None):
continue
yield r

def visit(self, block, root=False):
def visit(self, block):
if not isinstance(block, dict):
return ()

Expand All @@ -110,10 +116,14 @@ def visit(self, block, root=False):
refs.add(v)
if isinstance(v, (str, int, float, bool)):
continue
if isinstance(v, dict) and k != "__tfmeta":
refs.update(self.visit(v))
if isinstance(v, dict):
if k == "__tfmeta":
refs.update(r["id"]for r in v.get("references", ()))
else:
refs.update(self.visit(v))
if isinstance(v, list):
list(map(self.visit, v))
for entry in v:
self.visit(entry)

if refs and block.get("__tfmeta", {}).get("label"):
self._ref_map.setdefault(bid, []).extend(refs)
Expand Down
56 changes: 53 additions & 3 deletions tools/c7n_left/tests/test_left.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,8 @@
)
from c7n_left.providers.terraform.graph import Resolver
from c7n_left.providers.terraform.filters import Taggable

LEFT_INSTALLED = True
except ImportError:
pytest.skip(reason="c7n_left not installed", allow_module_level=True)
LEFT_INSTALLED = False
else:
load_resources(("terraform.*",))

Expand Down Expand Up @@ -816,6 +813,59 @@ def test_traverse_multi_resource_nested_or(tmp_path):
}


def test_traverse_match_values(policy_env, test):
policy_env.write_tf(
"""
resource "r" "r1" {
name = "r-r1"
}
resource "r" "r2" {
label = "r-r2"
}
resource "rr" "rx" {
rn = [r.r1.name]
}
resource "rr" "ry" {
rl = [r.r2.label]
}
"""
)
policy_env.write_policy(
{
"name": "test1",
"resource": "terraform.rr",
"filters": [
{
"type": "traverse",
"resources": "r",
"attrs": [{"name": "r-r1"}],
}
],
},
)
policy_env.write_policy(
{
"name": "test2",
"resource": "terraform.rr",
"filters": [
{
"type": "traverse",
"resources": "r",
"attrs": [{"label": "r-r2"}],
}
],
},
)
res1, res2 = (r.as_dict() for r in policy_env.run())
assert res1["policy"]["name"] == "test1"
assert res1["resource"]["__tfmeta"]["path"] == "rr.rx"
assert res2["policy"]["name"] == "test2"
assert res2["resource"]["__tfmeta"]["path"] == "rr.ry"


def test_traverse_filter_not_found(tmp_path):
resources = run_policy(
{
Expand Down
3 changes: 0 additions & 3 deletions tools/c7n_left/tests/test_left_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,8 @@
try:
from c7n_left import cli
from c7n_left import test as left_test

LEFT_INSTALLED = True
except ImportError:
pytest.skip(reason="c7n_left not installed", allow_module_level=True)
LEFT_INSTALLED = False


data_dir = Path(os.curdir).absolute() / "data"
Expand Down

0 comments on commit 7027dee

Please sign in to comment.