github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/restorewatcher/manifold.go (about)

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package restorewatcher
     5  
     6  import (
     7  	"sync"
     8  
     9  	"github.com/juju/errors"
    10  	"gopkg.in/juju/worker.v1"
    11  	"gopkg.in/juju/worker.v1/dependency"
    12  
    13  	"github.com/juju/juju/state"
    14  	workerstate "github.com/juju/juju/worker/state"
    15  )
    16  
    17  // ManifoldConfig holds the information necessary to run a restorewatcher
    18  // in a dependency.Engine.
    19  type ManifoldConfig struct {
    20  	StateName string
    21  	NewWorker func(Config) (RestoreStatusWorker, error)
    22  }
    23  
    24  // Validate validates the manifold configuration.
    25  func (config ManifoldConfig) Validate() error {
    26  	if config.StateName == "" {
    27  		return errors.NotValidf("empty StateName")
    28  	}
    29  	if config.NewWorker == nil {
    30  		return errors.NotValidf("nil NewWorker")
    31  	}
    32  	return nil
    33  }
    34  
    35  // RestoreStatusWorker is a worker that provides a means of observing
    36  // the restore status.
    37  type RestoreStatusWorker interface {
    38  	worker.Worker
    39  
    40  	// RestoreStatus returns the most recently observed restore status.
    41  	RestoreStatus() state.RestoreStatus
    42  }
    43  
    44  // Manifold returns a dependency.Manifold that will run a restorewatcher.
    45  func Manifold(config ManifoldConfig) dependency.Manifold {
    46  	return dependency.Manifold{
    47  		Inputs: []string{config.StateName},
    48  		Start:  config.start,
    49  		Output: manifoldOutput,
    50  	}
    51  }
    52  
    53  // start is a method on ManifoldConfig because it's more readable than a closure.
    54  func (config ManifoldConfig) start(context dependency.Context) (worker.Worker, error) {
    55  	if err := config.Validate(); err != nil {
    56  		return nil, errors.Trace(err)
    57  	}
    58  
    59  	var stTracker workerstate.StateTracker
    60  	if err := context.Get(config.StateName, &stTracker); err != nil {
    61  		return nil, errors.Trace(err)
    62  	}
    63  	statePool, err := stTracker.Use()
    64  	if err != nil {
    65  		return nil, errors.Trace(err)
    66  	}
    67  
    68  	st := statePool.SystemState()
    69  	w, err := config.NewWorker(Config{
    70  		RestoreInfoWatcher: RestoreInfoWatcherShim{st},
    71  	})
    72  	if err != nil {
    73  		stTracker.Done()
    74  		return nil, errors.Trace(err)
    75  	}
    76  	return &cleanupWorker{
    77  		RestoreStatusWorker: w,
    78  		cleanup:             func() { stTracker.Done() },
    79  	}, nil
    80  }
    81  
    82  func manifoldOutput(in worker.Worker, out interface{}) error {
    83  	inWorker, _ := in.(*cleanupWorker)
    84  	if inWorker == nil {
    85  		return errors.Errorf("in should be a %T; got %T", inWorker, in)
    86  	}
    87  	outf, ok := out.(*func() state.RestoreStatus)
    88  	if !ok {
    89  		return errors.Errorf("out should have type %T; got %T", outf, out)
    90  	}
    91  	*outf = inWorker.RestoreStatus
    92  	return nil
    93  }
    94  
    95  type cleanupWorker struct {
    96  	RestoreStatusWorker
    97  	cleanupOnce sync.Once
    98  	cleanup     func()
    99  }
   100  
   101  func (w *cleanupWorker) Wait() error {
   102  	err := w.RestoreStatusWorker.Wait()
   103  	w.cleanupOnce.Do(w.cleanup)
   104  	return err
   105  }