github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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  	"github.com/juju/version"
     9  
    10  	"github.com/juju/juju/apiserver/common"
    11  	"github.com/juju/juju/apiserver/params"
    12  	"github.com/juju/juju/state"
    13  	"github.com/juju/juju/state/watcher"
    14  	"github.com/juju/juju/tools"
    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  }
    25  
    26  // NewUnitUpgraderAPI creates a new server-side UnitUpgraderAPI facade.
    27  func NewUnitUpgraderAPI(
    28  	st *state.State,
    29  	resources *common.Resources,
    30  	authorizer common.Authorizer,
    31  ) (*UnitUpgraderAPI, error) {
    32  	if !authorizer.AuthUnitAgent() {
    33  		return nil, common.ErrPerm
    34  	}
    35  
    36  	getCanWrite := func() (common.AuthFunc, error) {
    37  		return authorizer.AuthOwner, nil
    38  	}
    39  	return &UnitUpgraderAPI{
    40  		ToolsSetter: common.NewToolsSetter(st, getCanWrite),
    41  		st:          st,
    42  		resources:   resources,
    43  		authorizer:  authorizer,
    44  	}, nil
    45  }
    46  
    47  func (u *UnitUpgraderAPI) watchAssignedMachine(unitTag names.Tag) (string, error) {
    48  	machine, err := u.getAssignedMachine(unitTag)
    49  	if err != nil {
    50  		return "", err
    51  	}
    52  	watch := machine.Watch()
    53  	// Consume the initial event. Technically, API
    54  	// calls to Watch 'transmit' the initial event
    55  	// in the Watch response. But NotifyWatchers
    56  	// have no state to transmit.
    57  	if _, ok := <-watch.Changes(); ok {
    58  		return u.resources.Register(watch), nil
    59  	}
    60  	return "", watcher.EnsureErr(watch)
    61  }
    62  
    63  // WatchAPIVersion starts a watcher to track if there is a new version
    64  // of the API that we want to upgrade to. The watcher tracks changes to
    65  // the unit's assigned machine since that's where the required agent version is stored.
    66  func (u *UnitUpgraderAPI) WatchAPIVersion(args params.Entities) (params.NotifyWatchResults, error) {
    67  	result := params.NotifyWatchResults{
    68  		Results: make([]params.NotifyWatchResult, len(args.Entities)),
    69  	}
    70  	for i, agent := range args.Entities {
    71  		tag, err := names.ParseTag(agent.Tag)
    72  		if err != nil {
    73  			result.Results[i].Error = common.ServerError(common.ErrPerm)
    74  			continue
    75  		}
    76  		err = common.ErrPerm
    77  		if u.authorizer.AuthOwner(tag) {
    78  			var watcherId string
    79  			watcherId, err = u.watchAssignedMachine(tag)
    80  			if err == nil {
    81  				result.Results[i].NotifyWatcherId = watcherId
    82  			}
    83  		}
    84  		result.Results[i].Error = common.ServerError(err)
    85  	}
    86  	return result, nil
    87  }
    88  
    89  // DesiredVersion reports the Agent Version that we want that unit to be running.
    90  // The desired version is what the unit's assigned machine is running.
    91  func (u *UnitUpgraderAPI) DesiredVersion(args params.Entities) (params.VersionResults, error) {
    92  	result := make([]params.VersionResult, len(args.Entities))
    93  	for i, entity := range args.Entities {
    94  		tag, err := names.ParseTag(entity.Tag)
    95  		if err != nil {
    96  			result[i].Error = common.ServerError(common.ErrPerm)
    97  			continue
    98  		}
    99  		err = common.ErrPerm
   100  		if u.authorizer.AuthOwner(tag) {
   101  			result[i].Version, err = u.getMachineToolsVersion(tag)
   102  		}
   103  		result[i].Error = common.ServerError(err)
   104  	}
   105  	return params.VersionResults{Results: result}, nil
   106  }
   107  
   108  // Tools finds the tools necessary for the given agents.
   109  func (u *UnitUpgraderAPI) Tools(args params.Entities) (params.ToolsResults, error) {
   110  	result := params.ToolsResults{
   111  		Results: make([]params.ToolsResult, len(args.Entities)),
   112  	}
   113  	for i, entity := range args.Entities {
   114  		result.Results[i].Error = common.ServerError(common.ErrPerm)
   115  		tag, err := names.ParseTag(entity.Tag)
   116  		if err != nil {
   117  			continue
   118  		}
   119  		if u.authorizer.AuthOwner(tag) {
   120  			result.Results[i] = u.getMachineTools(tag)
   121  		}
   122  	}
   123  	return result, nil
   124  }
   125  
   126  func (u *UnitUpgraderAPI) getAssignedMachine(tag names.Tag) (*state.Machine, error) {
   127  	// Check that we really have a unit tag.
   128  	switch tag := tag.(type) {
   129  	case names.UnitTag:
   130  		unit, err := u.st.Unit(tag.Id())
   131  		if err != nil {
   132  			return nil, common.ErrPerm
   133  		}
   134  		id, err := unit.AssignedMachineId()
   135  		if err != nil {
   136  			return nil, err
   137  		}
   138  		return u.st.Machine(id)
   139  	default:
   140  		return nil, common.ErrPerm
   141  	}
   142  }
   143  
   144  func (u *UnitUpgraderAPI) getMachineTools(tag names.Tag) params.ToolsResult {
   145  	var result params.ToolsResult
   146  	machine, err := u.getAssignedMachine(tag)
   147  	if err != nil {
   148  		result.Error = common.ServerError(err)
   149  		return result
   150  	}
   151  	machineTools, err := machine.AgentTools()
   152  	if err != nil {
   153  		result.Error = common.ServerError(err)
   154  		return result
   155  	}
   156  	// We are okay returning the tools for just the one API server
   157  	// address since the unit agent won't try to download tools that
   158  	// are already present on the machine.
   159  	result.ToolsList = tools.List{machineTools}
   160  	return result
   161  }
   162  
   163  func (u *UnitUpgraderAPI) getMachineToolsVersion(tag names.Tag) (*version.Number, error) {
   164  	machine, err := u.getAssignedMachine(tag)
   165  	if err != nil {
   166  		return nil, err
   167  	}
   168  	machineTools, err := machine.AgentTools()
   169  	if err != nil {
   170  		return nil, err
   171  	}
   172  	return &machineTools.Version.Number, nil
   173  }