- @suzuki-shunsuke
- Quipper で SRE やってます
- Conftest で Terraform Configuration と k8s manifest をテスト
- k8s manifest は kustomize build で生成したものに対して実行
- Terraform は Plan File に対して実行
- ただし Backend は plan file に含まれていないので、 HCL に対して直接 conftest を実行
- PR で変更されたコードに関して CI で実行
- 失敗したら PR にコメントで通知
opa fmt
,conftest verify (policy testing)
も CI で実行
- Backend の設定
- S3 の log の保存先
- aws_cloudwatch_log_group の retention_in_days が設定されているか
- etc
- nodeAffinity を指定しているか
- Resource Limit が設定されているか
- CPU Request
- Memory Limit/Request
- Argo Rollouts の Strategy
- etc
package main
deny_aws_s3_bucket_logging_target_prefix[msg] {
walk(input.planned_values.root_module, [path, value])
value.type == "aws_s3_bucket"
exp := sprintf("s3/%s/", [value.values.bucket])
value.values.logging[_].target_prefix != exp
msg = sprintf("%s: aws_s3_bucket.logging.target_prefix should be '%s'", [value.address, exp])
}
package main
wrap_single_resource(resource) = x {
x := {"planned_values": {"root_module": {"resources": [resource]}}}
}
test_deny_aws_s3_bucket_logging_target_prefix__pass {
resource := {
"type": "aws_s3_bucket",
"address": "aws_s3_bucket.main",
"values": {"bucket": "foo", "logging": [{"target_prefix": "s3/foo/"}]},
}
result := deny_aws_s3_bucket_logging_target_prefix with input as wrap_single_resource(resource)
count(result) == 0
}
test_deny_aws_s3_bucket_logging_target_prefix__failure {
resource := {
"type": "aws_s3_bucket",
"address": "aws_s3_bucket.main",
"values": {"bucket": "foo", "logging": [{"target_prefix": "s3/foo"}]},
}
resources := wrap_single_resource(resource)
msg := sprintf("%s: aws_s3_bucket.logging.target_prefix should be '%s'", [resource.address, "s3/foo/"])
deny_aws_s3_bucket_logging_target_prefix[msg] with input as resources
}
Conftest でファイルパスに依存したテストをしたい場合、 -combine option が便利
package main
deny_slo_terraform_backend_s3_key[msg] {
elem := input[_]
path := trim_prefix(elem.path, "./") # ex. ./slo/foo/jp/state.tf => slo/foo/jp/state.tf
startswith(path, "slo/") # only slo
ps := split(path, "/")
exp := concat("/", array.slice(ps, 1, count(ps) - 1))
key := elem.contents.terraform.backend.s3.key
not contains(key, sprintf("/%s/", [exp]))
msg = sprintf("%s: S3 backend's key got %s, want */%s/*", [path, key, exp])
}
複数の環境に同じ policy を適用したいが、環境によってパラメータが違う場合
https://www.conftest.dev/options/#-data
data.yaml
aws_s3_bucket:
logging:
target_bucket: example.com
package main
import data
deny_aws_s3_bucket_logging_target_bucket[msg] {
walk(input.planned_values.root_module, [path, value])
value.type == "aws_s3_bucket"
exp := data.aws_s3_bucket.logging.target_bucket
value.values.logging[_].target_bucket != exp
msg := sprintf("%s: aws_s3_bucket.logging.target_bucket should be '%s'", [value.address, exp])
}