github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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/version" 8 "gopkg.in/juju/names.v2" 9 10 "github.com/juju/juju/apiserver/common" 11 "github.com/juju/juju/apiserver/facade" 12 "github.com/juju/juju/apiserver/params" 13 "github.com/juju/juju/state" 14 "github.com/juju/juju/state/watcher" 15 "github.com/juju/juju/tools" 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 facade.Resources 24 authorizer facade.Authorizer 25 } 26 27 // NewUnitUpgraderAPI creates a new server-side UnitUpgraderAPI facade. 28 func NewUnitUpgraderAPI( 29 st *state.State, 30 resources facade.Resources, 31 authorizer facade.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 names.Tag) (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.EnsureErr(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 tag, err := names.ParseTag(agent.Tag) 73 if err != nil { 74 result.Results[i].Error = common.ServerError(common.ErrPerm) 75 continue 76 } 77 err = common.ErrPerm 78 if u.authorizer.AuthOwner(tag) { 79 var watcherId string 80 watcherId, err = u.watchAssignedMachine(tag) 81 if err == nil { 82 result.Results[i].NotifyWatcherId = watcherId 83 } 84 } 85 result.Results[i].Error = common.ServerError(err) 86 } 87 return result, nil 88 } 89 90 // DesiredVersion reports the Agent Version that we want that unit to be running. 91 // The desired version is what the unit's assigned machine is running. 92 func (u *UnitUpgraderAPI) DesiredVersion(args params.Entities) (params.VersionResults, error) { 93 result := make([]params.VersionResult, len(args.Entities)) 94 for i, entity := range args.Entities { 95 tag, err := names.ParseTag(entity.Tag) 96 if err != nil { 97 result[i].Error = common.ServerError(common.ErrPerm) 98 continue 99 } 100 err = common.ErrPerm 101 if u.authorizer.AuthOwner(tag) { 102 result[i].Version, err = u.getMachineToolsVersion(tag) 103 } 104 result[i].Error = common.ServerError(err) 105 } 106 return params.VersionResults{Results: result}, nil 107 } 108 109 // Tools finds the tools necessary for the given agents. 110 func (u *UnitUpgraderAPI) Tools(args params.Entities) (params.ToolsResults, error) { 111 result := params.ToolsResults{ 112 Results: make([]params.ToolsResult, len(args.Entities)), 113 } 114 for i, entity := range args.Entities { 115 result.Results[i].Error = common.ServerError(common.ErrPerm) 116 tag, err := names.ParseTag(entity.Tag) 117 if err != nil { 118 continue 119 } 120 if u.authorizer.AuthOwner(tag) { 121 result.Results[i] = u.getMachineTools(tag) 122 } 123 } 124 return result, nil 125 } 126 127 func (u *UnitUpgraderAPI) getAssignedMachine(tag names.Tag) (*state.Machine, error) { 128 // Check that we really have a unit tag. 129 switch tag := tag.(type) { 130 case names.UnitTag: 131 unit, err := u.st.Unit(tag.Id()) 132 if err != nil { 133 return nil, common.ErrPerm 134 } 135 id, err := unit.AssignedMachineId() 136 if err != nil { 137 return nil, err 138 } 139 return u.st.Machine(id) 140 default: 141 return nil, common.ErrPerm 142 } 143 } 144 145 func (u *UnitUpgraderAPI) getMachineTools(tag names.Tag) params.ToolsResult { 146 var result params.ToolsResult 147 machine, err := u.getAssignedMachine(tag) 148 if err != nil { 149 result.Error = common.ServerError(err) 150 return result 151 } 152 machineTools, err := machine.AgentTools() 153 if err != nil { 154 result.Error = common.ServerError(err) 155 return result 156 } 157 // We are okay returning the tools for just the one API server 158 // address since the unit agent won't try to download tools that 159 // are already present on the machine. 160 result.ToolsList = tools.List{machineTools} 161 return result 162 } 163 164 func (u *UnitUpgraderAPI) getMachineToolsVersion(tag names.Tag) (*version.Number, error) { 165 machine, err := u.getAssignedMachine(tag) 166 if err != nil { 167 return nil, err 168 } 169 machineTools, err := machine.AgentTools() 170 if err != nil { 171 return nil, err 172 } 173 return &machineTools.Version.Number, nil 174 }