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 }