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