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 }