github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/command/views/validate.go (about) 1 package views 2 3 import ( 4 "encoding/json" 5 "fmt" 6 7 "github.com/hashicorp/terraform/internal/command/arguments" 8 "github.com/hashicorp/terraform/internal/command/format" 9 viewsjson "github.com/hashicorp/terraform/internal/command/views/json" 10 "github.com/hashicorp/terraform/internal/tfdiags" 11 ) 12 13 // The Validate is used for the validate command. 14 type Validate interface { 15 // Results renders the diagnostics returned from a validation walk, and 16 // returns a CLI exit code: 0 if there are no errors, 1 otherwise 17 Results(diags tfdiags.Diagnostics) int 18 19 // Diagnostics renders early diagnostics, resulting from argument parsing. 20 Diagnostics(diags tfdiags.Diagnostics) 21 } 22 23 // NewValidate returns an initialized Validate implementation for the given ViewType. 24 func NewValidate(vt arguments.ViewType, view *View) Validate { 25 switch vt { 26 case arguments.ViewJSON: 27 return &ValidateJSON{view: view} 28 case arguments.ViewHuman: 29 return &ValidateHuman{view: view} 30 default: 31 panic(fmt.Sprintf("unknown view type %v", vt)) 32 } 33 } 34 35 // The ValidateHuman implementation renders diagnostics in a human-readable form, 36 // along with a success/failure message if Terraform is able to execute the 37 // validation walk. 38 type ValidateHuman struct { 39 view *View 40 } 41 42 var _ Validate = (*ValidateHuman)(nil) 43 44 func (v *ValidateHuman) Results(diags tfdiags.Diagnostics) int { 45 columns := v.view.outputColumns() 46 47 if len(diags) == 0 { 48 v.view.streams.Println(format.WordWrap(v.view.colorize.Color(validateSuccess), columns)) 49 } else { 50 v.Diagnostics(diags) 51 52 if !diags.HasErrors() { 53 v.view.streams.Println(format.WordWrap(v.view.colorize.Color(validateWarnings), columns)) 54 } 55 } 56 57 if diags.HasErrors() { 58 return 1 59 } 60 return 0 61 } 62 63 const validateSuccess = "[green][bold]Success![reset] The configuration is valid.\n" 64 65 const validateWarnings = "[green][bold]Success![reset] The configuration is valid, but there were some validation warnings as shown above.\n" 66 67 func (v *ValidateHuman) Diagnostics(diags tfdiags.Diagnostics) { 68 v.view.Diagnostics(diags) 69 } 70 71 // The ValidateJSON implementation renders validation results as a JSON object. 72 // This object includes top-level fields summarizing the result, and an array 73 // of JSON diagnostic objects. 74 type ValidateJSON struct { 75 view *View 76 } 77 78 var _ Validate = (*ValidateJSON)(nil) 79 80 func (v *ValidateJSON) Results(diags tfdiags.Diagnostics) int { 81 // FormatVersion represents the version of the json format and will be 82 // incremented for any change to this format that requires changes to a 83 // consuming parser. 84 const FormatVersion = "1.0" 85 86 type Output struct { 87 FormatVersion string `json:"format_version"` 88 89 // We include some summary information that is actually redundant 90 // with the detailed diagnostics, but avoids the need for callers 91 // to re-implement our logic for deciding these. 92 Valid bool `json:"valid"` 93 ErrorCount int `json:"error_count"` 94 WarningCount int `json:"warning_count"` 95 Diagnostics []*viewsjson.Diagnostic `json:"diagnostics"` 96 } 97 98 output := Output{ 99 FormatVersion: FormatVersion, 100 Valid: true, // until proven otherwise 101 } 102 configSources := v.view.configSources() 103 for _, diag := range diags { 104 output.Diagnostics = append(output.Diagnostics, viewsjson.NewDiagnostic(diag, configSources)) 105 106 switch diag.Severity() { 107 case tfdiags.Error: 108 output.ErrorCount++ 109 output.Valid = false 110 case tfdiags.Warning: 111 output.WarningCount++ 112 } 113 } 114 if output.Diagnostics == nil { 115 // Make sure this always appears as an array in our output, since 116 // this is easier to consume for dynamically-typed languages. 117 output.Diagnostics = []*viewsjson.Diagnostic{} 118 } 119 120 j, err := json.MarshalIndent(&output, "", " ") 121 if err != nil { 122 // Should never happen because we fully-control the input here 123 panic(err) 124 } 125 v.view.streams.Println(string(j)) 126 127 if diags.HasErrors() { 128 return 1 129 } 130 return 0 131 } 132 133 // Diagnostics should only be called if the validation walk cannot be executed. 134 // In this case, we choose to render human-readable diagnostic output, 135 // primarily for backwards compatibility. 136 func (v *ValidateJSON) Diagnostics(diags tfdiags.Diagnostics) { 137 v.view.Diagnostics(diags) 138 }