Skip to content

Commit 04c7f07

Browse files
authored
Add support for scoped data sources (#67)
1 parent 1c73e26 commit 04c7f07

File tree

6 files changed

+162
-3
lines changed

6 files changed

+162
-3
lines changed

integration/data_sources/main.tf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,13 @@ data "aws_ami" "valid" {
55
data "aws_ami" "invalid" {
66
owners = ["amazon"]
77
}
8+
9+
check "scoped" {
10+
data "aws_ami" "scoped_valid" {
11+
owners = ["self"]
12+
}
13+
14+
data "aws_ami" "scoped_invalid" {
15+
owners = ["amazon"]
16+
}
17+
}

integration/data_sources/policies/main_test.rego

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,18 @@ import future.keywords
44
mock_data_sources(type, schema, options) := terraform.mock_data_sources(type, schema, options, {"main.tf": `
55
data "aws_ami" "main" {
66
owners = ["amazon"]
7+
}
8+
9+
check "scope" {
10+
data "aws_ami" "scoped" {
11+
owners = ["amazon"]
12+
}
713
}`})
814

915
test_deny_other_ami_owners_passed if {
1016
issues := deny_other_ami_owners with terraform.data_sources as mock_data_sources
1117

12-
count(issues) == 1
18+
count(issues) == 2
1319
issue := issues[_]
1420
issue.msg == "third-party AMI is not allowed"
1521
}

integration/data_sources/result.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,26 @@
1919
}
2020
},
2121
"callers": []
22+
},
23+
{
24+
"rule": {
25+
"name": "opa_deny_other_ami_owners",
26+
"severity": "error",
27+
"link": "policies/main.rego:3"
28+
},
29+
"message": "third-party AMI is not allowed",
30+
"range": {
31+
"filename": "main.tf",
32+
"start": {
33+
"line": 15,
34+
"column": 14
35+
},
36+
"end": {
37+
"line": 15,
38+
"column": 24
39+
}
40+
},
41+
"callers": []
2242
}
2343
],
2444
"errors": []

integration/data_sources/result_test.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"rule": {
55
"name": "opa_test_deny_other_ami_owners_failed",
66
"severity": "error",
7-
"link": "policies/main_test.rego:17"
7+
"link": "policies/main_test.rego:23"
88
},
99
"message": "test failed",
1010
"range": {

opa/functions.go

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,80 @@ func dataSourcesFunc(runner tflint.Runner) *function3 {
156156
},
157157
},
158158
Func: func(_ rego.BuiltinContext, dataType *ast.Term, schema *ast.Term, options *ast.Term) (*ast.Term, error) {
159-
return typedBlockFunc(dataType, schema, options, "data", runner)
159+
var typeName string
160+
if err := ast.As(dataType.Value, &typeName); err != nil {
161+
return nil, err
162+
}
163+
var schemaJSON map[string]any
164+
if err := ast.As(schema.Value, &schemaJSON); err != nil {
165+
return nil, err
166+
}
167+
innerSchema, tyMap, err := jsonToSchema(schemaJSON, map[string]cty.Type{}, "schema")
168+
if err != nil {
169+
return nil, err
170+
}
171+
var optionJSON map[string]string
172+
if err := ast.As(options.Value, &optionJSON); err != nil {
173+
return nil, err
174+
}
175+
option, err := jsonToOption(optionJSON)
176+
if err != nil {
177+
return nil, err
178+
}
179+
180+
content, err := runner.GetModuleContent(&hclext.BodySchema{
181+
Blocks: []hclext.BlockSchema{
182+
{
183+
Type: "data",
184+
LabelNames: []string{"type", "name"},
185+
Body: innerSchema,
186+
},
187+
{
188+
Type: "check",
189+
LabelNames: []string{"name"},
190+
Body: &hclext.BodySchema{
191+
Blocks: []hclext.BlockSchema{
192+
{
193+
Type: "data",
194+
LabelNames: []string{"type", "name"},
195+
Body: innerSchema,
196+
},
197+
},
198+
},
199+
},
200+
},
201+
}, option.AsGetModuleContentOptions())
202+
if err != nil {
203+
return nil, err
204+
}
205+
206+
blocks := []*hclext.Block{}
207+
for _, block := range content.Blocks {
208+
switch block.Type {
209+
case "data":
210+
// "*" is a special character that returns all blocks
211+
if typeName == block.Labels[0] || typeName == "*" {
212+
blocks = append(blocks, block)
213+
}
214+
case "check":
215+
for _, inner := range block.Body.Blocks {
216+
if typeName == inner.Labels[0] || typeName == "*" {
217+
blocks = append(blocks, inner)
218+
}
219+
}
220+
}
221+
}
222+
223+
out, err := typedBlocksToJSON(blocks, tyMap, "schema", runner)
224+
if err != nil {
225+
return nil, err
226+
}
227+
v, err := ast.InterfaceToValue(out)
228+
if err != nil {
229+
return nil, err
230+
}
231+
232+
return ast.NewTerm(v), nil
160233
},
161234
}
162235
}

opa/functions_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,56 @@ data "aws_ami" "main" {
350350
},
351351
},
352352
},
353+
{
354+
name: "scoped data source",
355+
config: `
356+
check "scoped" {
357+
data "aws_ami" "main" {
358+
owners = ["self"]
359+
}
360+
}`,
361+
dataType: "aws_ami",
362+
schema: map[string]any{"owners": "list(string)"},
363+
want: []map[string]any{
364+
{
365+
"type": "aws_ami",
366+
"name": "main",
367+
"config": map[string]any{
368+
"owners": map[string]any{
369+
"value": []string{"self"},
370+
"unknown": false,
371+
"sensitive": false,
372+
"range": map[string]any{
373+
"filename": "main.tf",
374+
"start": map[string]int{
375+
"line": 4,
376+
"column": 12,
377+
"byte": 54,
378+
},
379+
"end": map[string]int{
380+
"line": 4,
381+
"column": 20,
382+
"byte": 62,
383+
},
384+
},
385+
},
386+
},
387+
"decl_range": map[string]any{
388+
"filename": "main.tf",
389+
"start": map[string]int{
390+
"line": 3,
391+
"column": 2,
392+
"byte": 19,
393+
},
394+
"end": map[string]int{
395+
"line": 3,
396+
"column": 23,
397+
"byte": 40,
398+
},
399+
},
400+
},
401+
},
402+
},
353403
}
354404

355405
for _, test := range tests {

0 commit comments

Comments
 (0)