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:
committed by
GitHub
parent
e7f2ca7edc
commit
fbc7e891db
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user