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 }