-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request juju#12161 from wallyworld/k8s-leadership-flapping
juju#12161 Fix 2 key issues with the k8s operator and uniter workers. The first is the permission check used for the facade authoriser. If the unit was removed, a permission denied error was returned, causing the entire operator worker to bounce and mess up unit leadership. Instead, the tag of the requested unit is checked to see if it matches the authenticated application agent. Any subsequent sate api call will return not found if the unit is accessed. The logic to do the permission checks was duplicated in a few places, so is not extracted to a common method. Also, the Remove() api call is now a no op if the unit has already been removed. Secondly, when a unit goes dying and is destroyed, we check to see if the unit agent had started - if not we can short circuit the cleanup and remove directly. However, if there is contention for the machine execution lock and a lot of k8s unit churn caused by many pod-spec-set calls, the unit agent may not have updated agent status even though it had started. This would lead to juju removing the unit and leaving the unit agent running. This then leads to the issue in item one, where an operation on the unit gets a permission error and the entire operator worker restarts. This causes the leadership tracker worker to flip the unit leader to a minion and messes up leadership tracking. The issue is fixed by adjusting the initial agent state to "installing agent". This is then updated to "initialising agent" when the unit agent starts, allowing juju to know whether to short circuit a unit destroy operation or not. ## QA steps bootstrap k8s deploy kubeflow ## Bug reference https://bugs.launchpad.net/bugs/1898966
- Loading branch information
Showing
24 changed files
with
262 additions
and
128 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
// Copyright 2020 Canonical Ltd. | ||
// Licensed under the AGPLv3, see LICENCE file for details. | ||
|
||
package unitcommon | ||
|
||
import ( | ||
"github.com/juju/errors" | ||
"github.com/juju/names/v4" | ||
|
||
"github.com/juju/juju/apiserver/common" | ||
"github.com/juju/juju/apiserver/facade" | ||
"github.com/juju/juju/state" | ||
) | ||
|
||
// ApplicationGetter provides a method | ||
// to determine if an application exists. | ||
type ApplicationGetter interface { | ||
ApplicationExists(string) error | ||
} | ||
|
||
type stateApplicationGetter interface { | ||
Application(string) (*state.Application, error) | ||
} | ||
|
||
// Backend returns an application abstraction for a | ||
// given state.State instance. | ||
func Backend(st stateApplicationGetter) ApplicationGetter { | ||
return backend{st} | ||
} | ||
|
||
type backend struct { | ||
stateApplicationGetter | ||
} | ||
|
||
// Application implements ApplicationGetter. | ||
func (b backend) ApplicationExists(name string) error { | ||
_, err := b.stateApplicationGetter.Application(name) | ||
return err | ||
} | ||
|
||
// UnitAccessor returns an auth function which determines if the | ||
// authenticated entity can access a unit or application. | ||
func UnitAccessor(authorizer facade.Authorizer, st ApplicationGetter) common.GetAuthFunc { | ||
return func() (common.AuthFunc, error) { | ||
switch tag := authorizer.GetAuthTag().(type) { | ||
case names.ApplicationTag: | ||
// If called by an application agent, any of the units | ||
// belonging to that application can be accessed. | ||
appName := tag.Name | ||
err := st.ApplicationExists(appName) | ||
if err != nil { | ||
return nil, errors.Trace(err) | ||
} | ||
return func(tag names.Tag) bool { | ||
if tag.Kind() != names.UnitTagKind { | ||
return false | ||
} | ||
unitApp, err := names.UnitApplication(tag.Id()) | ||
if err != nil { | ||
return false | ||
} | ||
return unitApp == appName | ||
}, nil | ||
case names.UnitTag: | ||
return func(tag names.Tag) bool { | ||
return authorizer.AuthOwner(tag) | ||
}, nil | ||
default: | ||
return nil, errors.Errorf("expected names.UnitTag or names.ApplicationTag, got %T", tag) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
// Copyright 2020 Canonical Ltd. | ||
// Licensed under the AGPLv3, see LICENCE file for details. | ||
|
||
package unitcommon_test | ||
|
||
import ( | ||
"github.com/juju/errors" | ||
"github.com/juju/names/v4" | ||
"github.com/juju/testing" | ||
jc "github.com/juju/testing/checkers" | ||
gc "gopkg.in/check.v1" | ||
|
||
"github.com/juju/juju/apiserver/common/unitcommon" | ||
apiservertesting "github.com/juju/juju/apiserver/testing" | ||
) | ||
|
||
type UnitAccessorSuite struct { | ||
testing.IsolationSuite | ||
} | ||
|
||
var _ = gc.Suite(&UnitAccessorSuite{}) | ||
|
||
type appGetter struct { | ||
exits bool | ||
} | ||
|
||
func (a appGetter) ApplicationExists(name string) error { | ||
if a.exits { | ||
return nil | ||
} | ||
return errors.NotFoundf("application %q", name) | ||
} | ||
|
||
func (s *UnitAccessorSuite) TestApplicationAgent(c *gc.C) { | ||
auth := apiservertesting.FakeAuthorizer{ | ||
Tag: names.NewApplicationTag("gitlab"), | ||
} | ||
getAuthFunc := unitcommon.UnitAccessor(auth, appGetter{true}) | ||
authFunc, err := getAuthFunc() | ||
c.Assert(err, jc.ErrorIsNil) | ||
ok := authFunc(names.NewUnitTag("gitlab/0")) | ||
c.Assert(ok, jc.IsTrue) | ||
ok = authFunc(names.NewUnitTag("mysql/0")) | ||
c.Assert(ok, jc.IsFalse) | ||
} | ||
|
||
func (s *UnitAccessorSuite) TestApplicationNotFound(c *gc.C) { | ||
auth := apiservertesting.FakeAuthorizer{ | ||
Tag: names.NewApplicationTag("gitlab"), | ||
} | ||
getAuthFunc := unitcommon.UnitAccessor(auth, appGetter{false}) | ||
_, err := getAuthFunc() | ||
c.Assert(err, jc.Satisfies, errors.IsNotFound) | ||
} | ||
|
||
func (s *UnitAccessorSuite) TestUnitAgent(c *gc.C) { | ||
auth := apiservertesting.FakeAuthorizer{ | ||
Tag: names.NewUnitTag("gitlab/0"), | ||
} | ||
getAuthFunc := unitcommon.UnitAccessor(auth, appGetter{true}) | ||
authFunc, err := getAuthFunc() | ||
c.Assert(err, jc.ErrorIsNil) | ||
ok := authFunc(names.NewUnitTag("gitlab/0")) | ||
c.Assert(ok, jc.IsTrue) | ||
ok = authFunc(names.NewUnitTag("gitlab/1")) | ||
c.Assert(ok, jc.IsFalse) | ||
ok = authFunc(names.NewUnitTag("mysql/0")) | ||
c.Assert(ok, jc.IsFalse) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// Copyright 2020 Canonical Ltd. | ||
// Licensed under the AGPLv3, see LICENCE file for details. | ||
|
||
package unitcommon_test | ||
|
||
import ( | ||
"testing" | ||
|
||
gc "gopkg.in/check.v1" | ||
) | ||
|
||
func TestPackage(t *testing.T) { | ||
gc.TestingT(t) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.