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

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package caasunitprovisioner
     5  
     6  import (
     7  	"sync"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/loggo"
    11  	"gopkg.in/juju/worker.v1"
    12  	"gopkg.in/juju/worker.v1/catacomb"
    13  
    14  	"github.com/juju/juju/core/life"
    15  )
    16  
    17  var logger = loggo.GetLogger("juju.workers.caasunitprovisioner")
    18  
    19  // Config holds configuration for the CAAS unit provisioner worker.
    20  type Config struct {
    21  	ApplicationGetter  ApplicationGetter
    22  	ApplicationUpdater ApplicationUpdater
    23  	ServiceBroker      ServiceBroker
    24  
    25  	ContainerBroker          ContainerBroker
    26  	ProvisioningInfoGetter   ProvisioningInfoGetter
    27  	ProvisioningStatusSetter ProvisioningStatusSetter
    28  	LifeGetter               LifeGetter
    29  	UnitUpdater              UnitUpdater
    30  }
    31  
    32  // Validate validates the worker configuration.
    33  func (config Config) Validate() error {
    34  	if config.ApplicationGetter == nil {
    35  		return errors.NotValidf("missing ApplicationGetter")
    36  	}
    37  	if config.ApplicationUpdater == nil {
    38  		return errors.NotValidf("missing ApplicationUpdater")
    39  	}
    40  	if config.ServiceBroker == nil {
    41  		return errors.NotValidf("missing ServiceBroker")
    42  	}
    43  	if config.ContainerBroker == nil {
    44  		return errors.NotValidf("missing ContainerBroker")
    45  	}
    46  	if config.ProvisioningInfoGetter == nil {
    47  		return errors.NotValidf("missing ProvisioningInfoGetter")
    48  	}
    49  	if config.LifeGetter == nil {
    50  		return errors.NotValidf("missing LifeGetter")
    51  	}
    52  	if config.UnitUpdater == nil {
    53  		return errors.NotValidf("missing UnitUpdater")
    54  	}
    55  	if config.ProvisioningStatusSetter == nil {
    56  		return errors.NotValidf("missing ProvisioningStatusSetter")
    57  	}
    58  	return nil
    59  }
    60  
    61  // NewWorker starts and returns a new CAAS unit provisioner worker.
    62  func NewWorker(config Config) (worker.Worker, error) {
    63  	if err := config.Validate(); err != nil {
    64  		return nil, errors.Trace(err)
    65  	}
    66  	p := &provisioner{config: config}
    67  	err := catacomb.Invoke(catacomb.Plan{
    68  		Site: &p.catacomb,
    69  		Work: p.loop,
    70  	})
    71  	return p, err
    72  }
    73  
    74  type provisioner struct {
    75  	catacomb catacomb.Catacomb
    76  	config   Config
    77  
    78  	// appWorkers holds the worker created to manage each application.
    79  	// It's defined here so that we can access it in tests.
    80  	appWorkers map[string]*applicationWorker
    81  	mu         sync.Mutex
    82  }
    83  
    84  // Kill is part of the worker.Worker interface.
    85  func (p *provisioner) Kill() {
    86  	p.catacomb.Kill(nil)
    87  }
    88  
    89  // Wait is part of the worker.Worker interface.
    90  func (p *provisioner) Wait() error {
    91  	return p.catacomb.Wait()
    92  }
    93  
    94  // These helper methods protect the appWorkers map so we can access for testing.
    95  
    96  func (p *provisioner) saveApplicationWorker(appName string, aw *applicationWorker) {
    97  	p.mu.Lock()
    98  	defer p.mu.Unlock()
    99  
   100  	if p.appWorkers == nil {
   101  		p.appWorkers = make(map[string]*applicationWorker)
   102  	}
   103  	p.appWorkers[appName] = aw
   104  }
   105  
   106  func (p *provisioner) deleteApplicationWorker(appName string) {
   107  	p.mu.Lock()
   108  	defer p.mu.Unlock()
   109  
   110  	delete(p.appWorkers, appName)
   111  }
   112  
   113  func (p *provisioner) getApplicationWorker(appName string) (*applicationWorker, bool) {
   114  	p.mu.Lock()
   115  	defer p.mu.Unlock()
   116  
   117  	if len(p.appWorkers) == 0 {
   118  		return nil, false
   119  	}
   120  	aw, ok := p.appWorkers[appName]
   121  	return aw, ok
   122  }
   123  
   124  func (p *provisioner) loop() error {
   125  	w, err := p.config.ApplicationGetter.WatchApplications()
   126  	if err != nil {
   127  		return errors.Trace(err)
   128  	}
   129  	if err := p.catacomb.Add(w); err != nil {
   130  		return errors.Trace(err)
   131  	}
   132  
   133  	for {
   134  		select {
   135  		case <-p.catacomb.Dying():
   136  			return p.catacomb.ErrDying()
   137  		case apps, ok := <-w.Changes():
   138  			if !ok {
   139  				return errors.New("watcher closed channel")
   140  			}
   141  			for _, appId := range apps {
   142  				appLife, err := p.config.LifeGetter.Life(appId)
   143  				if errors.IsNotFound(err) {
   144  					// Once an application is deleted, remove the k8s service and ingress resources.
   145  					if err := p.config.ServiceBroker.UnexposeService(appId); err != nil {
   146  						return errors.Trace(err)
   147  					}
   148  					if err := p.config.ServiceBroker.DeleteService(appId); err != nil {
   149  						return errors.Trace(err)
   150  					}
   151  					w, ok := p.getApplicationWorker(appId)
   152  					if ok {
   153  						// Before stopping the application worker, inform it that
   154  						// the app is gone so it has a chance to clean up.
   155  						// The worker will act on the removal prior to processing the
   156  						// Stop() request.
   157  						if err := worker.Stop(w); err != nil {
   158  							logger.Errorf("stopping application worker for %v: %v", appId, err)
   159  						}
   160  						p.deleteApplicationWorker(appId)
   161  					}
   162  					continue
   163  				}
   164  				if err != nil {
   165  					return errors.Trace(err)
   166  				}
   167  				if _, ok := p.getApplicationWorker(appId); ok || appLife == life.Dead {
   168  					// Already watching the application. or we're
   169  					// not yet watching it and it's dead.
   170  					continue
   171  				}
   172  				w, err := newApplicationWorker(
   173  					appId,
   174  					p.config.ServiceBroker,
   175  					p.config.ContainerBroker,
   176  					p.config.ProvisioningStatusSetter,
   177  					p.config.ProvisioningInfoGetter,
   178  					p.config.ApplicationGetter,
   179  					p.config.ApplicationUpdater,
   180  					p.config.UnitUpdater,
   181  				)
   182  				if err != nil {
   183  					return errors.Trace(err)
   184  				}
   185  				p.saveApplicationWorker(appId, w)
   186  				p.catacomb.Add(w)
   187  			}
   188  		}
   189  	}
   190  }