github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/facades/controller/agenttools/agenttools.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package agenttools
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/loggo"
     9  	"github.com/juju/version/v2"
    10  
    11  	apiservererrors "github.com/juju/juju/apiserver/errors"
    12  	"github.com/juju/juju/apiserver/facade"
    13  	"github.com/juju/juju/environs"
    14  	"github.com/juju/juju/environs/config"
    15  	"github.com/juju/juju/environs/simplestreams"
    16  	"github.com/juju/juju/environs/tools"
    17  	"github.com/juju/juju/state"
    18  	coretools "github.com/juju/juju/tools"
    19  )
    20  
    21  var logger = loggo.GetLogger("juju.apiserver.model")
    22  
    23  var (
    24  	findTools = tools.FindTools
    25  )
    26  
    27  // AgentToolsAPI implements the API used by the machine model worker.
    28  type AgentToolsAPI struct {
    29  	modelGetter ModelGetter
    30  	newEnviron  newEnvironFunc
    31  	authorizer  facade.Authorizer
    32  	// tools lookup
    33  	findTools        toolsFinder
    34  	envVersionUpdate envVersionUpdater
    35  }
    36  
    37  // NewAgentToolsAPI creates a new instance of the Model API.
    38  func NewAgentToolsAPI(
    39  	modelGetter ModelGetter,
    40  	newEnviron func() (environs.Environ, error),
    41  	findTools toolsFinder,
    42  	envVersionUpdate func(*state.Model, version.Number) error,
    43  	authorizer facade.Authorizer,
    44  ) (*AgentToolsAPI, error) {
    45  	return &AgentToolsAPI{
    46  		modelGetter:      modelGetter,
    47  		newEnviron:       newEnviron,
    48  		authorizer:       authorizer,
    49  		findTools:        findTools,
    50  		envVersionUpdate: envVersionUpdate,
    51  	}, nil
    52  }
    53  
    54  // ModelGetter represents a struct that can provide a state.Model.
    55  type ModelGetter interface {
    56  	Model() (*state.Model, error)
    57  }
    58  
    59  type newEnvironFunc func() (environs.Environ, error)
    60  type toolsFinder func(tools.SimplestreamsFetcher, environs.BootstrapEnviron, int, int, []string, coretools.Filter) (coretools.List, error)
    61  type envVersionUpdater func(*state.Model, version.Number) error
    62  
    63  func checkToolsAvailability(newEnviron newEnvironFunc, modelCfg *config.Config, finder toolsFinder) (version.Number, error) {
    64  	currentVersion, ok := modelCfg.AgentVersion()
    65  	if !ok || currentVersion == version.Zero {
    66  		return version.Zero, nil
    67  	}
    68  
    69  	env, err := newEnviron()
    70  	if err != nil {
    71  		return version.Zero, errors.Annotatef(err, "cannot make model")
    72  	}
    73  
    74  	ss := simplestreams.NewSimpleStreams(simplestreams.DefaultDataSourceFactory())
    75  
    76  	// finder receives major and minor as parameters as it uses them to filter versions and
    77  	// only return patches for the passed major.minor (from major.minor.patch).
    78  	// We'll try the released stream first, then fall back to the current configured stream
    79  	// if no released tools are found.
    80  	vers, err := finder(ss, env, currentVersion.Major, currentVersion.Minor, []string{tools.ReleasedStream}, coretools.Filter{})
    81  	preferredStream := tools.PreferredStreams(&currentVersion, modelCfg.Development(), modelCfg.AgentStream())[0]
    82  	if preferredStream != tools.ReleasedStream && errors.Cause(err) == coretools.ErrNoMatches {
    83  		vers, err = finder(ss, env, currentVersion.Major, currentVersion.Minor, []string{preferredStream}, coretools.Filter{})
    84  	}
    85  	if err != nil {
    86  		return version.Zero, errors.Annotatef(err, "cannot find available agent binaries")
    87  	}
    88  	// Newest also returns a list of the items in this list matching with the
    89  	// newest version.
    90  	newest, _ := vers.Newest()
    91  	return newest, nil
    92  }
    93  
    94  var modelConfig = func(e *state.Model) (*config.Config, error) {
    95  	return e.Config()
    96  }
    97  
    98  // Base implementation of envVersionUpdater
    99  func envVersionUpdate(env *state.Model, ver version.Number) error {
   100  	return env.UpdateLatestToolsVersion(ver)
   101  }
   102  
   103  func updateToolsAvailability(modelGetter ModelGetter, newEnviron newEnvironFunc, finder toolsFinder, update envVersionUpdater) error {
   104  	model, err := modelGetter.Model()
   105  	if err != nil {
   106  		return errors.Annotate(err, "cannot get model")
   107  	}
   108  	cfg, err := modelConfig(model)
   109  	if err != nil {
   110  		return errors.Annotate(err, "cannot get config")
   111  	}
   112  	ver, err := checkToolsAvailability(newEnviron, cfg, finder)
   113  	if err != nil {
   114  		if errors.IsNotFound(err) {
   115  			// No newer tools, so exit silently.
   116  			return nil
   117  		}
   118  		return errors.Annotate(err, "cannot get latest version")
   119  	}
   120  	if ver == version.Zero {
   121  		logger.Debugf("The lookup of agent binaries returned version Zero. This should only happen during bootstrap.")
   122  		return nil
   123  	}
   124  	return update(model, ver)
   125  }
   126  
   127  // UpdateToolsAvailable invokes a lookup and further update in environ
   128  // for new patches of the current tool versions.
   129  func (api *AgentToolsAPI) UpdateToolsAvailable() error {
   130  	if !api.authorizer.AuthController() {
   131  		return apiservererrors.ErrPerm
   132  	}
   133  	return updateToolsAvailability(api.modelGetter, api.newEnviron, api.findTools, api.envVersionUpdate)
   134  }