github.com/masahide/goansible@v0.0.0-20160116054156-01eac649e9f2/reporter.go (about)

     1  package goansible
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"os"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/mgutz/ansi"
    11  )
    12  
    13  type Reporter interface {
    14  	StartTasks(r *Runner)
    15  	FinishTasks(r *Runner)
    16  	StartHandlers(r *Runner)
    17  	FinishHandlers(r *Runner)
    18  	FinishAll(r *Runner)
    19  	EndReporter(r *Runner, err error)
    20  
    21  	StartTask(task *Task, name, args string, vars Vars)
    22  	FinishTask(task *Task, res *Result)
    23  
    24  	//FinishAsyncTask(act *AsyncAction)
    25  	//Progress(str string)
    26  	//JSONProgress(data []byte) error
    27  }
    28  
    29  /*
    30  type ProgressReporter interface {
    31  	Progress(string)
    32  	JSONProgress(data []byte) error
    33  }
    34  */
    35  
    36  type CLIReporter struct {
    37  	out    io.Writer
    38  	Output bool
    39  	Debug  bool
    40  	Start  time.Time
    41  }
    42  type StructReporter struct {
    43  	Hostname       string
    44  	ServerType     string
    45  	Project        string
    46  	Start          time.Time
    47  	TasksFinishDur time.Duration
    48  	FinishDur      time.Duration
    49  	TaskCount      int
    50  	Out            []TaskResult
    51  }
    52  
    53  var sCLIReporter *CLIReporter = &CLIReporter{out: os.Stdout}
    54  var sJsonChunkReporter *JsonChunkReporter = &JsonChunkReporter{out: os.Stdout}
    55  var sStructReporter *StructReporter = &StructReporter{}
    56  
    57  var failMsg, changedMsg, okMsg func(string) string
    58  
    59  func init() {
    60  	failMsg = ansi.ColorFunc("red+b")
    61  	changedMsg = ansi.ColorFunc("yellow")
    62  	okMsg = ansi.ColorFunc("green")
    63  }
    64  
    65  func (c *CLIReporter) StartTasks(r *Runner) {
    66  	c.Start = time.Now()
    67  	fmt.Fprintf(c.out, "== tasks @ %v\n", r.Start)
    68  }
    69  
    70  func (c *CLIReporter) FinishTasks(r *Runner) {
    71  	dur := time.Since(c.Start)
    72  
    73  	fmt.Fprintf(c.out, "%7.3f = All tasks to finish\n", dur.Seconds())
    74  }
    75  
    76  func (c *CLIReporter) StartHandlers(r *Runner) {
    77  	dur := time.Since(c.Start)
    78  
    79  	fmt.Fprintf(c.out, "%7.3f + Running any handlers\n", dur.Seconds())
    80  }
    81  
    82  func (c *CLIReporter) FinishHandlers(r *Runner) {
    83  	dur := time.Since(c.Start)
    84  	fmt.Fprintf(c.out, "%7.3f = All handlers to finish\n", dur.Seconds())
    85  }
    86  func (c *CLIReporter) FinishAll(r *Runner) {
    87  	dur := time.Since(c.Start)
    88  	fmt.Fprintf(c.out, "%7.3f = All finish\n", dur.Seconds())
    89  }
    90  
    91  func (c *CLIReporter) EndReporter(r *Runner, err error) {}
    92  
    93  func (c *CLIReporter) StartTask(task *Task, name, args string, vars Vars) {
    94  	dur := time.Since(c.Start)
    95  
    96  	if task.Async() {
    97  		fmt.Fprintf(c.out, "%7.3f - %s &\n", dur.Seconds(), name)
    98  	} else {
    99  		fmt.Fprintf(c.out, "%7.3f - %s\n", dur.Seconds(), name)
   100  	}
   101  
   102  	if c.Output {
   103  		fmt.Fprintf(c.out, "%7.3f   %s: %s\n", dur.Seconds(), task.Command(), inlineVars(vars))
   104  	}
   105  }
   106  
   107  func renderShellResult(res *Result) (string, bool) {
   108  	rcv, ok := res.Get("rc")
   109  	if !ok {
   110  		return "", false
   111  	}
   112  
   113  	stdoutv, ok := res.Get("stdout")
   114  	if !ok {
   115  		return "", false
   116  	}
   117  
   118  	stderrv, ok := res.Get("stderr")
   119  	if !ok {
   120  		return "", false
   121  	}
   122  
   123  	rc := rcv.Read().(int)
   124  	stdout := stdoutv.Read().(string)
   125  	stderr := stderrv.Read().(string)
   126  
   127  	if rc == 0 && len(stdout) == 0 && len(stderr) == 0 {
   128  		return "OK", true
   129  	} else if len(stderr) == 0 && len(stdout) < 60 {
   130  		stdout = strings.Replace(stdout, "\n", " ", -1)
   131  		return fmt.Sprintf(`rc: %d, stdout: "%s"`, rc, stdout), true
   132  	}
   133  
   134  	return "", false
   135  }
   136  
   137  func (c *CLIReporter) FinishTask(task *Task, res *Result) {
   138  	if res == nil {
   139  		return
   140  	}
   141  
   142  	dur := time.Since(c.Start)
   143  
   144  	indent := fmt.Sprintf("%7.3f   ", dur.Seconds())
   145  
   146  	label := changedMsg("Changed")
   147  
   148  	if !res.Changed {
   149  		label = okMsg("OK")
   150  	}
   151  	if res.Failed {
   152  		label = failMsg("Failed")
   153  	}
   154  
   155  	if str, ok := renderShellResult(res); ok {
   156  		str = strings.TrimSpace(str)
   157  
   158  		if str != "" {
   159  			lines := strings.Split(str, "\n")
   160  			indented := strings.Join(lines, indent+"\n")
   161  
   162  			fmt.Fprintf(c.out, "%7.3f * %s:\n", dur.Seconds(), label)
   163  			fmt.Fprintf(c.out, "%7.3f   %s\n", dur.Seconds(), indented)
   164  		}
   165  
   166  		return
   167  	}
   168  
   169  	if res.Data != nil {
   170  		fmt.Fprintf(c.out, "%7.3f * %s:\n", dur.Seconds(), label)
   171  		if c.Output {
   172  			fmt.Fprintf(c.out, "%s\n", indentedVars(Vars(res.Data), indent))
   173  		}
   174  	}
   175  }
   176  
   177  /*
   178  func (c *CLIReporter) FinishAsyncTask(act *AsyncAction) {
   179  	dur := time.Since(c.Start)
   180  
   181  	if act.Error == nil {
   182  		fmt.Fprintf(c.out, "%7.3f * %s (async success)\n", dur.Seconds(), act.Task.Name())
   183  	} else {
   184  		fmt.Fprintf(c.out, "%7.3f * %s (async error:%s)\n", dur.Seconds(), act.Task.Name(), act.Error)
   185  	}
   186  }
   187  */
   188  
   189  /*
   190  func (c *CLIReporter) Progress(str string) {
   191  	dur := time.Since(c.Start)
   192  
   193  	lines := strings.Split(str, "\n")
   194  	out := strings.Join(lines, fmt.Sprintf("\n%7.3f + ", dur.Seconds()))
   195  
   196  	fmt.Fprintf(c.out, "%7.3f + %s\n", dur.Seconds(), out)
   197  }
   198  
   199  func (c *CLIReporter) JSONProgress(data []byte) error {
   200  	cr := JsonChunkReconstitute{c}
   201  	return cr.Input(data)
   202  }
   203  
   204  
   205  type AdhocProgress struct {
   206  	out   io.Writer
   207  	Start time.Time
   208  }
   209  
   210  func (a *AdhocProgress) Progress(str string) {
   211  	dur := time.Since(a.Start)
   212  
   213  	lines := strings.Split(str, "\n")
   214  	out := strings.Join(lines, fmt.Sprintf("\n%7.3f ", dur.Seconds()))
   215  
   216  	fmt.Fprintf(a.out, "%7.3f %s\n", dur.Seconds(), out)
   217  }
   218  
   219  func (a *AdhocProgress) JSONProgress(data []byte) error {
   220  	cr := JsonChunkReconstitute{a}
   221  	return cr.Input(data)
   222  }
   223  */
   224  
   225  type JsonChunkReporter struct {
   226  	out   io.Writer
   227  	Start time.Time
   228  }
   229  
   230  func (c *JsonChunkReporter) send(args ...interface{}) {
   231  	b := ijson(args...)
   232  	fmt.Fprintf(c.out, "%d\n%s\n", len(b), string(b))
   233  }
   234  
   235  func (c *JsonChunkReporter) StartTasks(r *Runner) {
   236  	c.Start = r.Start
   237  	c.send("phase", "start", "time", r.Start.String())
   238  }
   239  
   240  func (c *JsonChunkReporter) FinishTasks(r *Runner) {
   241  	c.send("phase", "finish")
   242  }
   243  
   244  func (c *JsonChunkReporter) StartHandlers(r *Runner) {
   245  	c.send("phase", "start_handlers")
   246  }
   247  
   248  func (c *JsonChunkReporter) FinishHandlers(r *Runner) {
   249  	c.send("phase", "finish_handlers")
   250  }
   251  func (c *JsonChunkReporter) FinishAll(r *Runner) {}
   252  
   253  func (c *JsonChunkReporter) EndReporter(r *Runner, err error) {}
   254  
   255  func (c *JsonChunkReporter) StartTask(task *Task, name, args string, vars Vars) {
   256  	dur := time.Since(c.Start).Seconds()
   257  
   258  	typ := "sync"
   259  
   260  	if task.Async() {
   261  		typ = "async"
   262  	}
   263  
   264  	c.send(
   265  		"phase", "start_task",
   266  		"type", typ,
   267  		"name", name,
   268  		"command", task.Command(),
   269  		"args", args,
   270  		"vars", vars,
   271  		"delta", dur)
   272  }
   273  func (c *JsonChunkReporter) FinishTask(task *Task, res *Result) {
   274  	if res == nil {
   275  		return
   276  	}
   277  
   278  	dur := time.Since(c.Start).Seconds()
   279  
   280  	c.send(
   281  		"phase", "finish_task",
   282  		"delta", dur,
   283  		"result", res)
   284  }
   285  
   286  /*
   287  func (c *JsonChunkReporter) FinishAsyncTask(act *AsyncAction) {
   288  	dur := time.Since(c.Start).Seconds()
   289  
   290  	if act.Error == nil {
   291  		c.send(
   292  			"phase", "finish_task",
   293  			"delta", dur,
   294  			"result", act.Result)
   295  	} else {
   296  		c.send(
   297  			"phase", "finish_task",
   298  			"delta", dur,
   299  			"error", act.Error)
   300  	}
   301  }
   302  */
   303  
   304  /*
   305  func (c *JsonChunkReporter) Progress(str string) {
   306  	dur := time.Since(c.Start).Seconds()
   307  
   308  	c.send(
   309  		"phase", "progress",
   310  		"delta", dur,
   311  		"progress", str)
   312  }
   313  
   314  func (c *JsonChunkReporter) JSONProgress(data []byte) error {
   315  	dur := time.Since(c.Start).Seconds()
   316  
   317  	raw := json.RawMessage(data)
   318  
   319  	c.send(
   320  		"phase", "json_progress",
   321  		"delta", dur,
   322  		"progress", &raw)
   323  
   324  	return nil
   325  }
   326  */
   327  
   328  /*
   329  type JsonChunkReconstitute struct {
   330  	report ProgressReporter
   331  }
   332  
   333  func (j *JsonChunkReconstitute) Input(data []byte) error {
   334  	m := make(map[string]interface{})
   335  
   336  	err := json.Unmarshal(data, &m)
   337  	if err != nil {
   338  		return err
   339  	}
   340  
   341  	return j.InputMap(m, 0)
   342  }
   343  
   344  func (j *JsonChunkReconstitute) InputMap(m map[string]interface{}, depth int) error {
   345  	phase, ok := m["phase"]
   346  	if !ok {
   347  		return fmt.Errorf("No phase specified")
   348  	}
   349  
   350  	var prefix string
   351  
   352  	if depth > 0 {
   353  		prefix = fmt.Sprintf("[%d] ", depth)
   354  	}
   355  
   356  	switch phase {
   357  	case "start":
   358  		time, ok := m["time"]
   359  		if !ok {
   360  			time = "(unknown)"
   361  		}
   362  
   363  		j.report.Progress(fmt.Sprintf("%sremote tasks @ %s", prefix, time))
   364  	case "start_task":
   365  		j.report.Progress(fmt.Sprintf("%s- %s", prefix, m["name"]))
   366  		mv := m["vars"].(map[string]interface{})
   367  		j.report.Progress(fmt.Sprintf("%s  %s: %s", prefix, m["command"], inlineMap(mv)))
   368  	case "finish_task":
   369  		res := m["result"].(map[string]interface{})
   370  		data := res["data"].(map[string]interface{})
   371  
   372  		label := "result"
   373  
   374  		if res["changed"].(bool) == false {
   375  			label = "ok"
   376  		} else if res["failed"].(bool) == true {
   377  			label = "failed"
   378  		}
   379  
   380  		reported := false
   381  
   382  		if v, ok := data["_result"]; ok {
   383  			if str, ok := v.(string); ok {
   384  				if str != "" {
   385  					j.report.Progress(fmt.Sprintf("%s* %s:", prefix, label))
   386  					j.report.Progress(prefix + "  " + str)
   387  				}
   388  				reported = true
   389  			}
   390  		}
   391  
   392  		if !reported {
   393  			if len(data) > 0 {
   394  				j.report.Progress(fmt.Sprintf("%s* %s:", prefix, label))
   395  				j.report.Progress(indentedMap(data, prefix+"  "))
   396  			}
   397  		}
   398  	case "json_progress":
   399  		ds := m["progress"].(map[string]interface{})
   400  
   401  		j.InputMap(ds, depth+1)
   402  	}
   403  
   404  	return nil
   405  }
   406  */
   407  
   408  type TaskResult struct {
   409  	Name   string
   410  	Cmd    string
   411  	Args   string
   412  	Vars   Vars
   413  	Dur    time.Duration
   414  	Result Result
   415  }
   416  
   417  const MaxTaskResult = 500
   418  
   419  func (c *StructReporter) StartTasks(r *Runner) {
   420  	c.TaskCount = 0
   421  	c.Start = r.Start
   422  	c.Out = make([]TaskResult, 0, MaxTaskResult)
   423  }
   424  
   425  func (c *StructReporter) FinishTasks(r *Runner) {
   426  	c.TasksFinishDur = time.Since(c.Start)
   427  }
   428  
   429  func (c *StructReporter) StartHandlers(r *Runner) {
   430  }
   431  
   432  func (c *StructReporter) FinishHandlers(r *Runner) {
   433  	c.FinishDur = time.Since(c.Start)
   434  }
   435  
   436  func (c *StructReporter) StartTask(task *Task, name, args string, vars Vars) {
   437  	if len(c.Out) < MaxTaskResult {
   438  		c.Out = append(c.Out, TaskResult{
   439  			Name: name,
   440  			Cmd:  task.Command(),
   441  			Args: args,
   442  			Vars: vars,
   443  		})
   444  	}
   445  	c.TaskCount = len(c.Out)
   446  }
   447  func (c *StructReporter) FinishTask(task *Task, res *Result) {
   448  	if res == nil {
   449  		return
   450  	}
   451  	i := c.TaskCount - 1
   452  	c.Out[i].Result = *res
   453  	c.Out[i].Dur = time.Since(c.Start)
   454  
   455  }
   456  
   457  func (c *StructReporter) FinishAll(r *Runner)              {}
   458  func (c *StructReporter) EndReporter(r *Runner, err error) {}
   459  
   460  type Svinfo struct {
   461  	Project    string `yaml:"project"`
   462  	ServerType string `yaml:"servertype"`
   463  }