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(¤tVersion, 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 }