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  }