github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/apiserver/upgrader/upgrader.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/errors" 8 "github.com/juju/loggo" 9 "github.com/juju/version" 10 "gopkg.in/juju/names.v2" 11 12 "github.com/juju/juju/apiserver/common" 13 "github.com/juju/juju/apiserver/facade" 14 "github.com/juju/juju/apiserver/params" 15 "github.com/juju/juju/environs/config" 16 "github.com/juju/juju/state" 17 "github.com/juju/juju/state/stateenvirons" 18 "github.com/juju/juju/state/watcher" 19 jujuversion "github.com/juju/juju/version" 20 ) 21 22 var logger = loggo.GetLogger("juju.apiserver.upgrader") 23 24 func init() { 25 common.RegisterStandardFacade("Upgrader", 1, upgraderFacade) 26 } 27 28 // upgraderFacade is a bit unique vs the other API Facades, as it has two 29 // implementations that actually expose the same API and which one gets 30 // returned depends on who is calling. 31 // Both of them conform to the exact Upgrader API, so the actual calls that are 32 // available do not depend on who is currently connected. 33 func upgraderFacade(st *state.State, resources facade.Resources, auth facade.Authorizer) (Upgrader, error) { 34 // The type of upgrader we return depends on who is asking. 35 // Machines get an UpgraderAPI, units get a UnitUpgraderAPI. 36 // This is tested in the api/upgrader package since there 37 // are currently no direct srvRoot tests. 38 // TODO(dfc) this is redundant 39 tag, err := names.ParseTag(auth.GetAuthTag().String()) 40 if err != nil { 41 return nil, common.ErrPerm 42 } 43 switch tag.(type) { 44 case names.MachineTag: 45 return NewUpgraderAPI(st, resources, auth) 46 case names.UnitTag: 47 return NewUnitUpgraderAPI(st, resources, auth) 48 } 49 // Not a machine or unit. 50 return nil, common.ErrPerm 51 } 52 53 type Upgrader interface { 54 WatchAPIVersion(args params.Entities) (params.NotifyWatchResults, error) 55 DesiredVersion(args params.Entities) (params.VersionResults, error) 56 Tools(args params.Entities) (params.ToolsResults, error) 57 SetTools(args params.EntitiesVersion) (params.ErrorResults, error) 58 } 59 60 // UpgraderAPI provides access to the Upgrader API facade. 61 type UpgraderAPI struct { 62 *common.ToolsGetter 63 *common.ToolsSetter 64 65 st *state.State 66 resources facade.Resources 67 authorizer facade.Authorizer 68 } 69 70 // NewUpgraderAPI creates a new server-side UpgraderAPI facade. 71 func NewUpgraderAPI( 72 st *state.State, 73 resources facade.Resources, 74 authorizer facade.Authorizer, 75 ) (*UpgraderAPI, error) { 76 if !authorizer.AuthMachineAgent() { 77 return nil, common.ErrPerm 78 } 79 getCanReadWrite := func() (common.AuthFunc, error) { 80 return authorizer.AuthOwner, nil 81 } 82 env, err := st.Model() 83 if err != nil { 84 return nil, err 85 } 86 urlGetter := common.NewToolsURLGetter(env.UUID(), st) 87 configGetter := stateenvirons.EnvironConfigGetter{st} 88 return &UpgraderAPI{ 89 ToolsGetter: common.NewToolsGetter(st, configGetter, st, urlGetter, getCanReadWrite), 90 ToolsSetter: common.NewToolsSetter(st, getCanReadWrite), 91 st: st, 92 resources: resources, 93 authorizer: authorizer, 94 }, nil 95 } 96 97 // WatchAPIVersion starts a watcher to track if there is a new version 98 // of the API that we want to upgrade to 99 func (u *UpgraderAPI) WatchAPIVersion(args params.Entities) (params.NotifyWatchResults, error) { 100 result := params.NotifyWatchResults{ 101 Results: make([]params.NotifyWatchResult, len(args.Entities)), 102 } 103 for i, agent := range args.Entities { 104 tag, err := names.ParseTag(agent.Tag) 105 if err != nil { 106 return params.NotifyWatchResults{}, errors.Trace(err) 107 } 108 err = common.ErrPerm 109 if u.authorizer.AuthOwner(tag) { 110 watch := u.st.WatchForModelConfigChanges() 111 // Consume the initial event. Technically, API 112 // calls to Watch 'transmit' the initial event 113 // in the Watch response. But NotifyWatchers 114 // have no state to transmit. 115 if _, ok := <-watch.Changes(); ok { 116 result.Results[i].NotifyWatcherId = u.resources.Register(watch) 117 err = nil 118 } else { 119 err = watcher.EnsureErr(watch) 120 } 121 } 122 result.Results[i].Error = common.ServerError(err) 123 } 124 return result, nil 125 } 126 127 func (u *UpgraderAPI) getGlobalAgentVersion() (version.Number, *config.Config, error) { 128 // Get the Agent Version requested in the Environment Config 129 cfg, err := u.st.ModelConfig() 130 if err != nil { 131 return version.Number{}, nil, err 132 } 133 agentVersion, ok := cfg.AgentVersion() 134 if !ok { 135 return version.Number{}, nil, errors.New("agent version not set in model config") 136 } 137 return agentVersion, cfg, nil 138 } 139 140 type hasIsManager interface { 141 IsManager() bool 142 } 143 144 func (u *UpgraderAPI) entityIsManager(tag names.Tag) bool { 145 entity, err := u.st.FindEntity(tag) 146 if err != nil { 147 return false 148 } 149 if m, ok := entity.(hasIsManager); !ok { 150 return false 151 } else { 152 return m.IsManager() 153 } 154 } 155 156 // DesiredVersion reports the Agent Version that we want that agent to be running 157 func (u *UpgraderAPI) DesiredVersion(args params.Entities) (params.VersionResults, error) { 158 results := make([]params.VersionResult, len(args.Entities)) 159 if len(args.Entities) == 0 { 160 return params.VersionResults{}, nil 161 } 162 agentVersion, _, err := u.getGlobalAgentVersion() 163 if err != nil { 164 return params.VersionResults{}, common.ServerError(err) 165 } 166 // Is the desired version greater than the current API server version? 167 isNewerVersion := agentVersion.Compare(jujuversion.Current) > 0 168 for i, entity := range args.Entities { 169 tag, err := names.ParseTag(entity.Tag) 170 if err != nil { 171 results[i].Error = common.ServerError(err) 172 continue 173 } 174 err = common.ErrPerm 175 if u.authorizer.AuthOwner(tag) { 176 // Only return the globally desired agent version if the 177 // asking entity is a machine agent with JobManageModel or 178 // if this API server is running the globally desired agent 179 // version. Otherwise report this API server's current 180 // agent version. 181 // 182 // This ensures that state machine agents will upgrade 183 // first - once they have restarted and are running the 184 // new version other agents will start to see the new 185 // agent version. 186 if !isNewerVersion || u.entityIsManager(tag) { 187 results[i].Version = &agentVersion 188 } else { 189 logger.Debugf("desired version is %s, but current version is %s and agent is not a manager node", agentVersion, jujuversion.Current) 190 results[i].Version = &jujuversion.Current 191 } 192 err = nil 193 } 194 results[i].Error = common.ServerError(err) 195 } 196 return params.VersionResults{Results: results}, nil 197 }