-
Notifications
You must be signed in to change notification settings - Fork 10
/
state_report.go
115 lines (97 loc) · 4.64 KB
/
state_report.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
package checks
import (
"fmt"
"github.com/snyk/policy-engine/pkg/internal/terraform/addrs"
)
// These are the "Report"-prefixed methods of Checks used by Terraform Core
// to gradually signal the results of checks during a plan or apply operation.
// ReportCheckableObjects is the interface by which Terraform Core should
// tell the State object which specific checkable objects were declared
// by the given configuration object.
//
// This method will panic if the given configuration address isn't one known
// by this Checks to have pending checks, and if any of the given object
// addresses don't belong to the given configuration address.
func (c *State) ReportCheckableObjects(configAddr addrs.ConfigCheckable, objectAddrs addrs.Set[addrs.Checkable]) {
c.mu.Lock()
defer c.mu.Unlock()
st, ok := c.statuses.GetOk(configAddr)
if !ok {
panic(fmt.Sprintf("checkable objects report for unknown configuration object %s", configAddr))
}
if st.objects.Elems != nil {
// Can only report checkable objects once per configuration object
panic(fmt.Sprintf("duplicate checkable objects report for %s ", configAddr))
}
// At this point we pre-populate all of the check results as StatusUnknown,
// so that even if we never hear from Terraform Core again we'll still
// remember that these results were all pending.
st.objects = addrs.MakeMap[addrs.Checkable, map[addrs.CheckType][]Status]()
for _, objectAddr := range objectAddrs {
if gotConfigAddr := objectAddr.ConfigCheckable(); !addrs.Equivalent(configAddr, gotConfigAddr) {
// All of the given object addresses must belong to the specified configuration address
panic(fmt.Sprintf("%s belongs to %s, not %s", objectAddr, gotConfigAddr, configAddr))
}
checks := make(map[addrs.CheckType][]Status, len(st.checkTypes))
for checkType, count := range st.checkTypes {
// NOTE: This is intentionally a slice of count of the zero value
// of Status, which is StatusUnknown to represent that we don't
// yet have a report for that particular check.
checks[checkType] = make([]Status, count)
}
st.objects.Put(objectAddr, checks)
}
}
// ReportCheckResult is the interface by which Terraform Core should tell the
// State object the result of a specific check for an object that was
// previously registered with ReportCheckableObjects.
//
// If the given object address doesn't match a previously-reported object,
// or if the check index is out of bounds for the number of checks expected
// of the given type, this method will panic to indicate a bug in the caller.
//
// This method will also panic if the specified check already had a known
// status; each check should have its result reported only once.
func (c *State) ReportCheckResult(objectAddr addrs.Checkable, checkType addrs.CheckType, index int, status Status) {
c.mu.Lock()
defer c.mu.Unlock()
c.reportCheckResult(objectAddr, checkType, index, status)
}
// ReportCheckFailure is a more specialized version of ReportCheckResult which
// captures a failure outcome in particular, giving the opportunity to capture
// an author-specified error message string along with the failure.
//
// This always records the given check as having StatusFail. Don't use this for
// situations where the check condition was itself invalid, because that
// should be represented by StatusError instead, and the error signalled via
// diagnostics as normal.
func (c *State) ReportCheckFailure(objectAddr addrs.Checkable, checkType addrs.CheckType, index int, errorMessage string) {
c.mu.Lock()
defer c.mu.Unlock()
c.reportCheckResult(objectAddr, checkType, index, StatusFail)
if c.failureMsgs.Elems == nil {
c.failureMsgs = addrs.MakeMap[addrs.Check, string]()
}
checkAddr := addrs.NewCheck(objectAddr, checkType, index)
c.failureMsgs.Put(checkAddr, errorMessage)
}
// reportCheckResult is shared between both ReportCheckResult and
// ReportCheckFailure, and assumes its caller already holds the mutex.
func (c *State) reportCheckResult(objectAddr addrs.Checkable, checkType addrs.CheckType, index int, status Status) {
configAddr := objectAddr.ConfigCheckable()
st, ok := c.statuses.GetOk(configAddr)
if !ok {
panic(fmt.Sprintf("checkable object status report for unknown configuration object %s", configAddr))
}
checks, ok := st.objects.GetOk(objectAddr)
if !ok {
panic(fmt.Sprintf("checkable object status report for unexpected checkable object %s", objectAddr))
}
if index >= len(checks[checkType]) {
panic(fmt.Sprintf("%s index %d out of range for %s", checkType, index, objectAddr))
}
if checks[checkType][index] != StatusUnknown {
panic(fmt.Sprintf("duplicate status report for %s %s %d", objectAddr, checkType, index))
}
checks[checkType][index] = status
}