github.com/vektra/tachyon@v0.0.0-20150921164542-0da4f3861aef/reporter.go (about)

     1  package tachyon
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"strings"
     9  	"time"
    10  )
    11  
    12  type Reporter interface {
    13  	StartTasks(r *Runner)
    14  	FinishTasks(r *Runner)
    15  	StartHandlers(r *Runner)
    16  	FinishHandlers(r *Runner)
    17  
    18  	StartTask(task *Task, name, args string, vars Vars)
    19  	FinishTask(task *Task, res *Result)
    20  
    21  	FinishAsyncTask(act *AsyncAction)
    22  	Progress(str string)
    23  	JSONProgress(data []byte) error
    24  }
    25  
    26  type ProgressReporter interface {
    27  	Progress(string)
    28  	JSONProgress(data []byte) error
    29  }
    30  
    31  type CLIReporter struct {
    32  	out   io.Writer
    33  	Start time.Time
    34  }
    35  
    36  var sCLIReporter *CLIReporter = &CLIReporter{out: os.Stdout}
    37  
    38  func (c *CLIReporter) StartTasks(r *Runner) {
    39  	c.Start = time.Now()
    40  	fmt.Fprintf(c.out, "== tasks @ %v\n", r.Start)
    41  }
    42  
    43  func (c *CLIReporter) FinishTasks(r *Runner) {
    44  	dur := time.Since(c.Start)
    45  
    46  	fmt.Fprintf(c.out, "%7.3f ! Waiting on all tasks to finish...\n", dur.Seconds())
    47  }
    48  
    49  func (c *CLIReporter) StartHandlers(r *Runner) {
    50  	dur := time.Since(c.Start)
    51  
    52  	fmt.Fprintf(c.out, "%7.3f ! Running any handlers\n", dur.Seconds())
    53  }
    54  
    55  func (c *CLIReporter) FinishHandlers(r *Runner) {}
    56  
    57  func (c *CLIReporter) StartTask(task *Task, name, args string, vars Vars) {
    58  	dur := time.Since(c.Start)
    59  
    60  	if task.Async() {
    61  		fmt.Fprintf(c.out, "%7.3f - %s &\n", dur.Seconds(), name)
    62  	} else {
    63  		fmt.Fprintf(c.out, "%7.3f - %s\n", dur.Seconds(), name)
    64  	}
    65  
    66  	fmt.Fprintf(c.out, "%7.3f   %s: %s\n", dur.Seconds(), task.Command(), inlineVars(vars))
    67  }
    68  
    69  func (c *CLIReporter) Progress(str string) {
    70  	dur := time.Since(c.Start)
    71  
    72  	lines := strings.Split(str, "\n")
    73  	out := strings.Join(lines, fmt.Sprintf("\n%7.3f + ", dur.Seconds()))
    74  
    75  	fmt.Fprintf(c.out, "%7.3f + %s\n", dur.Seconds(), out)
    76  }
    77  
    78  func (c *CLIReporter) JSONProgress(data []byte) error {
    79  	cr := JsonChunkReconstitute{c}
    80  	return cr.Input(data)
    81  }
    82  
    83  func (c *CLIReporter) FinishTask(task *Task, res *Result) {
    84  	if res == nil {
    85  		return
    86  	}
    87  
    88  	dur := time.Since(c.Start)
    89  
    90  	indent := fmt.Sprintf("%7.3f   ", dur.Seconds())
    91  
    92  	label := "result"
    93  
    94  	if res.Changed == false {
    95  		label = "check"
    96  	} else if res.Failed == true {
    97  		label = "failed"
    98  	}
    99  
   100  	if render, ok := res.Get("_result"); ok {
   101  		out, ok := render.Read().(string)
   102  		if ok {
   103  			out = strings.TrimSpace(out)
   104  
   105  			if out != "" {
   106  				lines := strings.Split(out, "\n")
   107  				indented := strings.Join(lines, indent+"\n")
   108  
   109  				fmt.Fprintf(c.out, "%7.3f * %s:\n", dur.Seconds(), label)
   110  				fmt.Fprintf(c.out, "%7.3f   %s\n", dur.Seconds(), indented)
   111  			}
   112  
   113  			return
   114  		}
   115  	}
   116  
   117  	if len(res.Data) > 0 {
   118  		fmt.Fprintf(c.out, "%7.3f * %s:\n", dur.Seconds(), label)
   119  		fmt.Fprintf(c.out, "%s\n", indentedVars(Vars(res.Data), indent))
   120  	}
   121  }
   122  
   123  func (c *CLIReporter) FinishAsyncTask(act *AsyncAction) {
   124  	dur := time.Since(c.Start)
   125  
   126  	if act.Error == nil {
   127  		fmt.Fprintf(c.out, "%7.3f * %s (async success)\n", dur.Seconds(), act.Task.Name())
   128  	} else {
   129  		fmt.Fprintf(c.out, "%7.3f * %s (async error:%s)\n", dur.Seconds(), act.Task.Name(), act.Error)
   130  	}
   131  }
   132  
   133  type AdhocProgress struct {
   134  	out   io.Writer
   135  	Start time.Time
   136  }
   137  
   138  func (a *AdhocProgress) Progress(str string) {
   139  	dur := time.Since(a.Start)
   140  
   141  	lines := strings.Split(str, "\n")
   142  	out := strings.Join(lines, fmt.Sprintf("\n%7.3f ", dur.Seconds()))
   143  
   144  	fmt.Fprintf(a.out, "%7.3f %s\n", dur.Seconds(), out)
   145  }
   146  
   147  func (a *AdhocProgress) JSONProgress(data []byte) error {
   148  	cr := JsonChunkReconstitute{a}
   149  	return cr.Input(data)
   150  }
   151  
   152  type JsonChunkReporter struct {
   153  	out   io.Writer
   154  	Start time.Time
   155  }
   156  
   157  func (c *JsonChunkReporter) send(args ...interface{}) {
   158  	b := ijson(args...)
   159  	fmt.Fprintf(c.out, "%d\n%s\n", len(b), string(b))
   160  }
   161  
   162  var sJsonChunkReporter *JsonChunkReporter = &JsonChunkReporter{out: os.Stdout}
   163  
   164  func (c *JsonChunkReporter) StartTasks(r *Runner) {
   165  	c.Start = r.Start
   166  	c.send("phase", "start", "time", r.Start.String())
   167  }
   168  
   169  func (c *JsonChunkReporter) FinishTasks(r *Runner) {
   170  	c.send("phase", "finish")
   171  }
   172  
   173  func (c *JsonChunkReporter) StartHandlers(r *Runner) {
   174  	c.send("phase", "start_handlers")
   175  }
   176  
   177  func (c *JsonChunkReporter) FinishHandlers(r *Runner) {
   178  	c.send("phase", "finish_handlers")
   179  }
   180  
   181  func (c *JsonChunkReporter) StartTask(task *Task, name, args string, vars Vars) {
   182  	dur := time.Since(c.Start).Seconds()
   183  
   184  	typ := "sync"
   185  
   186  	if task.Async() {
   187  		typ = "async"
   188  	}
   189  
   190  	c.send(
   191  		"phase", "start_task",
   192  		"type", typ,
   193  		"name", name,
   194  		"command", task.Command(),
   195  		"args", args,
   196  		"vars", vars,
   197  		"delta", dur)
   198  }
   199  
   200  func (c *JsonChunkReporter) Progress(str string) {
   201  	dur := time.Since(c.Start).Seconds()
   202  
   203  	c.send(
   204  		"phase", "progress",
   205  		"delta", dur,
   206  		"progress", str)
   207  }
   208  
   209  func (c *JsonChunkReporter) JSONProgress(data []byte) error {
   210  	dur := time.Since(c.Start).Seconds()
   211  
   212  	raw := json.RawMessage(data)
   213  
   214  	c.send(
   215  		"phase", "json_progress",
   216  		"delta", dur,
   217  		"progress", &raw)
   218  
   219  	return nil
   220  }
   221  
   222  func (c *JsonChunkReporter) FinishTask(task *Task, res *Result) {
   223  	if res == nil {
   224  		return
   225  	}
   226  
   227  	dur := time.Since(c.Start).Seconds()
   228  
   229  	c.send(
   230  		"phase", "finish_task",
   231  		"delta", dur,
   232  		"result", res)
   233  }
   234  
   235  func (c *JsonChunkReporter) FinishAsyncTask(act *AsyncAction) {
   236  	dur := time.Since(c.Start).Seconds()
   237  
   238  	if act.Error == nil {
   239  		c.send(
   240  			"phase", "finish_task",
   241  			"delta", dur,
   242  			"result", act.Result)
   243  	} else {
   244  		c.send(
   245  			"phase", "finish_task",
   246  			"delta", dur,
   247  			"error", act.Error)
   248  	}
   249  }
   250  
   251  type JsonChunkReconstitute struct {
   252  	report ProgressReporter
   253  }
   254  
   255  func (j *JsonChunkReconstitute) Input(data []byte) error {
   256  	m := make(map[string]interface{})
   257  
   258  	err := json.Unmarshal(data, &m)
   259  	if err != nil {
   260  		return err
   261  	}
   262  
   263  	return j.InputMap(m, 0)
   264  }
   265  
   266  func (j *JsonChunkReconstitute) InputMap(m map[string]interface{}, depth int) error {
   267  	phase, ok := m["phase"]
   268  	if !ok {
   269  		return fmt.Errorf("No phase specified")
   270  	}
   271  
   272  	var prefix string
   273  
   274  	if depth > 0 {
   275  		prefix = fmt.Sprintf("[%d] ", depth)
   276  	}
   277  
   278  	switch phase {
   279  	case "start":
   280  		time, ok := m["time"]
   281  		if !ok {
   282  			time = "(unknown)"
   283  		}
   284  
   285  		j.report.Progress(fmt.Sprintf("%sremote tasks @ %s", prefix, time))
   286  	case "start_task":
   287  		j.report.Progress(fmt.Sprintf("%s- %s", prefix, m["name"]))
   288  		mv := m["vars"].(map[string]interface{})
   289  		j.report.Progress(fmt.Sprintf("%s  %s: %s", prefix, m["command"], inlineMap(mv)))
   290  	case "finish_task":
   291  		res := m["result"].(map[string]interface{})
   292  		data := res["data"].(map[string]interface{})
   293  
   294  		label := "result"
   295  
   296  		if res["changed"].(bool) == false {
   297  			label = "check"
   298  		} else if res["failed"].(bool) == true {
   299  			label = "failed"
   300  		}
   301  
   302  		reported := false
   303  
   304  		if v, ok := data["_result"]; ok {
   305  			if str, ok := v.(string); ok {
   306  				if str != "" {
   307  					j.report.Progress(fmt.Sprintf("%s* %s:", prefix, label))
   308  					j.report.Progress(prefix + "  " + str)
   309  				}
   310  				reported = true
   311  			}
   312  		}
   313  
   314  		if !reported {
   315  			if len(data) > 0 {
   316  				j.report.Progress(fmt.Sprintf("%s* %s:", prefix, label))
   317  				j.report.Progress(indentedMap(data, prefix+"  "))
   318  			}
   319  		}
   320  	case "json_progress":
   321  		ds := m["progress"].(map[string]interface{})
   322  
   323  		j.InputMap(ds, depth+1)
   324  	}
   325  
   326  	return nil
   327  }