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

     1  package jsonchecks
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"sort"
     7  
     8  	"github.com/hashicorp/terraform/internal/states"
     9  )
    10  
    11  // MarshalCheckStates is the main entry-point for this package, which takes
    12  // the top-level model object for checks in state and plan, and returns a
    13  // JSON representation of it suitable for use in public integration points.
    14  func MarshalCheckStates(results *states.CheckResults) []byte {
    15  	jsonResults := make([]checkResultStatic, 0, results.ConfigResults.Len())
    16  
    17  	for _, elem := range results.ConfigResults.Elems {
    18  		staticAddr := elem.Key
    19  		aggrResult := elem.Value
    20  
    21  		objects := make([]checkResultDynamic, 0, aggrResult.ObjectResults.Len())
    22  		for _, elem := range aggrResult.ObjectResults.Elems {
    23  			dynamicAddr := elem.Key
    24  			result := elem.Value
    25  
    26  			problems := make([]checkProblem, 0, len(result.FailureMessages))
    27  			for _, msg := range result.FailureMessages {
    28  				problems = append(problems, checkProblem{
    29  					Message: msg,
    30  				})
    31  			}
    32  			sort.Slice(problems, func(i, j int) bool {
    33  				return problems[i].Message < problems[j].Message
    34  			})
    35  
    36  			objects = append(objects, checkResultDynamic{
    37  				Address:  makeDynamicObjectAddr(dynamicAddr),
    38  				Status:   checkStatusForJSON(result.Status),
    39  				Problems: problems,
    40  			})
    41  		}
    42  
    43  		sort.Slice(objects, func(i, j int) bool {
    44  			return objects[i].Address["to_display"].(string) < objects[j].Address["to_display"].(string)
    45  		})
    46  
    47  		jsonResults = append(jsonResults, checkResultStatic{
    48  			Address:   makeStaticObjectAddr(staticAddr),
    49  			Status:    checkStatusForJSON(aggrResult.Status),
    50  			Instances: objects,
    51  		})
    52  	}
    53  
    54  	sort.Slice(jsonResults, func(i, j int) bool {
    55  		return jsonResults[i].Address["to_display"].(string) < jsonResults[j].Address["to_display"].(string)
    56  	})
    57  
    58  	ret, err := json.Marshal(jsonResults)
    59  	if err != nil {
    60  		// We totally control the input to json.Marshal, so any error here
    61  		// is a bug in the code above.
    62  		panic(fmt.Sprintf("invalid input to json.Marshal: %s", err))
    63  	}
    64  	return ret
    65  }
    66  
    67  // checkResultStatic is the container for the static, configuration-driven
    68  // idea of "checkable object" -- a resource block with conditions, for example --
    69  // which ensures that we can always say _something_ about each checkable
    70  // object in the configuration even if Terraform Core encountered an error
    71  // before being able to determine the dynamic instances of the checkable object.
    72  type checkResultStatic struct {
    73  	ExperimentalNote experimentalNote `json:"//"`
    74  
    75  	// Address is the address of the checkable object this result relates to.
    76  	Address staticObjectAddr `json:"address"`
    77  
    78  	// Status is the aggregate status for all of the dynamic objects belonging
    79  	// to this static object.
    80  	Status checkStatus `json:"status"`
    81  
    82  	// Instances contains the results for each individual dynamic object that
    83  	// belongs to this static object.
    84  	Instances []checkResultDynamic `json:"instances,omitempty"`
    85  }
    86  
    87  // checkResultDynamic describes the check result for a dynamic object, which
    88  // results from Terraform Core evaluating the "expansion" (e.g. count or for_each)
    89  // of the containing object or its own containing module(s).
    90  type checkResultDynamic struct {
    91  	// Address augments the Address of the containing checkResultStatic with
    92  	// instance-specific extra properties or overridden properties.
    93  	Address dynamicObjectAddr `json:"address"`
    94  
    95  	// Status is the status for this specific dynamic object.
    96  	Status checkStatus `json:"status"`
    97  
    98  	// Problems describes some optional details associated with a failure
    99  	// status, describing what fails.
   100  	//
   101  	// This does not include the errors for status "error", because Terraform
   102  	// Core emits those separately as normal diagnostics. However, if a
   103  	// particular object has a mixture of conditions that failed and conditions
   104  	// that were invalid then status can be "error" while simultaneously
   105  	// returning problems in this property.
   106  	Problems []checkProblem `json:"problems,omitempty"`
   107  }
   108  
   109  // checkProblem describes one of potentially several problems that led to
   110  // a check being classified as status "fail".
   111  type checkProblem struct {
   112  	// Message is the condition error message provided by the author.
   113  	Message string `json:"message"`
   114  
   115  	// We don't currently have any other problem-related data, but this is
   116  	// intentionally an object to allow us to add other data over time, such
   117  	// as the source location where the failing condition was defined.
   118  }
   119  
   120  type experimentalNote struct{}
   121  
   122  func (n experimentalNote) MarshalJSON() ([]byte, error) {
   123  	return []byte(`"EXPERIMENTAL: see docs for details"`), nil
   124  }