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