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 }