This repository has been archived by the owner on Jun 26, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 15
/
policy.go
142 lines (126 loc) · 3.26 KB
/
policy.go
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package policy
import (
"fmt"
"github.com/hashicorp/hcl2/hcl"
)
// Policy is
type Policy struct {
Config *Config
Rules []*Rule
Variables []*Variable
Outputs []*Output
}
// policySchema is the schema for the top-level of a config file. We use
// the low-level HCL API for this level so we can easily deal with each
// block type separately with its own decoding logic.
var policySchema = &hcl.BodySchema{
Blocks: []hcl.BlockHeaderSchema{
{
Type: "locals",
},
// lint
{
Type: "rule",
LabelNames: []string{"name"},
},
{
Type: "config",
},
{
Type: "function",
LabelNames: []string{"name"},
},
{
Type: "variable",
LabelNames: []string{"name"},
},
{
Type: "output",
LabelNames: []string{"name"},
},
{
Type: "debug",
LabelNames: []string{"name"},
},
},
}
// Decode is
func Decode(body hcl.Body) (*Policy, hcl.Diagnostics) {
policy := &Policy{}
content, diags := body.Content(policySchema)
for _, block := range content.Blocks {
switch block.Type {
case "variable":
cfg, cfgDiags := decodeVariableBlock(block, false)
diags = append(diags, cfgDiags...)
if cfg != nil {
policy.Variables = append(policy.Variables, cfg)
}
case "rule":
rule, ruleDiags := decodeRuleBlock(block)
diags = append(diags, ruleDiags...)
if rule != nil {
policy.Rules = append(policy.Rules, rule)
}
case "output":
output, outputDiags := decodeOutputBlock(block, false)
diags = append(diags, outputDiags...)
if output != nil {
policy.Outputs = append(policy.Outputs, output)
}
case "config":
config, configDiags := decodeConfigBlock(block)
diags = append(diags, configDiags...)
if config != nil {
policy.Config = config
}
case "function":
}
}
diags = append(diags, checkRulesUnique(policy.Rules)...)
diags = append(diags, checkRulesDependencies(policy.Rules)...)
return policy, diags
}
func checkRulesUnique(rules []*Rule) hcl.Diagnostics {
encountered := map[string]*Rule{}
var diags hcl.Diagnostics
for _, rule := range rules {
if existing, exist := encountered[rule.Name]; exist {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Duplicate rule definition",
Detail: fmt.Sprintf("A rule named %q was already defined at %s. Rule names must be unique within a policy.", existing.Name, existing.DeclRange),
Subject: &rule.DeclRange,
})
}
encountered[rule.Name] = rule
}
return diags
}
func checkRulesDependencies(rules []*Rule) hcl.Diagnostics {
var diags hcl.Diagnostics
for _, rule := range rules {
for _, dep := range rule.Dependencies {
exists := func(rules []*Rule) bool {
for _, rule := range rules {
if rule.Name == dep {
return true
}
}
return false
}(rules)
if !exists {
// TODO: Replace more suitable range with rule.DeclRange
// "rule.DeclRange" is the declaration range of this rule
// however, what we want here is the declaration range of "depends_on"
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid dependency rule",
Detail: fmt.Sprintf("A dependency rule %q specified in %q is not defined", dep, rule.Name),
Subject: &rule.DeclRange,
})
}
}
}
return diags
}