github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/worker/caasfirewaller/worker.go (about)

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package caasfirewaller
     5  
     6  import (
     7  	"github.com/juju/charm/v12"
     8  	"github.com/juju/errors"
     9  	"github.com/juju/worker/v3"
    10  	"github.com/juju/worker/v3/catacomb"
    11  
    12  	"github.com/juju/juju/core/life"
    13  )
    14  
    15  // Logger is here to stop the desire of creating a package level Logger.
    16  // Don't do this, instead use the one passed as manifold config.
    17  type logger interface{}
    18  
    19  var _ logger = struct{}{}
    20  
    21  // Config holds configuration for the CAAS unit firewaller worker.
    22  type Config struct {
    23  	ControllerUUID    string
    24  	ModelUUID         string
    25  	ApplicationGetter ApplicationGetter
    26  	LifeGetter        LifeGetter
    27  	CharmGetter       CharmGetter
    28  	ServiceExposer    ServiceExposer
    29  	Logger            Logger
    30  }
    31  
    32  // Validate validates the worker configuration.
    33  func (config Config) Validate() error {
    34  	if config.ControllerUUID == "" {
    35  		return errors.NotValidf("missing ControllerUUID")
    36  	}
    37  	if config.ModelUUID == "" {
    38  		return errors.NotValidf("missing ModelUUID")
    39  	}
    40  	if config.ApplicationGetter == nil {
    41  		return errors.NotValidf("missing ApplicationGetter")
    42  	}
    43  	if config.LifeGetter == nil {
    44  		return errors.NotValidf("missing LifeGetter")
    45  	}
    46  	if config.CharmGetter == nil {
    47  		return errors.NotValidf("missing CharmGetter")
    48  	}
    49  	if config.ServiceExposer == nil {
    50  		return errors.NotValidf("missing ServiceExposer")
    51  	}
    52  	if config.Logger == nil {
    53  		return errors.NotValidf("missing Logger")
    54  	}
    55  	return nil
    56  }
    57  
    58  // NewWorker starts and returns a new CAAS unit firewaller worker.
    59  func NewWorker(config Config) (worker.Worker, error) {
    60  	if err := config.Validate(); err != nil {
    61  		return nil, errors.Trace(err)
    62  	}
    63  	p := &firewaller{config: config}
    64  	err := catacomb.Invoke(catacomb.Plan{
    65  		Site: &p.catacomb,
    66  		Work: p.loop,
    67  	})
    68  	return p, err
    69  }
    70  
    71  type firewaller struct {
    72  	catacomb catacomb.Catacomb
    73  	config   Config
    74  }
    75  
    76  // Kill is part of the worker.Worker interface.
    77  func (p *firewaller) Kill() {
    78  	p.catacomb.Kill(nil)
    79  }
    80  
    81  // Wait is part of the worker.Worker interface.
    82  func (p *firewaller) Wait() error {
    83  	return p.catacomb.Wait()
    84  }
    85  
    86  func (p *firewaller) loop() error {
    87  	logger := p.config.Logger
    88  	appWatcher, err := p.config.ApplicationGetter.WatchApplications()
    89  	if err != nil {
    90  		return errors.Trace(err)
    91  	}
    92  	if err := p.catacomb.Add(appWatcher); err != nil {
    93  		return errors.Trace(err)
    94  	}
    95  
    96  	appWorkers := make(map[string]worker.Worker)
    97  	for {
    98  		select {
    99  		case <-p.catacomb.Dying():
   100  			return p.catacomb.ErrDying()
   101  		case apps, ok := <-appWatcher.Changes():
   102  			if !ok {
   103  				return errors.New("watcher closed channel")
   104  			}
   105  			for _, appName := range apps {
   106  				// If charm is (now) a v2 charm, skip processing.
   107  				format, err := p.charmFormat(appName)
   108  				if errors.IsNotFound(err) {
   109  					p.config.Logger.Debugf("application %q no longer exists", appName)
   110  					continue
   111  				} else if err != nil {
   112  					return errors.Trace(err)
   113  				}
   114  				if format >= charm.FormatV2 {
   115  					p.config.Logger.Tracef("v1 caasfirewaller got event for v2 app %q, skipping", appName)
   116  					continue
   117  				}
   118  
   119  				appLife, err := p.config.LifeGetter.Life(appName)
   120  				if errors.IsNotFound(err) || appLife == life.Dead {
   121  					if appWorker, ok := appWorkers[appName]; ok {
   122  						if err := worker.Stop(appWorker); err != nil {
   123  							logger.Errorf("error stopping caas firewaller: %v", err)
   124  						}
   125  						delete(appWorkers, appName)
   126  					}
   127  					continue
   128  				}
   129  				if err != nil {
   130  					return errors.Trace(err)
   131  				}
   132  				if _, ok := appWorkers[appName]; ok {
   133  					// Already watching the application.
   134  					continue
   135  				}
   136  				appWorker, err := newApplicationWorker(
   137  					p.config.ControllerUUID,
   138  					p.config.ModelUUID,
   139  					appName,
   140  					p.config.ApplicationGetter,
   141  					p.config.ServiceExposer,
   142  					p.config.LifeGetter,
   143  					p.config.CharmGetter,
   144  					logger,
   145  				)
   146  				if err != nil {
   147  					return errors.Trace(err)
   148  				}
   149  				appWorkers[appName] = appWorker
   150  				_ = p.catacomb.Add(appWorker)
   151  			}
   152  		}
   153  	}
   154  }
   155  
   156  func (p *firewaller) charmFormat(appName string) (charm.Format, error) {
   157  	charmInfo, err := p.config.CharmGetter.ApplicationCharmInfo(appName)
   158  	if err != nil {
   159  		return charm.FormatUnknown, errors.Annotatef(err, "failed to get charm info for application %q", appName)
   160  	}
   161  	return charm.MetaFormat(charmInfo.Charm()), nil
   162  }