github.com/ezbercih/terraform@v0.1.1-0.20140729011846-3c33865e0839/command/hook_ui.go (about) 1 package command 2 3 import ( 4 "bytes" 5 "fmt" 6 "sort" 7 "strings" 8 "sync" 9 10 "github.com/hashicorp/terraform/terraform" 11 "github.com/mitchellh/cli" 12 "github.com/mitchellh/colorstring" 13 ) 14 15 type UiHook struct { 16 terraform.NilHook 17 18 Colorize *colorstring.Colorize 19 Ui cli.Ui 20 21 l sync.Mutex 22 once sync.Once 23 resources map[string]uiResourceOp 24 ui cli.Ui 25 } 26 27 type uiResourceOp byte 28 29 const ( 30 uiResourceUnknown uiResourceOp = iota 31 uiResourceCreate 32 uiResourceModify 33 uiResourceDestroy 34 ) 35 36 func (h *UiHook) PreApply( 37 id string, 38 s *terraform.ResourceState, 39 d *terraform.ResourceDiff) (terraform.HookAction, error) { 40 h.once.Do(h.init) 41 42 op := uiResourceModify 43 if d.Destroy { 44 op = uiResourceDestroy 45 } else if s.ID == "" { 46 op = uiResourceCreate 47 } 48 49 h.l.Lock() 50 h.resources[id] = op 51 h.l.Unlock() 52 53 var operation string 54 switch op { 55 case uiResourceModify: 56 operation = "Modifying..." 57 case uiResourceDestroy: 58 operation = "Destroying..." 59 case uiResourceCreate: 60 operation = "Creating..." 61 case uiResourceUnknown: 62 return terraform.HookActionContinue, nil 63 } 64 65 attrBuf := new(bytes.Buffer) 66 67 // Get all the attributes that are changing, and sort them. Also 68 // determine the longest key so that we can align them all. 69 keyLen := 0 70 keys := make([]string, 0, len(d.Attributes)) 71 for key, _ := range d.Attributes { 72 // Skip the ID since we do that specially 73 if key == "id" { 74 continue 75 } 76 77 keys = append(keys, key) 78 if len(key) > keyLen { 79 keyLen = len(key) 80 } 81 } 82 sort.Strings(keys) 83 84 // Go through and output each attribute 85 for _, attrK := range keys { 86 attrDiff := d.Attributes[attrK] 87 88 v := attrDiff.New 89 if attrDiff.NewComputed { 90 v = "<computed>" 91 } 92 93 attrBuf.WriteString(fmt.Sprintf( 94 " %s:%s %#v => %#v\n", 95 attrK, 96 strings.Repeat(" ", keyLen-len(attrK)), 97 attrDiff.Old, 98 v)) 99 } 100 101 attrString := strings.TrimSpace(attrBuf.String()) 102 if attrString != "" { 103 attrString = "\n " + attrString 104 } 105 106 h.ui.Output(h.Colorize.Color(fmt.Sprintf( 107 "[reset][bold]%s: %s[reset_bold]%s", 108 id, 109 operation, 110 attrString))) 111 112 return terraform.HookActionContinue, nil 113 } 114 115 func (h *UiHook) PostApply( 116 id string, 117 s *terraform.ResourceState, 118 applyerr error) (terraform.HookAction, error) { 119 h.l.Lock() 120 op := h.resources[id] 121 delete(h.resources, id) 122 h.l.Unlock() 123 124 var msg string 125 switch op { 126 case uiResourceModify: 127 msg = "Modifications complete" 128 case uiResourceDestroy: 129 msg = "Destruction complete" 130 case uiResourceCreate: 131 msg = "Creation complete" 132 case uiResourceUnknown: 133 return terraform.HookActionContinue, nil 134 } 135 136 if applyerr != nil { 137 msg = fmt.Sprintf("Error: %s", applyerr) 138 } 139 140 h.ui.Output(h.Colorize.Color(fmt.Sprintf( 141 "[reset][bold]%s: %s[reset_bold]", 142 id, msg))) 143 144 return terraform.HookActionContinue, nil 145 } 146 147 func (h *UiHook) PreDiff( 148 id string, s *terraform.ResourceState) (terraform.HookAction, error) { 149 return terraform.HookActionContinue, nil 150 } 151 152 func (h *UiHook) PreProvision(id, provId string) (terraform.HookAction, error) { 153 h.ui.Output(h.Colorize.Color(fmt.Sprintf( 154 "[reset][bold]%s: Provisioning with '%s'...[reset_bold]", 155 id, provId))) 156 return terraform.HookActionContinue, nil 157 } 158 159 func (h *UiHook) PreRefresh( 160 id string, s *terraform.ResourceState) (terraform.HookAction, error) { 161 h.once.Do(h.init) 162 163 h.ui.Output(h.Colorize.Color(fmt.Sprintf( 164 "[reset][bold]%s: Refreshing state... (ID: %s)", 165 id, s.ID))) 166 return terraform.HookActionContinue, nil 167 } 168 169 func (h *UiHook) init() { 170 if h.Colorize == nil { 171 panic("colorize not given") 172 } 173 174 h.resources = make(map[string]uiResourceOp) 175 176 // Wrap the ui so that it is safe for concurrency regardless of the 177 // underlying reader/writer that is in place. 178 h.ui = &cli.ConcurrentUi{Ui: h.Ui} 179 }