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  }