github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/provider/vsphere/internal/vsphereclient/status.go (about) 1 // Copyright 2015-2017 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package vsphereclient 5 6 import ( 7 "context" 8 "fmt" 9 "sync" 10 "time" 11 12 "github.com/juju/clock" 13 "github.com/juju/errors" 14 "github.com/vmware/govmomi/object" 15 "github.com/vmware/govmomi/vim25/progress" 16 "github.com/vmware/govmomi/vim25/types" 17 ) 18 19 type taskWaiter struct { 20 clock clock.Clock 21 updateProgress func(string) 22 updateProgressInterval time.Duration 23 } 24 25 func (w *taskWaiter) waitTask(ctx context.Context, t *object.Task, action string) (*types.TaskInfo, error) { 26 var info *types.TaskInfo 27 var err error 28 withStatusUpdater( 29 ctx, action, w.clock, 30 w.updateProgress, 31 w.updateProgressInterval, 32 func(ctx context.Context, sinker progress.Sinker) { 33 info, err = t.WaitForResult(ctx, sinker) 34 }, 35 ) 36 return info, errors.Trace(err) 37 } 38 39 func withStatusUpdater( 40 ctx context.Context, 41 action string, 42 clock clock.Clock, 43 updateProgress func(string), 44 updateProgressInterval time.Duration, 45 f func(context.Context, progress.Sinker), 46 ) { 47 statusUpdater := statusUpdater{ 48 ch: make(chan progress.Report), 49 clock: clock, 50 update: updateProgress, 51 action: action, 52 interval: updateProgressInterval, 53 } 54 var wg sync.WaitGroup 55 wg.Add(1) 56 ctx, cancel := context.WithCancel(ctx) 57 go func() { 58 defer wg.Done() 59 statusUpdater.loop(ctx.Done()) 60 }() 61 defer wg.Wait() 62 defer cancel() 63 f(ctx, &statusUpdater) 64 } 65 66 type statusUpdater struct { 67 clock clock.Clock 68 ch chan progress.Report 69 update func(string) 70 action string 71 interval time.Duration 72 } 73 74 // Sink is part of the progress.Sinker interface. 75 func (u *statusUpdater) Sink() chan<- progress.Report { 76 return u.ch 77 } 78 79 func (u *statusUpdater) loop(done <-chan struct{}) { 80 timer := u.clock.NewTimer(u.interval) 81 defer timer.Stop() 82 var timerChan <-chan time.Time 83 84 var message string 85 for { 86 select { 87 case <-done: 88 return 89 case <-timerChan: 90 u.update(message) 91 timer.Reset(u.interval) 92 timerChan = nil 93 case report, ok := <-u.ch: 94 if !ok { 95 return 96 } 97 if err := report.Error(); err != nil { 98 message = fmt.Sprintf("%s: %s", u.action, err) 99 } else { 100 message = fmt.Sprintf( 101 "%s: %.2f%%", 102 u.action, 103 report.Percentage(), 104 ) 105 if detail := report.Detail(); detail != "" { 106 message += " (" + detail + ")" 107 } 108 } 109 timerChan = timer.Chan() 110 } 111 } 112 }