github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/state/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 "errors" 8 9 "github.com/juju/loggo" 10 11 "github.com/juju/juju/environs/config" 12 "github.com/juju/juju/state" 13 "github.com/juju/juju/state/api/params" 14 "github.com/juju/juju/state/apiserver/common" 15 "github.com/juju/juju/state/watcher" 16 "github.com/juju/juju/version" 17 ) 18 19 var logger = loggo.GetLogger("juju.state.apiserver.upgrader") 20 21 type Upgrader interface { 22 WatchAPIVersion(args params.Entities) (params.NotifyWatchResults, error) 23 DesiredVersion(args params.Entities) (params.VersionResults, error) 24 Tools(args params.Entities) (params.ToolsResults, error) 25 SetTools(args params.EntitiesVersion) (params.ErrorResults, error) 26 } 27 28 // UpgraderAPI provides access to the Upgrader API facade. 29 type UpgraderAPI struct { 30 *common.ToolsGetter 31 *common.ToolsSetter 32 33 st *state.State 34 resources *common.Resources 35 authorizer common.Authorizer 36 } 37 38 // NewUpgraderAPI creates a new client-side UpgraderAPI facade. 39 func NewUpgraderAPI( 40 st *state.State, 41 resources *common.Resources, 42 authorizer common.Authorizer, 43 ) (*UpgraderAPI, error) { 44 if !authorizer.AuthMachineAgent() { 45 return nil, common.ErrPerm 46 } 47 getCanReadWrite := func() (common.AuthFunc, error) { 48 return authorizer.AuthOwner, nil 49 } 50 return &UpgraderAPI{ 51 ToolsGetter: common.NewToolsGetter(st, getCanReadWrite), 52 ToolsSetter: common.NewToolsSetter(st, getCanReadWrite), 53 st: st, 54 resources: resources, 55 authorizer: authorizer, 56 }, nil 57 } 58 59 // WatchAPIVersion starts a watcher to track if there is a new version 60 // of the API that we want to upgrade to 61 func (u *UpgraderAPI) WatchAPIVersion(args params.Entities) (params.NotifyWatchResults, error) { 62 result := params.NotifyWatchResults{ 63 Results: make([]params.NotifyWatchResult, len(args.Entities)), 64 } 65 for i, agent := range args.Entities { 66 err := common.ErrPerm 67 if u.authorizer.AuthOwner(agent.Tag) { 68 watch := u.st.WatchForEnvironConfigChanges() 69 // Consume the initial event. Technically, API 70 // calls to Watch 'transmit' the initial event 71 // in the Watch response. But NotifyWatchers 72 // have no state to transmit. 73 if _, ok := <-watch.Changes(); ok { 74 result.Results[i].NotifyWatcherId = u.resources.Register(watch) 75 err = nil 76 } else { 77 err = watcher.MustErr(watch) 78 } 79 } 80 result.Results[i].Error = common.ServerError(err) 81 } 82 return result, nil 83 } 84 85 func (u *UpgraderAPI) getGlobalAgentVersion() (version.Number, *config.Config, error) { 86 // Get the Agent Version requested in the Environment Config 87 cfg, err := u.st.EnvironConfig() 88 if err != nil { 89 return version.Number{}, nil, err 90 } 91 agentVersion, ok := cfg.AgentVersion() 92 if !ok { 93 return version.Number{}, nil, errors.New("agent version not set in environment config") 94 } 95 return agentVersion, cfg, nil 96 } 97 98 type hasIsManager interface { 99 IsManager() bool 100 } 101 102 func (u *UpgraderAPI) entityIsManager(tag string) bool { 103 entity, err := u.st.FindEntity(tag) 104 if err != nil { 105 return false 106 } 107 if m, ok := entity.(hasIsManager); !ok { 108 return false 109 } else { 110 return m.IsManager() 111 } 112 } 113 114 // DesiredVersion reports the Agent Version that we want that agent to be running 115 func (u *UpgraderAPI) DesiredVersion(args params.Entities) (params.VersionResults, error) { 116 results := make([]params.VersionResult, len(args.Entities)) 117 if len(args.Entities) == 0 { 118 return params.VersionResults{}, nil 119 } 120 agentVersion, _, err := u.getGlobalAgentVersion() 121 if err != nil { 122 return params.VersionResults{}, common.ServerError(err) 123 } 124 // Is the desired version greater than the current API server version? 125 isNewerVersion := agentVersion.Compare(version.Current.Number) > 0 126 for i, entity := range args.Entities { 127 err := common.ErrPerm 128 if u.authorizer.AuthOwner(entity.Tag) { 129 // Only return the globally desired agent version if the 130 // asking entity is a machine agent with JobManageEnviron or 131 // if this API server is running the globally desired agent 132 // version. Otherwise report this API server's current 133 // agent version. 134 // 135 // This ensures that state machine agents will upgrade 136 // first - once they have restarted and are running the 137 // new version other agents will start to see the new 138 // agent version. 139 if !isNewerVersion || u.entityIsManager(entity.Tag) { 140 results[i].Version = &agentVersion 141 } else { 142 logger.Debugf("desired version is %s, but current version is %s and agent is not a manager node", agentVersion, version.Current.Number) 143 results[i].Version = &version.Current.Number 144 } 145 err = nil 146 } 147 results[i].Error = common.ServerError(err) 148 } 149 return params.VersionResults{Results: results}, nil 150 }