github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/apiserver/upgrader/upgrader.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package upgrader
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/loggo"
     9  	"github.com/juju/version"
    10  	"gopkg.in/juju/names.v2"
    11  
    12  	"github.com/juju/juju/apiserver/common"
    13  	"github.com/juju/juju/apiserver/facade"
    14  	"github.com/juju/juju/apiserver/params"
    15  	"github.com/juju/juju/environs/config"
    16  	"github.com/juju/juju/state"
    17  	"github.com/juju/juju/state/stateenvirons"
    18  	"github.com/juju/juju/state/watcher"
    19  	jujuversion "github.com/juju/juju/version"
    20  )
    21  
    22  var logger = loggo.GetLogger("juju.apiserver.upgrader")
    23  
    24  func init() {
    25  	common.RegisterStandardFacade("Upgrader", 1, upgraderFacade)
    26  }
    27  
    28  // upgraderFacade is a bit unique vs the other API Facades, as it has two
    29  // implementations that actually expose the same API and which one gets
    30  // returned depends on who is calling.
    31  // Both of them conform to the exact Upgrader API, so the actual calls that are
    32  // available do not depend on who is currently connected.
    33  func upgraderFacade(st *state.State, resources facade.Resources, auth facade.Authorizer) (Upgrader, error) {
    34  	// The type of upgrader we return depends on who is asking.
    35  	// Machines get an UpgraderAPI, units get a UnitUpgraderAPI.
    36  	// This is tested in the api/upgrader package since there
    37  	// are currently no direct srvRoot tests.
    38  	// TODO(dfc) this is redundant
    39  	tag, err := names.ParseTag(auth.GetAuthTag().String())
    40  	if err != nil {
    41  		return nil, common.ErrPerm
    42  	}
    43  	switch tag.(type) {
    44  	case names.MachineTag:
    45  		return NewUpgraderAPI(st, resources, auth)
    46  	case names.UnitTag:
    47  		return NewUnitUpgraderAPI(st, resources, auth)
    48  	}
    49  	// Not a machine or unit.
    50  	return nil, common.ErrPerm
    51  }
    52  
    53  type Upgrader interface {
    54  	WatchAPIVersion(args params.Entities) (params.NotifyWatchResults, error)
    55  	DesiredVersion(args params.Entities) (params.VersionResults, error)
    56  	Tools(args params.Entities) (params.ToolsResults, error)
    57  	SetTools(args params.EntitiesVersion) (params.ErrorResults, error)
    58  }
    59  
    60  // UpgraderAPI provides access to the Upgrader API facade.
    61  type UpgraderAPI struct {
    62  	*common.ToolsGetter
    63  	*common.ToolsSetter
    64  
    65  	st         *state.State
    66  	resources  facade.Resources
    67  	authorizer facade.Authorizer
    68  }
    69  
    70  // NewUpgraderAPI creates a new server-side UpgraderAPI facade.
    71  func NewUpgraderAPI(
    72  	st *state.State,
    73  	resources facade.Resources,
    74  	authorizer facade.Authorizer,
    75  ) (*UpgraderAPI, error) {
    76  	if !authorizer.AuthMachineAgent() {
    77  		return nil, common.ErrPerm
    78  	}
    79  	getCanReadWrite := func() (common.AuthFunc, error) {
    80  		return authorizer.AuthOwner, nil
    81  	}
    82  	env, err := st.Model()
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  	urlGetter := common.NewToolsURLGetter(env.UUID(), st)
    87  	configGetter := stateenvirons.EnvironConfigGetter{st}
    88  	return &UpgraderAPI{
    89  		ToolsGetter: common.NewToolsGetter(st, configGetter, st, urlGetter, getCanReadWrite),
    90  		ToolsSetter: common.NewToolsSetter(st, getCanReadWrite),
    91  		st:          st,
    92  		resources:   resources,
    93  		authorizer:  authorizer,
    94  	}, nil
    95  }
    96  
    97  // WatchAPIVersion starts a watcher to track if there is a new version
    98  // of the API that we want to upgrade to
    99  func (u *UpgraderAPI) WatchAPIVersion(args params.Entities) (params.NotifyWatchResults, error) {
   100  	result := params.NotifyWatchResults{
   101  		Results: make([]params.NotifyWatchResult, len(args.Entities)),
   102  	}
   103  	for i, agent := range args.Entities {
   104  		tag, err := names.ParseTag(agent.Tag)
   105  		if err != nil {
   106  			return params.NotifyWatchResults{}, errors.Trace(err)
   107  		}
   108  		err = common.ErrPerm
   109  		if u.authorizer.AuthOwner(tag) {
   110  			watch := u.st.WatchForModelConfigChanges()
   111  			// Consume the initial event. Technically, API
   112  			// calls to Watch 'transmit' the initial event
   113  			// in the Watch response. But NotifyWatchers
   114  			// have no state to transmit.
   115  			if _, ok := <-watch.Changes(); ok {
   116  				result.Results[i].NotifyWatcherId = u.resources.Register(watch)
   117  				err = nil
   118  			} else {
   119  				err = watcher.EnsureErr(watch)
   120  			}
   121  		}
   122  		result.Results[i].Error = common.ServerError(err)
   123  	}
   124  	return result, nil
   125  }
   126  
   127  func (u *UpgraderAPI) getGlobalAgentVersion() (version.Number, *config.Config, error) {
   128  	// Get the Agent Version requested in the Environment Config
   129  	cfg, err := u.st.ModelConfig()
   130  	if err != nil {
   131  		return version.Number{}, nil, err
   132  	}
   133  	agentVersion, ok := cfg.AgentVersion()
   134  	if !ok {
   135  		return version.Number{}, nil, errors.New("agent version not set in model config")
   136  	}
   137  	return agentVersion, cfg, nil
   138  }
   139  
   140  type hasIsManager interface {
   141  	IsManager() bool
   142  }
   143  
   144  func (u *UpgraderAPI) entityIsManager(tag names.Tag) bool {
   145  	entity, err := u.st.FindEntity(tag)
   146  	if err != nil {
   147  		return false
   148  	}
   149  	if m, ok := entity.(hasIsManager); !ok {
   150  		return false
   151  	} else {
   152  		return m.IsManager()
   153  	}
   154  }
   155  
   156  // DesiredVersion reports the Agent Version that we want that agent to be running
   157  func (u *UpgraderAPI) DesiredVersion(args params.Entities) (params.VersionResults, error) {
   158  	results := make([]params.VersionResult, len(args.Entities))
   159  	if len(args.Entities) == 0 {
   160  		return params.VersionResults{}, nil
   161  	}
   162  	agentVersion, _, err := u.getGlobalAgentVersion()
   163  	if err != nil {
   164  		return params.VersionResults{}, common.ServerError(err)
   165  	}
   166  	// Is the desired version greater than the current API server version?
   167  	isNewerVersion := agentVersion.Compare(jujuversion.Current) > 0
   168  	for i, entity := range args.Entities {
   169  		tag, err := names.ParseTag(entity.Tag)
   170  		if err != nil {
   171  			results[i].Error = common.ServerError(err)
   172  			continue
   173  		}
   174  		err = common.ErrPerm
   175  		if u.authorizer.AuthOwner(tag) {
   176  			// Only return the globally desired agent version if the
   177  			// asking entity is a machine agent with JobManageModel or
   178  			// if this API server is running the globally desired agent
   179  			// version. Otherwise report this API server's current
   180  			// agent version.
   181  			//
   182  			// This ensures that state machine agents will upgrade
   183  			// first - once they have restarted and are running the
   184  			// new version other agents will start to see the new
   185  			// agent version.
   186  			if !isNewerVersion || u.entityIsManager(tag) {
   187  				results[i].Version = &agentVersion
   188  			} else {
   189  				logger.Debugf("desired version is %s, but current version is %s and agent is not a manager node", agentVersion, jujuversion.Current)
   190  				results[i].Version = &jujuversion.Current
   191  			}
   192  			err = nil
   193  		}
   194  		results[i].Error = common.ServerError(err)
   195  	}
   196  	return params.VersionResults{Results: results}, nil
   197  }