github.com/kevinklinger/open_terraform@v1.3.6/noninternal/checks/state_report.go (about) 1 package checks 2 3 import ( 4 "fmt" 5 6 "github.com/kevinklinger/open_terraform/noninternal/addrs" 7 ) 8 9 // These are the "Report"-prefixed methods of Checks used by Terraform Core 10 // to gradually signal the results of checks during a plan or apply operation. 11 12 // ReportCheckableObjects is the interface by which Terraform Core should 13 // tell the State object which specific checkable objects were declared 14 // by the given configuration object. 15 // 16 // This method will panic if the given configuration address isn't one known 17 // by this Checks to have pending checks, and if any of the given object 18 // addresses don't belong to the given configuration address. 19 func (c *State) ReportCheckableObjects(configAddr addrs.ConfigCheckable, objectAddrs addrs.Set[addrs.Checkable]) { 20 c.mu.Lock() 21 defer c.mu.Unlock() 22 23 st, ok := c.statuses.GetOk(configAddr) 24 if !ok { 25 panic(fmt.Sprintf("checkable objects report for unknown configuration object %s", configAddr)) 26 } 27 if st.objects.Elems != nil { 28 // Can only report checkable objects once per configuration object 29 panic(fmt.Sprintf("duplicate checkable objects report for %s ", configAddr)) 30 } 31 32 // At this point we pre-populate all of the check results as StatusUnknown, 33 // so that even if we never hear from Terraform Core again we'll still 34 // remember that these results were all pending. 35 st.objects = addrs.MakeMap[addrs.Checkable, map[addrs.CheckType][]Status]() 36 for _, objectAddr := range objectAddrs { 37 if gotConfigAddr := objectAddr.ConfigCheckable(); !addrs.Equivalent(configAddr, gotConfigAddr) { 38 // All of the given object addresses must belong to the specified configuration address 39 panic(fmt.Sprintf("%s belongs to %s, not %s", objectAddr, gotConfigAddr, configAddr)) 40 } 41 42 checks := make(map[addrs.CheckType][]Status, len(st.checkTypes)) 43 for checkType, count := range st.checkTypes { 44 // NOTE: This is intentionally a slice of count of the zero value 45 // of Status, which is StatusUnknown to represent that we don't 46 // yet have a report for that particular check. 47 checks[checkType] = make([]Status, count) 48 } 49 50 st.objects.Put(objectAddr, checks) 51 } 52 } 53 54 // ReportCheckResult is the interface by which Terraform Core should tell the 55 // State object the result of a specific check for an object that was 56 // previously registered with ReportCheckableObjects. 57 // 58 // If the given object address doesn't match a previously-reported object, 59 // or if the check index is out of bounds for the number of checks expected 60 // of the given type, this method will panic to indicate a bug in the caller. 61 // 62 // This method will also panic if the specified check already had a known 63 // status; each check should have its result reported only once. 64 func (c *State) ReportCheckResult(objectAddr addrs.Checkable, checkType addrs.CheckType, index int, status Status) { 65 c.mu.Lock() 66 defer c.mu.Unlock() 67 68 c.reportCheckResult(objectAddr, checkType, index, status) 69 } 70 71 // ReportCheckFailure is a more specialized version of ReportCheckResult which 72 // captures a failure outcome in particular, giving the opportunity to capture 73 // an author-specified error message string along with the failure. 74 // 75 // This always records the given check as having StatusFail. Don't use this for 76 // situations where the check condition was itself invalid, because that 77 // should be represented by StatusError instead, and the error signalled via 78 // diagnostics as normal. 79 func (c *State) ReportCheckFailure(objectAddr addrs.Checkable, checkType addrs.CheckType, index int, errorMessage string) { 80 c.mu.Lock() 81 defer c.mu.Unlock() 82 83 c.reportCheckResult(objectAddr, checkType, index, StatusFail) 84 if c.failureMsgs.Elems == nil { 85 c.failureMsgs = addrs.MakeMap[addrs.Check, string]() 86 } 87 checkAddr := addrs.NewCheck(objectAddr, checkType, index) 88 c.failureMsgs.Put(checkAddr, errorMessage) 89 } 90 91 // reportCheckResult is shared between both ReportCheckResult and 92 // ReportCheckFailure, and assumes its caller already holds the mutex. 93 func (c *State) reportCheckResult(objectAddr addrs.Checkable, checkType addrs.CheckType, index int, status Status) { 94 configAddr := objectAddr.ConfigCheckable() 95 96 st, ok := c.statuses.GetOk(configAddr) 97 if !ok { 98 panic(fmt.Sprintf("checkable object status report for unknown configuration object %s", configAddr)) 99 } 100 101 checks, ok := st.objects.GetOk(objectAddr) 102 if !ok { 103 panic(fmt.Sprintf("checkable object status report for unexpected checkable object %s", objectAddr)) 104 } 105 106 if index >= len(checks[checkType]) { 107 panic(fmt.Sprintf("%s index %d out of range for %s", checkType, index, objectAddr)) 108 } 109 if checks[checkType][index] != StatusUnknown { 110 panic(fmt.Sprintf("duplicate status report for %s %s %d", objectAddr, checkType, index)) 111 } 112 113 checks[checkType][index] = status 114 115 }