127 lines
4.3 KiB
Python
Executable File
127 lines
4.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# Copyright 2025 Google LLC
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# https://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import click
|
|
import glob
|
|
import subprocess
|
|
import yaml
|
|
|
|
from pathlib import Path
|
|
import os
|
|
import shutil
|
|
import tempfile
|
|
|
|
BASEDIR = Path(__file__).parents[1]
|
|
|
|
# if we're copying the module, we might as well ignore files and
|
|
# directories that are automatically read by terraform. Useful
|
|
# to avoid surprises if, for example, you have an active fast
|
|
# deployment with links to configs)
|
|
ignore_patterns = shutil.ignore_patterns('*.auto.tfvars', '*.auto.tfvars.json',
|
|
'[0-9]-*-providers.tf',
|
|
'terraform.tfstate*',
|
|
'.terraform.lock.hcl',
|
|
'terraform.tfvars', '.terraform')
|
|
|
|
|
|
def tflint_module(module_path, var_path, extra_dirs, junit):
|
|
with tempfile.TemporaryDirectory(dir=module_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(BASEDIR / module_path, tmp_path, dirs_exist_ok=True,
|
|
symlinks=False, ignore=ignore_patterns)
|
|
|
|
for extra_dir in extra_dirs:
|
|
os.symlink(extra_dir, tmp_path / extra_dir.name)
|
|
args = ['tflint']
|
|
if junit:
|
|
args += ['--format=junit']
|
|
args += [
|
|
'--chdir',
|
|
str(tmp_path.absolute()),
|
|
'--var-file',
|
|
str((BASEDIR / var_path).absolute()),
|
|
'--config',
|
|
str((BASEDIR / ".tflint.hcl").absolute()),
|
|
]
|
|
if junit:
|
|
with open(f'tflint-fast-{str(module_path).replace("/", "_")}.xml',
|
|
'w+') as output:
|
|
return subprocess.run(args, stderr=subprocess.STDOUT,
|
|
stdout=output).returncode
|
|
else:
|
|
return subprocess.run(args, stderr=subprocess.STDOUT).returncode
|
|
|
|
|
|
def is_affected(files, module_path, tftest_path, extra_dirs):
|
|
# no files provided, run all tftests
|
|
if not files:
|
|
return True
|
|
absolute_files = [Path(x).absolute() for x in files]
|
|
# check if the files modified the module
|
|
ret = any(x.is_relative_to(module_path.absolute()) for x in absolute_files)
|
|
if ret:
|
|
return ret
|
|
# check if the files modified the test definition
|
|
ret = any(x.is_relative_to(tftest_path.absolute()) for x in absolute_files)
|
|
if ret:
|
|
return ret
|
|
# check if the files modified extra dirs
|
|
for extra_dir in extra_dirs:
|
|
ret = any(x.is_relative_to(extra_dir.absolute()) for x in absolute_files)
|
|
if ret:
|
|
return ret
|
|
return False
|
|
|
|
|
|
@click.option('--junit', default=False, is_flag=True)
|
|
@click.argument('files', nargs=-1, type=click.Path(), required=False)
|
|
@click.command()
|
|
def main(junit, files):
|
|
ret = 0
|
|
for tftest_yaml in sorted(
|
|
glob.glob(f'{BASEDIR}/tests/fast/**/tftest.yaml', recursive=True)):
|
|
with open(tftest_yaml, 'r') as f:
|
|
tftest = yaml.safe_load(f)
|
|
module_path = Path(tftest['module'])
|
|
tftest_path = Path(tftest_yaml).parent
|
|
simple_test = tftest['tests'].get('simple', {})
|
|
if not simple_test:
|
|
simple_test = {}
|
|
relative_extra_dirs = simple_test.get('extra_dirs')
|
|
extra_dirs = [
|
|
(module_path.absolute() / Path(x)) for x in relative_extra_dirs
|
|
] if relative_extra_dirs else []
|
|
var_path = (tftest_path / 'simple.tfvars')
|
|
|
|
if not var_path.exists():
|
|
print(f'## {module_path}: skipping stage as no simple.tfvars found there')
|
|
continue
|
|
if not is_affected(files, module_path, tftest_path, extra_dirs):
|
|
print(
|
|
f'## {module_path}: skipping stage as it is not affected by provided files'
|
|
)
|
|
continue
|
|
click.echo(f'## {module_path}')
|
|
ret |= tflint_module(module_path, var_path, extra_dirs, junit)
|
|
# end for
|
|
exit(ret)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|