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  }