github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/worker/undertaker/undertaker.go (about)

     1  // Copyright 2015-2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package undertaker
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  
     9  	"github.com/juju/juju/apiserver/params"
    10  	"github.com/juju/juju/environs"
    11  	"github.com/juju/juju/status"
    12  	"github.com/juju/juju/watcher"
    13  	"github.com/juju/juju/worker/catacomb"
    14  )
    15  
    16  // Facade covers the parts of the api/undertaker.UndertakerClient that we
    17  // need for the worker. It's more than a little raw, but we'll survive.
    18  type Facade interface {
    19  	ModelInfo() (params.UndertakerModelInfoResult, error)
    20  	WatchModelResources() (watcher.NotifyWatcher, error)
    21  	ProcessDyingModel() error
    22  	RemoveModel() error
    23  	SetStatus(status status.Status, message string, data map[string]interface{}) error
    24  }
    25  
    26  // Config holds the resources and configuration necessary to run an
    27  // undertaker worker.
    28  type Config struct {
    29  	Facade  Facade
    30  	Environ environs.Environ
    31  }
    32  
    33  // Validate returns an error if the config cannot be expected to drive
    34  // a functional undertaker worker.
    35  func (config Config) Validate() error {
    36  	if config.Facade == nil {
    37  		return errors.NotValidf("nil Facade")
    38  	}
    39  	if config.Environ == nil {
    40  		return errors.NotValidf("nil Environ")
    41  	}
    42  	return nil
    43  }
    44  
    45  // NewUndertaker returns a worker which processes a dying model.
    46  func NewUndertaker(config Config) (*Undertaker, error) {
    47  	if err := config.Validate(); err != nil {
    48  		return nil, errors.Trace(err)
    49  	}
    50  	u := &Undertaker{
    51  		config: config,
    52  	}
    53  	err := catacomb.Invoke(catacomb.Plan{
    54  		Site: &u.catacomb,
    55  		Work: u.run,
    56  	})
    57  	if err != nil {
    58  		return nil, errors.Trace(err)
    59  	}
    60  	return u, nil
    61  }
    62  
    63  type Undertaker struct {
    64  	catacomb catacomb.Catacomb
    65  	config   Config
    66  }
    67  
    68  // Kill is part of the worker.Worker interface.
    69  func (u *Undertaker) Kill() {
    70  	u.catacomb.Kill(nil)
    71  }
    72  
    73  // Wait is part of the worker.Worker interface.
    74  func (u *Undertaker) Wait() error {
    75  	return u.catacomb.Wait()
    76  }
    77  
    78  func (u *Undertaker) run() error {
    79  	result, err := u.config.Facade.ModelInfo()
    80  	if err != nil {
    81  		return errors.Trace(err)
    82  	}
    83  	if result.Error != nil {
    84  		return errors.Trace(result.Error)
    85  	}
    86  	modelInfo := result.Result
    87  
    88  	if modelInfo.Life == params.Alive {
    89  		return errors.Errorf("model still alive")
    90  	}
    91  
    92  	if modelInfo.Life == params.Dying {
    93  		// TODO(axw) 2016-04-14 #1570285
    94  		// We should update status with information
    95  		// about the remaining resources here, and
    96  		// also make the worker responsible for
    97  		// checking the emptiness criteria before
    98  		// attempting to remove the model.
    99  		if err := u.setStatus(
   100  			status.Destroying,
   101  			"cleaning up cloud resources",
   102  		); err != nil {
   103  			return errors.Trace(err)
   104  		}
   105  		// Process the dying model. This blocks until the model
   106  		// is dead or the worker is stopped.
   107  		if err := u.processDyingModel(); err != nil {
   108  			return errors.Trace(err)
   109  		}
   110  	}
   111  
   112  	// If we get this far, the model must be dead (or *have been*
   113  	// dead, but actually removed by something else since the call).
   114  	if modelInfo.IsSystem {
   115  		// Nothing to do. We don't destroy environ resources or
   116  		// delete model docs for a controller model, because we're
   117  		// running inside that controller and can't safely clean up
   118  		// our own infrastructure. (That'll be the client's job in
   119  		// the end, once we've reported that we've tidied up what we
   120  		// can, by returning nil here, indicating that we've set it
   121  		// to Dead -- implied by processDyingModel succeeding.)
   122  		return nil
   123  	}
   124  
   125  	// Now the model is known to be hosted and dead, we can tidy up any
   126  	// provider resources it might have used.
   127  	if err := u.setStatus(
   128  		status.Destroying, "tearing down cloud environment",
   129  	); err != nil {
   130  		return errors.Trace(err)
   131  	}
   132  	if err := u.config.Environ.Destroy(); err != nil {
   133  		return errors.Trace(err)
   134  	}
   135  
   136  	// Finally, remove the model.
   137  	if err := u.config.Facade.RemoveModel(); err != nil {
   138  		return errors.Annotate(err, "cannot remove model")
   139  	}
   140  	return nil
   141  }
   142  
   143  func (u *Undertaker) setStatus(modelStatus status.Status, message string) error {
   144  	return u.config.Facade.SetStatus(modelStatus, message, nil)
   145  }
   146  
   147  func (u *Undertaker) processDyingModel() error {
   148  	watcher, err := u.config.Facade.WatchModelResources()
   149  	if err != nil {
   150  		return errors.Trace(err)
   151  	}
   152  	if err := u.catacomb.Add(watcher); err != nil {
   153  		return errors.Trace(err)
   154  	}
   155  	defer watcher.Kill() // The watcher is not needed once this func returns.
   156  
   157  	for {
   158  		select {
   159  		case <-u.catacomb.Dying():
   160  			return u.catacomb.ErrDying()
   161  		case <-watcher.Changes():
   162  			// TODO(fwereade): this is wrong. If there's a time
   163  			// it's ok to ignore an error, it's when we know
   164  			// exactly what an error is/means. If there's a
   165  			// specific code for "not done yet", *that* is what
   166  			// we should be ignoring. But unknown errors are
   167  			// *unknown*, and we can't just assume that it's
   168  			// safe to continue.
   169  			err := u.config.Facade.ProcessDyingModel()
   170  			if err == nil {
   171  				// ProcessDyingModel succeeded. We're free to
   172  				// destroy any remaining environ resources.
   173  				return nil
   174  			}
   175  			// Yes, we ignore the error. See comment above.
   176  		}
   177  	}
   178  }