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