github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/state/apiserver/upgrader/unitupgrader.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/names"
     8  
     9  	"github.com/juju/juju/environs"
    10  	envtools "github.com/juju/juju/environs/tools"
    11  	"github.com/juju/juju/state"
    12  	"github.com/juju/juju/state/api/params"
    13  	"github.com/juju/juju/state/apiserver/common"
    14  	"github.com/juju/juju/state/watcher"
    15  	"github.com/juju/juju/version"
    16  )
    17  
    18  // UnitUpgraderAPI provides access to the UnitUpgrader API facade.
    19  type UnitUpgraderAPI struct {
    20  	*common.ToolsSetter
    21  
    22  	st         *state.State
    23  	resources  *common.Resources
    24  	authorizer common.Authorizer
    25  }
    26  
    27  // NewUnitUpgraderAPI creates a new server-side UnitUpgraderAPI facade.
    28  func NewUnitUpgraderAPI(
    29  	st *state.State,
    30  	resources *common.Resources,
    31  	authorizer common.Authorizer,
    32  ) (*UnitUpgraderAPI, error) {
    33  	if !authorizer.AuthUnitAgent() {
    34  		return nil, common.ErrPerm
    35  	}
    36  
    37  	getCanWrite := func() (common.AuthFunc, error) {
    38  		return authorizer.AuthOwner, nil
    39  	}
    40  	return &UnitUpgraderAPI{
    41  		ToolsSetter: common.NewToolsSetter(st, getCanWrite),
    42  		st:          st,
    43  		resources:   resources,
    44  		authorizer:  authorizer,
    45  	}, nil
    46  }
    47  
    48  func (u *UnitUpgraderAPI) watchAssignedMachine(unitTag string) (string, error) {
    49  	machine, err := u.getAssignedMachine(unitTag)
    50  	if err != nil {
    51  		return "", err
    52  	}
    53  	watch := machine.Watch()
    54  	// Consume the initial event. Technically, API
    55  	// calls to Watch 'transmit' the initial event
    56  	// in the Watch response. But NotifyWatchers
    57  	// have no state to transmit.
    58  	if _, ok := <-watch.Changes(); ok {
    59  		return u.resources.Register(watch), nil
    60  	}
    61  	return "", watcher.MustErr(watch)
    62  }
    63  
    64  // WatchAPIVersion starts a watcher to track if there is a new version
    65  // of the API that we want to upgrade to. The watcher tracks changes to
    66  // the unit's assigned machine since that's where the required agent version is stored.
    67  func (u *UnitUpgraderAPI) WatchAPIVersion(args params.Entities) (params.NotifyWatchResults, error) {
    68  	result := params.NotifyWatchResults{
    69  		Results: make([]params.NotifyWatchResult, len(args.Entities)),
    70  	}
    71  	for i, agent := range args.Entities {
    72  		err := common.ErrPerm
    73  		if u.authorizer.AuthOwner(agent.Tag) {
    74  			var watcherId string
    75  			watcherId, err = u.watchAssignedMachine(agent.Tag)
    76  			if err == nil {
    77  				result.Results[i].NotifyWatcherId = watcherId
    78  			}
    79  		}
    80  		result.Results[i].Error = common.ServerError(err)
    81  	}
    82  	return result, nil
    83  }
    84  
    85  // DesiredVersion reports the Agent Version that we want that unit to be running.
    86  // The desired version is what the unit's assigned machine is running.
    87  func (u *UnitUpgraderAPI) DesiredVersion(args params.Entities) (params.VersionResults, error) {
    88  	result := make([]params.VersionResult, len(args.Entities))
    89  	if len(args.Entities) == 0 {
    90  		return params.VersionResults{}, nil
    91  	}
    92  	for i, entity := range args.Entities {
    93  		err := common.ErrPerm
    94  		if u.authorizer.AuthOwner(entity.Tag) {
    95  			result[i].Version, err = u.getMachineToolsVersion(entity.Tag)
    96  		}
    97  		result[i].Error = common.ServerError(err)
    98  	}
    99  	return params.VersionResults{Results: result}, nil
   100  }
   101  
   102  // Tools finds the tools necessary for the given agents.
   103  func (u *UnitUpgraderAPI) Tools(args params.Entities) (params.ToolsResults, error) {
   104  	result := params.ToolsResults{
   105  		Results: make([]params.ToolsResult, len(args.Entities)),
   106  	}
   107  	for i, entity := range args.Entities {
   108  		result.Results[i].Error = common.ServerError(common.ErrPerm)
   109  		if u.authorizer.AuthOwner(entity.Tag) {
   110  			result.Results[i] = u.getMachineTools(entity.Tag)
   111  		}
   112  	}
   113  	return result, nil
   114  }
   115  
   116  func (u *UnitUpgraderAPI) getAssignedMachine(tag string) (*state.Machine, error) {
   117  	// Check that we really have a unit tag.
   118  	_, unitName, err := names.ParseTag(tag, names.UnitTagKind)
   119  	if err != nil {
   120  		return nil, common.ErrPerm
   121  	}
   122  	unit, err := u.st.Unit(unitName)
   123  	if err != nil {
   124  		return nil, common.ErrPerm
   125  	}
   126  	id, err := unit.AssignedMachineId()
   127  	if err != nil {
   128  		return nil, err
   129  	}
   130  	return u.st.Machine(id)
   131  }
   132  
   133  func (u *UnitUpgraderAPI) getMachineTools(tag string) params.ToolsResult {
   134  	var result params.ToolsResult
   135  	machine, err := u.getAssignedMachine(tag)
   136  	if err != nil {
   137  		result.Error = common.ServerError(err)
   138  		return result
   139  	}
   140  	machineTools, err := machine.AgentTools()
   141  	if err != nil {
   142  		result.Error = common.ServerError(err)
   143  		return result
   144  	}
   145  	// For older 1.16 upgrader workers, we need to supply a tools URL since the worker will attempt to
   146  	// download the tools even though they already have been fetched by the machine agent. Newer upgrader
   147  	// workers do not have this problem. So to be compatible across all versions, we return the full
   148  	// tools metadata.
   149  	// TODO (wallyworld) - remove in 1.20, just return machineTools
   150  	cfg, err := u.st.EnvironConfig()
   151  	if err != nil {
   152  		result.Error = common.ServerError(err)
   153  		return result
   154  	}
   155  	// SSLHostnameVerification defaults to true, so we need to
   156  	// invert that, for backwards-compatibility (older versions
   157  	// will have DisableSSLHostnameVerification: false by default).
   158  	result.DisableSSLHostnameVerification = !cfg.SSLHostnameVerification()
   159  	env, err := environs.New(cfg)
   160  	if err != nil {
   161  		result.Error = common.ServerError(err)
   162  		return result
   163  	}
   164  	agentTools, err := envtools.FindExactTools(
   165  		env, machineTools.Version.Number, machineTools.Version.Series, machineTools.Version.Arch)
   166  	if err != nil {
   167  		result.Error = common.ServerError(err)
   168  		return result
   169  	}
   170  	result.Tools = agentTools
   171  	return result
   172  }
   173  
   174  func (u *UnitUpgraderAPI) getMachineToolsVersion(tag string) (*version.Number, error) {
   175  	machine, err := u.getAssignedMachine(tag)
   176  	if err != nil {
   177  		return nil, err
   178  	}
   179  	machineTools, err := machine.AgentTools()
   180  	if err != nil {
   181  		return nil, err
   182  	}
   183  	return &machineTools.Version.Number, nil
   184  }