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 }