Skip to content

Commit

Permalink
Merge pull request juju#10366 from manadart/2.6-cache-testing-infrast…
Browse files Browse the repository at this point in the history
…ructure

juju#10366

## Description of change

This patch generalises functionality for putting state entities directly into a cache for testing purposes. It obviates the need for running a multi-watcher and cache-worker for cache population.

Included is a relocation of uniter API server access control methods to their own module to tidy up the API constructor.

## QA steps

Tests from `apiserver/facades/agent/logger` and `worker/modelcache` continue to pass.

## Documentation changes

None.

## Bug reference

N/A
  • Loading branch information
jujubot authored Jun 21, 2019
2 parents 56d6c10 + f402984 commit 7671273
Show file tree
Hide file tree
Showing 8 changed files with 483 additions and 265 deletions.
69 changes: 17 additions & 52 deletions apiserver/facades/agent/logger/logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
package logger_test

import (
"time"

jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"
"gopkg.in/juju/names.v2"
Expand All @@ -21,7 +19,6 @@ import (
"github.com/juju/juju/core/cache/cachetest"
"github.com/juju/juju/state"
statetesting "github.com/juju/juju/state/testing"
"github.com/juju/juju/testing"
)

type loggerSuite struct {
Expand All @@ -34,11 +31,8 @@ type loggerSuite struct {
resources *common.Resources
authorizer apiservertesting.FakeAuthorizer

change cache.ModelChange
changes chan interface{}
controller *cache.Controller
events chan interface{}
capture func(change interface{})
ctrl *cachetest.TestController
capture func(change interface{})
}

var _ = gc.Suite(&loggerSuite{})
Expand All @@ -58,52 +52,26 @@ func (s *loggerSuite) SetUpTest(c *gc.C) {
Tag: s.rawMachine.Tag(),
}

s.events = make(chan interface{})
notify := func(change interface{}) {
send := false
switch change.(type) {
case cache.ModelChange:
send = true
case cache.RemoveModel:
send = true
default:
// no-op
}
if send {
c.Logf("sending %#v", change)
select {
case s.events <- change:
case <-time.After(testing.LongWait):
c.Fatalf("change not processed by test")
}
}
}
s.ctrl = cachetest.NewTestController(cachetest.ModelEvents)
s.ctrl.Init(c)

s.changes = make(chan interface{})
controller, err := cache.NewController(cache.ControllerConfig{
Changes: s.changes,
Notify: notify,
})
c.Assert(err, jc.ErrorIsNil)
s.controller = controller
s.AddCleanup(func(c *gc.C) { workertest.CleanKill(c, s.controller) })
// Add the current model to the controller.
s.change = cachetest.ModelChangeFromState(c, s.State)
s.changes <- s.change
// Ensure it is processed before we create the logger api.
select {
case <-s.events:
case <-time.After(testing.LongWait):
c.Fatalf("change not processed by test")
}
m := cachetest.ModelChangeFromState(c, s.State)
s.ctrl.SendChange(m)

// Ensure it is processed before we create the logger API.
_ = s.ctrl.NextChange(c)

s.AddCleanup(func(c *gc.C) { workertest.CleanKill(c, s.ctrl.Controller) })

s.logger, err = s.makeLoggerAPI(s.authorizer)
c.Assert(err, jc.ErrorIsNil)
}

func (s *loggerSuite) makeLoggerAPI(auth facade.Authorizer) (*logger.LoggerAPI, error) {
ctx := facadetest.Context{
Auth_: auth,
Controller_: s.controller,
Controller_: s.ctrl.Controller,
Resources_: s.resources,
State_: s.State,
}
Expand Down Expand Up @@ -143,13 +111,10 @@ func (s *loggerSuite) TestWatchLoggingConfigNothing(c *gc.C) {
}

func (s *loggerSuite) setLoggingConfig(c *gc.C, loggingConfig string) {
s.change.Config["logging-config"] = loggingConfig
s.changes <- s.change
select {
case <-s.events:
case <-time.After(testing.LongWait):
c.Fatalf("change not processed by test")
}
m := cachetest.ModelChangeFromState(c, s.State)
m.Config["logging-config"] = loggingConfig
s.ctrl.SendChange(m)
_ = s.ctrl.NextChange(c)
}

func (s *loggerSuite) TestWatchLoggingConfig(c *gc.C) {
Expand Down
129 changes: 129 additions & 0 deletions apiserver/facades/agent/uniter/access.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// Copyright 2019 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package uniter

import (
"github.com/juju/errors"
"github.com/juju/juju/apiserver/facades/client/application"
"gopkg.in/juju/names.v2"

"github.com/juju/juju/apiserver/common"
"github.com/juju/juju/apiserver/facade"
"github.com/juju/juju/state"
)

// unitAccessor creates a accessUnit function for accessing a unit
func unitAccessor(authorizer facade.Authorizer, st *state.State) 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.
app, err := st.Application(tag.Name)
if err != nil {
return nil, errors.Trace(err)
}
allUnits, err := app.AllUnits()
if err != nil {
return nil, errors.Trace(err)
}
return func(tag names.Tag) bool {
for _, u := range allUnits {
if u.Tag() == tag {
return true
}
}
return false
}, 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)
}
}
}

