Description:
state/apiserver/upgrader: Managers upgrade first
This addresses bug #1304340. It gives us a step along the path of having
Manager nodes (API Servers) upgrade themselves before we have all the
other machine agents upgrade (before we have the Unit agents upgrade).
We already had the last two steps. This isn't 100% reliable in HA
circumstances, because we aren't waiting for *all* nodes to be upgraded.
We could potentially change the check so it was just if DesiredVersion
!= CurrentVersion (rather than >= CurrentVersion). It also doesn't probe
the database unless there is an upgrade pending, so DesiredVersion
should still be cheap. This doesn't change FindTools, but it doesn't
seem like it needs to. (All places that call FindTools first call
DesiredVersion, because we broke stuff in the past when we didn't have
API Credentials in time.)
Also, this falls back to returning version.Current rather than returning
the version of the machine/unit/etc had recorded in State. I'm not sure
if that is worth implementing, but when we do the DB lookup to find out
if this entity.IsManager() we could grab its current version.
+type hasIsManager interface {
+ IsManager() bool
+}
+
+func (u *UpgraderAPI) entityIsManager(tag string) bool {
+ entity, err := u.st.FindEntity(tag)
+ if err != nil {
+ return false
+ }
+ if m, ok := entity.(hasIsManager); !ok {
+ return false
+ } else {
+ return m.IsManager()
+ }
+}
+
// DesiredVersion reports the Agent Version that we want that agent to be
running
func (u *UpgraderAPI) DesiredVersion(args params.Entities)
(params.VersionResults, error) {
results := make([]params.VersionResult, len(args.Entities))
@@ -101,10 +121,17 @@
if err != nil {
return params.VersionResults{}, common.ServerError(err)
}
+ // Is the desired version greater than the current API server version?
+ isNewerVersion := agentVersion.Compare(version.Current.Number) > 0
for i, entity := range args.Entities {
err := common.ErrPerm
if u.authorizer.AuthOwner(entity.Tag) {
- results[i].Version = &agentVersion
+ if !isNewerVersion || u.entityIsManager(entity.Tag) {
+ results[i].Version = &agentVersion
+ } else {
+ logger.Debugf("desired version is %s, but current version is %s and
agent is not a manager node", agentVersion, version.Current.Number)
+ results[i].Version = &version.Current.Number
+ }
err = nil
}
results[i].Error = common.ServerError(err)
Index: state/apiserver/upgrader/upgrader_test.go
=== modified file 'state/apiserver/upgrader/upgrader_test.go'
--- state/apiserver/upgrader/upgrader_test.go 2014-03-13 07:54:56 +0000
+++ state/apiserver/upgrader/upgrader_test.go 2014-04-08 12:50:26 +0000
@@ -25,6 +25,7 @@
// These are raw State objects. Use them for setup and assertions, but
// should never be touched by the API calls themselves
rawMachine *state.Machine
+ apiMachine *state.Machine
upgrader *upgrader.UpgraderAPI
resources *common.Resources
authorizer apiservertesting.FakeAuthorizer
@@ -38,6 +39,11 @@
// Create a machine to work with
var err error
+ // The first machine created is the only one allowed to
+ // JobManageEnviron
+ s.apiMachine, err = s.State.AddMachine("quantal", state.JobHostUnits,
+ state.JobManageEnviron)
+ c.Assert(err, gc.IsNil)
s.rawMachine, err = s.State.AddMachine("quantal", state.JobHostUnits)
c.Assert(err, gc.IsNil)
Reviewers: mp+214743_ code.launchpad. net,
Message:
Please take a look.
Description: /upgrader: Managers upgrade first
state/apiserver
This addresses bug #1304340. It gives us a step along the path of having
Manager nodes (API Servers) upgrade themselves before we have all the
other machine agents upgrade (before we have the Unit agents upgrade).
We already had the last two steps. This isn't 100% reliable in HA
circumstances, because we aren't waiting for *all* nodes to be upgraded.
We could potentially change the check so it was just if DesiredVersion
!= CurrentVersion (rather than >= CurrentVersion). It also doesn't probe
the database unless there is an upgrade pending, so DesiredVersion
should still be cheap. This doesn't change FindTools, but it doesn't
seem like it needs to. (All places that call FindTools first call
DesiredVersion, because we broke stuff in the past when we didn't have
API Credentials in time.)
Also, this falls back to returning version.Current rather than returning
the version of the machine/unit/etc had recorded in State. I'm not sure
if that is worth implementing, but when we do the DB lookup to find out
if this entity.IsManager() we could grab its current version.
https:/ /code.launchpad .net/~jameinel/ juju-core/ desiredversion- not-newer- 1304340/ +merge/ 214743
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/85450043/
Affected files (+86, -1 lines): /upgrader/ upgrader. go /upgrader/ upgrader_ test.go
A [revision details]
M state/apiserver
M state/apiserver
Index: [revision details] 20140408095416- cm5185gdi4wm6d6 n
=== added file '[revision details]'
--- [revision details] 2012-01-01 00:00:00 +0000
+++ [revision details] 2012-01-01 00:00:00 +0000
@@ -0,0 +1,2 @@
+Old revision: tarmac-
+New revision: <email address hidden>
Index: state/apiserver /upgrader/ upgrader. go apiserver/ upgrader/ upgrader. go' /upgrader/ upgrader. go 2014-02-18 00:15:30 +0000 /upgrader/ upgrader. go 2014-04-08 12:50:26 +0000
=== modified file 'state/
--- state/apiserver
+++ state/apiserver
@@ -6,6 +6,8 @@
import (
"errors"
+ "github. com/juju/ loggo" net/juju- core/environs/ config" net/juju- core/state" net/juju- core/state/ api/params" net/juju- core/version"
+
"launchpad.
"launchpad.
"launchpad.
@@ -14,6 +16,8 @@
"launchpad.
)
+var logger = loggo.GetLogger ("juju. state.apiserver .upgrader" ) ion(args params.Entities) (params. NotifyWatchResu lts, error) on(args params.Entities) (params. VersionResults, error)
+
type Upgrader interface {
WatchAPIVers
DesiredVersi
@@ -91,6 +95,22 @@
return agentVersion, cfg, nil
}
+type hasIsManager interface { (tag) (hasIsManager) ; !ok { VersionResults, error) { params. VersionResult, len(args.Entities)) VersionResults{ }, common. ServerError( err) Compare( version. Current. Number) > 0 AuthOwner( entity. Tag) { er(entity. Tag) { Debugf( "desired version is %s, but current version is %s and Current. Number) Current. Number i].Error = common. ServerError( err)
+ IsManager() bool
+}
+
+func (u *UpgraderAPI) entityIsManager(tag string) bool {
+ entity, err := u.st.FindEntity
+ if err != nil {
+ return false
+ }
+ if m, ok := entity.
+ return false
+ } else {
+ return m.IsManager()
+ }
+}
+
// DesiredVersion reports the Agent Version that we want that agent to be
running
func (u *UpgraderAPI) DesiredVersion(args params.Entities)
(params.
results := make([]
@@ -101,10 +121,17 @@
if err != nil {
return params.
}
+ // Is the desired version greater than the current API server version?
+ isNewerVersion := agentVersion.
for i, entity := range args.Entities {
err := common.ErrPerm
if u.authorizer.
- results[i].Version = &agentVersion
+ if !isNewerVersion || u.entityIsManag
+ results[i].Version = &agentVersion
+ } else {
+ logger.
agent is not a manager node", agentVersion, version.
+ results[i].Version = &version.
+ }
err = nil
}
results[
Index: state/apiserver /upgrader/ upgrader_ test.go apiserver/ upgrader/ upgrader_ test.go' /upgrader/ upgrader_ test.go 2014-03-13 07:54:56 +0000 /upgrader/ upgrader_ test.go 2014-04-08 12:50:26 +0000 UpgraderAPI g.FakeAuthorize r
=== modified file 'state/
--- state/apiserver
+++ state/apiserver
@@ -25,6 +25,7 @@
// These are raw State objects. Use them for setup and assertions, but
// should never be touched by the API calls themselves
rawMachine *state.Machine
+ apiMachine *state.Machine
upgrader *upgrader.
resources *common.Resources
authorizer apiservertestin
@@ -38,6 +39,11 @@
// Create a machine to work with AddMachine( "quantal" , state.JobHostUnits, Environ) AddMachine( "quantal" , state.JobHostUnits)
var err error
+ // The first machine created is the only one allowed to
+ // JobManageEnviron
+ s.apiMachine, err = s.State.
+ state.JobManage
+ c.Assert(err, gc.IsNil)
s.rawMachine, err = s.State.
c.Assert(err, gc.IsNil)
@@ -277,3 +283,53 @@ agentVersion, gc.NotNil) *agentVersion, gc.DeepEquals, version. Current. Number) tVersion( c *gc.C) version.Number { Version we have to first SetTools on SetAgentVersion (version. Current) SetAgentVersion (version. Current) SetEnvironAgent Version( newer.Number) EnvironConfig( ) ionUnrestricted ForAPIAgents( c entVersion( c) g.FakeAuthorize r{ NewUpgraderAPI( s.State, s.resources, Entities{ Entities: []params. Entity{ {Tag: Tag()}} } DesiredVersion( args) results. Results, gc.HasLen, 1) results. Results[ 0].Error, gc.IsNil) Results[ 0].Version agentVersion, gc.NotNil) *agentVersion, gc.DeepEquals, newVersion) ionRestrictedFo rNonAPIAgents( c entVersion( c) newVersion, gc.Not(gc.Equals), version. Current. Number) Entities{ Entities: []params. Entity{ {Tag: Tag()}} } DesiredVersion( args) results. Results, gc.HasLen, 1) results. Results[ 0].Error, gc.IsNil) Results[ 0].Version agentVersion, gc.NotNil) *agentVersion, gc.DeepEquals, version. Current. Number)
c.Assert(
c.Check(
}
+
+func (s *upgraderSuite) bumpDesiredAgen
+ // In order to call SetEnvironAgent
+ // all the existing machines
+ s.apiMachine.
+ s.rawMachine.
+ newer := version.Current
+ newer.Patch++
+ err := s.State.
+ c.Assert(err, gc.IsNil)
+ cfg, err := s.State.
+ c.Assert(err, gc.IsNil)
+ vers, ok := cfg.AgentVersion()
+ c.Assert(ok, jc.IsTrue)
+ c.Check(vers, gc.Equals, newer.Number)
+ return newer.Number
+}
+
+func (s *upgraderSuite) TestDesiredVers
*gc.C) {
+ newVersion := s.bumpDesiredAg
+ // Grab a different Upgrader for the apiMachine
+ authorizer := apiservertestin
+ Tag: s.apiMachine.Tag(),
+ LoggedIn: true,
+ MachineAgent: true,
+ }
+ upgraderAPI, err := upgrader.
authorizer)
+ c.Assert(err, gc.IsNil)
+ args := params.
s.apiMachine.
+ results, err := upgraderAPI.
+ c.Assert(err, gc.IsNil)
+ c.Check(
+ c.Assert(
+ agentVersion := results.
+ c.Assert(
+ c.Check(
+}
+
+func (s *upgraderSuite) TestDesiredVers
*gc.C) {
+ newVersion := s.bumpDesiredAg
+ c.Assert(
+ args := params.
s.rawMachine.
+ results, err := s.upgrader.
+ c.Assert(err, gc.IsNil)
+ c.Check(
+ c.Assert(
+ agentVersion := results.
+ c.Assert(
+ c.Check(
+}