github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/worker/unitassigner/unitassigner.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package unitassigner
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/names/v5"
     9  	"github.com/juju/worker/v3"
    10  
    11  	"github.com/juju/juju/core/status"
    12  	"github.com/juju/juju/core/watcher"
    13  	"github.com/juju/juju/rpc/params"
    14  )
    15  
    16  // logger is here to stop the desire of creating a package level logger.
    17  // Don't do this, instead use the one passed as manifold config.
    18  type logger interface{}
    19  
    20  var _ logger = struct{}{}
    21  
    22  type UnitAssigner interface {
    23  	AssignUnits(tags []names.UnitTag) ([]error, error)
    24  	WatchUnitAssignments() (watcher.StringsWatcher, error)
    25  	SetAgentStatus(args params.SetStatus) error
    26  }
    27  
    28  func New(ua UnitAssigner, logger Logger) (worker.Worker, error) {
    29  	return watcher.NewStringsWorker(watcher.StringsConfig{
    30  		Handler: unitAssignerHandler{api: ua, logger: logger},
    31  	})
    32  }
    33  
    34  type unitAssignerHandler struct {
    35  	api    UnitAssigner
    36  	logger Logger
    37  }
    38  
    39  func (u unitAssignerHandler) SetUp() (watcher.StringsWatcher, error) {
    40  	return u.api.WatchUnitAssignments()
    41  }
    42  
    43  func (u unitAssignerHandler) Handle(_ <-chan struct{}, ids []string) error {
    44  	u.logger.Tracef("Handling unit assignments: %q", ids)
    45  	if len(ids) == 0 {
    46  		return nil
    47  	}
    48  
    49  	units := make([]names.UnitTag, len(ids))
    50  	for i, id := range ids {
    51  		if !names.IsValidUnit(id) {
    52  			return errors.Errorf("%q is not a valid unit id", id)
    53  		}
    54  		units[i] = names.NewUnitTag(id)
    55  	}
    56  
    57  	results, err := u.api.AssignUnits(units)
    58  	if err != nil {
    59  		return err
    60  	}
    61  
    62  	failures := map[string]error{}
    63  
    64  	u.logger.Tracef("Unit assignment results: %q", results)
    65  	// errors are returned in the same order as the ids given. Any errors from
    66  	// the assign units call must be reported as error statuses on the
    67  	// respective units (though the assignments will be retried).  Not found
    68  	// errors indicate that the unit was removed before the assignment was
    69  	// requested, which can be safely ignored.
    70  	for i, err := range results {
    71  		if err != nil && !errors.IsNotFound(err) {
    72  			failures[units[i].String()] = err
    73  		}
    74  	}
    75  
    76  	if len(failures) > 0 {
    77  		args := params.SetStatus{
    78  			Entities: make([]params.EntityStatusArgs, len(failures)),
    79  		}
    80  
    81  		x := 0
    82  		for unit, err := range failures {
    83  			args.Entities[x] = params.EntityStatusArgs{
    84  				Tag:    unit,
    85  				Status: status.Error.String(),
    86  				Info:   err.Error(),
    87  			}
    88  			x++
    89  		}
    90  
    91  		return u.api.SetAgentStatus(args)
    92  	}
    93  	return nil
    94  }
    95  
    96  func (unitAssignerHandler) TearDown() error {
    97  	return nil
    98  }