github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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"
    10  
    11  	"github.com/juju/juju/apiserver/common"
    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/tools"
    16  	"github.com/juju/juju/state"
    17  	"github.com/juju/juju/state/stateenvirons"
    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  // NewFacade is used to register the facade.
    38  func NewFacade(st *state.State, resources facade.Resources, authorizer facade.Authorizer) (*AgentToolsAPI, error) {
    39  	newEnviron := func() (environs.Environ, error) {
    40  		newEnviron := stateenvirons.GetNewEnvironFunc(environs.New)
    41  		return newEnviron(st)
    42  	}
    43  	return NewAgentToolsAPI(st, newEnviron, findTools, envVersionUpdate, authorizer)
    44  }
    45  
    46  // NewAgentToolsAPI creates a new instance of the Model API.
    47  func NewAgentToolsAPI(
    48  	modelGetter ModelGetter,
    49  	newEnviron func() (environs.Environ, error),
    50  	findTools toolsFinder,
    51  	envVersionUpdate func(*state.Model, version.Number) error,
    52  	authorizer facade.Authorizer,
    53  ) (*AgentToolsAPI, error) {
    54  	return &AgentToolsAPI{
    55  		modelGetter:      modelGetter,
    56  		newEnviron:       newEnviron,
    57  		authorizer:       authorizer,
    58  		findTools:        findTools,
    59  		envVersionUpdate: envVersionUpdate,
    60  	}, nil
    61  }
    62  
    63  // ModelGetter represents a struct that can provide a state.Model.
    64  type ModelGetter interface {
    65  	Model() (*state.Model, error)
    66  }
    67  
    68  type newEnvironFunc func() (environs.Environ, error)
    69  type toolsFinder func(environs.BootstrapEnviron, int, int, []string, coretools.Filter) (coretools.List, error)
    70  type envVersionUpdater func(*state.Model, version.Number) error
    71  
    72  func checkToolsAvailability(newEnviron newEnvironFunc, modelCfg *config.Config, finder toolsFinder) (version.Number, error) {
    73  	currentVersion, ok := modelCfg.AgentVersion()
    74  	if !ok || currentVersion == version.Zero {
    75  		return version.Zero, nil
    76  	}
    77  
    78  	env, err := newEnviron()
    79  	if err != nil {
    80  		return version.Zero, errors.Annotatef(err, "cannot make model")
    81  	}
    82  
    83  	// finder receives major and minor as parameters as it uses them to filter versions and
    84  	// only return patches for the passed major.minor (from major.minor.patch).
    85  	// We'll try the released stream first, then fall back to the current configured stream
    86  	// if no released tools are found.
    87  	vers, err := finder(env, currentVersion.Major, currentVersion.Minor, []string{tools.ReleasedStream}, coretools.Filter{})
    88  	preferredStream := tools.PreferredStreams(&currentVersion, modelCfg.Development(), modelCfg.AgentStream())[0]
    89  	if preferredStream != tools.ReleasedStream && errors.Cause(err) == coretools.ErrNoMatches {
    90  		vers, err = finder(env, currentVersion.Major, currentVersion.Minor, []string{preferredStream}, coretools.Filter{})
    91  	}
    92  	if err != nil {
    93  		return version.Zero, errors.Annotatef(err, "cannot find available agent binaries")
    94  	}
    95  	// Newest also returns a list of the items in this list matching with the
    96  	// newest version.
    97  	newest, _ := vers.Newest()
    98  	return newest, nil
    99  }
   100  
   101  var modelConfig = func(e *state.Model) (*config.Config, error) {
   102  	return e.Config()
   103  }
   104  
   105  // Base implementation of envVersionUpdater
   106  func envVersionUpdate(env *state.Model, ver version.Number) error {
   107  	return env.UpdateLatestToolsVersion(ver)
   108  }
   109  
   110  func updateToolsAvailability(modelGetter ModelGetter, newEnviron newEnvironFunc, finder toolsFinder, update envVersionUpdater) error {
   111  	model, err := modelGetter.Model()
   112  	if err != nil {
   113  		return errors.Annotate(err, "cannot get model")
   114  	}
   115  	cfg, err := modelConfig(model)
   116  	if err != nil {
   117  		return errors.Annotate(err, "cannot get config")
   118  	}
   119  	ver, err := checkToolsAvailability(newEnviron, cfg, finder)
   120  	if err != nil {
   121  		if errors.IsNotFound(err) {
   122  			// No newer tools, so exit silently.
   123  			return nil
   124  		}
   125  		return errors.Annotate(err, "cannot get latest version")
   126  	}
   127  	if ver == version.Zero {
   128  		logger.Debugf("The lookup of agent binaries returned version Zero. This should only happen during bootstrap.")
   129  		return nil
   130  	}
   131  	return update(model, ver)
   132  }
   133  
   134  // UpdateToolsAvailable invokes a lookup and further update in environ
   135  // for new patches of the current tool versions.
   136  func (api *AgentToolsAPI) UpdateToolsAvailable() error {
   137  	if !api.authorizer.AuthController() {
   138  		return common.ErrPerm
   139  	}
   140  	return updateToolsAvailability(api.modelGetter, api.newEnviron, api.findTools, api.envVersionUpdate)
   141  }