mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-25 08:21:07 -06:00
cb2e9119aa
Signed-off-by: namgyalangmo <75657887+namgyalangmo@users.noreply.github.com>
122 lines
4.8 KiB
Go
122 lines
4.8 KiB
Go
// Copyright (c) The OpenTofu Authors
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
// Copyright (c) 2023 HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package checks
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/opentofu/opentofu/internal/addrs"
|
|
)
|
|
|
|
// These are the "Report"-prefixed methods of Checks used by OpenTofu Core
|
|
// to gradually signal the results of checks during a plan or apply operation.
|
|
|
|
// ReportCheckableObjects is the interface by which OpenTofu 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
|
|
// This is not a problem as the result is already cached.
|
|
return
|
|
}
|
|
|
|
// At this point we pre-populate all of the check results as StatusUnknown,
|
|
// so that even if we never hear from OpenTofu Core again we'll still
|
|
// remember that these results were all pending.
|
|
st.objects = addrs.MakeMap[addrs.Checkable, map[addrs.CheckRuleType][]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.CheckRuleType][]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 OpenTofu 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.CheckRuleType, 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.CheckRuleType, 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.CheckRule, string]()
|
|
}
|
|
checkAddr := addrs.NewCheckRule(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.CheckRuleType, 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
|
|
|
|
}
|