func applicationAccessor(authorizer facade.Authorizer, st *state.State) common.GetAuthFunc {
return func() (common.AuthFunc, error) {
switch tag := authorizer.GetAuthTag().(type) {
case names.ApplicationTag:
return func(applicationTag names.Tag) bool {
return tag == applicationTag
}, nil
case names.UnitTag:
entity, err := st.Unit(tag.Id())
if err != nil {
return nil, errors.Trace(err)
}
applicationName := entity.ApplicationName()
applicationTag := names.NewApplicationTag(applicationName)
return func(tag names.Tag) bool {
return tag == applicationTag
}, nil
default:
return nil, errors.Errorf("expected names.UnitTag or names.ApplicationTag, got %T", tag)
}
}
}

func machineAccessor(authorizer facade.Authorizer, st *state.State) common.GetAuthFunc {
return func() (common.AuthFunc, error) {
switch tag := authorizer.GetAuthTag().(type) {
// Application agents can't access machines.
case names.ApplicationTag:
return func(tag names.Tag) bool {
return false
}, nil
case names.UnitTag:
entity, err := st.Unit(tag.Id())
if err != nil {
return nil, errors.Trace(err)
}
machineId, err := entity.AssignedMachineId()
if err != nil {
return nil, errors.Trace(err)
}
machineTag := names.NewMachineTag(machineId)
return func(tag names.Tag) bool {
return tag == machineTag
}, nil
default:
return nil, errors.Errorf("expected names.UnitTag or names.ApplicationTag, got %T", tag)
}
}
}

func cloudSpecAccessor(authorizer facade.Authorizer, st *state.State) func() (func() bool, error) {
return func() (func() bool, error) {
var appName string
var err error

switch tag := authorizer.GetAuthTag().(type) {
case names.ApplicationTag:
appName = tag.Id()
case names.UnitTag:
entity, err := st.Unit(tag.Id())
if err != nil {
return nil, errors.Trace(err)
}
appName = entity.ApplicationName()
default:
return nil, errors.Errorf("expected names.UnitTag or names.ApplicationTag, got %T", tag)
}

app, err := st.Application(appName)
if err != nil {
return nil, errors.Trace(err)
}
config, err := app.ApplicationConfig()
if err != nil {
return nil, errors.Trace(err)
}
return func() bool {
return config.GetBool(application.TrustConfigOptionName, false)
}, nil
}
}
111 changes: 4 additions & 107 deletions apiserver/facades/agent/uniter/uniter.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import (
"github.com/juju/juju/apiserver/facade"
leadershipapiserver "github.com/juju/juju/apiserver/facades/agent/leadership"
"github.com/juju/juju/apiserver/facades/agent/meterstatus"
"github.com/juju/juju/apiserver/facades/client/application"
"github.com/juju/juju/apiserver/params"
"github.com/juju/juju/caas"
"github.com/juju/juju/caas/kubernetes/provider"
Expand Down Expand Up @@ -127,39 +126,6 @@ type UniterAPIV4 struct {
UniterAPIV5
}

// unitAccessor creates a accessUnit function for accessing a unit
func unitAccessor(authorizer facade.Authorizer, st *state.State) 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.
app, err := st.Application(tag.Name)
if err != nil {
return nil, errors.Trace(err)
}
allUnits, err := app.AllUnits()
if err != nil {
return nil, errors.Trace(err)
}
return func(tag names.Tag) bool {
for _, u := range allUnits {
if u.Tag() == tag {
return true
}
}
return false
}, 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)
}
}
}

