Net LB App Internal Cross-Region recipe (#2214)

* reorder tfdoc methods

* add support for recipes to tfdoc

* fix repo url in tfdoc

* update module README

* validated untested recipe

* validated untested refactored recipe

* add optional proxy subnet creation, outputs, fixes

* tested

* tfdoc fix

* fix README

* exclude examples from test collector
This commit is contained in:
Ludovico Magnocavallo
2024-04-14 18:38:05 +02:00
committed by GitHub
parent e7f2ca7edc
commit fbc7e891db
12 changed files with 921 additions and 230 deletions

View File

@@ -1,4 +1,4 @@
# Copyright 2023 Google LLC
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -25,7 +25,8 @@ FABRIC_ROOT = Path(__file__).parents[2]
FILE_TEST_RE = re.compile(r'# tftest-file +id=([\w_.-]+) +path=([\S]+)')
FIXTURE_TEST_RE = re.compile(r'# tftest-fixture +id=([\w_.-]+)')
Example = collections.namedtuple('Example', 'name code module files fixtures')
Example = collections.namedtuple('Example',
'name code module files fixtures type')
File = collections.namedtuple('File', 'path content')
@@ -74,9 +75,11 @@ def pytest_generate_tests(metafunc, test_group='example',
index += 1
code = child.children[0].children
tftest_tag = get_tftest_directive(code)
if tftest_tag is None:
continue
if tftest_tag and not filter_tests(tftest_tag):
continue
if child.lang == 'hcl':
if child.lang in ('hcl', 'tfvars'):
path = module.relative_to(FABRIC_ROOT)
name = f'{path}:{last_header}'
if index > 1:
@@ -88,7 +91,8 @@ def pytest_generate_tests(metafunc, test_group='example',
# see: https://pytest-xdist.readthedocs.io/en/latest/distribution.html
marks = [pytest.mark.xdist_group("serial")
] if 'serial' in tftest_tag else []
example = Example(name, code, path, files[last_header], fixtures)
example = Example(name, code, path, files[last_header], fixtures,
child.lang)
examples.append(pytest.param(example, marks=marks))
elif isinstance(child, marko.block.Heading):
last_header = child.children[0].children

View File

@@ -1,4 +1,4 @@
# Copyright 2023 Google LLC
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -15,6 +15,8 @@
import re
import subprocess
import yaml
import shutil
import tempfile
from pathlib import Path
BASE_PATH = Path(__file__).parent
@@ -47,49 +49,59 @@ def prepare_files(example, test_path, files, fixtures):
destination.write_text(example.fixtures[f])
def test_example(plan_validator, tmp_path, example):
def test_example(plan_validator, example):
if match := COUNT_TEST_RE.search(example.code):
(tmp_path / 'fabric').symlink_to(BASE_PATH.parents[1])
(tmp_path / 'variables.tf').symlink_to(BASE_PATH / 'variables.tf')
(tmp_path / 'main.tf').write_text(example.code)
assets_path = BASE_PATH.parent / str(example.module).replace('-',
'_') / 'assets'
if assets_path.exists():
(tmp_path / 'assets').symlink_to(assets_path)
# for tfvars-based tests, create the temporary directory with the
# same parent as the original module
directory = example.module.parent if example.type == 'tfvars' else None
prefix = f'pytest-{example.module.name}'
with tempfile.TemporaryDirectory(prefix=prefix, dir=directory) as tmp_path:
tmp_path = Path(tmp_path)
tf_var_files = []
if example.type == 'hcl':
(tmp_path / 'fabric').symlink_to(BASE_PATH.parents[1])
(tmp_path / 'variables.tf').symlink_to(BASE_PATH / 'variables.tf')
(tmp_path / 'main.tf').write_text(example.code)
assets_path = (BASE_PATH.parent /
str(example.module).replace('-', '_') / 'assets')
if assets_path.exists():
(tmp_path / 'assets').symlink_to(assets_path)
expected_modules = int(match.group('modules'))
expected_resources = int(match.group('resources'))
prepare_files(example, tmp_path, match.group('files'),
match.group('fixtures'))
elif example.type == 'tfvars':
(tmp_path / 'terraform.auto.tfvars').write_text(example.code)
shutil.copytree(example.module, tmp_path, dirs_exist_ok=True)
tf_var_files = [(tmp_path / 'terraform.auto.tfvars').resolve()]
prepare_files(example, tmp_path, match.group('files'),
match.group('fixtures'))
expected_modules = int(match.group('modules'))
expected_resources = int(match.group('resources'))
inventory = []
if match.group('inventory') is not None:
python_test_path = str(example.module).replace('-', '_')
inventory = BASE_PATH.parent / python_test_path / 'examples'
inventory = inventory / match.group('inventory')
inventory = []
if match.group('inventory') is not None:
python_test_path = str(example.module).replace('-', '_')
inventory = BASE_PATH.parent / python_test_path / 'examples'
inventory = inventory / match.group('inventory')
# TODO: force plan_validator to never copy files (we're already
# running from a temp dir)
summary = plan_validator(module_path=tmp_path, inventory_paths=inventory,
tf_var_files=[])
summary = plan_validator(module_path=tmp_path, inventory_paths=inventory,
tf_var_files=tf_var_files)
print('\n')
print(yaml.dump({'values': summary.values}))
print(yaml.dump({'counts': summary.counts}))
print(yaml.dump({'outputs': summary.outputs}))
print('\n')
print(yaml.dump({'values': summary.values}))
print(yaml.dump({'counts': summary.counts}))
print(yaml.dump({'outputs': summary.outputs}))
counts = summary.counts
num_modules, num_resources = counts['modules'], counts['resources']
assert expected_modules == num_modules, 'wrong number of modules'
assert expected_resources == num_resources, 'wrong number of resources'
counts = summary.counts
num_modules, num_resources = counts['modules'], counts['resources']
assert expected_modules == num_modules, 'wrong number of modules'
assert expected_resources == num_resources, 'wrong number of resources'
# TODO(jccb): this should probably be done in check_documentation
# but we already have all the data here.
result = subprocess.run(
'terraform fmt -check -diff -no-color main.tf'.split(), cwd=tmp_path,
stdout=subprocess.PIPE, encoding='utf-8')
assert result.returncode == 0, f'terraform code not formatted correctly\n{result.stdout}'
# TODO(jccb): this should probably be done in check_documentation
# but we already have all the data here.
result = subprocess.run(
'terraform fmt -check -diff -no-color main.tf'.split(), cwd=tmp_path,
stdout=subprocess.PIPE, encoding='utf-8')
assert result.returncode == 0, f'terraform code not formatted correctly\n{result.stdout}'
else:
assert False, "can't find tftest directive"

View File

@@ -1,4 +1,4 @@
# Copyright 2023 Google LLC
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -34,10 +34,9 @@ PlanSummary = collections.namedtuple('PlanSummary', 'values counts outputs')
def _prepare_root_module(path):
"""Context manager to prepare a terraform module to be tested.
If the TFTEST_COPY environment variable is set, `path` is copied to
a temporary directory and a few terraform files (e.g.
terraform.tfvars) are deleted to ensure a clean test environment.
Otherwise, `path` is simply returned untouched.
`path` is copied to a temporary directory and a few terraform files
(e.g. terraform.tfvars) are deleted to ensure a clean test
environment.
"""
# if we're copying the module, we might as well ignore files and
# directories that are automatically read by terraform. Useful
@@ -50,31 +49,18 @@ def _prepare_root_module(path):
'.terraform.lock.hcl',
'terraform.tfvars', '.terraform')
if os.environ.get('TFTEST_COPY'):
# if the TFTEST_COPY is set, create temp dir and copy the root
# module there
with tempfile.TemporaryDirectory(dir=path.parent) as tmp_path:
tmp_path = Path(tmp_path)
with tempfile.TemporaryDirectory(dir=path.parent) as tmp_path:
tmp_path = Path(tmp_path)
# Running tests in a copy made with symlinks=True makes them run
# ~20% slower than when run in a copy made with symlinks=False.
shutil.copytree(path, tmp_path, dirs_exist_ok=True, symlinks=False,
ignore=ignore_patterns)
lockfile = _REPO_ROOT / 'tools' / 'lockfile' / '.terraform.lock.hcl'
if lockfile.exists():
shutil.copy(lockfile, tmp_path / '.terraform.lock.hcl')
# Running tests in a copy made with symlinks=True makes them run
# ~20% slower than when run in a copy made with symlinks=False.
shutil.copytree(path, tmp_path, dirs_exist_ok=True, symlinks=False,
ignore=ignore_patterns)
lockfile = _REPO_ROOT / 'tools' / 'lockfile' / '.terraform.lock.hcl'
if lockfile.exists():
shutil.copy(lockfile, tmp_path / '.terraform.lock.hcl')
yield tmp_path
else:
# check if any ignore_patterns files are present in path
if unwanted_files := ignore_patterns(path, os.listdir(path=path)):
# prevent shooting yourself in the foot (unexpected test results) when ignored files are present
raise RuntimeError(
f'Test in path {path} contains {", ".join(unwanted_files)} which may affect '
f'test results. Please run tests with TFTEST_COPY=1 environment variable'
)
# if TFTEST_COPY is not set, just return the same path
yield path
yield tmp_path
def plan_summary(module_path, basedir, tf_var_files=None, extra_files=None,