Skip to content

Commit dc03e69

Browse files
authored
Merge branch 'master' into fix-issue-279
2 parents a2ace20 + 0888baf commit dc03e69

File tree

25 files changed

+231
-10
lines changed

25 files changed

+231
-10
lines changed

CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# CHANGELOG
22

3+
## 1.2.5 (unreleased)
4+
* Fixed a bug where some empty found values would be treated as not found. ([#249](https://github.com/eerkunt/terraform-compliance/issues/249))
5+
* Improved some error messages that might create some confusion about the failure results. ([#284](https://github.com/eerkunt/terraform-compliance/issues/284))
6+
* Fixed a problem where using `@warning` tag was causing a problem where error messages was hidden on `-q` usage.
7+
8+
## 1.2.4 (2020-06-03)
9+
* Add ability to reference a git repo by branch name and directory via `<repo>.git//<directory>?ref=<branch-name`. ([#218](https://github.com/eerkunt/terraform-compliance/issues/218))
10+
311
## 1.2.3 (2020-05-25)
412
* Fixed a crash where some module outputs could not be processed. ([#275](https://github.com/eerkunt/terraform-compliance/issues/275))
513

docs/pages/usage/index.md

+12
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,18 @@ or if the repository is a private repository ;
7070
The authentication to that git repository will be handled via your `~/.ssh/config`. If you are using a different
7171
ssh key for this repository then you also need to provide `-i` parameter to pointing your private key.
7272
73+
New in `1.2.4`, a repository can be referenced by branch name and directory. This uses syntax similar to Terraform
74+
[Modules in Package Sub-directories](https://www.terraform.io/docs/modules/sources.html#modules-in-package-sub-directories).
75+
The reference must include `//` after `.git` and end with `?ref=<branch-name>` or `?ref=<tag>`.
76+
```
77+
[~] $ terraform-compliance -f git:ssh://github.com/user/repo.git//directory?ref=v1.0.0 ...
78+
```
79+
The directory is optional.
80+
```
81+
[~] $ terraform-compliance -f git:ssh://github.com/user/repo.git//?ref=staging ...
82+
```
83+
84+
7385
### -p / --planfile
7486
{: .d-inline-block }
7587
REQUIRED

requirements.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
radish-bdd==0.13.1
22
mock==4.0.2
3-
gitpython==3.1.2
3+
gitpython==3.1.3
44
netaddr==0.7.19
55
colorful==0.5.4
66
filetype==1.0.7
77
junit-xml==1.9
88
emoji==0.5.4
99
lxml==4.5.1
1010
ddt==1.4.1
11-
pytest==5.4.2
11+
pytest==5.4.3
1212
nose==1.3.7

terraform_compliance/common/error_handling.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def _process(self):
5151
# Prepare message
5252
msg = []
5353
for msg_index in range(0,len(self.message)):
54-
if self.exit_on_failure is False:
54+
if self.exit_on_failure is False or self.no_failure is True:
5555
msg_header = '{}{}'.format(self.exception_name,
5656
colorful.bold_white(':')) if msg_index == 0 else ' '*(len(self.exception_name)+1)
5757
msg.append('\t\t{} {}'.format(colorful.bold_red(msg_header), colorful.red(self.message[msg_index])))
@@ -62,7 +62,7 @@ def _process(self):
6262
colorful.bold_white(':'),
6363
self.message[msg_index]))
6464

65-
if self.exit_on_failure is False:
65+
if self.exit_on_failure is False or (self.no_failure is True and msg):
6666
for message in msg:
6767
console_write(message)
6868

terraform_compliance/main.py

+24-4
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,34 @@ def cli(arghandling=ArgHandling(), argparser=ArgumentParser(prog=__app_name__,
6161
if args.ssh_key:
6262
ssh_cmd = {'GIT_SSH_COMMAND': 'ssh -l {} -i {}'.format('git', args.ssh_key)}
6363

64+
features_dir = '/'
6465
# A remote repository used here
6566
if args.features.startswith(('http', 'https', 'ssh')):
66-
features_git_repo = args.features
67+
# Default to master branch and full repository
68+
if args.features.endswith('.git'):
69+
features_git_repo = args.features
70+
features_git_branch = 'master'
71+
72+
# Optionally allow for directory and branch
73+
elif '.git//' in args.features and '?ref=' in args.features:
74+
# Split on .git/
75+
features_git_list = args.features.split('.git/', 1)
76+
# Everything up to .git is the repository
77+
features_git_repo = features_git_list[0] + '.git'
78+
79+
# Split the directory and branch ref
80+
features_git_list = features_git_list[1].split('?ref=', 1)
81+
features_dir = features_git_list[0]
82+
features_git_branch = features_git_list[1]
83+
84+
else: # invalid
85+
raise ValueError("Bad feature directory:" + args.features)
86+
87+
# Clone repository
6788
args.features = mkdtemp()
89+
Repo.clone_from(url=features_git_repo, to_path=args.features, env=ssh_cmd, depth=1, branch=features_git_branch)
6890

69-
Repo.clone_from(url=features_git_repo, to_path=args.features, env=ssh_cmd)
70-
71-
features_directory = os.path.join(os.path.abspath(args.features))
91+
features_directory = os.path.join(os.path.abspath(args.features) + features_dir)
7292

7393
commands = ['radish',
7494
'--write-steps-once',

terraform_compliance/steps/then/it_must_contain_something.py

+2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ def it_must_contain_something(_step_obj, something, inherited_values=Null):
4040

4141
if isinstance(found_key, dict):
4242
found_value = jsonify(found_key.get(something, found_key))
43+
found_value = found_value if found_value not in ([], '') else found_key
4344
else:
4445
found_value = found_key
4546
elif isinstance(values, list):
@@ -137,6 +138,7 @@ def it_must_not_contain_something(_step_obj, something, inherited_values=Null):
137138

138139
if isinstance(found_key, dict):
139140
found_value = jsonify(found_key.get(something, found_key))
141+
found_value = found_value if found_value not in ([], '') else found_key
140142
else:
141143
found_value = found_key
142144
elif isinstance(values, list):

terraform_compliance/steps/when/its_key_is_value.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,10 @@ def to_lower_key(d):
6969
_step_obj.context.stash = found_list
7070
_step_obj.context.addresses = get_resource_address_list_from_stash(found_list)
7171
else:
72-
if dict_value is None:
72+
if object_key is Null:
73+
skip_step(_step_obj, message='Could not find {} in {}.'.format(key,
74+
', '.join(_step_obj.context.addresses)))
75+
elif dict_value is None:
7376
skip_step(_step_obj, message='Can not find {} {} in {}.'.format(value, orig_key,
7477
', '.join(_step_obj.context.addresses)))
7578
else:
@@ -126,7 +129,10 @@ def its_key_is_not_value(_step_obj, key, value, dict_value=None, address=Null):
126129
_step_obj.context.stash = found_list
127130
_step_obj.context.addresses = get_resource_address_list_from_stash(found_list)
128131
else:
129-
if dict_value is None:
132+
if object_key is Null:
133+
skip_step(_step_obj, message='Could not find {} in {}.'.format(key,
134+
', '.join(_step_obj.context.addresses)))
135+
elif dict_value is None:
130136
skip_step(_step_obj, message='Found {} {} in {}.'.format(value, orig_key,
131137
', '.join(_step_obj.context.addresses)))
132138
else:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Failure: module.alb.aws_alb.alb \(aws_alb\) does not have bad_something_waf_custom property.

tests/functional/test_issue-249-bad_feature/.failure

Whitespace-only changes.

tests/functional/test_issue-249-bad_feature/plan.out.json

+1
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Fixed version of the features provided on issue 249.
2+
# Bad feature: Last line changed to something that's not in tags
3+
Feature: Resources should be properly tagged
4+
In order to keep track of resource ownership
5+
As engineers
6+
We'll enforce tagging on all resources
7+
8+
# Check required tag for ALB and CF
9+
Scenario: Ensure that waf_policy for ALB/CF tag is defined
10+
Given I have aws_alb defined
11+
When it contains tags
12+
Then it must contain waf_policy
13+
And its value must match the "^(internal|external|custom)$" regex
14+
15+
Scenario: Ensure that waf_custom for ALB/CF tag is defined
16+
Given I have aws_alb defined
17+
When it contains tags
18+
Then it must contain bad_something_waf_custom

tests/functional/test_issue-249-has/plan.out.json

+1
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Fixed version of the features provided on issue 249
2+
# Has: "when it contains" steps are changed to "when it has"
3+
Feature: Resources should be properly tagged
4+
In order to keep track of resource ownership
5+
As engineers
6+
We'll enforce tagging on all resources
7+
8+
# Check required tag for ALB and CF
9+
Scenario: Ensure that waf_policy for ALB/CF tag is defined
10+
Given I have aws_alb defined
11+
When it has tags
12+
Then it must contain tags
13+
Then it must contain waf_policy
14+
And its value must match the "^(internal|external|custom)$" regex
15+
16+
Scenario: Ensure that waf_custom for ALB/CF tag is defined
17+
Given I have aws_alb defined
18+
When it has tags
19+
Then it must contain waf_custom
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Failure: waf_policy property exists in module.alb.aws_alb.alb \(aws_alb\).
2+
Failure: waf_custom property exists in module.alb.aws_alb.alb \(aws_alb\).

tests/functional/test_issue-249-must_not/.failure

Whitespace-only changes.

tests/functional/test_issue-249-must_not/plan.out.json

+1
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Fixed version of the features provided on issue 249
2+
# Must not: Testing must not changes
3+
Feature: Resources should be properly tagged
4+
In order to keep track of resource ownership
5+
As engineers
6+
We'll enforce tagging on all resources
7+
8+
# Check required tag for ALB and CF
9+
Scenario: Ensure that waf_policy for ALB/CF tag is defined
10+
Given I have aws_alb defined
11+
When it contains tags
12+
Then it must not contain waf_policy
13+
14+
Scenario: Ensure that waf_custom for ALB/CF tag is defined
15+
Given I have aws_alb defined
16+
When it contains tags
17+
Then it must not contain waf_custom

tests/functional/test_issue-249-must_not_bad_feature/plan.out.json

+1
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Fixed version of the features provided on issue 249
2+
# Must not: Testing must not changes
3+
# Bad feature: Last line changed to something that's not in tags
4+
Feature: Resources should be properly tagged
5+
In order to keep track of resource ownership
6+
As engineers
7+
We'll enforce tagging on all resources
8+
9+
# Check required tag for ALB and CF
10+
Scenario: Ensure that waf_policy for ALB/CF tag is defined
11+
Given I have aws_alb defined
12+
When it contains tags
13+
Then it must not contain bad_waf_policy
14+
15+
Scenario: Ensure that waf_custom for ALB/CF tag is defined
16+
Given I have aws_alb defined
17+
When it contains tags
18+
Then it must not contain bad_something_waf_custom

tests/functional/test_issue-249/plan.out.json

+1
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Fixed version of the features provided on issue 249
2+
Feature: Resources should be properly tagged
3+
In order to keep track of resource ownership
4+
As engineers
5+
We'll enforce tagging on all resources
6+
7+
# Check required tag for ALB and CF
8+
Scenario: Ensure that waf_policy for ALB/CF tag is defined
9+
Given I have aws_alb defined
10+
When it contains tags
11+
Then it must contain waf_policy
12+
And its value must match the "^(internal|external|custom)$" regex
13+
14+
Scenario: Ensure that waf_custom for ALB/CF tag is defined
15+
Given I have aws_alb defined
16+
When it contains tags
17+
Then it must contain waf_custom
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
SKIPPING: Could not find permissions in aws_s3_bucket.bucket.
+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
resource "aws_s3_bucket" "bucket" {
2+
bucket = "mybucket"
3+
4+
grant {
5+
id = "current_user_id"
6+
type = "CanonicalUser"
7+
permissions = ["FULL_CONTROL"]
8+
}
9+
10+
grant {
11+
type = "Group"
12+
permissions = ["READ", "WRITE"]
13+
uri = "http://acs.amazonaws.com/groups/s3/LogDelivery"
14+
}
15+
}
16+
17+
# Create a new load balancer
18+
resource "aws_elb" "bar" {
19+
name = "foobar-terraform-elb"
20+
availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"]
21+
22+
access_logs {
23+
bucket = "foo"
24+
bucket_prefix = "bar"
25+
interval = 60
26+
}
27+
28+
listener {
29+
instance_port = 8000
30+
instance_protocol = "http"
31+
lb_port = 80
32+
lb_protocol = "http"
33+
}
34+
35+
listener {
36+
instance_port = 8000
37+
instance_protocol = "http"
38+
lb_port = 443
39+
lb_protocol = "https"
40+
ssl_certificate_id = "arn:aws:iam::123456789012:server-certificate/certName"
41+
}
42+
43+
health_check {
44+
healthy_threshold = 2
45+
unhealthy_threshold = 2
46+
timeout = 3
47+
target = "HTTP:8000/"
48+
interval = 30
49+
}
50+
51+
cross_zone_load_balancing = true
52+
idle_timeout = 400
53+
connection_draining = true
54+
connection_draining_timeout = 400
55+
56+
tags = {
57+
Name = "foobar-terraform-elb"
58+
}
59+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"format_version":"0.1","terraform_version":"0.12.25","planned_values":{"root_module":{"resources":[{"address":"aws_elb.bar","mode":"managed","type":"aws_elb","name":"bar","provider_name":"aws","schema_version":0,"values":{"access_logs":[{"bucket":"foo","bucket_prefix":"bar","enabled":true,"interval":60}],"availability_zones":["us-west-2a","us-west-2b","us-west-2c"],"connection_draining":true,"connection_draining_timeout":400,"cross_zone_load_balancing":true,"health_check":[{"healthy_threshold":2,"interval":30,"target":"HTTP:8000/","timeout":3,"unhealthy_threshold":2}],"idle_timeout":400,"listener":[{"instance_port":8000,"instance_protocol":"http","lb_port":443,"lb_protocol":"https","ssl_certificate_id":"arn:aws:iam::123456789012:server-certificate/certName"},{"instance_port":8000,"instance_protocol":"http","lb_port":80,"lb_protocol":"http","ssl_certificate_id":""}],"name":"foobar-terraform-elb","name_prefix":null,"tags":{"Name":"foobar-terraform-elb"}}},{"address":"aws_s3_bucket.bucket","mode":"managed","type":"aws_s3_bucket","name":"bucket","provider_name":"aws","schema_version":0,"values":{"acl":"private","bucket":"mybucket","bucket_prefix":null,"cors_rule":[],"force_destroy":false,"grant":[{"id":"","permissions":["READ","WRITE"],"type":"Group","uri":"http://acs.amazonaws.com/groups/s3/LogDelivery"},{"id":"current_user_id","permissions":["FULL_CONTROL"],"type":"CanonicalUser","uri":""}],"lifecycle_rule":[],"logging":[],"object_lock_configuration":[],"policy":null,"replication_configuration":[],"server_side_encryption_configuration":[],"tags":null,"website":[]}}]}},"resource_changes":[{"address":"aws_elb.bar","mode":"managed","type":"aws_elb","name":"bar","provider_name":"aws","change":{"actions":["create"],"before":null,"after":{"access_logs":[{"bucket":"foo","bucket_prefix":"bar","enabled":true,"interval":60}],"availability_zones":["us-west-2a","us-west-2b","us-west-2c"],"connection_draining":true,"connection_draining_timeout":400,"cross_zone_load_balancing":true,"health_check":[{"healthy_threshold":2,"interval":30,"target":"HTTP:8000/","timeout":3,"unhealthy_threshold":2}],"idle_timeout":400,"listener":[{"instance_port":8000,"instance_protocol":"http","lb_port":443,"lb_protocol":"https","ssl_certificate_id":"arn:aws:iam::123456789012:server-certificate/certName"},{"instance_port":8000,"instance_protocol":"http","lb_port":80,"lb_protocol":"http","ssl_certificate_id":""}],"name":"foobar-terraform-elb","name_prefix":null,"tags":{"Name":"foobar-terraform-elb"}},"after_unknown":{"access_logs":[{}],"arn":true,"availability_zones":[false,false,false],"dns_name":true,"health_check":[{}],"id":true,"instances":true,"internal":true,"listener":[{},{}],"security_groups":true,"source_security_group":true,"source_security_group_id":true,"subnets":true,"tags":{},"zone_id":true}}},{"address":"aws_s3_bucket.bucket","mode":"managed","type":"aws_s3_bucket","name":"bucket","provider_name":"aws","change":{"actions":["create"],"before":null,"after":{"acl":"private","bucket":"mybucket","bucket_prefix":null,"cors_rule":[],"force_destroy":false,"grant":[{"id":"","permissions":["READ","WRITE"],"type":"Group","uri":"http://acs.amazonaws.com/groups/s3/LogDelivery"},{"id":"current_user_id","permissions":["FULL_CONTROL"],"type":"CanonicalUser","uri":""}],"lifecycle_rule":[],"logging":[],"object_lock_configuration":[],"policy":null,"replication_configuration":[],"server_side_encryption_configuration":[],"tags":null,"website":[]},"after_unknown":{"acceleration_status":true,"arn":true,"bucket_domain_name":true,"bucket_regional_domain_name":true,"cors_rule":[],"grant":[{"permissions":[false,false]},{"permissions":[false]}],"hosted_zone_id":true,"id":true,"lifecycle_rule":[],"logging":[],"object_lock_configuration":[],"region":true,"replication_configuration":[],"request_payer":true,"server_side_encryption_configuration":[],"versioning":true,"website":[],"website_domain":true,"website_endpoint":true}}}],"configuration":{"root_module":{"resources":[{"address":"aws_elb.bar","mode":"managed","type":"aws_elb","name":"bar","provider_config_key":"aws","expressions":{"access_logs":[{"bucket":{"constant_value":"foo"},"bucket_prefix":{"constant_value":"bar"},"interval":{"constant_value":60}}],"availability_zones":{"constant_value":["us-west-2a","us-west-2b","us-west-2c"]},"connection_draining":{"constant_value":true},"connection_draining_timeout":{"constant_value":400},"cross_zone_load_balancing":{"constant_value":true},"health_check":[{"healthy_threshold":{"constant_value":2},"interval":{"constant_value":30},"target":{"constant_value":"HTTP:8000/"},"timeout":{"constant_value":3},"unhealthy_threshold":{"constant_value":2}}],"idle_timeout":{"constant_value":400},"listener":[{"instance_port":{"constant_value":8000},"instance_protocol":{"constant_value":"http"},"lb_port":{"constant_value":80},"lb_protocol":{"constant_value":"http"}},{"instance_port":{"constant_value":8000},"instance_protocol":{"constant_value":"http"},"lb_port":{"constant_value":443},"lb_protocol":{"constant_value":"https"},"ssl_certificate_id":{"constant_value":"arn:aws:iam::123456789012:server-certificate/certName"}}],"name":{"constant_value":"foobar-terraform-elb"},"tags":{"constant_value":{"Name":"foobar-terraform-elb"}}},"schema_version":0},{"address":"aws_s3_bucket.bucket","mode":"managed","type":"aws_s3_bucket","name":"bucket","provider_config_key":"aws","expressions":{"bucket":{"constant_value":"mybucket"},"grant":[{"id":{"constant_value":"current_user_id"},"permissions":{"constant_value":["FULL_CONTROL"]},"type":{"constant_value":"CanonicalUser"}},{"permissions":{"constant_value":["READ","WRITE"]},"type":{"constant_value":"Group"},"uri":{"constant_value":"http://acs.amazonaws.com/groups/s3/LogDelivery"}}]},"schema_version":0}]}}}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Feature: Feature for issue 284
2+
In order to something
3+
As engineers
4+
We'll enforce something else
5+
6+
Scenario Outline: Ensure S3 Bucket's ACL grant does not include write permissions, test2
7+
Given I have aws_s3_bucket defined
8+
When it has grant
9+
And its permissions does not include <value>
10+
Examples:
11+
| value |
12+
| WRITE_ACP |
13+
| WRITE_bidi |
14+
| FULL_CONTROL_d |
15+
| Something_bad |

0 commit comments

Comments
 (0)