forked from hashicorp/terraform-json
-
Notifications
You must be signed in to change notification settings - Fork 0
/
validate.go
149 lines (120 loc) · 4.74 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
139
140
141
142
143
144
145
146
147
148
149
package tfjson
import (
"encoding/json"
"errors"
"fmt"
"github.com/hashicorp/go-version"
)
// ValidateFormatVersionConstraints defines the versions of the JSON
// validate format that are supported by this package.
var ValidateFormatVersionConstraints = ">= 0.1, < 2.0"
// Pos represents a position in a config file
type Pos struct {
Line int `json:"line"`
Column int `json:"column"`
Byte int `json:"byte"`
}
// Range represents a range of bytes between two positions
type Range struct {
Filename string `json:"filename"`
Start Pos `json:"start"`
End Pos `json:"end"`
}
type DiagnosticSeverity string
// These severities map to the tfdiags.Severity values, plus an explicit
// unknown in case that enum grows without us noticing here.
const (
DiagnosticSeverityUnknown DiagnosticSeverity = "unknown"
DiagnosticSeverityError DiagnosticSeverity = "error"
DiagnosticSeverityWarning DiagnosticSeverity = "warning"
)
// Diagnostic represents information to be presented to a user about an
// error or anomaly in parsing or evaluating configuration
type Diagnostic struct {
Severity DiagnosticSeverity `json:"severity,omitempty"`
Summary string `json:"summary,omitempty"`
Detail string `json:"detail,omitempty"`
Range *Range `json:"range,omitempty"`
Snippet *DiagnosticSnippet `json:"snippet,omitempty"`
}
// DiagnosticSnippet represents source code information about the diagnostic.
// It is possible for a diagnostic to have a source (and therefore a range) but
// no source code can be found. In this case, the range field will be present and
// the snippet field will not.
type DiagnosticSnippet struct {
// Context is derived from HCL's hcled.ContextString output. This gives a
// high-level summary of the root context of the diagnostic: for example,
// the resource block in which an expression causes an error.
Context *string `json:"context"`
// Code is a possibly-multi-line string of Terraform configuration, which
// includes both the diagnostic source and any relevant context as defined
// by the diagnostic.
Code string `json:"code"`
// StartLine is the line number in the source file for the first line of
// the snippet code block. This is not necessarily the same as the value of
// Range.Start.Line, as it is possible to have zero or more lines of
// context source code before the diagnostic range starts.
StartLine int `json:"start_line"`
// HighlightStartOffset is the character offset into Code at which the
// diagnostic source range starts, which ought to be highlighted as such by
// the consumer of this data.
HighlightStartOffset int `json:"highlight_start_offset"`
// HighlightEndOffset is the character offset into Code at which the
// diagnostic source range ends.
HighlightEndOffset int `json:"highlight_end_offset"`
// Values is a sorted slice of expression values which may be useful in
// understanding the source of an error in a complex expression.
Values []DiagnosticExpressionValue `json:"values"`
}
// DiagnosticExpressionValue represents an HCL traversal string (e.g.
// "var.foo") and a statement about its value while the expression was
// evaluated (e.g. "is a string", "will be known only after apply"). These are
// intended to help the consumer diagnose why an expression caused a diagnostic
// to be emitted.
type DiagnosticExpressionValue struct {
Traversal string `json:"traversal"`
Statement string `json:"statement"`
}
// ValidateOutput represents JSON output from terraform validate
// (available from 0.12 onwards)
type ValidateOutput struct {
FormatVersion string `json:"format_version"`
Valid bool `json:"valid"`
ErrorCount int `json:"error_count"`
WarningCount int `json:"warning_count"`
Diagnostics []Diagnostic `json:"diagnostics"`
}
// Validate checks to ensure that data is present, and the
// version matches the version supported by this library.
func (vo *ValidateOutput) Validate() error {
if vo == nil {
return errors.New("validation output is nil")
}
if vo.FormatVersion == "" {
// The format was not versioned in the past
return nil
}
constraint, err := version.NewConstraint(ValidateFormatVersionConstraints)
if err != nil {
return fmt.Errorf("invalid version constraint: %w", err)
}
version, err := version.NewVersion(vo.FormatVersion)
if err != nil {
return fmt.Errorf("invalid format version %q: %w", vo.FormatVersion, err)
}
if !constraint.Check(version) {
return fmt.Errorf("unsupported validation output format version: %q does not satisfy %q",
version, constraint)
}
return nil
}
func (vo *ValidateOutput) UnmarshalJSON(b []byte) error {
type rawOutput ValidateOutput
var schemas rawOutput
err := json.Unmarshal(b, &schemas)
if err != nil {
return err
}
*vo = *(*ValidateOutput)(&schemas)
return vo.Validate()
}