github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/apiserver/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  	"encoding/json"
     8  	"regexp"
     9  	"time"
    10  
    11  	"github.com/juju/errors"
    12  	"github.com/juju/loggo"
    13  	jc "github.com/juju/testing/checkers"
    14  	"github.com/juju/utils"
    15  	gc "gopkg.in/check.v1"
    16  	"gopkg.in/juju/names.v2"
    17  	"gopkg.in/macaroon.v1"
    18  
    19  	"github.com/juju/juju/apiserver"
    20  	"github.com/juju/juju/apiserver/common"
    21  	"github.com/juju/juju/apiserver/controller"
    22  	"github.com/juju/juju/apiserver/facade/facadetest"
    23  	"github.com/juju/juju/apiserver/params"
    24  	apiservertesting "github.com/juju/juju/apiserver/testing"
    25  	"github.com/juju/juju/cloud"
    26  	"github.com/juju/juju/environs"
    27  	"github.com/juju/juju/environs/config"
    28  	"github.com/juju/juju/permission"
    29  	"github.com/juju/juju/state"
    30  	"github.com/juju/juju/state/multiwatcher"
    31  	statetesting "github.com/juju/juju/state/testing"
    32  	"github.com/juju/juju/testing"
    33  	"github.com/juju/juju/testing/factory"
    34  )
    35  
    36  type controllerSuite struct {
    37  	statetesting.StateSuite
    38  
    39  	controller *controller.ControllerAPI
    40  	resources  *common.Resources
    41  	authorizer apiservertesting.FakeAuthorizer
    42  }
    43  
    44  var _ = gc.Suite(&controllerSuite{})
    45  
    46  func (s *controllerSuite) SetUpTest(c *gc.C) {
    47  	// Initial config needs to be set before the StateSuite SetUpTest.
    48  	s.InitialConfig = testing.CustomModelConfig(c, testing.Attrs{
    49  		"name": "controller",
    50  	})
    51  
    52  	s.StateSuite.SetUpTest(c)
    53  	s.resources = common.NewResources()
    54  	s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() })
    55  
    56  	s.authorizer = apiservertesting.FakeAuthorizer{
    57  		Tag:      s.Owner,
    58  		AdminTag: s.Owner,
    59  	}
    60  
    61  	controller, err := controller.NewControllerAPI(s.State, s.resources, s.authorizer)
    62  	c.Assert(err, jc.ErrorIsNil)
    63  	s.controller = controller
    64  
    65  	loggo.GetLogger("juju.apiserver.controller").SetLogLevel(loggo.TRACE)
    66  }
    67  
    68  func (s *controllerSuite) TestNewAPIRefusesNonClient(c *gc.C) {
    69  	anAuthoriser := apiservertesting.FakeAuthorizer{
    70  		Tag: names.NewUnitTag("mysql/0"),
    71  	}
    72  	endPoint, err := controller.NewControllerAPI(s.State, s.resources, anAuthoriser)
    73  	c.Assert(endPoint, gc.IsNil)
    74  	c.Assert(err, gc.ErrorMatches, "permission denied")
    75  }
    76  
    77  func (s *controllerSuite) checkEnvironmentMatches(c *gc.C, env params.Model, expected *state.Model) {
    78  	c.Check(env.Name, gc.Equals, expected.Name())
    79  	c.Check(env.UUID, gc.Equals, expected.UUID())
    80  	c.Check(env.OwnerTag, gc.Equals, expected.Owner().String())
    81  }
    82  
    83  func (s *controllerSuite) TestAllModels(c *gc.C) {
    84  	admin := s.Factory.MakeUser(c, &factory.UserParams{Name: "foobar"})
    85  
    86  	s.Factory.MakeModel(c, &factory.ModelParams{
    87  		Name: "owned", Owner: admin.UserTag()}).Close()
    88  	remoteUserTag := names.NewUserTag("user@remote")
    89  	st := s.Factory.MakeModel(c, &factory.ModelParams{
    90  		Name: "user", Owner: remoteUserTag})
    91  	defer st.Close()
    92  	st.AddModelUser(st.ModelUUID(),
    93  		state.UserAccessSpec{
    94  			User:        admin.UserTag(),
    95  			CreatedBy:   remoteUserTag,
    96  			DisplayName: "Foo Bar",
    97  			Access:      permission.ReadAccess})
    98  
    99  	s.Factory.MakeModel(c, &factory.ModelParams{
   100  		Name: "no-access", Owner: remoteUserTag}).Close()
   101  
   102  	response, err := s.controller.AllModels()
   103  	c.Assert(err, jc.ErrorIsNil)
   104  	// The results are sorted.
   105  	expected := []string{"controller", "no-access", "owned", "user"}
   106  	var obtained []string
   107  	for _, env := range response.UserModels {
   108  		obtained = append(obtained, env.Name)
   109  		stateEnv, err := s.State.GetModel(names.NewModelTag(env.UUID))
   110  		c.Assert(err, jc.ErrorIsNil)
   111  		s.checkEnvironmentMatches(c, env.Model, stateEnv)
   112  	}
   113  	c.Assert(obtained, jc.DeepEquals, expected)
   114  }
   115  
   116  func (s *controllerSuite) TestHostedModelConfigs_OnlyHostedModelsReturned(c *gc.C) {
   117  	owner := s.Factory.MakeUser(c, nil)
   118  	s.Factory.MakeModel(c, &factory.ModelParams{
   119  		Name: "first", Owner: owner.UserTag()}).Close()
   120  	remoteUserTag := names.NewUserTag("user@remote")
   121  	s.Factory.MakeModel(c, &factory.ModelParams{
   122  		Name: "second", Owner: remoteUserTag}).Close()
   123  
   124  	results, err := s.controller.HostedModelConfigs()
   125  	c.Assert(err, jc.ErrorIsNil)
   126  	c.Assert(len(results.Models), gc.Equals, 2)
   127  
   128  	one := results.Models[0]
   129  	two := results.Models[1]
   130  
   131  	c.Assert(one.Name, gc.Equals, "first")
   132  	c.Assert(one.OwnerTag, gc.Equals, owner.UserTag().String())
   133  	c.Assert(two.Name, gc.Equals, "second")
   134  	c.Assert(two.OwnerTag, gc.Equals, remoteUserTag.String())
   135  }
   136  
   137  func (s *controllerSuite) makeCloudSpec(c *gc.C, pSpec *params.CloudSpec) environs.CloudSpec {
   138  	c.Assert(pSpec, gc.NotNil)
   139  	var credential *cloud.Credential
   140  	if pSpec.Credential != nil {
   141  		credentialValue := cloud.NewCredential(
   142  			cloud.AuthType(pSpec.Credential.AuthType),
   143  			pSpec.Credential.Attributes,
   144  		)
   145  		credential = &credentialValue
   146  	}
   147  	spec := environs.CloudSpec{
   148  		Type:             pSpec.Type,
   149  		Name:             pSpec.Name,
   150  		Region:           pSpec.Region,
   151  		Endpoint:         pSpec.Endpoint,
   152  		IdentityEndpoint: pSpec.IdentityEndpoint,
   153  		StorageEndpoint:  pSpec.StorageEndpoint,
   154  		Credential:       credential,
   155  	}
   156  	c.Assert(spec.Validate(), jc.ErrorIsNil)
   157  	return spec
   158  }
   159  
   160  func (s *controllerSuite) TestHostedModelConfigs_CanOpenEnviron(c *gc.C) {
   161  	owner := s.Factory.MakeUser(c, nil)
   162  	s.Factory.MakeModel(c, &factory.ModelParams{
   163  		Name: "first", Owner: owner.UserTag()}).Close()
   164  	remoteUserTag := names.NewUserTag("user@remote")
   165  	s.Factory.MakeModel(c, &factory.ModelParams{
   166  		Name: "second", Owner: remoteUserTag}).Close()
   167  
   168  	results, err := s.controller.HostedModelConfigs()
   169  	c.Assert(err, jc.ErrorIsNil)
   170  	c.Assert(len(results.Models), gc.Equals, 2)
   171  
   172  	for _, model := range results.Models {
   173  		c.Assert(model.Error, gc.IsNil)
   174  
   175  		cfg, err := config.New(config.NoDefaults, model.Config)
   176  		c.Assert(err, jc.ErrorIsNil)
   177  		spec := s.makeCloudSpec(c, model.CloudSpec)
   178  		_, err = environs.New(environs.OpenParams{
   179  			Cloud:  spec,
   180  			Config: cfg,
   181  		})
   182  		c.Assert(err, jc.ErrorIsNil)
   183  	}
   184  }
   185  
   186  func (s *controllerSuite) TestListBlockedModels(c *gc.C) {
   187  	st := s.Factory.MakeModel(c, &factory.ModelParams{
   188  		Name: "test"})
   189  	defer st.Close()
   190  
   191  	s.State.SwitchBlockOn(state.DestroyBlock, "TestBlockDestroyModel")
   192  	s.State.SwitchBlockOn(state.ChangeBlock, "TestChangeBlock")
   193  	st.SwitchBlockOn(state.DestroyBlock, "TestBlockDestroyModel")
   194  	st.SwitchBlockOn(state.ChangeBlock, "TestChangeBlock")
   195  
   196  	list, err := s.controller.ListBlockedModels()
   197  	c.Assert(err, jc.ErrorIsNil)
   198  
   199  	c.Assert(list.Models, jc.DeepEquals, []params.ModelBlockInfo{
   200  		params.ModelBlockInfo{
   201  			Name:     "controller",
   202  			UUID:     s.State.ModelUUID(),
   203  			OwnerTag: s.Owner.String(),
   204  			Blocks: []string{
   205  				"BlockDestroy",
   206  				"BlockChange",
   207  			},
   208  		},
   209  		params.ModelBlockInfo{
   210  			Name:     "test",
   211  			UUID:     st.ModelUUID(),
   212  			OwnerTag: s.Owner.String(),
   213  			Blocks: []string{
   214  				"BlockDestroy",
   215  				"BlockChange",
   216  			},
   217  		},
   218  	})
   219  
   220  }
   221  
   222  func (s *controllerSuite) TestListBlockedModelsNoBlocks(c *gc.C) {
   223  	list, err := s.controller.ListBlockedModels()
   224  	c.Assert(err, jc.ErrorIsNil)
   225  	c.Assert(list.Models, gc.HasLen, 0)
   226  }
   227  
   228  func (s *controllerSuite) TestModelConfig(c *gc.C) {
   229  	env, err := s.controller.ModelConfig()
   230  	c.Assert(err, jc.ErrorIsNil)
   231  	c.Assert(env.Config["name"], jc.DeepEquals, params.ConfigValue{Value: "controller"})
   232  }
   233  
   234  func (s *controllerSuite) TestModelConfigFromNonController(c *gc.C) {
   235  	st := s.Factory.MakeModel(c, &factory.ModelParams{
   236  		Name: "test"})
   237  	defer st.Close()
   238  
   239  	authorizer := &apiservertesting.FakeAuthorizer{
   240  		Tag:      s.Owner,
   241  		AdminTag: s.Owner,
   242  	}
   243  	controller, err := controller.NewControllerAPI(st, common.NewResources(), authorizer)
   244  	c.Assert(err, jc.ErrorIsNil)
   245  	cfg, err := controller.ModelConfig()
   246  	c.Assert(err, jc.ErrorIsNil)
   247  	c.Assert(cfg.Config["name"], jc.DeepEquals, params.ConfigValue{Value: "controller"})
   248  }
   249  
   250  func (s *controllerSuite) TestControllerConfig(c *gc.C) {
   251  	cfg, err := s.controller.ControllerConfig()
   252  	c.Assert(err, jc.ErrorIsNil)
   253  	cfgFromDB, err := s.State.ControllerConfig()
   254  	c.Assert(err, jc.ErrorIsNil)
   255  	c.Assert(cfg.Config["controller-uuid"], gc.Equals, cfgFromDB.ControllerUUID())
   256  	c.Assert(cfg.Config["state-port"], gc.Equals, cfgFromDB.StatePort())
   257  	c.Assert(cfg.Config["api-port"], gc.Equals, cfgFromDB.APIPort())
   258  }
   259  
   260  func (s *controllerSuite) TestControllerConfigFromNonController(c *gc.C) {
   261  	st := s.Factory.MakeModel(c, &factory.ModelParams{
   262  		Name: "test"})
   263  	defer st.Close()
   264  
   265  	authorizer := &apiservertesting.FakeAuthorizer{Tag: s.Owner}
   266  	controller, err := controller.NewControllerAPI(st, common.NewResources(), authorizer)
   267  	c.Assert(err, jc.ErrorIsNil)
   268  	cfg, err := controller.ControllerConfig()
   269  	c.Assert(err, jc.ErrorIsNil)
   270  	cfgFromDB, err := s.State.ControllerConfig()
   271  	c.Assert(err, jc.ErrorIsNil)
   272  	c.Assert(cfg.Config["controller-uuid"], gc.Equals, cfgFromDB.ControllerUUID())
   273  	c.Assert(cfg.Config["state-port"], gc.Equals, cfgFromDB.StatePort())
   274  	c.Assert(cfg.Config["api-port"], gc.Equals, cfgFromDB.APIPort())
   275  }
   276  
   277  func (s *controllerSuite) TestRemoveBlocks(c *gc.C) {
   278  	st := s.Factory.MakeModel(c, &factory.ModelParams{
   279  		Name: "test"})
   280  	defer st.Close()
   281  
   282  	s.State.SwitchBlockOn(state.DestroyBlock, "TestBlockDestroyModel")
   283  	s.State.SwitchBlockOn(state.ChangeBlock, "TestChangeBlock")
   284  	st.SwitchBlockOn(state.DestroyBlock, "TestBlockDestroyModel")
   285  	st.SwitchBlockOn(state.ChangeBlock, "TestChangeBlock")
   286  
   287  	err := s.controller.RemoveBlocks(params.RemoveBlocksArgs{All: true})
   288  	c.Assert(err, jc.ErrorIsNil)
   289  
   290  	blocks, err := s.State.AllBlocksForController()
   291  	c.Assert(err, jc.ErrorIsNil)
   292  	c.Assert(blocks, gc.HasLen, 0)
   293  }
   294  
   295  func (s *controllerSuite) TestRemoveBlocksNotAll(c *gc.C) {
   296  	err := s.controller.RemoveBlocks(params.RemoveBlocksArgs{})
   297  	c.Assert(err, gc.ErrorMatches, "not supported")
   298  }
   299  
   300  func (s *controllerSuite) TestWatchAllModels(c *gc.C) {
   301  	watcherId, err := s.controller.WatchAllModels()
   302  	c.Assert(err, jc.ErrorIsNil)
   303  
   304  	watcherAPI_, err := apiserver.NewAllWatcher(facadetest.Context{
   305  		State_:     s.State,
   306  		Resources_: s.resources,
   307  		Auth_:      s.authorizer,
   308  		ID_:        watcherId.AllWatcherId,
   309  	})
   310  	c.Assert(err, jc.ErrorIsNil)
   311  	watcherAPI := watcherAPI_.(*apiserver.SrvAllWatcher)
   312  	defer func() {
   313  		err := watcherAPI.Stop()
   314  		c.Assert(err, jc.ErrorIsNil)
   315  	}()
   316  
   317  	resultC := make(chan params.AllWatcherNextResults)
   318  	go func() {
   319  		result, err := watcherAPI.Next()
   320  		c.Assert(err, jc.ErrorIsNil)
   321  		resultC <- result
   322  	}()
   323  
   324  	select {
   325  	case result := <-resultC:
   326  		// Expect to see the initial environment be reported.
   327  		deltas := result.Deltas
   328  		c.Assert(deltas, gc.HasLen, 1)
   329  		envInfo := deltas[0].Entity.(*multiwatcher.ModelInfo)
   330  		c.Assert(envInfo.ModelUUID, gc.Equals, s.State.ModelUUID())
   331  	case <-time.After(testing.LongWait):
   332  		c.Fatal("timed out")
   333  	}
   334  }
   335  
   336  func (s *controllerSuite) TestInitiateMigration(c *gc.C) {
   337  	// Create two hosted models to migrate.
   338  	st1 := s.Factory.MakeModel(c, nil)
   339  	defer st1.Close()
   340  
   341  	st2 := s.Factory.MakeModel(c, nil)
   342  	defer st2.Close()
   343  
   344  	mac, err := macaroon.New([]byte("secret"), "id", "location")
   345  	c.Assert(err, jc.ErrorIsNil)
   346  	macsJSON, err := json.Marshal([]macaroon.Slice{{mac}})
   347  	c.Assert(err, jc.ErrorIsNil)
   348  
   349  	controller.SetPrecheckResult(s, nil)
   350  
   351  	// Kick off migrations
   352  	args := params.InitiateMigrationArgs{
   353  		Specs: []params.MigrationSpec{
   354  			{
   355  				ModelTag: st1.ModelTag().String(),
   356  				TargetInfo: params.MigrationTargetInfo{
   357  					ControllerTag: randomControllerTag(),
   358  					Addrs:         []string{"1.1.1.1:1111", "2.2.2.2:2222"},
   359  					CACert:        "cert1",
   360  					AuthTag:       names.NewUserTag("admin1").String(),
   361  					Password:      "secret1",
   362  				},
   363  			}, {
   364  				ModelTag: st2.ModelTag().String(),
   365  				TargetInfo: params.MigrationTargetInfo{
   366  					ControllerTag: randomControllerTag(),
   367  					Addrs:         []string{"3.3.3.3:3333"},
   368  					CACert:        "cert2",
   369  					AuthTag:       names.NewUserTag("admin2").String(),
   370  					Macaroons:     string(macsJSON),
   371  					Password:      "secret2",
   372  				},
   373  				ExternalControl: true,
   374  			},
   375  		},
   376  	}
   377  	out, err := s.controller.InitiateMigration(args)
   378  	c.Assert(err, jc.ErrorIsNil)
   379  	c.Assert(out.Results, gc.HasLen, 2)
   380  
   381  	states := []*state.State{st1, st2}
   382  	for i, spec := range args.Specs {
   383  		c.Log(i)
   384  		st := states[i]
   385  		result := out.Results[i]
   386  
   387  		c.Assert(result.Error, gc.IsNil)
   388  		c.Check(result.ModelTag, gc.Equals, spec.ModelTag)
   389  		expectedId := st.ModelUUID() + ":0"
   390  		c.Check(result.MigrationId, gc.Equals, expectedId)
   391  
   392  		// Ensure the migration made it into the DB correctly.
   393  		mig, err := st.LatestMigration()
   394  		c.Assert(err, jc.ErrorIsNil)
   395  		c.Check(mig.Id(), gc.Equals, expectedId)
   396  		c.Check(mig.ModelUUID(), gc.Equals, st.ModelUUID())
   397  		c.Check(mig.InitiatedBy(), gc.Equals, s.Owner.Id())
   398  		c.Check(mig.ExternalControl(), gc.Equals, args.Specs[i].ExternalControl)
   399  
   400  		targetInfo, err := mig.TargetInfo()
   401  		c.Assert(err, jc.ErrorIsNil)
   402  		c.Check(targetInfo.ControllerTag.String(), gc.Equals, spec.TargetInfo.ControllerTag)
   403  		c.Check(targetInfo.Addrs, jc.SameContents, spec.TargetInfo.Addrs)
   404  		c.Check(targetInfo.CACert, gc.Equals, spec.TargetInfo.CACert)
   405  		c.Check(targetInfo.AuthTag.String(), gc.Equals, spec.TargetInfo.AuthTag)
   406  		c.Check(targetInfo.Password, gc.Equals, spec.TargetInfo.Password)
   407  
   408  		if spec.TargetInfo.Macaroons != "" {
   409  			macJSONdb, err := json.Marshal(targetInfo.Macaroons)
   410  			c.Assert(err, jc.ErrorIsNil)
   411  			c.Check(string(macJSONdb), gc.Equals, spec.TargetInfo.Macaroons)
   412  		}
   413  	}
   414  }
   415  
   416  func (s *controllerSuite) TestInitiateMigrationSpecError(c *gc.C) {
   417  	// Create a hosted model to migrate.
   418  	st := s.Factory.MakeModel(c, nil)
   419  	defer st.Close()
   420  
   421  	// Kick off the migration with missing details.
   422  	args := params.InitiateMigrationArgs{
   423  		Specs: []params.MigrationSpec{{
   424  			ModelTag: st.ModelTag().String(),
   425  			// TargetInfo missing
   426  		}},
   427  	}
   428  	out, err := s.controller.InitiateMigration(args)
   429  	c.Assert(err, jc.ErrorIsNil)
   430  	c.Assert(out.Results, gc.HasLen, 1)
   431  	result := out.Results[0]
   432  	c.Check(result.ModelTag, gc.Equals, args.Specs[0].ModelTag)
   433  	c.Check(result.MigrationId, gc.Equals, "")
   434  	c.Check(result.Error, gc.ErrorMatches, "controller tag: .+ is not a valid tag")
   435  }
   436  
   437  func (s *controllerSuite) TestInitiateMigrationPartialFailure(c *gc.C) {
   438  	st := s.Factory.MakeModel(c, nil)
   439  	defer st.Close()
   440  	controller.SetPrecheckResult(s, nil)
   441  
   442  	args := params.InitiateMigrationArgs{
   443  		Specs: []params.MigrationSpec{
   444  			{
   445  				ModelTag: st.ModelTag().String(),
   446  				TargetInfo: params.MigrationTargetInfo{
   447  					ControllerTag: randomControllerTag(),
   448  					Addrs:         []string{"1.1.1.1:1111", "2.2.2.2:2222"},
   449  					CACert:        "cert",
   450  					AuthTag:       names.NewUserTag("admin").String(),
   451  					Password:      "secret",
   452  				},
   453  			}, {
   454  				ModelTag: randomModelTag(), // Doesn't exist.
   455  			},
   456  		},
   457  	}
   458  	out, err := s.controller.InitiateMigration(args)
   459  	c.Assert(err, jc.ErrorIsNil)
   460  	c.Assert(out.Results, gc.HasLen, 2)
   461  
   462  	c.Check(out.Results[0].ModelTag, gc.Equals, st.ModelTag().String())
   463  	c.Check(out.Results[0].Error, gc.IsNil)
   464  
   465  	c.Check(out.Results[1].ModelTag, gc.Equals, args.Specs[1].ModelTag)
   466  	c.Check(out.Results[1].Error, gc.ErrorMatches, "unable to read model: .+")
   467  }
   468  
   469  func (s *controllerSuite) TestInitiateMigrationInvalidMacaroons(c *gc.C) {
   470  	st := s.Factory.MakeModel(c, nil)
   471  	defer st.Close()
   472  
   473  	args := params.InitiateMigrationArgs{
   474  		Specs: []params.MigrationSpec{
   475  			{
   476  				ModelTag: st.ModelTag().String(),
   477  				TargetInfo: params.MigrationTargetInfo{
   478  					ControllerTag: randomControllerTag(),
   479  					Addrs:         []string{"1.1.1.1:1111", "2.2.2.2:2222"},
   480  					CACert:        "cert",
   481  					AuthTag:       names.NewUserTag("admin").String(),
   482  					Macaroons:     "BLAH",
   483  				},
   484  			},
   485  		},
   486  	}
   487  	out, err := s.controller.InitiateMigration(args)
   488  	c.Assert(err, jc.ErrorIsNil)
   489  	c.Assert(out.Results, gc.HasLen, 1)
   490  	result := out.Results[0]
   491  	c.Check(result.ModelTag, gc.Equals, args.Specs[0].ModelTag)
   492  	c.Check(result.Error, gc.ErrorMatches, "invalid macaroons: .+")
   493  }
   494  
   495  func (s *controllerSuite) TestInitiateMigrationPrecheckFail(c *gc.C) {
   496  	st := s.Factory.MakeModel(c, nil)
   497  	defer st.Close()
   498  
   499  	controller.SetPrecheckResult(s, errors.New("boom"))
   500  
   501  	args := params.InitiateMigrationArgs{
   502  		Specs: []params.MigrationSpec{{
   503  			ModelTag: st.ModelTag().String(),
   504  			TargetInfo: params.MigrationTargetInfo{
   505  				ControllerTag: randomControllerTag(),
   506  				Addrs:         []string{"1.1.1.1:1111"},
   507  				CACert:        "cert1",
   508  				AuthTag:       names.NewUserTag("admin1").String(),
   509  				Password:      "secret1",
   510  			},
   511  		}},
   512  	}
   513  	out, err := s.controller.InitiateMigration(args)
   514  	c.Assert(out.Results, gc.HasLen, 1)
   515  	c.Check(out.Results[0].Error, gc.ErrorMatches, "boom")
   516  
   517  	active, err := st.IsMigrationActive()
   518  	c.Assert(err, jc.ErrorIsNil)
   519  	c.Check(active, jc.IsFalse)
   520  }
   521  
   522  func (s *controllerSuite) TestInitiateMigrationSkipPrechecks(c *gc.C) {
   523  	st := s.Factory.MakeModel(c, nil)
   524  	defer st.Close()
   525  	controller.SetPrecheckResult(s, errors.New("should not happen"))
   526  
   527  	args := params.InitiateMigrationArgs{
   528  		Specs: []params.MigrationSpec{
   529  			{
   530  				ModelTag: st.ModelTag().String(),
   531  				TargetInfo: params.MigrationTargetInfo{
   532  					ControllerTag: randomControllerTag(),
   533  					Addrs:         []string{"1.1.1.1:1111", "2.2.2.2:2222"},
   534  					CACert:        "cert",
   535  					AuthTag:       names.NewUserTag("admin").String(),
   536  					Password:      "secret",
   537  				},
   538  				ExternalControl:      true,
   539  				SkipInitialPrechecks: true,
   540  			},
   541  		},
   542  	}
   543  	out, err := s.controller.InitiateMigration(args)
   544  	c.Assert(err, jc.ErrorIsNil)
   545  	c.Assert(out.Results, gc.HasLen, 1)
   546  	c.Check(out.Results[0].ModelTag, gc.Equals, st.ModelTag().String())
   547  	c.Check(out.Results[0].Error, gc.IsNil)
   548  }
   549  
   550  func randomControllerTag() string {
   551  	uuid := utils.MustNewUUID().String()
   552  	return names.NewControllerTag(uuid).String()
   553  }
   554  
   555  func randomModelTag() string {
   556  	uuid := utils.MustNewUUID().String()
   557  	return names.NewModelTag(uuid).String()
   558  }
   559  
   560  func (s *controllerSuite) modifyControllerAccess(c *gc.C, user names.UserTag, action params.ControllerAction, access string) error {
   561  	args := params.ModifyControllerAccessRequest{
   562  		Changes: []params.ModifyControllerAccess{{
   563  			UserTag: user.String(),
   564  			Action:  action,
   565  			Access:  access,
   566  		}}}
   567  	result, err := s.controller.ModifyControllerAccess(args)
   568  	c.Assert(err, jc.ErrorIsNil)
   569  	return result.OneError()
   570  }
   571  
   572  func (s *controllerSuite) controllerGrant(c *gc.C, user names.UserTag, access string) error {
   573  	return s.modifyControllerAccess(c, user, params.GrantControllerAccess, access)
   574  }
   575  
   576  func (s *controllerSuite) controllerRevoke(c *gc.C, user names.UserTag, access string) error {
   577  	return s.modifyControllerAccess(c, user, params.RevokeControllerAccess, access)
   578  }
   579  
   580  func (s *controllerSuite) TestGrantMissingUserFails(c *gc.C) {
   581  	user := names.NewLocalUserTag("foobar")
   582  	err := s.controllerGrant(c, user, string(permission.AddModelAccess))
   583  	expectedErr := `could not grant controller access: user "foobar" does not exist locally: user "foobar" not found`
   584  	c.Assert(err, gc.ErrorMatches, expectedErr)
   585  }
   586  
   587  func (s *controllerSuite) TestRevokeSuperuserLeavesAddModelAccess(c *gc.C) {
   588  	user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true})
   589  
   590  	err := s.controllerGrant(c, user.UserTag(), string(permission.SuperuserAccess))
   591  	c.Assert(err, gc.IsNil)
   592  	ctag := names.NewControllerTag(s.State.ControllerUUID())
   593  	controllerUser, err := s.State.UserAccess(user.UserTag(), ctag)
   594  	c.Assert(err, jc.ErrorIsNil)
   595  	c.Assert(controllerUser.Access, gc.Equals, permission.SuperuserAccess)
   596  
   597  	err = s.controllerRevoke(c, user.UserTag(), string(permission.SuperuserAccess))
   598  	c.Assert(err, gc.IsNil)
   599  
   600  	controllerUser, err = s.State.UserAccess(user.UserTag(), controllerUser.Object)
   601  	c.Assert(err, jc.ErrorIsNil)
   602  	c.Assert(controllerUser.Access, gc.Equals, permission.AddModelAccess)
   603  }
   604  
   605  func (s *controllerSuite) TestRevokeAddModelLeavesLoginAccess(c *gc.C) {
   606  	user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true})
   607  
   608  	err := s.controllerGrant(c, user.UserTag(), string(permission.AddModelAccess))
   609  	c.Assert(err, gc.IsNil)
   610  	ctag := names.NewControllerTag(s.State.ControllerUUID())
   611  	controllerUser, err := s.State.UserAccess(user.UserTag(), ctag)
   612  	c.Assert(err, jc.ErrorIsNil)
   613  	c.Assert(controllerUser.Access, gc.Equals, permission.AddModelAccess)
   614  
   615  	err = s.controllerRevoke(c, user.UserTag(), string(permission.AddModelAccess))
   616  	c.Assert(err, gc.IsNil)
   617  
   618  	controllerUser, err = s.State.UserAccess(user.UserTag(), controllerUser.Object)
   619  	c.Assert(err, jc.ErrorIsNil)
   620  	c.Assert(controllerUser.Access, gc.Equals, permission.LoginAccess)
   621  }
   622  
   623  func (s *controllerSuite) TestRevokeLoginRemovesControllerUser(c *gc.C) {
   624  	user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true})
   625  	err := s.controllerRevoke(c, user.UserTag(), string(permission.LoginAccess))
   626  	c.Assert(err, gc.IsNil)
   627  
   628  	ctag := names.NewControllerTag(s.State.ControllerUUID())
   629  	_, err = s.State.UserAccess(user.UserTag(), ctag)
   630  
   631  	c.Assert(errors.IsNotFound(err), jc.IsTrue)
   632  }
   633  
   634  func (s *controllerSuite) TestRevokeControllerMissingUser(c *gc.C) {
   635  	user := names.NewLocalUserTag("foobar")
   636  	err := s.controllerRevoke(c, user, string(permission.AddModelAccess))
   637  	expectedErr := `could not look up controller access for user: user "foobar" not found`
   638  	c.Assert(err, gc.ErrorMatches, expectedErr)
   639  }
   640  
   641  func (s *controllerSuite) TestGrantOnlyGreaterAccess(c *gc.C) {
   642  	user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true})
   643  
   644  	err := s.controllerGrant(c, user.UserTag(), string(permission.AddModelAccess))
   645  	c.Assert(err, gc.IsNil)
   646  	ctag := names.NewControllerTag(s.State.ControllerUUID())
   647  	controllerUser, err := s.State.UserAccess(user.UserTag(), ctag)
   648  	c.Assert(err, jc.ErrorIsNil)
   649  	c.Assert(controllerUser.Access, gc.Equals, permission.AddModelAccess)
   650  
   651  	err = s.controllerGrant(c, user.UserTag(), string(permission.AddModelAccess))
   652  	expectedErr := `could not grant controller access: user already has "add-model" access or greater`
   653  	c.Assert(err, gc.ErrorMatches, expectedErr)
   654  }
   655  
   656  func (s *controllerSuite) TestGrantControllerAddRemoteUser(c *gc.C) {
   657  	userTag := names.NewUserTag("foobar@ubuntuone")
   658  
   659  	err := s.controllerGrant(c, userTag, string(permission.AddModelAccess))
   660  	c.Assert(err, jc.ErrorIsNil)
   661  
   662  	ctag := names.NewControllerTag(s.State.ControllerUUID())
   663  	controllerUser, err := s.State.UserAccess(userTag, ctag)
   664  	c.Assert(err, jc.ErrorIsNil)
   665  
   666  	c.Assert(controllerUser.Access, gc.Equals, permission.AddModelAccess)
   667  }
   668  
   669  func (s *controllerSuite) TestGrantControllerInvalidUserTag(c *gc.C) {
   670  	for _, testParam := range []struct {
   671  		tag      string
   672  		validTag bool
   673  	}{{
   674  		tag:      "unit-foo/0",
   675  		validTag: true,
   676  	}, {
   677  		tag:      "application-foo",
   678  		validTag: true,
   679  	}, {
   680  		tag:      "relation-wordpress:db mysql:db",
   681  		validTag: true,
   682  	}, {
   683  		tag:      "machine-0",
   684  		validTag: true,
   685  	}, {
   686  		tag:      "user@local",
   687  		validTag: false,
   688  	}, {
   689  		tag:      "user-Mua^h^h^h^arh",
   690  		validTag: true,
   691  	}, {
   692  		tag:      "user@",
   693  		validTag: false,
   694  	}, {
   695  		tag:      "user@ubuntuone",
   696  		validTag: false,
   697  	}, {
   698  		tag:      "user@ubuntuone",
   699  		validTag: false,
   700  	}, {
   701  		tag:      "@ubuntuone",
   702  		validTag: false,
   703  	}, {
   704  		tag:      "in^valid.",
   705  		validTag: false,
   706  	}, {
   707  		tag:      "",
   708  		validTag: false,
   709  	},
   710  	} {
   711  		var expectedErr string
   712  		errPart := `could not modify controller access: "` + regexp.QuoteMeta(testParam.tag) + `" is not a valid `
   713  
   714  		if testParam.validTag {
   715  			// The string is a valid tag, but not a user tag.
   716  			expectedErr = errPart + `user tag`
   717  		} else {
   718  			// The string is not a valid tag of any kind.
   719  			expectedErr = errPart + `tag`
   720  		}
   721  
   722  		args := params.ModifyControllerAccessRequest{
   723  			Changes: []params.ModifyControllerAccess{{
   724  				UserTag: testParam.tag,
   725  				Action:  params.GrantControllerAccess,
   726  				Access:  string(permission.SuperuserAccess),
   727  			}}}
   728  
   729  		result, err := s.controller.ModifyControllerAccess(args)
   730  		c.Assert(err, jc.ErrorIsNil)
   731  		c.Assert(result.OneError(), gc.ErrorMatches, expectedErr)
   732  	}
   733  }
   734  
   735  func (s *controllerSuite) TestModifyControllerAccessEmptyArgs(c *gc.C) {
   736  	args := params.ModifyControllerAccessRequest{Changes: []params.ModifyControllerAccess{{}}}
   737  
   738  	result, err := s.controller.ModifyControllerAccess(args)
   739  	c.Assert(err, jc.ErrorIsNil)
   740  	expectedErr := `"" controller access not valid`
   741  	c.Assert(result.OneError(), gc.ErrorMatches, expectedErr)
   742  }
   743  
   744  func (s *controllerSuite) TestModifyControllerAccessInvalidAction(c *gc.C) {
   745  	var dance params.ControllerAction = "dance"
   746  	args := params.ModifyControllerAccessRequest{
   747  		Changes: []params.ModifyControllerAccess{{
   748  			UserTag: "user-user@local",
   749  			Action:  dance,
   750  			Access:  string(permission.LoginAccess),
   751  		}}}
   752  
   753  	result, err := s.controller.ModifyControllerAccess(args)
   754  	c.Assert(err, jc.ErrorIsNil)
   755  	expectedErr := `unknown action "dance"`
   756  	c.Assert(result.OneError(), gc.ErrorMatches, expectedErr)
   757  }
   758  
   759  func (s *controllerSuite) TestGetControllerAccess(c *gc.C) {
   760  	user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true})
   761  	user2 := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true})
   762  
   763  	err := s.controllerGrant(c, user.UserTag(), string(permission.SuperuserAccess))
   764  	c.Assert(err, gc.IsNil)
   765  	err = s.controllerGrant(c, user2.UserTag(), string(permission.AddModelAccess))
   766  	c.Assert(err, gc.IsNil)
   767  	req := params.Entities{
   768  		Entities: []params.Entity{{Tag: user.Tag().String()}, {Tag: user2.Tag().String()}},
   769  	}
   770  	results, err := s.controller.GetControllerAccess(req)
   771  	c.Assert(err, jc.ErrorIsNil)
   772  	c.Assert(results.Results, gc.DeepEquals, []params.UserAccessResult{{
   773  		Result: &params.UserAccess{
   774  			Access:  "superuser",
   775  			UserTag: user.Tag().String(),
   776  		}}, {
   777  		Result: &params.UserAccess{
   778  			Access:  "add-model",
   779  			UserTag: user2.Tag().String(),
   780  		}}})
   781  }
   782  
   783  func (s *controllerSuite) TestGetControllerAccessPermissions(c *gc.C) {
   784  	// Set up the user making the call.
   785  	user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true})
   786  	anAuthoriser := apiservertesting.FakeAuthorizer{
   787  		Tag: user.Tag(),
   788  	}
   789  	endpoint, err := controller.NewControllerAPI(s.State, s.resources, anAuthoriser)
   790  	c.Assert(err, jc.ErrorIsNil)
   791  	args := params.ModifyControllerAccessRequest{
   792  		Changes: []params.ModifyControllerAccess{{
   793  			UserTag: user.Tag().String(),
   794  			Action:  params.GrantControllerAccess,
   795  			Access:  "superuser",
   796  		}}}
   797  	result, err := s.controller.ModifyControllerAccess(args)
   798  	c.Assert(err, jc.ErrorIsNil)
   799  	c.Assert(result.OneError(), jc.ErrorIsNil)
   800  
   801  	// We ask for permissions for a different user as well as ourselves.
   802  	differentUser := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true})
   803  	req := params.Entities{
   804  		Entities: []params.Entity{{Tag: user.Tag().String()}, {Tag: differentUser.Tag().String()}},
   805  	}
   806  	results, err := endpoint.GetControllerAccess(req)
   807  	c.Assert(err, jc.ErrorIsNil)
   808  	c.Assert(results.Results, gc.HasLen, 2)
   809  	c.Assert(*results.Results[0].Result, jc.DeepEquals, params.UserAccess{
   810  		Access:  "superuser",
   811  		UserTag: user.Tag().String(),
   812  	})
   813  	c.Assert(*results.Results[1].Error, gc.DeepEquals, params.Error{
   814  		Message: "permission denied", Code: "unauthorized access",
   815  	})
   816  }