-
Notifications
You must be signed in to change notification settings - Fork 151
/
readable_plan.py
88 lines (73 loc) · 3.45 KB
/
readable_plan.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import sys
import os
from argparse import Action
import orjson
import filetype
from terraform_compliance.common.terraform_files import convert_terraform_plan_to_json
from terraform_compliance.common.exceptions import TerraformComplianceInternalFailure
class ReadablePlan(Action):
def __init__(self, option_strings, dest, **kwargs):
super(ReadablePlan, self).__init__(option_strings, dest, **kwargs)
self.option_strings = option_strings
self.dest = dest
self.nargs = kwargs.get('nargs', None)
self.const = kwargs.get('const', None)
self.default = kwargs.get('default', None)
self.type = kwargs.get('type', None)
self.choices = kwargs.get('choices', None)
self.required = kwargs.get('required', None)
self.help = kwargs.get('help', None)
self.metavar = kwargs.get('metavar', None)
self.parser = kwargs.get('parser', None)
def __call__(self, parser, namespace, values, option_string=None):
# Check if the given path is a file
if not os.path.isfile(values):
print('ERROR: {} is not a file.'.format(values))
sys.exit(1)
# Check if the given file is a native terraform plan file
given_file = filetype.guess(values)
if given_file is not None:
terraform_executable = getattr(namespace, 'terraform_file', None)
values = convert_terraform_plan_to_json(os.path.abspath(values), terraform_executable)
# Check if the given file is a json file.
try:
plan_lines = [line for line in open(values, 'r', encoding='utf-8')]
# Some Github Actions (hashicorp/setup-terraform) has internal wrappers which is
# breaking the json file that is read by the terraform-compliance
file_change_required = False
if len(plan_lines) > 1:
plan_lines = plan_lines[1]
file_change_required = True
else:
plan_lines = plan_lines[0]
data = orjson.loads(plan_lines)
# Write the changed plan file to the same file, since it is used in other places.
if file_change_required:
with open(values, 'w', encoding='utf-8') as plan_file:
plan_file.write(plan_lines)
except orjson.JSONDecodeError:
print('ERROR: {} is not a valid JSON file'.format(values))
sys.exit(1)
except UnicodeDecodeError:
print('ERROR: {} is not a valid JSON file.'.format(values))
print(' Did you try to convert the binary plan file to json with '
'"terraform show -json {} > {}.json" ?'.format(values, values))
sys.exit(1)
except:
raise TerraformComplianceInternalFailure('Invalid file type.')
# Check if this is a correct terraform plan file
try:
assert data['format_version']
assert data['terraform_version']
# Check if this is a state file
if 'values' in data:
assert data['values']['root_module']['resources']
# Then it must be a terraform plan file
else:
assert data['planned_values']
assert data['configuration']
except KeyError:
print('ERROR: {} is not a valid terraform plan json output.'.format(values))
sys.exit(1)
setattr(namespace, self.dest, os.path.abspath(values))
return True