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