github.com/kanishk98/terraform@v1.3.0-dev.0.20220917174235-661ca8088a6a/internal/states/checks.go (about)

     1  package states
     2  
     3  import (
     4  	"github.com/hashicorp/terraform/internal/addrs"
     5  	"github.com/hashicorp/terraform/internal/checks"
     6  )
     7  
     8  // CheckResults represents a summary snapshot of the status of a set of checks
     9  // declared in configuration, updated after each Terraform Core run that
    10  // changes the state or remote system in a way that might impact the check
    11  // results.
    12  //
    13  // Unlike a checks.State, this type only tracks the overall results for
    14  // each checkable object and doesn't aim to preserve the identity of individual
    15  // checks in the configuration. For our UI reporting purposes, it is entire
    16  // objects that pass or fail based on their declared checks; the individual
    17  // checks have no durable identity between runs, and so are only a language
    18  // design convenience to help authors describe various independent conditions
    19  // with different failure messages each.
    20  //
    21  // CheckResults should typically be considered immutable once constructed:
    22  // instead of updating it in-place,instead construct an entirely new
    23  // CheckResults object based on a fresh checks.State.
    24  type CheckResults struct {
    25  	// ConfigResults has all of the individual check results grouped by the
    26  	// configuration object they relate to.
    27  	//
    28  	// The top-level map here will always have a key for every configuration
    29  	// object that includes checks at the time of evaluating the results,
    30  	// even if there turned out to be no instances of that object and
    31  	// therefore no individual check results.
    32  	ConfigResults addrs.Map[addrs.ConfigCheckable, *CheckResultAggregate]
    33  }
    34  
    35  // CheckResultAggregate represents both the overall result for a particular
    36  // configured object that has checks and the individual checkable objects
    37  // it declared, if any.
    38  type CheckResultAggregate struct {
    39  	// Status is the aggregate status across all objects.
    40  	//
    41  	// Sometimes an error or check failure during planning will prevent
    42  	// Terraform Core from even determining the individual checkable objects
    43  	// associated with a downstream configuration object, and that situation is
    44  	// described here by this Status being checks.StatusUnknown and there being
    45  	// no elements in the ObjectResults field.
    46  	//
    47  	// That's different than Terraform Core explicitly reporting that there are
    48  	// no instances of the config object (e.g. a resource with count = 0),
    49  	// which leads to the aggregate status being checks.StatusPass while
    50  	// ObjectResults is still empty.
    51  	Status checks.Status
    52  
    53  	ObjectResults addrs.Map[addrs.Checkable, *CheckResultObject]
    54  }
    55  
    56  // CheckResultObject is the check status for a single checkable object.
    57  //
    58  // This aggregates together all of the checks associated with a particular
    59  // object into a single pass/fail/error/unknown result, because checkable
    60  // objects have durable addresses that can survive between runs, but their
    61  // individual checks do not. (Module authors are free to reorder their checks
    62  // for a particular object in the configuration with no change in meaning.)
    63  type CheckResultObject struct {
    64  	// Status is the check status of the checkable object, derived from the
    65  	// results of all of its individual checks.
    66  	Status checks.Status
    67  
    68  	// FailureMessages is an optional set of module-author-defined messages
    69  	// describing the problems that the checks detected, for objects whose
    70  	// status is checks.StatusFail.
    71  	//
    72  	// (checks.StatusError problems get reported as normal diagnostics during
    73  	// evaluation instead, and so will not appear here.)
    74  	FailureMessages []string
    75  }
    76  
    77  // NewCheckResults constructs a new states.CheckResults object that is a
    78  // snapshot of the check statuses recorded in the given checks.State object.
    79  //
    80  // This should be called only after a Terraform Core run has completed and
    81  // recorded any results from running the checks in the given object.
    82  func NewCheckResults(source *checks.State) *CheckResults {
    83  	ret := &CheckResults{
    84  		ConfigResults: addrs.MakeMap[addrs.ConfigCheckable, *CheckResultAggregate](),
    85  	}
    86  
    87  	for _, configAddr := range source.AllConfigAddrs() {
    88  		aggr := &CheckResultAggregate{
    89  			Status:        source.AggregateCheckStatus(configAddr),
    90  			ObjectResults: addrs.MakeMap[addrs.Checkable, *CheckResultObject](),
    91  		}
    92  
    93  		for _, objectAddr := range source.ObjectAddrs(configAddr) {
    94  			obj := &CheckResultObject{
    95  				Status:          source.ObjectCheckStatus(objectAddr),
    96  				FailureMessages: source.ObjectFailureMessages(objectAddr),
    97  			}
    98  			aggr.ObjectResults.Put(objectAddr, obj)
    99  		}
   100  
   101  		ret.ConfigResults.Put(configAddr, aggr)
   102  	}
   103  
   104  	// If there aren't actually any configuration objects then we'll just
   105  	// leave the map as a whole nil, because having it be zero-value makes
   106  	// life easier for deep comparisons in unit tests elsewhere.
   107  	if ret.ConfigResults.Len() == 0 {
   108  		ret.ConfigResults.Elems = nil
   109  	}
   110  
   111  	return ret
   112  }
   113  
   114  // GetObjectResult looks up the result for a single object, or nil if there
   115  // is no such object.
   116  //
   117  // In main code we shouldn't typically need to look up individual objects
   118  // like this, since we'll usually be reporting check results in an aggregate
   119  // form, but determining the result of a particular object is useful in our
   120  // internal unit tests, and so this is here primarily for that purpose.
   121  func (r *CheckResults) GetObjectResult(objectAddr addrs.Checkable) *CheckResultObject {
   122  	configAddr := objectAddr.ConfigCheckable()
   123  
   124  	aggr := r.ConfigResults.Get(configAddr)
   125  	if aggr == nil {
   126  		return nil
   127  	}
   128  
   129  	return aggr.ObjectResults.Get(objectAddr)
   130  }
   131  
   132  func (r *CheckResults) DeepCopy() *CheckResults {
   133  	if r == nil {
   134  		return nil
   135  	}
   136  	ret := &CheckResults{}
   137  	if r.ConfigResults.Elems == nil {
   138  		return ret
   139  	}
   140  
   141  	ret.ConfigResults = addrs.MakeMap[addrs.ConfigCheckable, *CheckResultAggregate]()
   142  
   143  	for _, configElem := range r.ConfigResults.Elems {
   144  		aggr := &CheckResultAggregate{
   145  			Status: configElem.Value.Status,
   146  		}
   147  
   148  		if configElem.Value.ObjectResults.Elems != nil {
   149  			aggr.ObjectResults = addrs.MakeMap[addrs.Checkable, *CheckResultObject]()
   150  
   151  			for _, objectElem := range configElem.Value.ObjectResults.Elems {
   152  				result := &CheckResultObject{
   153  					Status: objectElem.Value.Status,
   154  
   155  					// NOTE: We don't deep-copy this slice because it's
   156  					// immutable once constructed by convention.
   157  					FailureMessages: objectElem.Value.FailureMessages,
   158  				}
   159  				aggr.ObjectResults.Put(objectElem.Key, result)
   160  			}
   161  		}
   162  
   163  		ret.ConfigResults.Put(configElem.Key, aggr)
   164  	}
   165  
   166  	return ret
   167  }
   168  
   169  // ObjectAddrsKnown determines whether the set of objects recorded in this
   170  // aggregate is accurate (true) or if it's incomplete as a result of the
   171  // run being interrupted before instance expansion.
   172  func (r *CheckResultAggregate) ObjectAddrsKnown() bool {
   173  	if r.ObjectResults.Len() != 0 {
   174  		// If there are any object results at all then we definitely know.
   175  		return true
   176  	}
   177  
   178  	// If we don't have any object addresses then we distinguish a known
   179  	// empty set of objects from an unknown set of objects by the aggregate
   180  	// status being unknown.
   181  	return r.Status != checks.StatusUnknown
   182  }