github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/facades/client/controller/controller_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package controller_test
     5  
     6  import (
     7  	stdcontext "context"
     8  	"encoding/json"
     9  	"regexp"
    10  	"time"
    11  
    12  	"github.com/juju/clock"
    13  	"github.com/juju/errors"
    14  	"github.com/juju/loggo"
    15  	"github.com/juju/names/v5"
    16  	"github.com/juju/pubsub/v2"
    17  	jc "github.com/juju/testing/checkers"
    18  	"github.com/juju/utils/v3"
    19  	"github.com/juju/version/v2"
    20  	"github.com/juju/worker/v3/workertest"
    21  	"github.com/kr/pretty"
    22  	"github.com/prometheus/client_golang/prometheus"
    23  	gc "gopkg.in/check.v1"
    24  	"gopkg.in/macaroon.v2"
    25  
    26  	"github.com/juju/juju/apiserver"
    27  	"github.com/juju/juju/apiserver/common"
    28  	"github.com/juju/juju/apiserver/facade/facadetest"
    29  	"github.com/juju/juju/apiserver/facades/client/controller"
    30  	apiservertesting "github.com/juju/juju/apiserver/testing"
    31  	"github.com/juju/juju/cloud"
    32  	corecontroller "github.com/juju/juju/controller"
    33  	"github.com/juju/juju/core/cache"
    34  	coremultiwatcher "github.com/juju/juju/core/multiwatcher"
    35  	"github.com/juju/juju/core/permission"
    36  	"github.com/juju/juju/docker"
    37  	"github.com/juju/juju/environs"
    38  	environscloudspec "github.com/juju/juju/environs/cloudspec"
    39  	"github.com/juju/juju/environs/config"
    40  	pscontroller "github.com/juju/juju/pubsub/controller"
    41  	"github.com/juju/juju/rpc/params"
    42  	"github.com/juju/juju/state"
    43  	statetesting "github.com/juju/juju/state/testing"
    44  	"github.com/juju/juju/testing"
    45  	"github.com/juju/juju/testing/factory"
    46  	"github.com/juju/juju/worker/gate"
    47  	"github.com/juju/juju/worker/modelcache"
    48  	"github.com/juju/juju/worker/multiwatcher"
    49  )
    50  
    51  type controllerSuite struct {
    52  	statetesting.StateSuite
    53  
    54  	controller *controller.ControllerAPI
    55  	resources  *common.Resources
    56  	authorizer apiservertesting.FakeAuthorizer
    57  	hub        *pubsub.StructuredHub
    58  	context    facadetest.Context
    59  }
    60  
    61  var _ = gc.Suite(&controllerSuite{})
    62  
    63  func (s *controllerSuite) SetUpTest(c *gc.C) {
    64  	// Initial config needs to be set before the StateSuite SetUpTest.
    65  	s.InitialConfig = testing.CustomModelConfig(c, testing.Attrs{
    66  		"name": "controller",
    67  	})
    68  
    69  	s.StateSuite.SetUpTest(c)
    70  
    71  	allWatcherBacking, err := state.NewAllWatcherBacking(s.StatePool)
    72  	c.Assert(err, jc.ErrorIsNil)
    73  	multiWatcherWorker, err := multiwatcher.NewWorker(multiwatcher.Config{
    74  		Clock:                clock.WallClock,
    75  		Logger:               loggo.GetLogger("test"),
    76  		Backing:              allWatcherBacking,
    77  		PrometheusRegisterer: noopRegisterer{},
    78  	})
    79  	c.Assert(err, jc.ErrorIsNil)
    80  	// The worker itself is a coremultiwatcher.Factory.
    81  	s.AddCleanup(func(c *gc.C) { workertest.CleanKill(c, multiWatcherWorker) })
    82  
    83  	initialized := gate.NewLock()
    84  	s.hub = pubsub.NewStructuredHub(nil)
    85  	modelCache, err := modelcache.NewWorker(modelcache.Config{
    86  		StatePool:            s.StatePool,
    87  		Hub:                  s.hub,
    88  		InitializedGate:      initialized,
    89  		Logger:               loggo.GetLogger("test"),
    90  		WatcherFactory:       multiWatcherWorker.WatchController,
    91  		PrometheusRegisterer: noopRegisterer{},
    92  		Cleanup:              func() {},
    93  	}.WithDefaultRestartStrategy())
    94  	c.Assert(err, jc.ErrorIsNil)
    95  	s.AddCleanup(func(c *gc.C) { workertest.CleanKill(c, modelCache) })
    96  
    97  	select {
    98  	case <-initialized.Unlocked():
    99  	case <-time.After(10 * time.Second):
   100  		c.Error("model cache not initialized after 10 seconds")
   101  	}
   102  
   103  	var cacheController *cache.Controller
   104  	err = modelcache.ExtractCacheController(modelCache, &cacheController)
   105  	c.Assert(err, jc.ErrorIsNil)
   106  
   107  	s.resources = common.NewResources()
   108  	s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() })
   109  
   110  	s.authorizer = apiservertesting.FakeAuthorizer{
   111  		Tag:      s.Owner,
   112  		AdminTag: s.Owner,
   113  	}
   114  
   115  	s.context = facadetest.Context{
   116  		State_:               s.State,
   117  		StatePool_:           s.StatePool,
   118  		Resources_:           s.resources,
   119  		Auth_:                s.authorizer,
   120  		Controller_:          cacheController,
   121  		Hub_:                 s.hub,
   122  		MultiwatcherFactory_: multiWatcherWorker,
   123  	}
   124  	controller, err := controller.LatestAPI(s.context)
   125  	c.Assert(err, jc.ErrorIsNil)
   126  	s.controller = controller
   127  
   128  	loggo.GetLogger("juju.apiserver.controller").SetLogLevel(loggo.TRACE)
   129  }
   130  
   131  func (s *controllerSuite) TestNewAPIRefusesNonClient(c *gc.C) {
   132  	anAuthoriser := apiservertesting.FakeAuthorizer{
   133  		Tag: names.NewUnitTag("mysql/0"),
   134  	}
   135  	endPoint, err := controller.LatestAPI(
   136  		facadetest.Context{
   137  			State_:     s.State,
   138  			StatePool_: s.StatePool,
   139  			Resources_: s.resources,
   140  			Auth_:      anAuthoriser,
   141  		})
   142  	c.Assert(endPoint, gc.IsNil)
   143  	c.Assert(err, gc.ErrorMatches, "permission denied")
   144  }
   145  
   146  func (s *controllerSuite) checkModelMatches(c *gc.C, model params.Model, expected *state.Model) {
   147  	c.Check(model.Name, gc.Equals, expected.Name())
   148  	c.Check(model.UUID, gc.Equals, expected.UUID())
   149  	c.Check(model.OwnerTag, gc.Equals, expected.Owner().String())
   150  }
   151  
   152  func (s *controllerSuite) TestAllModels(c *gc.C) {
   153  	admin := s.Factory.MakeUser(c, &factory.UserParams{Name: "foobar"})
   154  
   155  	s.Factory.MakeModel(c, &factory.ModelParams{
   156  		Name: "owned", Owner: admin.UserTag()}).Close()
   157  	remoteUserTag := names.NewUserTag("user@remote")
   158  	st := s.Factory.MakeModel(c, &factory.ModelParams{
   159  		Name: "user", Owner: remoteUserTag})
   160  	defer st.Close()
   161  	model, err := st.Model()
   162  	c.Assert(err, jc.ErrorIsNil)
   163  
   164  	model.AddUser(
   165  		state.UserAccessSpec{
   166  			User:        admin.UserTag(),
   167  			CreatedBy:   remoteUserTag,
   168  			DisplayName: "Foo Bar",
   169  			Access:      permission.WriteAccess})
   170  
   171  	s.Factory.MakeModel(c, &factory.ModelParams{
   172  		Name: "no-access", Owner: remoteUserTag}).Close()
   173  
   174  	response, err := s.controller.AllModels()
   175  	c.Assert(err, jc.ErrorIsNil)
   176  	// The results are sorted.
   177  	expected := []string{"controller", "no-access", "owned", "user"}
   178  	var obtained []string
   179  	for _, userModel := range response.UserModels {
   180  		c.Assert(userModel.Type, gc.Equals, "iaas")
   181  		obtained = append(obtained, userModel.Name)
   182  		stateModel, ph, err := s.StatePool.GetModel(userModel.UUID)
   183  		c.Assert(err, jc.ErrorIsNil)
   184  		defer ph.Release()
   185  		s.checkModelMatches(c, userModel.Model, stateModel)
   186  	}
   187  	c.Assert(obtained, jc.DeepEquals, expected)
   188  }
   189  
   190  func (s *controllerSuite) TestHostedModelConfigs_OnlyHostedModelsReturned(c *gc.C) {
   191  	owner := s.Factory.MakeUser(c, nil)
   192  	s.Factory.MakeModel(c, &factory.ModelParams{
   193  		Name: "first", Owner: owner.UserTag()}).Close()
   194  	remoteUserTag := names.NewUserTag("user@remote")
   195  	s.Factory.MakeModel(c, &factory.ModelParams{
   196  		Name: "second", Owner: remoteUserTag}).Close()
   197  
   198  	results, err := s.controller.HostedModelConfigs()
   199  	c.Assert(err, jc.ErrorIsNil)
   200  	c.Assert(len(results.Models), gc.Equals, 2)
   201  
   202  	one := results.Models[0]
   203  	two := results.Models[1]
   204  
   205  	c.Assert(one.Name, gc.Equals, "first")
   206  	c.Assert(one.OwnerTag, gc.Equals, owner.UserTag().String())
   207  	c.Assert(two.Name, gc.Equals, "second")
   208  	c.Assert(two.OwnerTag, gc.Equals, remoteUserTag.String())
   209  }
   210  
   211  func (s *controllerSuite) makeCloudSpec(c *gc.C, pSpec *params.CloudSpec) environscloudspec.CloudSpec {
   212  	c.Assert(pSpec, gc.NotNil)
   213  	var credential *cloud.Credential
   214  	if pSpec.Credential != nil {
   215  		credentialValue := cloud.NewCredential(
   216  			cloud.AuthType(pSpec.Credential.AuthType),
   217  			pSpec.Credential.Attributes,
   218  		)
   219  		credential = &credentialValue
   220  	}
   221  	spec := environscloudspec.CloudSpec{
   222  		Type:             pSpec.Type,
   223  		Name:             pSpec.Name,
   224  		Region:           pSpec.Region,
   225  		Endpoint:         pSpec.Endpoint,
   226  		IdentityEndpoint: pSpec.IdentityEndpoint,
   227  		StorageEndpoint:  pSpec.StorageEndpoint,
   228  		Credential:       credential,
   229  	}
   230  	c.Assert(spec.Validate(), jc.ErrorIsNil)
   231  	return spec
   232  }
   233  
   234  func (s *controllerSuite) TestHostedModelConfigs_CanOpenEnviron(c *gc.C) {
   235  	owner := s.Factory.MakeUser(c, nil)
   236  	s.Factory.MakeModel(c, &factory.ModelParams{
   237  		Name: "first", Owner: owner.UserTag()}).Close()
   238  	remoteUserTag := names.NewUserTag("user@remote")
   239  	s.Factory.MakeModel(c, &factory.ModelParams{
   240  		Name: "second", Owner: remoteUserTag}).Close()
   241  
   242  	results, err := s.controller.HostedModelConfigs()
   243  	c.Assert(err, jc.ErrorIsNil)
   244  	c.Assert(len(results.Models), gc.Equals, 2)
   245  
   246  	for _, model := range results.Models {
   247  		c.Assert(model.Error, gc.IsNil)
   248  
   249  		cfg, err := config.New(config.NoDefaults, model.Config)
   250  		c.Assert(err, jc.ErrorIsNil)
   251  		spec := s.makeCloudSpec(c, model.CloudSpec)
   252  		_, err = environs.New(stdcontext.TODO(), environs.OpenParams{
   253  			Cloud:  spec,
   254  			Config: cfg,
   255  		})
   256  		c.Assert(err, jc.ErrorIsNil)
   257  	}
   258  }
   259  
   260  func (s *controllerSuite) TestListBlockedModels(c *gc.C) {
   261  	st := s.Factory.MakeModel(c, &factory.ModelParams{
   262  		Name: "test"})
   263  	defer st.Close()
   264  
   265  	s.State.SwitchBlockOn(state.DestroyBlock, "TestBlockDestroyModel")
   266  	s.State.SwitchBlockOn(state.ChangeBlock, "TestChangeBlock")
   267  	st.SwitchBlockOn(state.DestroyBlock, "TestBlockDestroyModel")
   268  	st.SwitchBlockOn(state.ChangeBlock, "TestChangeBlock")
   269  
   270  	list, err := s.controller.ListBlockedModels()
   271  	c.Assert(err, jc.ErrorIsNil)
   272  
   273  	c.Assert(list.Models, jc.DeepEquals, []params.ModelBlockInfo{
   274  		{
   275  			Name:     "controller",
   276  			UUID:     s.State.ModelUUID(),
   277  			OwnerTag: s.Owner.String(),
   278  			Blocks: []string{
   279  				"BlockDestroy",
   280  				"BlockChange",
   281  			},
   282  		},
   283  		{
   284  			Name:     "test",
   285  			UUID:     st.ModelUUID(),
   286  			OwnerTag: s.Owner.String(),
   287  			Blocks: []string{
   288  				"BlockDestroy",
   289  				"BlockChange",
   290  			},
   291  		},
   292  	})
   293  
   294  }
   295  
   296  func (s *controllerSuite) TestListBlockedModelsNoBlocks(c *gc.C) {
   297  	list, err := s.controller.ListBlockedModels()
   298  	c.Assert(err, jc.ErrorIsNil)
   299  	c.Assert(list.Models, gc.HasLen, 0)
   300  }
   301  
   302  func (s *controllerSuite) TestModelConfig(c *gc.C) {
   303  	cfg, err := s.controller.ModelConfig()
   304  	c.Assert(err, jc.ErrorIsNil)
   305  	c.Assert(cfg.Config["name"], jc.DeepEquals, params.ConfigValue{Value: "controller"})
   306  }
   307  
   308  func (s *controllerSuite) TestModelConfigFromNonController(c *gc.C) {
   309  	st := s.Factory.MakeModel(c, &factory.ModelParams{
   310  		Name: "test"})
   311  	defer st.Close()
   312  
   313  	authorizer := &apiservertesting.FakeAuthorizer{
   314  		Tag:      s.Owner,
   315  		AdminTag: s.Owner,
   316  	}
   317  	controller, err := controller.NewControllerAPIv11(
   318  		facadetest.Context{
   319  			State_:     st,
   320  			StatePool_: s.StatePool,
   321  			Resources_: common.NewResources(),
   322  			Auth_:      authorizer,
   323  		})
   324  
   325  	c.Assert(err, jc.ErrorIsNil)
   326  	cfg, err := controller.ModelConfig()
   327  	c.Assert(err, jc.ErrorIsNil)
   328  	c.Assert(cfg.Config["name"], jc.DeepEquals, params.ConfigValue{Value: "controller"})
   329  }
   330  
   331  func (s *controllerSuite) TestControllerConfig(c *gc.C) {
   332  	cfg, err := s.controller.ControllerConfig()
   333  	c.Assert(err, jc.ErrorIsNil)
   334  	cfgFromDB, err := s.State.ControllerConfig()
   335  	c.Assert(err, jc.ErrorIsNil)
   336  	c.Assert(cfg.Config["controller-uuid"], gc.Equals, cfgFromDB.ControllerUUID())
   337  	c.Assert(cfg.Config["state-port"], gc.Equals, cfgFromDB.StatePort())
   338  	c.Assert(cfg.Config["api-port"], gc.Equals, cfgFromDB.APIPort())
   339  }
   340  
   341  func (s *controllerSuite) TestControllerConfigFromNonController(c *gc.C) {
   342  	st := s.Factory.MakeModel(c, &factory.ModelParams{
   343  		Name: "test"})
   344  	defer st.Close()
   345  
   346  	authorizer := &apiservertesting.FakeAuthorizer{Tag: s.Owner}
   347  	controller, err := controller.NewControllerAPIv11(
   348  		facadetest.Context{
   349  			State_:     st,
   350  			StatePool_: s.StatePool,
   351  			Resources_: common.NewResources(),
   352  			Auth_:      authorizer,
   353  		})
   354  	c.Assert(err, jc.ErrorIsNil)
   355  	cfg, err := controller.ControllerConfig()
   356  	c.Assert(err, jc.ErrorIsNil)
   357  	cfgFromDB, err := s.State.ControllerConfig()
   358  	c.Assert(err, jc.ErrorIsNil)
   359  	c.Assert(cfg.Config["controller-uuid"], gc.Equals, cfgFromDB.ControllerUUID())
   360  	c.Assert(cfg.Config["state-port"], gc.Equals, cfgFromDB.StatePort())
   361  	c.Assert(cfg.Config["api-port"], gc.Equals, cfgFromDB.APIPort())
   362  }
   363  
   364  func (s *controllerSuite) TestRemoveBlocks(c *gc.C) {
   365  	st := s.Factory.MakeModel(c, &factory.ModelParams{
   366  		Name: "test"})
   367  	defer st.Close()
   368  
   369  	s.State.SwitchBlockOn(state.DestroyBlock, "TestBlockDestroyModel")
   370  	s.State.SwitchBlockOn(state.ChangeBlock, "TestChangeBlock")
   371  	st.SwitchBlockOn(state.DestroyBlock, "TestBlockDestroyModel")
   372  	st.SwitchBlockOn(state.ChangeBlock, "TestChangeBlock")
   373  
   374  	err := s.controller.RemoveBlocks(params.RemoveBlocksArgs{All: true})
   375  	c.Assert(err, jc.ErrorIsNil)
   376  
   377  	blocks, err := s.State.AllBlocksForController()
   378  	c.Assert(err, jc.ErrorIsNil)
   379  	c.Assert(blocks, gc.HasLen, 0)
   380  }
   381  
   382  func (s *controllerSuite) TestRemoveBlocksNotAll(c *gc.C) {
   383  	err := s.controller.RemoveBlocks(params.RemoveBlocksArgs{})
   384  	c.Assert(err, gc.ErrorMatches, "not supported")
   385  }
   386  
   387  func (s *controllerSuite) TestWatchAllModels(c *gc.C) {
   388  	watcherId, err := s.controller.WatchAllModels()
   389  	c.Assert(err, jc.ErrorIsNil)
   390  
   391  	var disposed bool
   392  	watcherAPI_, err := apiserver.NewAllWatcher(facadetest.Context{
   393  		State_:     s.State,
   394  		StatePool_: s.StatePool,
   395  		Resources_: s.resources,
   396  		Auth_:      s.authorizer,
   397  		ID_:        watcherId.AllWatcherId,
   398  		Dispose_:   func() { disposed = true },
   399  	})
   400  	c.Assert(err, jc.ErrorIsNil)
   401  	watcherAPI := watcherAPI_.(*apiserver.SrvAllWatcher)
   402  	defer func() {
   403  		err := watcherAPI.Stop()
   404  		c.Assert(err, jc.ErrorIsNil)
   405  		c.Assert(disposed, jc.IsTrue)
   406  	}()
   407  
   408  	done := make(chan bool)
   409  	defer close(done)
   410  	resultC := make(chan params.AllWatcherNextResults)
   411  	go func() {
   412  		for {
   413  			select {
   414  			case <-done:
   415  				return
   416  			default:
   417  				result, err := watcherAPI.Next()
   418  				if err != nil {
   419  					c.Assert(err, jc.Satisfies, coremultiwatcher.IsErrStopped)
   420  					return
   421  				}
   422  				resultC <- result
   423  			}
   424  		}
   425  	}()
   426  
   427  	select {
   428  	case result := <-resultC:
   429  		// Expect to see the initial model be reported.
   430  		deltas := result.Deltas
   431  		c.Assert(deltas, gc.HasLen, 1)
   432  		modelInfo := deltas[0].Entity.(*params.ModelUpdate)
   433  		c.Assert(modelInfo.ModelUUID, gc.Equals, s.State.ModelUUID())
   434  		c.Assert(modelInfo.IsController, gc.Equals, s.State.IsController())
   435  	case <-time.After(testing.LongWait):
   436  		c.Fatal("timed out")
   437  	}
   438  
   439  	// To ensure we really watch all models, make another one.
   440  	st := s.Factory.MakeModel(c, &factory.ModelParams{
   441  		Name: "test"})
   442  	defer st.Close()
   443  
   444  	// Update the model agent versions to ensure settings changes cause an update.
   445  	err = s.State.SetModelAgentVersion(version.MustParse("2.6.666"), nil, true)
   446  	c.Assert(err, jc.ErrorIsNil)
   447  	err = st.SetModelAgentVersion(version.MustParse("2.6.667"), nil, true)
   448  	c.Assert(err, jc.ErrorIsNil)
   449  	expectedVersions := map[string]string{
   450  		s.State.ModelUUID(): "2.6.666",
   451  		st.ModelUUID():      "2.6.667",
   452  	}
   453  
   454  	for resultCount := 0; resultCount != 2; {
   455  		select {
   456  		case result := <-resultC:
   457  			c.Logf("got change: %# v", pretty.Formatter(result))
   458  			for _, d := range result.Deltas {
   459  				if d.Removed {
   460  					continue
   461  				}
   462  				modelInfo, ok := d.Entity.(*params.ModelUpdate)
   463  				if !ok {
   464  					continue
   465  				}
   466  				if modelInfo.Config["agent-version"] == expectedVersions[modelInfo.ModelUUID] {
   467  					resultCount = resultCount + 1
   468  				}
   469  			}
   470  		case <-time.After(testing.LongWait):
   471  			c.Fatalf("timed out waiting for 2 model updates, got %d", resultCount)
   472  		}
   473  	}
   474  }
   475  
   476  func (s *controllerSuite) TestInitiateMigration(c *gc.C) {
   477  	// Create two hosted models to migrate.
   478  	st1 := s.Factory.MakeModel(c, nil)
   479  	defer st1.Close()
   480  	model1, err := st1.Model()
   481  	c.Assert(err, jc.ErrorIsNil)
   482  
   483  	st2 := s.Factory.MakeModel(c, nil)
   484  	defer st2.Close()
   485  	model2, err := st2.Model()
   486  	c.Assert(err, jc.ErrorIsNil)
   487  
   488  	mac, err := macaroon.New([]byte("secret"), []byte("id"), "location", macaroon.LatestVersion)
   489  	c.Assert(err, jc.ErrorIsNil)
   490  	macsJSON, err := json.Marshal([]macaroon.Slice{{mac}})
   491  	c.Assert(err, jc.ErrorIsNil)
   492  
   493  	controller.SetPrecheckResult(s, nil)
   494  
   495  	// Kick off migrations
   496  	args := params.InitiateMigrationArgs{
   497  		Specs: []params.MigrationSpec{
   498  			{
   499  				ModelTag: model1.ModelTag().String(),
   500  				TargetInfo: params.MigrationTargetInfo{
   501  					ControllerTag:   randomControllerTag(),
   502  					ControllerAlias: "", // intentionally left empty; simulates older client
   503  					Addrs:           []string{"1.1.1.1:1111", "2.2.2.2:2222"},
   504  					CACert:          "cert1",
   505  					AuthTag:         names.NewUserTag("admin1").String(),
   506  					Password:        "secret1",
   507  				},
   508  			}, {
   509  				ModelTag: model2.ModelTag().String(),
   510  				TargetInfo: params.MigrationTargetInfo{
   511  					ControllerTag:   randomControllerTag(),
   512  					ControllerAlias: "target-controller",
   513  					Addrs:           []string{"3.3.3.3:3333"},
   514  					CACert:          "cert2",
   515  					AuthTag:         names.NewUserTag("admin2").String(),
   516  					Macaroons:       string(macsJSON),
   517  					Password:        "secret2",
   518  				},
   519  			},
   520  		},
   521  	}
   522  	out, err := s.controller.InitiateMigration(args)
   523  	c.Assert(err, jc.ErrorIsNil)
   524  	c.Assert(out.Results, gc.HasLen, 2)
   525  
   526  	states := []*state.State{st1, st2}
   527  	for i, spec := range args.Specs {
   528  		c.Log(i)
   529  		st := states[i]
   530  		result := out.Results[i]
   531  
   532  		c.Assert(result.Error, gc.IsNil)
   533  		c.Check(result.ModelTag, gc.Equals, spec.ModelTag)
   534  		expectedId := st.ModelUUID() + ":0"
   535  		c.Check(result.MigrationId, gc.Equals, expectedId)
   536  
   537  		// Ensure the migration made it into the DB correctly.
   538  		mig, err := st.LatestMigration()
   539  		c.Assert(err, jc.ErrorIsNil)
   540  		c.Check(mig.Id(), gc.Equals, expectedId)
   541  		c.Check(mig.ModelUUID(), gc.Equals, st.ModelUUID())
   542  		c.Check(mig.InitiatedBy(), gc.Equals, s.Owner.Id())
   543  
   544  		targetInfo, err := mig.TargetInfo()
   545  		c.Assert(err, jc.ErrorIsNil)
   546  		c.Check(targetInfo.ControllerTag.String(), gc.Equals, spec.TargetInfo.ControllerTag)
   547  		c.Check(targetInfo.ControllerAlias, gc.Equals, spec.TargetInfo.ControllerAlias)
   548  		c.Check(targetInfo.Addrs, jc.SameContents, spec.TargetInfo.Addrs)
   549  		c.Check(targetInfo.CACert, gc.Equals, spec.TargetInfo.CACert)
   550  		c.Check(targetInfo.AuthTag.String(), gc.Equals, spec.TargetInfo.AuthTag)
   551  		c.Check(targetInfo.Password, gc.Equals, spec.TargetInfo.Password)
   552  
   553  		if spec.TargetInfo.Macaroons != "" {
   554  			macJSONdb, err := json.Marshal(targetInfo.Macaroons)
   555  			c.Assert(err, jc.ErrorIsNil)
   556  			c.Check(string(macJSONdb), gc.Equals, spec.TargetInfo.Macaroons)
   557  		}
   558  	}
   559  }
   560  
   561  func (s *controllerSuite) TestInitiateMigrationSpecError(c *gc.C) {
   562  	// Create a hosted model to migrate.
   563  	st := s.Factory.MakeModel(c, nil)
   564  	defer st.Close()
   565  	model, err := st.Model()
   566  	c.Assert(err, jc.ErrorIsNil)
   567  
   568  	// Kick off the migration with missing details.
   569  	args := params.InitiateMigrationArgs{
   570  		Specs: []params.MigrationSpec{{
   571  			ModelTag: model.ModelTag().String(),
   572  			// TargetInfo missing
   573  		}},
   574  	}
   575  	out, err := s.controller.InitiateMigration(args)
   576  	c.Assert(err, jc.ErrorIsNil)
   577  	c.Assert(out.Results, gc.HasLen, 1)
   578  	result := out.Results[0]
   579  	c.Check(result.ModelTag, gc.Equals, args.Specs[0].ModelTag)
   580  	c.Check(result.MigrationId, gc.Equals, "")
   581  	c.Check(result.Error, gc.ErrorMatches, "controller tag: .+ is not a valid tag")
   582  }
   583  
   584  func (s *controllerSuite) TestInitiateMigrationPartialFailure(c *gc.C) {
   585  	st := s.Factory.MakeModel(c, nil)
   586  	defer st.Close()
   587  	controller.SetPrecheckResult(s, nil)
   588  
   589  	m, err := st.Model()
   590  	c.Assert(err, jc.ErrorIsNil)
   591  
   592  	args := params.InitiateMigrationArgs{
   593  		Specs: []params.MigrationSpec{
   594  			{
   595  				ModelTag: m.ModelTag().String(),
   596  				TargetInfo: params.MigrationTargetInfo{
   597  					ControllerTag: randomControllerTag(),
   598  					Addrs:         []string{"1.1.1.1:1111", "2.2.2.2:2222"},
   599  					CACert:        "cert",
   600  					AuthTag:       names.NewUserTag("admin").String(),
   601  					Password:      "secret",
   602  				},
   603  			}, {
   604  				ModelTag: randomModelTag(), // Doesn't exist.
   605  			},
   606  		},
   607  	}
   608  	out, err := s.controller.InitiateMigration(args)
   609  	c.Assert(err, jc.ErrorIsNil)
   610  	c.Assert(out.Results, gc.HasLen, 2)
   611  
   612  	c.Check(out.Results[0].ModelTag, gc.Equals, m.ModelTag().String())
   613  	c.Check(out.Results[0].Error, gc.IsNil)
   614  
   615  	c.Check(out.Results[1].ModelTag, gc.Equals, args.Specs[1].ModelTag)
   616  	c.Check(out.Results[1].Error, gc.ErrorMatches, "model not found")
   617  }
   618  
   619  func (s *controllerSuite) TestInitiateMigrationInvalidMacaroons(c *gc.C) {
   620  	st := s.Factory.MakeModel(c, nil)
   621  	defer st.Close()
   622  
   623  	m, err := st.Model()
   624  	c.Assert(err, jc.ErrorIsNil)
   625  
   626  	args := params.InitiateMigrationArgs{
   627  		Specs: []params.MigrationSpec{
   628  			{
   629  				ModelTag: m.ModelTag().String(),
   630  				TargetInfo: params.MigrationTargetInfo{
   631  					ControllerTag: randomControllerTag(),
   632  					Addrs:         []string{"1.1.1.1:1111", "2.2.2.2:2222"},
   633  					CACert:        "cert",
   634  					AuthTag:       names.NewUserTag("admin").String(),
   635  					Macaroons:     "BLAH",
   636  				},
   637  			},
   638  		},
   639  	}
   640  	out, err := s.controller.InitiateMigration(args)
   641  	c.Assert(err, jc.ErrorIsNil)
   642  	c.Assert(out.Results, gc.HasLen, 1)
   643  	result := out.Results[0]
   644  	c.Check(result.ModelTag, gc.Equals, args.Specs[0].ModelTag)
   645  	c.Check(result.Error, gc.ErrorMatches, "invalid macaroons: .+")
   646  }
   647  
   648  func (s *controllerSuite) TestInitiateMigrationPrecheckFail(c *gc.C) {
   649  	st := s.Factory.MakeModel(c, nil)
   650  	defer st.Close()
   651  
   652  	controller.SetPrecheckResult(s, errors.New("boom"))
   653  
   654  	m, err := st.Model()
   655  	c.Assert(err, jc.ErrorIsNil)
   656  
   657  	args := params.InitiateMigrationArgs{
   658  		Specs: []params.MigrationSpec{{
   659  			ModelTag: m.ModelTag().String(),
   660  			TargetInfo: params.MigrationTargetInfo{
   661  				ControllerTag: randomControllerTag(),
   662  				Addrs:         []string{"1.1.1.1:1111"},
   663  				CACert:        "cert1",
   664  				AuthTag:       names.NewUserTag("admin1").String(),
   665  				Password:      "secret1",
   666  			},
   667  		}},
   668  	}
   669  	out, err := s.controller.InitiateMigration(args)
   670  	c.Assert(err, jc.ErrorIsNil)
   671  	c.Assert(out.Results, gc.HasLen, 1)
   672  	c.Check(out.Results[0].Error, gc.ErrorMatches, "boom")
   673  
   674  	active, err := st.IsMigrationActive()
   675  	c.Assert(err, jc.ErrorIsNil)
   676  	c.Check(active, jc.IsFalse)
   677  }
   678  
   679  func randomControllerTag() string {
   680  	uuid := utils.MustNewUUID().String()
   681  	return names.NewControllerTag(uuid).String()
   682  }
   683  
   684  func randomModelTag() string {
   685  	uuid := utils.MustNewUUID().String()
   686  	return names.NewModelTag(uuid).String()
   687  }
   688  
   689  func (s *controllerSuite) modifyControllerAccess(c *gc.C, user names.UserTag, action params.ControllerAction, access string) error {
   690  	args := params.ModifyControllerAccessRequest{
   691  		Changes: []params.ModifyControllerAccess{{
   692  			UserTag: user.String(),
   693  			Action:  action,
   694  			Access:  access,
   695  		}}}
   696  	result, err := s.controller.ModifyControllerAccess(args)
   697  	c.Assert(err, jc.ErrorIsNil)
   698  	return result.OneError()
   699  }
   700  
   701  func (s *controllerSuite) controllerGrant(c *gc.C, user names.UserTag, access string) error {
   702  	return s.modifyControllerAccess(c, user, params.GrantControllerAccess, access)
   703  }
   704  
   705  func (s *controllerSuite) controllerRevoke(c *gc.C, user names.UserTag, access string) error {
   706  	return s.modifyControllerAccess(c, user, params.RevokeControllerAccess, access)
   707  }
   708  
   709  func (s *controllerSuite) TestGrantMissingUserFails(c *gc.C) {
   710  	user := names.NewLocalUserTag("foobar")
   711  	err := s.controllerGrant(c, user, string(permission.SuperuserAccess))
   712  	expectedErr := `could not grant controller access: user "foobar" does not exist locally: user "foobar" not found`
   713  	c.Assert(err, gc.ErrorMatches, expectedErr)
   714  }
   715  
   716  func (s *controllerSuite) TestRevokeSuperuserLeavesLoginAccess(c *gc.C) {
   717  	user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true})
   718  
   719  	err := s.controllerGrant(c, user.UserTag(), string(permission.SuperuserAccess))
   720  	c.Assert(err, gc.IsNil)
   721  	ctag := names.NewControllerTag(s.State.ControllerUUID())
   722  	controllerUser, err := s.State.UserAccess(user.UserTag(), ctag)
   723  	c.Assert(err, jc.ErrorIsNil)
   724  	c.Assert(controllerUser.Access, gc.Equals, permission.SuperuserAccess)
   725  
   726  	err = s.controllerRevoke(c, user.UserTag(), string(permission.SuperuserAccess))
   727  	c.Assert(err, gc.IsNil)
   728  
   729  	controllerUser, err = s.State.UserAccess(user.UserTag(), controllerUser.Object)
   730  	c.Assert(err, jc.ErrorIsNil)
   731  	c.Assert(controllerUser.Access, gc.Equals, permission.LoginAccess)
   732  }
   733  
   734  func (s *controllerSuite) TestRevokeLoginRemovesControllerUser(c *gc.C) {
   735  	user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true})
   736  	err := s.controllerRevoke(c, user.UserTag(), string(permission.LoginAccess))
   737  	c.Assert(err, gc.IsNil)
   738  
   739  	ctag := names.NewControllerTag(s.State.ControllerUUID())
   740  	_, err = s.State.UserAccess(user.UserTag(), ctag)
   741  
   742  	c.Assert(errors.IsNotFound(err), jc.IsTrue)
   743  }
   744  
   745  func (s *controllerSuite) TestRevokeControllerMissingUser(c *gc.C) {
   746  	user := names.NewLocalUserTag("foobar")
   747  	err := s.controllerRevoke(c, user, string(permission.SuperuserAccess))
   748  	expectedErr := `could not look up controller access for user: user "foobar" not found`
   749  	c.Assert(err, gc.ErrorMatches, expectedErr)
   750  }
   751  
   752  func (s *controllerSuite) TestGrantOnlyGreaterAccess(c *gc.C) {
   753  	user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true})
   754  
   755  	err := s.controllerGrant(c, user.UserTag(), string(permission.SuperuserAccess))
   756  	c.Assert(err, gc.IsNil)
   757  	ctag := names.NewControllerTag(s.State.ControllerUUID())
   758  	controllerUser, err := s.State.UserAccess(user.UserTag(), ctag)
   759  	c.Assert(err, jc.ErrorIsNil)
   760  	c.Assert(controllerUser.Access, gc.Equals, permission.SuperuserAccess)
   761  
   762  	err = s.controllerGrant(c, user.UserTag(), string(permission.SuperuserAccess))
   763  	expectedErr := `could not grant controller access: user already has "superuser" access or greater`
   764  	c.Assert(err, gc.ErrorMatches, expectedErr)
   765  }
   766  
   767  func (s *controllerSuite) TestGrantControllerAddRemoteUser(c *gc.C) {
   768  	userTag := names.NewUserTag("foobar@ubuntuone")
   769  
   770  	err := s.controllerGrant(c, userTag, string(permission.SuperuserAccess))
   771  	c.Assert(err, jc.ErrorIsNil)
   772  
   773  	ctag := names.NewControllerTag(s.State.ControllerUUID())
   774  	controllerUser, err := s.State.UserAccess(userTag, ctag)
   775  	c.Assert(err, jc.ErrorIsNil)
   776  
   777  	c.Assert(controllerUser.Access, gc.Equals, permission.SuperuserAccess)
   778  }
   779  
   780  func (s *controllerSuite) TestGrantControllerInvalidUserTag(c *gc.C) {
   781  	for _, testParam := range []struct {
   782  		tag      string
   783  		validTag bool
   784  	}{{
   785  		tag:      "unit-foo/0",
   786  		validTag: true,
   787  	}, {
   788  		tag:      "application-foo",
   789  		validTag: true,
   790  	}, {
   791  		tag:      "relation-wordpress:db mysql:db",
   792  		validTag: true,
   793  	}, {
   794  		tag:      "machine-0",
   795  		validTag: true,
   796  	}, {
   797  		tag:      "user@local",
   798  		validTag: false,
   799  	}, {
   800  		tag:      "user-Mua^h^h^h^arh",
   801  		validTag: true,
   802  	}, {
   803  		tag:      "user@",
   804  		validTag: false,
   805  	}, {
   806  		tag:      "user@ubuntuone",
   807  		validTag: false,
   808  	}, {
   809  		tag:      "user@ubuntuone",
   810  		validTag: false,
   811  	}, {
   812  		tag:      "@ubuntuone",
   813  		validTag: false,
   814  	}, {
   815  		tag:      "in^valid.",
   816  		validTag: false,
   817  	}, {
   818  		tag:      "",
   819  		validTag: false,
   820  	},
   821  	} {
   822  		var expectedErr string
   823  		errPart := `could not modify controller access: "` + regexp.QuoteMeta(testParam.tag) + `" is not a valid `
   824  
   825  		if testParam.validTag {
   826  			// The string is a valid tag, but not a user tag.
   827  			expectedErr = errPart + `user tag`
   828  		} else {
   829  			// The string is not a valid tag of any kind.
   830  			expectedErr = errPart + `tag`
   831  		}
   832  
   833  		args := params.ModifyControllerAccessRequest{
   834  			Changes: []params.ModifyControllerAccess{{
   835  				UserTag: testParam.tag,
   836  				Action:  params.GrantControllerAccess,
   837  				Access:  string(permission.SuperuserAccess),
   838  			}}}
   839  
   840  		result, err := s.controller.ModifyControllerAccess(args)
   841  		c.Assert(err, jc.ErrorIsNil)
   842  		c.Assert(result.OneError(), gc.ErrorMatches, expectedErr)
   843  	}
   844  }
   845  
   846  func (s *controllerSuite) TestModifyControllerAccessEmptyArgs(c *gc.C) {
   847  	args := params.ModifyControllerAccessRequest{Changes: []params.ModifyControllerAccess{{}}}
   848  
   849  	result, err := s.controller.ModifyControllerAccess(args)
   850  	c.Assert(err, jc.ErrorIsNil)
   851  	expectedErr := `"" controller access not valid`
   852  	c.Assert(result.OneError(), gc.ErrorMatches, expectedErr)
   853  }
   854  
   855  func (s *controllerSuite) TestModifyControllerAccessInvalidAction(c *gc.C) {
   856  	var dance params.ControllerAction = "dance"
   857  	args := params.ModifyControllerAccessRequest{
   858  		Changes: []params.ModifyControllerAccess{{
   859  			UserTag: "user-user@local",
   860  			Action:  dance,
   861  			Access:  string(permission.LoginAccess),
   862  		}}}
   863  
   864  	result, err := s.controller.ModifyControllerAccess(args)
   865  	c.Assert(err, jc.ErrorIsNil)
   866  	expectedErr := `unknown action "dance"`
   867  	c.Assert(result.OneError(), gc.ErrorMatches, expectedErr)
   868  }
   869  
   870  func (s *controllerSuite) TestGetControllerAccess(c *gc.C) {
   871  	user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true})
   872  	user2 := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true})
   873  
   874  	err := s.controllerGrant(c, user.UserTag(), string(permission.SuperuserAccess))
   875  	c.Assert(err, gc.IsNil)
   876  	req := params.Entities{
   877  		Entities: []params.Entity{{Tag: user.Tag().String()}, {Tag: user2.Tag().String()}},
   878  	}
   879  	results, err := s.controller.GetControllerAccess(req)
   880  	c.Assert(err, jc.ErrorIsNil)
   881  	c.Assert(results.Results, gc.DeepEquals, []params.UserAccessResult{{
   882  		Result: &params.UserAccess{
   883  			Access:  "superuser",
   884  			UserTag: user.Tag().String(),
   885  		}}, {
   886  		Result: &params.UserAccess{
   887  			Access:  "login",
   888  			UserTag: user2.Tag().String(),
   889  		}}})
   890  }
   891  
   892  func (s *controllerSuite) TestGetControllerAccessPermissions(c *gc.C) {
   893  	// Set up the user making the call.
   894  	user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true})
   895  	anAuthoriser := apiservertesting.FakeAuthorizer{
   896  		Tag: user.Tag(),
   897  	}
   898  	endpoint, err := controller.NewControllerAPIv11(
   899  		facadetest.Context{
   900  			State_:     s.State,
   901  			StatePool_: s.StatePool,
   902  			Resources_: s.resources,
   903  			Auth_:      anAuthoriser,
   904  		})
   905  	c.Assert(err, jc.ErrorIsNil)
   906  	args := params.ModifyControllerAccessRequest{
   907  		Changes: []params.ModifyControllerAccess{{
   908  			UserTag: user.Tag().String(),
   909  			Action:  params.GrantControllerAccess,
   910  			Access:  "superuser",
   911  		}}}
   912  	result, err := s.controller.ModifyControllerAccess(args)
   913  	c.Assert(err, jc.ErrorIsNil)
   914  	c.Assert(result.OneError(), jc.ErrorIsNil)
   915  
   916  	// We ask for permissions for a different user as well as ourselves.
   917  	differentUser := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true})
   918  	req := params.Entities{
   919  		Entities: []params.Entity{{Tag: user.Tag().String()}, {Tag: differentUser.Tag().String()}},
   920  	}
   921  	results, err := endpoint.GetControllerAccess(req)
   922  	c.Assert(err, jc.ErrorIsNil)
   923  	c.Assert(results.Results, gc.HasLen, 2)
   924  	c.Assert(*results.Results[0].Result, jc.DeepEquals, params.UserAccess{
   925  		Access:  "superuser",
   926  		UserTag: user.Tag().String(),
   927  	})
   928  	c.Assert(*results.Results[1].Error, gc.DeepEquals, params.Error{
   929  		Message: "permission denied", Code: "unauthorized access",
   930  	})
   931  }
   932  
   933  func (s *controllerSuite) TestModelStatus(c *gc.C) {
   934  	// Check that we don't err out immediately if a model errs.
   935  	results, err := s.controller.ModelStatus(params.Entities{[]params.Entity{{
   936  		Tag: "bad-tag",
   937  	}, {
   938  		Tag: s.Model.ModelTag().String(),
   939  	}}})
   940  	c.Assert(err, jc.ErrorIsNil)
   941  	c.Assert(results.Results, gc.HasLen, 2)
   942  	c.Assert(results.Results[0].Error, gc.ErrorMatches, `"bad-tag" is not a valid tag`)
   943  
   944  	// Check that we don't err out if a model errs even if some firsts in collection pass.
   945  	results, err = s.controller.ModelStatus(params.Entities{[]params.Entity{{
   946  		Tag: s.Model.ModelTag().String(),
   947  	}, {
   948  		Tag: "bad-tag",
   949  	}}})
   950  	c.Assert(err, jc.ErrorIsNil)
   951  	c.Assert(results.Results, gc.HasLen, 2)
   952  	c.Assert(results.Results[1].Error, gc.ErrorMatches, `"bad-tag" is not a valid tag`)
   953  
   954  	// Check that we return successfully if no errors.
   955  	results, err = s.controller.ModelStatus(params.Entities{[]params.Entity{{
   956  		Tag: s.Model.ModelTag().String(),
   957  	}}})
   958  	c.Assert(err, jc.ErrorIsNil)
   959  	c.Assert(results.Results, gc.HasLen, 1)
   960  }
   961  
   962  func (s *controllerSuite) TestConfigSet(c *gc.C) {
   963  	config, err := s.State.ControllerConfig()
   964  	c.Assert(err, jc.ErrorIsNil)
   965  	// Sanity check.
   966  	c.Assert(config.AuditingEnabled(), gc.Equals, false)
   967  
   968  	err = s.controller.ConfigSet(params.ControllerConfigSet{Config: map[string]interface{}{
   969  		"auditing-enabled": true,
   970  	}})
   971  	c.Assert(err, jc.ErrorIsNil)
   972  
   973  	config, err = s.State.ControllerConfig()
   974  	c.Assert(err, jc.ErrorIsNil)
   975  	c.Assert(config.AuditingEnabled(), gc.Equals, true)
   976  }
   977  
   978  func (s *controllerSuite) TestConfigSetRequiresSuperUser(c *gc.C) {
   979  	user := s.Factory.MakeUser(c, &factory.UserParams{
   980  		Access: permission.ReadAccess,
   981  	})
   982  	anAuthoriser := apiservertesting.FakeAuthorizer{
   983  		Tag: user.Tag(),
   984  	}
   985  	endpoint, err := controller.NewControllerAPIv11(
   986  		facadetest.Context{
   987  			State_:     s.State,
   988  			StatePool_: s.StatePool,
   989  			Resources_: s.resources,
   990  			Auth_:      anAuthoriser,
   991  		})
   992  	c.Assert(err, jc.ErrorIsNil)
   993  
   994  	err = endpoint.ConfigSet(params.ControllerConfigSet{Config: map[string]interface{}{
   995  		"something": 23,
   996  	}})
   997  
   998  	c.Assert(err, gc.ErrorMatches, "permission denied")
   999  }
  1000  
  1001  func (s *controllerSuite) TestConfigSetPublishesEvent(c *gc.C) {
  1002  	done := make(chan struct{})
  1003  	var config corecontroller.Config
  1004  	s.hub.Subscribe(pscontroller.ConfigChanged, func(topic string, data pscontroller.ConfigChangedMessage, err error) {
  1005  		c.Check(err, jc.ErrorIsNil)
  1006  		config = data.Config
  1007  		close(done)
  1008  	})
  1009  
  1010  	err := s.controller.ConfigSet(params.ControllerConfigSet{Config: map[string]interface{}{
  1011  		"features": []string{"foo", "bar"},
  1012  	}})
  1013  	c.Assert(err, jc.ErrorIsNil)
  1014  
  1015  	select {
  1016  	case <-done:
  1017  	case <-time.After(testing.LongWait):
  1018  		c.Fatal("no event sent}")
  1019  	}
  1020  
  1021  	c.Assert(config.Features().SortedValues(), jc.DeepEquals, []string{"bar", "foo"})
  1022  }
  1023  
  1024  func (s *controllerSuite) TestConfigSetCAASImageRepo(c *gc.C) {
  1025  	config, err := s.State.ControllerConfig()
  1026  	c.Assert(err, jc.ErrorIsNil)
  1027  	c.Assert(config.CAASImageRepo(), gc.Equals, "")
  1028  
  1029  	err = s.controller.ConfigSet(params.ControllerConfigSet{Config: map[string]interface{}{
  1030  		"caas-image-repo": "juju-repo.local",
  1031  	}})
  1032  	c.Assert(err, gc.ErrorMatches, `cannot change caas-image-repo as it is not currently set`)
  1033  
  1034  	err = s.State.UpdateControllerConfig(map[string]interface{}{
  1035  		"caas-image-repo": "jujusolutions",
  1036  	}, nil)
  1037  	c.Assert(err, jc.ErrorIsNil)
  1038  
  1039  	err = s.controller.ConfigSet(params.ControllerConfigSet{Config: map[string]interface{}{
  1040  		"caas-image-repo": "juju-repo.local",
  1041  	}})
  1042  	c.Assert(err, gc.ErrorMatches, `cannot change caas-image-repo: repository read-only, only authentication can be updated`)
  1043  
  1044  	err = s.controller.ConfigSet(params.ControllerConfigSet{Config: map[string]interface{}{
  1045  		"caas-image-repo": `{"repository":"jujusolutions","username":"foo","password":"bar"}`,
  1046  	}})
  1047  	c.Assert(err, gc.ErrorMatches, `cannot change caas-image-repo: unable to add authentication details`)
  1048  
  1049  	err = s.State.UpdateControllerConfig(map[string]interface{}{
  1050  		"caas-image-repo": `{"repository":"jujusolutions","username":"bar","password":"foo"}`,
  1051  	}, nil)
  1052  	c.Assert(err, jc.ErrorIsNil)
  1053  
  1054  	err = s.controller.ConfigSet(params.ControllerConfigSet{Config: map[string]interface{}{
  1055  		"caas-image-repo": `{"repository":"jujusolutions","username":"foo","password":"bar"}`,
  1056  	}})
  1057  	c.Assert(err, jc.ErrorIsNil)
  1058  
  1059  	config, err = s.State.ControllerConfig()
  1060  	c.Assert(err, jc.ErrorIsNil)
  1061  	repoDetails, err := docker.NewImageRepoDetails(config.CAASImageRepo())
  1062  	c.Assert(err, jc.ErrorIsNil)
  1063  	c.Assert(repoDetails, gc.DeepEquals, docker.ImageRepoDetails{
  1064  		Repository: "jujusolutions",
  1065  		BasicAuthConfig: docker.BasicAuthConfig{
  1066  			Username: "foo",
  1067  			Password: "bar",
  1068  		},
  1069  	})
  1070  }
  1071  
  1072  func (s *controllerSuite) TestMongoVersion(c *gc.C) {
  1073  	result, err := s.controller.MongoVersion()
  1074  	c.Assert(err, jc.ErrorIsNil)
  1075  
  1076  	var resErr *params.Error
  1077  	c.Assert(result.Error, gc.Equals, resErr)
  1078  	// We can't guarantee which version of mongo is running, so let's just
  1079  	// attempt to match it to a very basic version (major.minor.patch)
  1080  	c.Assert(result.Result, gc.Matches, "^([0-9]{1,}).([0-9]{1,}).([0-9]{1,})$")
  1081  }
  1082  
  1083  func (s *controllerSuite) TestIdentityProviderURL(c *gc.C) {
  1084  	// Preserve default controller config as we will be mutating it just
  1085  	// for this test
  1086  	defer func(orig map[string]interface{}) {
  1087  		s.ControllerConfig = orig
  1088  	}(s.ControllerConfig)
  1089  
  1090  	// Our default test configuration does not specify an IdentityURL
  1091  	urlRes, err := s.controller.IdentityProviderURL()
  1092  	c.Assert(err, jc.ErrorIsNil)
  1093  	c.Assert(urlRes.Result, gc.Equals, "")
  1094  
  1095  	// IdentityURL cannot be changed after bootstrap; we need to spin up
  1096  	// another controller with IdentityURL pre-configured
  1097  	s.TearDownTest(c)
  1098  	expURL := "https://api.jujucharms.com/identity"
  1099  	s.ControllerConfig = map[string]interface{}{
  1100  		corecontroller.IdentityURL: expURL,
  1101  	}
  1102  	s.SetUpTest(c)
  1103  
  1104  	urlRes, err = s.controller.IdentityProviderURL()
  1105  	c.Assert(err, jc.ErrorIsNil)
  1106  	c.Assert(urlRes.Result, gc.Equals, expURL)
  1107  }
  1108  
  1109  func (s *controllerSuite) newSummaryWatcherFacade(c *gc.C, id string) *apiserver.SrvModelSummaryWatcher {
  1110  	context := s.context
  1111  	context.ID_ = id
  1112  	watcher, err := apiserver.NewModelSummaryWatcher(context)
  1113  	c.Assert(err, jc.ErrorIsNil)
  1114  	return watcher
  1115  }
  1116  
  1117  func (s *controllerSuite) TestWatchAllModelSummariesByAdmin(c *gc.C) {
  1118  	// Default authorizer is an admin.
  1119  	result, err := s.controller.WatchAllModelSummaries()
  1120  	c.Assert(err, jc.ErrorIsNil)
  1121  
  1122  	watcherAPI := s.newSummaryWatcherFacade(c, result.WatcherID)
  1123  
  1124  	resultC := make(chan params.SummaryWatcherNextResults)
  1125  	go func() {
  1126  		result, err := watcherAPI.Next()
  1127  		c.Assert(err, jc.ErrorIsNil)
  1128  		resultC <- result
  1129  	}()
  1130  
  1131  	select {
  1132  	case result := <-resultC:
  1133  		// Expect to see the initial environment be reported.
  1134  		c.Assert(result, jc.DeepEquals, params.SummaryWatcherNextResults{
  1135  			Models: []params.ModelAbstract{
  1136  				{
  1137  					UUID:       "deadbeef-0bad-400d-8000-4b1d0d06f00d",
  1138  					Controller: "", // TODO(thumper): add controller name next branch
  1139  					Name:       "controller",
  1140  					Admins:     []string{"test-admin"},
  1141  					Cloud:      "dummy",
  1142  					Region:     "dummy-region",
  1143  					Status:     "green",
  1144  					Messages:   []params.ModelSummaryMessage{},
  1145  				}}})
  1146  	case <-time.After(testing.LongWait):
  1147  		c.Fatal("timed out")
  1148  	}
  1149  }
  1150  
  1151  func (s *controllerSuite) TestWatchAllModelSummariesByNonAdmin(c *gc.C) {
  1152  	anAuthoriser := apiservertesting.FakeAuthorizer{
  1153  		Tag: names.NewLocalUserTag("bob"),
  1154  	}
  1155  	endPoint, err := controller.LatestAPI(
  1156  		facadetest.Context{
  1157  			State_:     s.State,
  1158  			StatePool_: s.StatePool,
  1159  			Resources_: s.resources,
  1160  			Auth_:      anAuthoriser,
  1161  		})
  1162  	c.Assert(err, jc.ErrorIsNil)
  1163  
  1164  	_, err = endPoint.WatchAllModelSummaries()
  1165  	c.Assert(err, gc.ErrorMatches, "permission denied")
  1166  }
  1167  
  1168  func (s *controllerSuite) makeBobsModel(c *gc.C) string {
  1169  	bob := s.Factory.MakeUser(c, &factory.UserParams{
  1170  		Name:        "bob",
  1171  		NoModelUser: true,
  1172  	})
  1173  	st := s.Factory.MakeModel(c, &factory.ModelParams{
  1174  		Owner: bob.UserTag(),
  1175  		Name:  "bobs-model"})
  1176  	uuid := st.ModelUUID()
  1177  	st.Close()
  1178  	s.WaitForModelWatchersIdle(c, uuid)
  1179  	return uuid
  1180  }
  1181  
  1182  func (s *controllerSuite) TestWatchModelSummariesByNonAdmin(c *gc.C) {
  1183  	s.makeBobsModel(c)
  1184  
  1185  	// Default authorizer is an admin. As a user, admin can't see
  1186  	// Bob's model.
  1187  	result, err := s.controller.WatchModelSummaries()
  1188  	c.Assert(err, jc.ErrorIsNil)
  1189  
  1190  	watcherAPI := s.newSummaryWatcherFacade(c, result.WatcherID)
  1191  
  1192  	resultC := make(chan params.SummaryWatcherNextResults)
  1193  	go func() {
  1194  		result, err := watcherAPI.Next()
  1195  		c.Assert(err, jc.ErrorIsNil)
  1196  		resultC <- result
  1197  	}()
  1198  
  1199  	select {
  1200  	case result := <-resultC:
  1201  		// Expect to see the initial environment be reported.
  1202  		c.Assert(result, jc.DeepEquals, params.SummaryWatcherNextResults{
  1203  			Models: []params.ModelAbstract{
  1204  				{
  1205  					UUID:       "deadbeef-0bad-400d-8000-4b1d0d06f00d",
  1206  					Controller: "", // TODO(thumper): add controller name next branch
  1207  					Name:       "controller",
  1208  					Admins:     []string{"test-admin"},
  1209  					Cloud:      "dummy",
  1210  					Region:     "dummy-region",
  1211  					Status:     "green",
  1212  					Messages:   []params.ModelSummaryMessage{},
  1213  				}}})
  1214  	case <-time.After(testing.LongWait):
  1215  		c.Fatal("timed out")
  1216  	}
  1217  
  1218  }
  1219  
  1220  type noopRegisterer struct {
  1221  	prometheus.Registerer
  1222  }
  1223  
  1224  func (noopRegisterer) Register(prometheus.Collector) error {
  1225  	return nil
  1226  }
  1227  
  1228  func (noopRegisterer) Unregister(prometheus.Collector) bool {
  1229  	return true
  1230  }