-
Notifications
You must be signed in to change notification settings - Fork 9.5k
/
validate.go
138 lines (114 loc) · 4.09 KB
/
validate.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
package views
import (
"encoding/json"
"fmt"
"github.com/hashicorp/terraform/command/arguments"
"github.com/hashicorp/terraform/command/format"
viewsjson "github.com/hashicorp/terraform/command/views/json"
"github.com/hashicorp/terraform/tfdiags"
)
// The Validate is used for the validate command.
type Validate interface {
// Results renders the diagnostics returned from a validation walk, and
// returns a CLI exit code: 0 if there are no errors, 1 otherwise
Results(diags tfdiags.Diagnostics) int
// Diagnostics renders early diagnostics, resulting from argument parsing.
Diagnostics(diags tfdiags.Diagnostics)
}
// NewValidate returns an initialized Validate implementation for the given ViewType.
func NewValidate(vt arguments.ViewType, view *View) Validate {
switch vt {
case arguments.ViewJSON:
return &ValidateJSON{view: view}
case arguments.ViewHuman:
return &ValidateHuman{view: view}
default:
panic(fmt.Sprintf("unknown view type %v", vt))
}
}
// The ValidateHuman implementation renders diagnostics in a human-readable form,
// along with a success/failure message if Terraform is able to execute the
// validation walk.
type ValidateHuman struct {
view *View
}
var _ Validate = (*ValidateHuman)(nil)
func (v *ValidateHuman) Results(diags tfdiags.Diagnostics) int {
columns := v.view.outputColumns()
if len(diags) == 0 {
v.view.streams.Println(format.WordWrap(v.view.colorize.Color(validateSuccess), columns))
} else {
v.Diagnostics(diags)
if !diags.HasErrors() {
v.view.streams.Println(format.WordWrap(v.view.colorize.Color(validateWarnings), columns))
}
}
if diags.HasErrors() {
return 1
}
return 0
}
const validateSuccess = "[green][bold]Success![reset] The configuration is valid.\n"
const validateWarnings = "[green][bold]Success![reset] The configuration is valid, but there were some validation warnings as shown above.\n"
func (v *ValidateHuman) Diagnostics(diags tfdiags.Diagnostics) {
v.view.Diagnostics(diags)
}
// The ValidateJSON implementation renders validation results as a JSON object.
// This object includes top-level fields summarizing the result, and an array
// of JSON diagnostic objects.
type ValidateJSON struct {
view *View
}
var _ Validate = (*ValidateJSON)(nil)
func (v *ValidateJSON) Results(diags tfdiags.Diagnostics) int {
// FormatVersion represents the version of the json format and will be
// incremented for any change to this format that requires changes to a
// consuming parser.
const FormatVersion = "0.1"
type Output struct {
FormatVersion string `json:"format_version"`
// We include some summary information that is actually redundant
// with the detailed diagnostics, but avoids the need for callers
// to re-implement our logic for deciding these.
Valid bool `json:"valid"`
ErrorCount int `json:"error_count"`
WarningCount int `json:"warning_count"`
Diagnostics []*viewsjson.Diagnostic `json:"diagnostics"`
}
output := Output{
FormatVersion: FormatVersion,
Valid: true, // until proven otherwise
}
configSources := v.view.configSources()
for _, diag := range diags {
output.Diagnostics = append(output.Diagnostics, viewsjson.NewDiagnostic(diag, configSources))
switch diag.Severity() {
case tfdiags.Error:
output.ErrorCount++
output.Valid = false
case tfdiags.Warning:
output.WarningCount++
}
}
if output.Diagnostics == nil {
// Make sure this always appears as an array in our output, since
// this is easier to consume for dynamically-typed languages.
output.Diagnostics = []*viewsjson.Diagnostic{}
}
j, err := json.MarshalIndent(&output, "", " ")
if err != nil {
// Should never happen because we fully-control the input here
panic(err)
}
v.view.streams.Println(string(j))
if diags.HasErrors() {
return 1
}
return 0
}
// Diagnostics should only be called if the validation walk cannot be executed.
// In this case, we choose to render human-readable diagnostic output,
// primarily for backwards compatibility.
func (v *ValidateJSON) Diagnostics(diags tfdiags.Diagnostics) {
v.view.Diagnostics(diags)
}