// NewUniterAPI creates a new instance of the core Uniter API.
func NewUniterAPI(context facade.Context) (*UniterAPI, error) {
authorizer := context.Auth()
Expand All @@ -168,84 +134,15 @@ func NewUniterAPI(context facade.Context) (*UniterAPI, error) {
}
st := context.State()
resources := context.Resources()
accessUnit := unitAccessor(authorizer, st)
leadershipChecker, err := context.LeadershipChecker()
if err != nil {
return nil, errors.Trace(err)
}
accessApplication := func() (common.AuthFunc, error) {
switch tag := authorizer.GetAuthTag().(type) {
case names.ApplicationTag:
return func(applicationTag names.Tag) bool {
return tag == applicationTag
}, nil
case names.UnitTag:
entity, err := st.Unit(tag.Id())
if err != nil {
return nil, errors.Trace(err)
}
applicationName := entity.ApplicationName()
applicationTag := names.NewApplicationTag(applicationName)
return func(tag names.Tag) bool {
return tag == applicationTag
}, nil
default:
return nil, errors.Errorf("expected names.UnitTag or names.ApplicationTag, got %T", tag)
}
}
accessMachine := func() (common.AuthFunc, error) {
switch tag := authorizer.GetAuthTag().(type) {
// Application agents can't access machines.
case names.ApplicationTag:
return func(tag names.Tag) bool {
return false
}, nil
case names.UnitTag:
entity, err := st.Unit(tag.Id())
if err != nil {
return nil, errors.Trace(err)
}
machineId, err := entity.AssignedMachineId()
if err != nil {
return nil, errors.Trace(err)
}
machineTag := names.NewMachineTag(machineId)
return func(tag names.Tag) bool {
return tag == machineTag
}, nil
default:
return nil, errors.Errorf("expected names.UnitTag or names.ApplicationTag, got %T", tag)
}
}
accessCloudSpec := func() (func() bool, error) {
var appName string
var err error

switch tag := authorizer.GetAuthTag().(type) {
case names.ApplicationTag:
appName = tag.Id()
case names.UnitTag:
entity, err := st.Unit(tag.Id())
if err != nil {
return nil, errors.Trace(err)
}
appName = entity.ApplicationName()
default:
return nil, errors.Errorf("expected names.UnitTag or names.ApplicationTag, got %T", tag)
}

app, err := st.Application(appName)
if err != nil {
return nil, errors.Trace(err)
}
config, err := app.ApplicationConfig()
if err != nil {
return nil, errors.Trace(err)
}
return func() bool {
return config.GetBool(application.TrustConfigOptionName, false)
}, nil
}
accessUnit := unitAccessor(authorizer, st)
accessApplication := applicationAccessor(authorizer, st)
accessMachine := machineAccessor(authorizer, st)
accessCloudSpec := cloudSpecAccessor(authorizer, st)

m, err := st.Model()
if err != nil {
Expand Down
Loading

0 comments on commit 7671273

Please sign in to comment.