github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/command/views/apply.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package views 5 6 import ( 7 "fmt" 8 9 "github.com/terramate-io/tf/command/arguments" 10 "github.com/terramate-io/tf/command/format" 11 "github.com/terramate-io/tf/command/views/json" 12 "github.com/terramate-io/tf/states" 13 "github.com/terramate-io/tf/terraform" 14 "github.com/terramate-io/tf/tfdiags" 15 ) 16 17 // The Apply view is used for the apply command. 18 type Apply interface { 19 ResourceCount(stateOutPath string) 20 Outputs(outputValues map[string]*states.OutputValue) 21 22 Operation() Operation 23 Hooks() []terraform.Hook 24 25 Diagnostics(diags tfdiags.Diagnostics) 26 HelpPrompt() 27 } 28 29 // NewApply returns an initialized Apply implementation for the given ViewType. 30 func NewApply(vt arguments.ViewType, destroy bool, view *View) Apply { 31 switch vt { 32 case arguments.ViewJSON: 33 return &ApplyJSON{ 34 view: NewJSONView(view), 35 destroy: destroy, 36 countHook: &countHook{}, 37 } 38 case arguments.ViewHuman: 39 return &ApplyHuman{ 40 view: view, 41 destroy: destroy, 42 inAutomation: view.RunningInAutomation(), 43 countHook: &countHook{}, 44 } 45 default: 46 panic(fmt.Sprintf("unknown view type %v", vt)) 47 } 48 } 49 50 // The ApplyHuman implementation renders human-readable text logs, suitable for 51 // a scrolling terminal. 52 type ApplyHuman struct { 53 view *View 54 55 destroy bool 56 inAutomation bool 57 58 countHook *countHook 59 } 60 61 var _ Apply = (*ApplyHuman)(nil) 62 63 func (v *ApplyHuman) ResourceCount(stateOutPath string) { 64 if v.destroy { 65 v.view.streams.Printf( 66 v.view.colorize.Color("[reset][bold][green]\nDestroy complete! Resources: %d destroyed.\n"), 67 v.countHook.Removed, 68 ) 69 } else if v.countHook.Imported > 0 { 70 v.view.streams.Printf( 71 v.view.colorize.Color("[reset][bold][green]\nApply complete! Resources: %d imported, %d added, %d changed, %d destroyed.\n"), 72 v.countHook.Imported, 73 v.countHook.Added, 74 v.countHook.Changed, 75 v.countHook.Removed, 76 ) 77 } else { 78 v.view.streams.Printf( 79 v.view.colorize.Color("[reset][bold][green]\nApply complete! Resources: %d added, %d changed, %d destroyed.\n"), 80 v.countHook.Added, 81 v.countHook.Changed, 82 v.countHook.Removed, 83 ) 84 } 85 if (v.countHook.Added > 0 || v.countHook.Changed > 0) && stateOutPath != "" { 86 v.view.streams.Printf("\n%s\n\n", format.WordWrap(stateOutPathPostApply, v.view.outputColumns())) 87 v.view.streams.Printf("State path: %s\n", stateOutPath) 88 } 89 } 90 91 func (v *ApplyHuman) Outputs(outputValues map[string]*states.OutputValue) { 92 if len(outputValues) > 0 { 93 v.view.streams.Print(v.view.colorize.Color("[reset][bold][green]\nOutputs:\n\n")) 94 NewOutput(arguments.ViewHuman, v.view).Output("", outputValues) 95 } 96 } 97 98 func (v *ApplyHuman) Operation() Operation { 99 return NewOperation(arguments.ViewHuman, v.inAutomation, v.view) 100 } 101 102 func (v *ApplyHuman) Hooks() []terraform.Hook { 103 return []terraform.Hook{ 104 v.countHook, 105 NewUiHook(v.view), 106 } 107 } 108 109 func (v *ApplyHuman) Diagnostics(diags tfdiags.Diagnostics) { 110 v.view.Diagnostics(diags) 111 } 112 113 func (v *ApplyHuman) HelpPrompt() { 114 command := "apply" 115 if v.destroy { 116 command = "destroy" 117 } 118 v.view.HelpPrompt(command) 119 } 120 121 const stateOutPathPostApply = "The state of your infrastructure has been saved to the path below. This state is required to modify and destroy your infrastructure, so keep it safe. To inspect the complete state use the `terraform show` command." 122 123 // The ApplyJSON implementation renders streaming JSON logs, suitable for 124 // integrating with other software. 125 type ApplyJSON struct { 126 view *JSONView 127 128 destroy bool 129 130 countHook *countHook 131 } 132 133 var _ Apply = (*ApplyJSON)(nil) 134 135 func (v *ApplyJSON) ResourceCount(stateOutPath string) { 136 operation := json.OperationApplied 137 if v.destroy { 138 operation = json.OperationDestroyed 139 } 140 v.view.ChangeSummary(&json.ChangeSummary{ 141 Add: v.countHook.Added, 142 Change: v.countHook.Changed, 143 Remove: v.countHook.Removed, 144 Import: v.countHook.Imported, 145 Operation: operation, 146 }) 147 } 148 149 func (v *ApplyJSON) Outputs(outputValues map[string]*states.OutputValue) { 150 outputs, diags := json.OutputsFromMap(outputValues) 151 if diags.HasErrors() { 152 v.Diagnostics(diags) 153 } else { 154 v.view.Outputs(outputs) 155 } 156 } 157 158 func (v *ApplyJSON) Operation() Operation { 159 return &OperationJSON{view: v.view} 160 } 161 162 func (v *ApplyJSON) Hooks() []terraform.Hook { 163 return []terraform.Hook{ 164 v.countHook, 165 newJSONHook(v.view), 166 } 167 } 168 169 func (v *ApplyJSON) Diagnostics(diags tfdiags.Diagnostics) { 170 v.view.Diagnostics(diags) 171 } 172 173 func (v *ApplyJSON) HelpPrompt() { 174 }