Skip to content

Commit 09d0175

Browse files
author
omenevseoglu
committed
feat: Add validation for lifecycle_rule along with terraform tests
1 parent 8f3f3d4 commit 09d0175

File tree

4 files changed

+178
-2
lines changed

4 files changed

+178
-2
lines changed

.pre-commit-config.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ repos:
33
rev: v1.96.1
44
hooks:
55
- id: terraform_fmt
6+
args:
7+
- --args=-recursive
68
- id: terraform_wrapper_module_for_each
79
- id: terraform_docs
810
args:

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ No modules.
221221
| <a name="input_inventory_self_source_destination"></a> [inventory\_self\_source\_destination](#input\_inventory\_self\_source\_destination) | Whether or not the inventory source bucket is also the destination bucket. | `bool` | `false` | no |
222222
| <a name="input_inventory_source_account_id"></a> [inventory\_source\_account\_id](#input\_inventory\_source\_account\_id) | The inventory source account id. | `string` | `null` | no |
223223
| <a name="input_inventory_source_bucket_arn"></a> [inventory\_source\_bucket\_arn](#input\_inventory\_source\_bucket\_arn) | The inventory source bucket ARN. | `string` | `null` | no |
224-
| <a name="input_lifecycle_rule"></a> [lifecycle\_rule](#input\_lifecycle\_rule) | List of maps containing configuration of object lifecycle management. | `any` | `[]` | no |
224+
| <a name="input_lifecycle_rule"></a> [lifecycle\_rule](#input\_lifecycle\_rule) | List of maps containing configuration of object lifecycle management. Each lifecycle rule must contain 'id' and either 'enabled' or 'status', and may contain: 'filter', 'abort\_incomplete\_multipart\_upload\_days', 'expiration', 'transition', 'noncurrent\_version\_expiration', or 'noncurrent\_version\_transition'. | `any` | `[]` | no |
225225
| <a name="input_logging"></a> [logging](#input\_logging) | Map containing access bucket logging configuration. | `any` | `{}` | no |
226226
| <a name="input_metric_configuration"></a> [metric\_configuration](#input\_metric\_configuration) | Map containing bucket metric configuration. | `any` | `[]` | no |
227227
| <a name="input_object_lock_configuration"></a> [object\_lock\_configuration](#input\_object\_lock\_configuration) | Map containing S3 object locking configuration. | `any` | `{}` | no |
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
# Default AWS provider configuration
2+
mock_provider "aws" {
3+
}
4+
5+
# Default required test variables
6+
variables {
7+
bucket_name = "test-bucket"
8+
kms_key_arn = "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
9+
readonly_iam_role_arns = []
10+
readwrite_iam_role_arns = []
11+
backup_enabled = false
12+
}
13+
14+
# Test 1
15+
run "verify_valid_lifecycle_rules" {
16+
command = plan
17+
18+
variables {
19+
lifecycle_rules = [
20+
{
21+
id = "log"
22+
enabled = true
23+
filter = {
24+
tags = {
25+
some = "value"
26+
another = "value2"
27+
}
28+
}
29+
transition = [
30+
{
31+
days = 30
32+
storage_class = "ONEZONE_IA"
33+
},
34+
{
35+
days = 60
36+
storage_class = "GLACIER"
37+
}
38+
]
39+
},
40+
{
41+
id = "log1"
42+
enabled = true
43+
abort_incomplete_multipart_upload_days = 7
44+
noncurrent_version_transition = [
45+
{
46+
days = 30
47+
storage_class = "STANDARD_IA"
48+
}
49+
]
50+
noncurrent_version_expiration = {
51+
days = 300
52+
}
53+
},
54+
{
55+
id = "expire_all_objects"
56+
status = "Enabled"
57+
expiration = {
58+
days = 7
59+
}
60+
noncurrent_version_expiration = {
61+
noncurrent_days = 3
62+
}
63+
abort_incomplete_multipart_upload_days = 1
64+
}
65+
]
66+
}
67+
68+
assert {
69+
condition = length(var.lifecycle_rules) == 3
70+
error_message = "Expected 3 lifecycle rules"
71+
}
72+
73+
assert {
74+
condition = alltrue([
75+
for rule in var.lifecycle_rules : contains(keys(rule), "id")
76+
])
77+
error_message = "All rules must have an id"
78+
}
79+
80+
assert {
81+
condition = alltrue([
82+
for rule in var.lifecycle_rules :
83+
anytrue([contains(keys(rule), "enabled"), contains(keys(rule), "status")])
84+
])
85+
error_message = "All rules must have either enabled or status field"
86+
}
87+
88+
assert {
89+
condition = alltrue([
90+
for rule in var.lifecycle_rules :
91+
anytrue([
92+
!contains(keys(rule), "abort_incomplete_multipart_upload_days"),
93+
can(tonumber(rule.abort_incomplete_multipart_upload_days))
94+
])
95+
])
96+
error_message = "abort_incomplete_multipart_upload_days must be a number"
97+
}
98+
}
99+
100+
# Test 2
101+
run "fail_invalid_lifecycle_rules" {
102+
command = plan
103+
104+
variables {
105+
lifecycle_rules = [
106+
{
107+
id = "log1"
108+
enabled = true
109+
abort_incomplete_multipart_upload = {
110+
days_after_initiation = "1"
111+
}
112+
noncurrent_version_transition = [
113+
{
114+
days = 30
115+
storage_class = "STANDARD_IA"
116+
}
117+
]
118+
noncurrent_version_expiration = {
119+
days = 300
120+
}
121+
}
122+
]
123+
}
124+
125+
expect_failures = [
126+
var.lifecycle_rules
127+
]
128+
129+
assert {
130+
condition = !alltrue([
131+
for rule in var.lifecycle_rules : (
132+
contains(keys(rule), "id") &&
133+
(contains(keys(rule), "enabled") || contains(keys(rule), "status")) &&
134+
alltrue([
135+
for key in keys(rule) : contains([
136+
"id",
137+
"enabled",
138+
"status",
139+
"filter",
140+
"abort_incomplete_multipart_upload_days",
141+
"expiration",
142+
"transition",
143+
"noncurrent_version_expiration",
144+
"noncurrent_version_transition"
145+
], key)
146+
])
147+
)
148+
])
149+
error_message = "Each lifecycle rule must contain 'id' and either 'enabled' or 'status', and may contain: 'filter', 'abort_incomplete_multipart_upload_days', 'expiration', 'transition', 'noncurrent_version_expiration', or 'noncurrent_version_transition'."
150+
}
151+
}

variables.tf

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,9 +191,32 @@ variable "transition_default_minimum_object_size" {
191191
}
192192

193193
variable "lifecycle_rule" {
194-
description = "List of maps containing configuration of object lifecycle management."
194+
description = "List of maps containing configuration of object lifecycle management. Each lifecycle rule must contain 'id' and either 'enabled' or 'status', and may contain: 'filter', 'abort_incomplete_multipart_upload_days', 'expiration', 'transition', 'noncurrent_version_expiration', or 'noncurrent_version_transition'."
195195
type = any
196196
default = []
197+
198+
validation {
199+
condition = alltrue([
200+
for rule in var.lifecycle_rule : (
201+
contains(keys(rule), "id") &&
202+
(contains(keys(rule), "enabled") || contains(keys(rule), "status")) &&
203+
alltrue([
204+
for key in keys(rule) : contains([
205+
"id",
206+
"enabled",
207+
"status",
208+
"filter",
209+
"abort_incomplete_multipart_upload_days",
210+
"expiration",
211+
"transition",
212+
"noncurrent_version_expiration",
213+
"noncurrent_version_transition"
214+
], key)
215+
])
216+
)
217+
])
218+
error_message = "Each lifecycle rule must contain 'id' and either 'enabled' or 'status', and may contain: 'filter', 'abort_incomplete_multipart_upload_days', 'expiration', 'transition', 'noncurrent_version_expiration', or 'noncurrent_version_transition'."
219+
}
197220
}
198221

199222
variable "replication_configuration" {

0 commit comments

Comments
 (0)