github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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  	"encoding/json"
     8  	"regexp"
     9  	"time"
    10  
    11  	"github.com/juju/errors"
    12  	"github.com/juju/loggo"
    13  	"github.com/juju/pubsub"
    14  	jc "github.com/juju/testing/checkers"
    15  	"github.com/juju/utils"
    16  	gc "gopkg.in/check.v1"
    17  	"gopkg.in/juju/names.v2"
    18  	"gopkg.in/macaroon.v2-unstable"
    19  
    20  	"github.com/juju/juju/apiserver"
    21  	"github.com/juju/juju/apiserver/common"
    22  	"github.com/juju/juju/apiserver/facade/facadetest"
    23  	"github.com/juju/juju/apiserver/facades/client/controller"
    24  	"github.com/juju/juju/apiserver/params"
    25  	apiservertesting "github.com/juju/juju/apiserver/testing"
    26  	"github.com/juju/juju/cloud"
    27  	corecontroller "github.com/juju/juju/controller"
    28  	"github.com/juju/juju/environs"
    29  	"github.com/juju/juju/environs/config"
    30  	"github.com/juju/juju/permission"
    31  	pscontroller "github.com/juju/juju/pubsub/controller"
    32  	"github.com/juju/juju/state"
    33  	"github.com/juju/juju/state/multiwatcher"
    34  	statetesting "github.com/juju/juju/state/testing"
    35  	"github.com/juju/juju/testing"
    36  	"github.com/juju/juju/testing/factory"
    37  )
    38  
    39  type controllerSuite struct {
    40  	statetesting.StateSuite
    41  
    42  	controller *controller.ControllerAPI
    43  	resources  *common.Resources
    44  	authorizer apiservertesting.FakeAuthorizer
    45  	hub        *pubsub.StructuredHub
    46  }
    47  
    48  var _ = gc.Suite(&controllerSuite{})
    49  
    50  func (s *controllerSuite) SetUpTest(c *gc.C) {
    51  	// Initial config needs to be set before the StateSuite SetUpTest.
    52  	s.InitialConfig = testing.CustomModelConfig(c, testing.Attrs{
    53  		"name": "controller",
    54  	})
    55  
    56  	s.StateSuite.SetUpTest(c)
    57  
    58  	s.resources = common.NewResources()
    59  	s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() })
    60  
    61  	s.authorizer = apiservertesting.FakeAuthorizer{
    62  		Tag:      s.Owner,
    63  		AdminTag: s.Owner,
    64  	}
    65  	s.hub = pubsub.NewStructuredHub(nil)
    66  
    67  	controller, err := controller.NewControllerAPIv7(
    68  		facadetest.Context{
    69  			State_:     s.State,
    70  			StatePool_: s.StatePool,
    71  			Resources_: s.resources,
    72  			Auth_:      s.authorizer,
    73  			Hub_:       s.hub,
    74  		})
    75  	c.Assert(err, jc.ErrorIsNil)
    76  	s.controller = controller
    77  
    78  	loggo.GetLogger("juju.apiserver.controller").SetLogLevel(loggo.TRACE)
    79  }
    80  
    81  func (s *controllerSuite) TestNewAPIRefusesNonClient(c *gc.C) {
    82  	anAuthoriser := apiservertesting.FakeAuthorizer{
    83  		Tag: names.NewUnitTag("mysql/0"),
    84  	}
    85  	endPoint, err := controller.NewControllerAPIv4(
    86  		facadetest.Context{
    87  			State_:     s.State,
    88  			Resources_: s.resources,
    89  			Auth_:      anAuthoriser,
    90  		})
    91  	c.Assert(endPoint, gc.IsNil)
    92  	c.Assert(err, gc.ErrorMatches, "permission denied")
    93  }
    94  
    95  func (s *controllerSuite) checkModelMatches(c *gc.C, model params.Model, expected *state.Model) {
    96  	c.Check(model.Name, gc.Equals, expected.Name())
    97  	c.Check(model.UUID, gc.Equals, expected.UUID())
    98  	c.Check(model.OwnerTag, gc.Equals, expected.Owner().String())
    99  }
   100  
   101  func (s *controllerSuite) TestAllModels(c *gc.C) {
   102  	admin := s.Factory.MakeUser(c, &factory.UserParams{Name: "foobar"})
   103  
   104  	s.Factory.MakeModel(c, &factory.ModelParams{
   105  		Name: "owned", Owner: admin.UserTag()}).Close()
   106  	remoteUserTag := names.NewUserTag("user@remote")
   107  	st := s.Factory.MakeModel(c, &factory.ModelParams{
   108  		Name: "user", Owner: remoteUserTag})
   109  	defer st.Close()
   110  	model, err := st.Model()
   111  	c.Assert(err, jc.ErrorIsNil)
   112  
   113  	model.AddUser(
   114  		state.UserAccessSpec{
   115  			User:        admin.UserTag(),
   116  			CreatedBy:   remoteUserTag,
   117  			DisplayName: "Foo Bar",
   118  			Access:      permission.WriteAccess})
   119  
   120  	s.Factory.MakeModel(c, &factory.ModelParams{
   121  		Name: "no-access", Owner: remoteUserTag}).Close()
   122  
   123  	response, err := s.controller.AllModels()
   124  	c.Assert(err, jc.ErrorIsNil)
   125  	// The results are sorted.
   126  	expected := []string{"controller", "no-access", "owned", "user"}
   127  	var obtained []string
   128  	for _, userModel := range response.UserModels {
   129  		c.Assert(userModel.Type, gc.Equals, "iaas")
   130  		obtained = append(obtained, userModel.Name)
   131  		stateModel, ph, err := s.StatePool.GetModel(userModel.UUID)
   132  		c.Assert(err, jc.ErrorIsNil)
   133  		defer ph.Release()
   134  		s.checkModelMatches(c, userModel.Model, stateModel)
   135  	}
   136  	c.Assert(obtained, jc.DeepEquals, expected)
   137  }
   138  
   139  func (s *controllerSuite) TestHostedModelConfigs_OnlyHostedModelsReturned(c *gc.C) {
   140  	owner := s.Factory.MakeUser(c, nil)
   141  	s.Factory.MakeModel(c, &factory.ModelParams{
   142  		Name: "first", Owner: owner.UserTag()}).Close()
   143  	remoteUserTag := names.NewUserTag("user@remote")
   144  	s.Factory.MakeModel(c, &factory.ModelParams{
   145  		Name: "second", Owner: remoteUserTag}).Close()
   146  
   147  	results, err := s.controller.HostedModelConfigs()
   148  	c.Assert(err, jc.ErrorIsNil)
   149  	c.Assert(len(results.Models), gc.Equals, 2)
   150  
   151  	one := results.Models[0]
   152  	two := results.Models[1]
   153  
   154  	c.Assert(one.Name, gc.Equals, "first")
   155  	c.Assert(one.OwnerTag, gc.Equals, owner.UserTag().String())
   156  	c.Assert(two.Name, gc.Equals, "second")
   157  	c.Assert(two.OwnerTag, gc.Equals, remoteUserTag.String())
   158  }
   159  
   160  func (s *controllerSuite) makeCloudSpec(c *gc.C, pSpec *params.CloudSpec) environs.CloudSpec {
   161  	c.Assert(pSpec, gc.NotNil)
   162  	var credential *cloud.Credential
   163  	if pSpec.Credential != nil {
   164  		credentialValue := cloud.NewCredential(
   165  			cloud.AuthType(pSpec.Credential.AuthType),
   166  			pSpec.Credential.Attributes,
   167  		)
   168  		credential = &credentialValue
   169  	}
   170  	spec := environs.CloudSpec{
   171  		Type:             pSpec.Type,
   172  		Name:             pSpec.Name,
   173  		Region:           pSpec.Region,
   174  		Endpoint:         pSpec.Endpoint,
   175  		IdentityEndpoint: pSpec.IdentityEndpoint,
   176  		StorageEndpoint:  pSpec.StorageEndpoint,
   177  		Credential:       credential,
   178  	}
   179  	c.Assert(spec.Validate(), jc.ErrorIsNil)
   180  	return spec
   181  }
   182  
   183  func (s *controllerSuite) TestHostedModelConfigs_CanOpenEnviron(c *gc.C) {
   184  	owner := s.Factory.MakeUser(c, nil)
   185  	s.Factory.MakeModel(c, &factory.ModelParams{
   186  		Name: "first", Owner: owner.UserTag()}).Close()
   187  	remoteUserTag := names.NewUserTag("user@remote")
   188  	s.Factory.MakeModel(c, &factory.ModelParams{
   189  		Name: "second", Owner: remoteUserTag}).Close()
   190  
   191  	results, err := s.controller.HostedModelConfigs()
   192  	c.Assert(err, jc.ErrorIsNil)
   193  	c.Assert(len(results.Models), gc.Equals, 2)
   194  
   195  	for _, model := range results.Models {
   196  		c.Assert(model.Error, gc.IsNil)
   197  
   198  		cfg, err := config.New(config.NoDefaults, model.Config)
   199  		c.Assert(err, jc.ErrorIsNil)
   200  		spec := s.makeCloudSpec(c, model.CloudSpec)
   201  		_, err = environs.New(environs.OpenParams{
   202  			Cloud:  spec,
   203  			Config: cfg,
   204  		})
   205  		c.Assert(err, jc.ErrorIsNil)
   206  	}
   207  }
   208  
   209  func (s *controllerSuite) TestListBlockedModels(c *gc.C) {
   210  	st := s.Factory.MakeModel(c, &factory.ModelParams{
   211  		Name: "test"})
   212  	defer st.Close()
   213  
   214  	s.State.SwitchBlockOn(state.DestroyBlock, "TestBlockDestroyModel")
   215  	s.State.SwitchBlockOn(state.ChangeBlock, "TestChangeBlock")
   216  	st.SwitchBlockOn(state.DestroyBlock, "TestBlockDestroyModel")
   217  	st.SwitchBlockOn(state.ChangeBlock, "TestChangeBlock")
   218  
   219  	list, err := s.controller.ListBlockedModels()
   220  	c.Assert(err, jc.ErrorIsNil)
   221  
   222  	c.Assert(list.Models, jc.DeepEquals, []params.ModelBlockInfo{
   223  		{
   224  			Name:     "controller",
   225  			UUID:     s.State.ModelUUID(),
   226  			OwnerTag: s.Owner.String(),
   227  			Blocks: []string{
   228  				"BlockDestroy",
   229  				"BlockChange",
   230  			},
   231  		},
   232  		{
   233  			Name:     "test",
   234  			UUID:     st.ModelUUID(),
   235  			OwnerTag: s.Owner.String(),
   236  			Blocks: []string{
   237  				"BlockDestroy",
   238  				"BlockChange",
   239  			},
   240  		},
   241  	})
   242  
   243  }
   244  
   245  func (s *controllerSuite) TestListBlockedModelsNoBlocks(c *gc.C) {
   246  	list, err := s.controller.ListBlockedModels()
   247  	c.Assert(err, jc.ErrorIsNil)
   248  	c.Assert(list.Models, gc.HasLen, 0)
   249  }
   250  
   251  func (s *controllerSuite) TestModelConfig(c *gc.C) {
   252  	cfg, err := s.controller.ModelConfig()
   253  	c.Assert(err, jc.ErrorIsNil)
   254  	c.Assert(cfg.Config["name"], jc.DeepEquals, params.ConfigValue{Value: "controller"})
   255  }
   256  
   257  func (s *controllerSuite) TestModelConfigFromNonController(c *gc.C) {
   258  	st := s.Factory.MakeModel(c, &factory.ModelParams{
   259  		Name: "test"})
   260  	defer st.Close()
   261  
   262  	authorizer := &apiservertesting.FakeAuthorizer{
   263  		Tag:      s.Owner,
   264  		AdminTag: s.Owner,
   265  	}
   266  	controller, err := controller.NewControllerAPIv4(
   267  		facadetest.Context{
   268  			State_:     st,
   269  			StatePool_: s.StatePool,
   270  			Resources_: common.NewResources(),
   271  			Auth_:      authorizer,
   272  		})
   273  
   274  	c.Assert(err, jc.ErrorIsNil)
   275  	cfg, err := controller.ModelConfig()
   276  	c.Assert(err, jc.ErrorIsNil)
   277  	c.Assert(cfg.Config["name"], jc.DeepEquals, params.ConfigValue{Value: "controller"})
   278  }
   279  
   280  func (s *controllerSuite) TestControllerConfig(c *gc.C) {
   281  	cfg, err := s.controller.ControllerConfig()
   282  	c.Assert(err, jc.ErrorIsNil)
   283  	cfgFromDB, err := s.State.ControllerConfig()
   284  	c.Assert(err, jc.ErrorIsNil)
   285  	c.Assert(cfg.Config["controller-uuid"], gc.Equals, cfgFromDB.ControllerUUID())
   286  	c.Assert(cfg.Config["state-port"], gc.Equals, cfgFromDB.StatePort())
   287  	c.Assert(cfg.Config["api-port"], gc.Equals, cfgFromDB.APIPort())
   288  }
   289  
   290  func (s *controllerSuite) TestControllerConfigFromNonController(c *gc.C) {
   291  	st := s.Factory.MakeModel(c, &factory.ModelParams{
   292  		Name: "test"})
   293  	defer st.Close()
   294  
   295  	authorizer := &apiservertesting.FakeAuthorizer{Tag: s.Owner}
   296  	controller, err := controller.NewControllerAPIv4(
   297  		facadetest.Context{
   298  			State_:     st,
   299  			Resources_: common.NewResources(),
   300  			Auth_:      authorizer,
   301  		})
   302  	c.Assert(err, jc.ErrorIsNil)
   303  	cfg, err := controller.ControllerConfig()
   304  	c.Assert(err, jc.ErrorIsNil)
   305  	cfgFromDB, err := s.State.ControllerConfig()
   306  	c.Assert(err, jc.ErrorIsNil)
   307  	c.Assert(cfg.Config["controller-uuid"], gc.Equals, cfgFromDB.ControllerUUID())
   308  	c.Assert(cfg.Config["state-port"], gc.Equals, cfgFromDB.StatePort())
   309  	c.Assert(cfg.Config["api-port"], gc.Equals, cfgFromDB.APIPort())
   310  }
   311  
   312  func (s *controllerSuite) TestRemoveBlocks(c *gc.C) {
   313  	st := s.Factory.MakeModel(c, &factory.ModelParams{
   314  		Name: "test"})
   315  	defer st.Close()
   316  
   317  	s.State.SwitchBlockOn(state.DestroyBlock, "TestBlockDestroyModel")
   318  	s.State.SwitchBlockOn(state.ChangeBlock, "TestChangeBlock")
   319  	st.SwitchBlockOn(state.DestroyBlock, "TestBlockDestroyModel")
   320  	st.SwitchBlockOn(state.ChangeBlock, "TestChangeBlock")
   321  
   322  	err := s.controller.RemoveBlocks(params.RemoveBlocksArgs{All: true})
   323  	c.Assert(err, jc.ErrorIsNil)
   324  
   325  	blocks, err := s.State.AllBlocksForController()
   326  	c.Assert(err, jc.ErrorIsNil)
   327  	c.Assert(blocks, gc.HasLen, 0)
   328  }
   329  
   330  func (s *controllerSuite) TestRemoveBlocksNotAll(c *gc.C) {
   331  	err := s.controller.RemoveBlocks(params.RemoveBlocksArgs{})
   332  	c.Assert(err, gc.ErrorMatches, "not supported")
   333  }
   334  
   335  func (s *controllerSuite) TestWatchAllModels(c *gc.C) {
   336  	watcherId, err := s.controller.WatchAllModels()
   337  	c.Assert(err, jc.ErrorIsNil)
   338  
   339  	var disposed bool
   340  	watcherAPI_, err := apiserver.NewAllWatcher(facadetest.Context{
   341  		State_:     s.State,
   342  		Resources_: s.resources,
   343  		Auth_:      s.authorizer,
   344  		ID_:        watcherId.AllWatcherId,
   345  		Dispose_:   func() { disposed = true },
   346  	})
   347  	c.Assert(err, jc.ErrorIsNil)
   348  	watcherAPI := watcherAPI_.(*apiserver.SrvAllWatcher)
   349  	defer func() {
   350  		err := watcherAPI.Stop()
   351  		c.Assert(err, jc.ErrorIsNil)
   352  		c.Assert(disposed, jc.IsTrue)
   353  	}()
   354  
   355  	resultC := make(chan params.AllWatcherNextResults)
   356  	go func() {
   357  		result, err := watcherAPI.Next()
   358  		c.Assert(err, jc.ErrorIsNil)
   359  		resultC <- result
   360  	}()
   361  
   362  	select {
   363  	case result := <-resultC:
   364  		// Expect to see the initial environment be reported.
   365  		deltas := result.Deltas
   366  		c.Assert(deltas, gc.HasLen, 1)
   367  		modelInfo := deltas[0].Entity.(*multiwatcher.ModelInfo)
   368  		c.Assert(modelInfo.ModelUUID, gc.Equals, s.State.ModelUUID())
   369  		c.Assert(modelInfo.IsController, gc.Equals, s.State.IsController())
   370  	case <-time.After(testing.LongWait):
   371  		c.Fatal("timed out")
   372  	}
   373  }
   374  
   375  func (s *controllerSuite) TestInitiateMigration(c *gc.C) {
   376  	// Create two hosted models to migrate.
   377  	st1 := s.Factory.MakeModel(c, nil)
   378  	defer st1.Close()
   379  	model1, err := st1.Model()
   380  	c.Assert(err, jc.ErrorIsNil)
   381  
   382  	st2 := s.Factory.MakeModel(c, nil)
   383  	defer st2.Close()
   384  	model2, err := st2.Model()
   385  	c.Assert(err, jc.ErrorIsNil)
   386  
   387  	mac, err := macaroon.New([]byte("secret"), []byte("id"), "location")
   388  	c.Assert(err, jc.ErrorIsNil)
   389  	macsJSON, err := json.Marshal([]macaroon.Slice{{mac}})
   390  	c.Assert(err, jc.ErrorIsNil)
   391  
   392  	controller.SetPrecheckResult(s, nil)
   393  
   394  	// Kick off migrations
   395  	args := params.InitiateMigrationArgs{
   396  		Specs: []params.MigrationSpec{
   397  			{
   398  				ModelTag: model1.ModelTag().String(),
   399  				TargetInfo: params.MigrationTargetInfo{
   400  					ControllerTag: randomControllerTag(),
   401  					Addrs:         []string{"1.1.1.1:1111", "2.2.2.2:2222"},
   402  					CACert:        "cert1",
   403  					AuthTag:       names.NewUserTag("admin1").String(),
   404  					Password:      "secret1",
   405  				},
   406  			}, {
   407  				ModelTag: model2.ModelTag().String(),
   408  				TargetInfo: params.MigrationTargetInfo{
   409  					ControllerTag: randomControllerTag(),
   410  					Addrs:         []string{"3.3.3.3:3333"},
   411  					CACert:        "cert2",
   412  					AuthTag:       names.NewUserTag("admin2").String(),
   413  					Macaroons:     string(macsJSON),
   414  					Password:      "secret2",
   415  				},
   416  			},
   417  		},
   418  	}
   419  	out, err := s.controller.InitiateMigration(args)
   420  	c.Assert(err, jc.ErrorIsNil)
   421  	c.Assert(out.Results, gc.HasLen, 2)
   422  
   423  	states := []*state.State{st1, st2}
   424  	for i, spec := range args.Specs {
   425  		c.Log(i)
   426  		st := states[i]
   427  		result := out.Results[i]
   428  
   429  		c.Assert(result.Error, gc.IsNil)
   430  		c.Check(result.ModelTag, gc.Equals, spec.ModelTag)
   431  		expectedId := st.ModelUUID() + ":0"
   432  		c.Check(result.MigrationId, gc.Equals, expectedId)
   433  
   434  		// Ensure the migration made it into the DB correctly.
   435  		mig, err := st.LatestMigration()
   436  		c.Assert(err, jc.ErrorIsNil)
   437  		c.Check(mig.Id(), gc.Equals, expectedId)
   438  		c.Check(mig.ModelUUID(), gc.Equals, st.ModelUUID())
   439  		c.Check(mig.InitiatedBy(), gc.Equals, s.Owner.Id())
   440  
   441  		targetInfo, err := mig.TargetInfo()
   442  		c.Assert(err, jc.ErrorIsNil)
   443  		c.Check(targetInfo.ControllerTag.String(), gc.Equals, spec.TargetInfo.ControllerTag)
   444  		c.Check(targetInfo.Addrs, jc.SameContents, spec.TargetInfo.Addrs)
   445  		c.Check(targetInfo.CACert, gc.Equals, spec.TargetInfo.CACert)
   446  		c.Check(targetInfo.AuthTag.String(), gc.Equals, spec.TargetInfo.AuthTag)
   447  		c.Check(targetInfo.Password, gc.Equals, spec.TargetInfo.Password)
   448  
   449  		if spec.TargetInfo.Macaroons != "" {
   450  			macJSONdb, err := json.Marshal(targetInfo.Macaroons)
   451  			c.Assert(err, jc.ErrorIsNil)
   452  			c.Check(string(macJSONdb), gc.Equals, spec.TargetInfo.Macaroons)
   453  		}
   454  	}
   455  }
   456  
   457  func (s *controllerSuite) TestInitiateMigrationSpecError(c *gc.C) {
   458  	// Create a hosted model to migrate.
   459  	st := s.Factory.MakeModel(c, nil)
   460  	defer st.Close()
   461  	model, err := st.Model()
   462  	c.Assert(err, jc.ErrorIsNil)
   463  
   464  	// Kick off the migration with missing details.
   465  	args := params.InitiateMigrationArgs{
   466  		Specs: []params.MigrationSpec{{
   467  			ModelTag: model.ModelTag().String(),
   468  			// TargetInfo missing
   469  		}},
   470  	}
   471  	out, err := s.controller.InitiateMigration(args)
   472  	c.Assert(err, jc.ErrorIsNil)
   473  	c.Assert(out.Results, gc.HasLen, 1)
   474  	result := out.Results[0]
   475  	c.Check(result.ModelTag, gc.Equals, args.Specs[0].ModelTag)
   476  	c.Check(result.MigrationId, gc.Equals, "")
   477  	c.Check(result.Error, gc.ErrorMatches, "controller tag: .+ is not a valid tag")
   478  }
   479  
   480  func (s *controllerSuite) TestInitiateMigrationPartialFailure(c *gc.C) {
   481  	st := s.Factory.MakeModel(c, nil)
   482  	defer st.Close()
   483  	controller.SetPrecheckResult(s, nil)
   484  
   485  	m, err := st.Model()
   486  	c.Assert(err, jc.ErrorIsNil)
   487  
   488  	args := params.InitiateMigrationArgs{
   489  		Specs: []params.MigrationSpec{
   490  			{
   491  				ModelTag: m.ModelTag().String(),
   492  				TargetInfo: params.MigrationTargetInfo{
   493  					ControllerTag: randomControllerTag(),
   494  					Addrs:         []string{"1.1.1.1:1111", "2.2.2.2:2222"},
   495  					CACert:        "cert",
   496  					AuthTag:       names.NewUserTag("admin").String(),
   497  					Password:      "secret",
   498  				},
   499  			}, {
   500  				ModelTag: randomModelTag(), // Doesn't exist.
   501  			},
   502  		},
   503  	}
   504  	out, err := s.controller.InitiateMigration(args)
   505  	c.Assert(err, jc.ErrorIsNil)
   506  	c.Assert(out.Results, gc.HasLen, 2)
   507  
   508  	c.Check(out.Results[0].ModelTag, gc.Equals, m.ModelTag().String())
   509  	c.Check(out.Results[0].Error, gc.IsNil)
   510  
   511  	c.Check(out.Results[1].ModelTag, gc.Equals, args.Specs[1].ModelTag)
   512  	c.Check(out.Results[1].Error, gc.ErrorMatches, "model not found")
   513  }
   514  
   515  func (s *controllerSuite) TestInitiateMigrationInvalidMacaroons(c *gc.C) {
   516  	st := s.Factory.MakeModel(c, nil)
   517  	defer st.Close()
   518  
   519  	m, err := st.Model()
   520  	c.Assert(err, jc.ErrorIsNil)
   521  
   522  	args := params.InitiateMigrationArgs{
   523  		Specs: []params.MigrationSpec{
   524  			{
   525  				ModelTag: m.ModelTag().String(),
   526  				TargetInfo: params.MigrationTargetInfo{
   527  					ControllerTag: randomControllerTag(),
   528  					Addrs:         []string{"1.1.1.1:1111", "2.2.2.2:2222"},
   529  					CACert:        "cert",
   530  					AuthTag:       names.NewUserTag("admin").String(),
   531  					Macaroons:     "BLAH",
   532  				},
   533  			},
   534  		},
   535  	}
   536  	out, err := s.controller.InitiateMigration(args)
   537  	c.Assert(err, jc.ErrorIsNil)
   538  	c.Assert(out.Results, gc.HasLen, 1)
   539  	result := out.Results[0]
   540  	c.Check(result.ModelTag, gc.Equals, args.Specs[0].ModelTag)
   541  	c.Check(result.Error, gc.ErrorMatches, "invalid macaroons: .+")
   542  }
   543  
   544  func (s *controllerSuite) TestInitiateMigrationPrecheckFail(c *gc.C) {
   545  	st := s.Factory.MakeModel(c, nil)
   546  	defer st.Close()
   547  
   548  	controller.SetPrecheckResult(s, errors.New("boom"))
   549  
   550  	m, err := st.Model()
   551  	c.Assert(err, jc.ErrorIsNil)
   552  
   553  	args := params.InitiateMigrationArgs{
   554  		Specs: []params.MigrationSpec{{
   555  			ModelTag: m.ModelTag().String(),
   556  			TargetInfo: params.MigrationTargetInfo{
   557  				ControllerTag: randomControllerTag(),
   558  				Addrs:         []string{"1.1.1.1:1111"},
   559  				CACert:        "cert1",
   560  				AuthTag:       names.NewUserTag("admin1").String(),
   561  				Password:      "secret1",
   562  			},
   563  		}},
   564  	}
   565  	out, err := s.controller.InitiateMigration(args)
   566  	c.Assert(out.Results, gc.HasLen, 1)
   567  	c.Check(out.Results[0].Error, gc.ErrorMatches, "boom")
   568  
   569  	active, err := st.IsMigrationActive()
   570  	c.Assert(err, jc.ErrorIsNil)
   571  	c.Check(active, jc.IsFalse)
   572  }
   573  
   574  func randomControllerTag() string {
   575  	uuid := utils.MustNewUUID().String()
   576  	return names.NewControllerTag(uuid).String()
   577  }
   578  
   579  func randomModelTag() string {
   580  	uuid := utils.MustNewUUID().String()
   581  	return names.NewModelTag(uuid).String()
   582  }
   583  
   584  func (s *controllerSuite) modifyControllerAccess(c *gc.C, user names.UserTag, action params.ControllerAction, access string) error {
   585  	args := params.ModifyControllerAccessRequest{
   586  		Changes: []params.ModifyControllerAccess{{
   587  			UserTag: user.String(),
   588  			Action:  action,
   589  			Access:  access,
   590  		}}}
   591  	result, err := s.controller.ModifyControllerAccess(args)
   592  	c.Assert(err, jc.ErrorIsNil)
   593  	return result.OneError()
   594  }
   595  
   596  func (s *controllerSuite) controllerGrant(c *gc.C, user names.UserTag, access string) error {
   597  	return s.modifyControllerAccess(c, user, params.GrantControllerAccess, access)
   598  }
   599  
   600  func (s *controllerSuite) controllerRevoke(c *gc.C, user names.UserTag, access string) error {
   601  	return s.modifyControllerAccess(c, user, params.RevokeControllerAccess, access)
   602  }
   603  
   604  func (s *controllerSuite) TestGrantMissingUserFails(c *gc.C) {
   605  	user := names.NewLocalUserTag("foobar")
   606  	err := s.controllerGrant(c, user, string(permission.SuperuserAccess))
   607  	expectedErr := `could not grant controller access: user "foobar" does not exist locally: user "foobar" not found`
   608  	c.Assert(err, gc.ErrorMatches, expectedErr)
   609  }
   610  
   611  func (s *controllerSuite) TestRevokeSuperuserLeavesLoginAccess(c *gc.C) {
   612  	user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true})
   613  
   614  	err := s.controllerGrant(c, user.UserTag(), string(permission.SuperuserAccess))
   615  	c.Assert(err, gc.IsNil)
   616  	ctag := names.NewControllerTag(s.State.ControllerUUID())
   617  	controllerUser, err := s.State.UserAccess(user.UserTag(), ctag)
   618  	c.Assert(err, jc.ErrorIsNil)
   619  	c.Assert(controllerUser.Access, gc.Equals, permission.SuperuserAccess)
   620  
   621  	err = s.controllerRevoke(c, user.UserTag(), string(permission.SuperuserAccess))
   622  	c.Assert(err, gc.IsNil)
   623  
   624  	controllerUser, err = s.State.UserAccess(user.UserTag(), controllerUser.Object)
   625  	c.Assert(err, jc.ErrorIsNil)
   626  	c.Assert(controllerUser.Access, gc.Equals, permission.LoginAccess)
   627  }
   628  
   629  func (s *controllerSuite) TestRevokeLoginRemovesControllerUser(c *gc.C) {
   630  	user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true})
   631  	err := s.controllerRevoke(c, user.UserTag(), string(permission.LoginAccess))
   632  	c.Assert(err, gc.IsNil)
   633  
   634  	ctag := names.NewControllerTag(s.State.ControllerUUID())
   635  	_, err = s.State.UserAccess(user.UserTag(), ctag)
   636  
   637  	c.Assert(errors.IsNotFound(err), jc.IsTrue)
   638  }
   639  
   640  func (s *controllerSuite) TestRevokeAddModelBackwardCompatibility(c *gc.C) {
   641  	user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true})
   642  
   643  	controllerInfo, err := s.State.ControllerInfo()
   644  	c.Assert(err, jc.ErrorIsNil)
   645  	err = s.State.CreateCloudAccess(controllerInfo.CloudName, user.UserTag(), permission.AddModelAccess)
   646  	c.Assert(err, jc.ErrorIsNil)
   647  
   648  	err = s.controllerRevoke(c, user.UserTag(), string(permission.AddModelAccess))
   649  	c.Assert(err, jc.ErrorIsNil)
   650  
   651  	_, err = s.State.GetCloudAccess(controllerInfo.CloudName, user.UserTag())
   652  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   653  }
   654  
   655  func (s *controllerSuite) TestRevokeControllerMissingUser(c *gc.C) {
   656  	user := names.NewLocalUserTag("foobar")
   657  	err := s.controllerRevoke(c, user, string(permission.SuperuserAccess))
   658  	expectedErr := `could not look up controller access for user: user "foobar" not found`
   659  	c.Assert(err, gc.ErrorMatches, expectedErr)
   660  }
   661  
   662  func (s *controllerSuite) TestGrantOnlyGreaterAccess(c *gc.C) {
   663  	user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true})
   664  
   665  	err := s.controllerGrant(c, user.UserTag(), string(permission.SuperuserAccess))
   666  	c.Assert(err, gc.IsNil)
   667  	ctag := names.NewControllerTag(s.State.ControllerUUID())
   668  	controllerUser, err := s.State.UserAccess(user.UserTag(), ctag)
   669  	c.Assert(err, jc.ErrorIsNil)
   670  	c.Assert(controllerUser.Access, gc.Equals, permission.SuperuserAccess)
   671  
   672  	err = s.controllerGrant(c, user.UserTag(), string(permission.SuperuserAccess))
   673  	expectedErr := `could not grant controller access: user already has "superuser" access or greater`
   674  	c.Assert(err, gc.ErrorMatches, expectedErr)
   675  }
   676  
   677  func (s *controllerSuite) TestGrantControllerAddRemoteUser(c *gc.C) {
   678  	userTag := names.NewUserTag("foobar@ubuntuone")
   679  
   680  	err := s.controllerGrant(c, userTag, string(permission.SuperuserAccess))
   681  	c.Assert(err, jc.ErrorIsNil)
   682  
   683  	ctag := names.NewControllerTag(s.State.ControllerUUID())
   684  	controllerUser, err := s.State.UserAccess(userTag, ctag)
   685  	c.Assert(err, jc.ErrorIsNil)
   686  
   687  	c.Assert(controllerUser.Access, gc.Equals, permission.SuperuserAccess)
   688  }
   689  
   690  func (s *controllerSuite) TestGrantAddModelBackwardCompatibility(c *gc.C) {
   691  	user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true})
   692  
   693  	err := s.controllerGrant(c, user.UserTag(), string(permission.AddModelAccess))
   694  	c.Assert(err, jc.ErrorIsNil)
   695  
   696  	controllerInfo, err := s.State.ControllerInfo()
   697  	c.Assert(err, jc.ErrorIsNil)
   698  	perm, err := s.State.GetCloudAccess(controllerInfo.CloudName, user.UserTag())
   699  	c.Assert(err, jc.ErrorIsNil)
   700  	c.Assert(perm, gc.Equals, permission.AddModelAccess)
   701  }
   702  
   703  func (s *controllerSuite) TestGrantControllerInvalidUserTag(c *gc.C) {
   704  	for _, testParam := range []struct {
   705  		tag      string
   706  		validTag bool
   707  	}{{
   708  		tag:      "unit-foo/0",
   709  		validTag: true,
   710  	}, {
   711  		tag:      "application-foo",
   712  		validTag: true,
   713  	}, {
   714  		tag:      "relation-wordpress:db mysql:db",
   715  		validTag: true,
   716  	}, {
   717  		tag:      "machine-0",
   718  		validTag: true,
   719  	}, {
   720  		tag:      "user@local",
   721  		validTag: false,
   722  	}, {
   723  		tag:      "user-Mua^h^h^h^arh",
   724  		validTag: true,
   725  	}, {
   726  		tag:      "user@",
   727  		validTag: false,
   728  	}, {
   729  		tag:      "user@ubuntuone",
   730  		validTag: false,
   731  	}, {
   732  		tag:      "user@ubuntuone",
   733  		validTag: false,
   734  	}, {
   735  		tag:      "@ubuntuone",
   736  		validTag: false,
   737  	}, {
   738  		tag:      "in^valid.",
   739  		validTag: false,
   740  	}, {
   741  		tag:      "",
   742  		validTag: false,
   743  	},
   744  	} {
   745  		var expectedErr string
   746  		errPart := `could not modify controller access: "` + regexp.QuoteMeta(testParam.tag) + `" is not a valid `
   747  
   748  		if testParam.validTag {
   749  			// The string is a valid tag, but not a user tag.
   750  			expectedErr = errPart + `user tag`
   751  		} else {
   752  			// The string is not a valid tag of any kind.
   753  			expectedErr = errPart + `tag`
   754  		}
   755  
   756  		args := params.ModifyControllerAccessRequest{
   757  			Changes: []params.ModifyControllerAccess{{
   758  				UserTag: testParam.tag,
   759  				Action:  params.GrantControllerAccess,
   760  				Access:  string(permission.SuperuserAccess),
   761  			}}}
   762  
   763  		result, err := s.controller.ModifyControllerAccess(args)
   764  		c.Assert(err, jc.ErrorIsNil)
   765  		c.Assert(result.OneError(), gc.ErrorMatches, expectedErr)
   766  	}
   767  }
   768  
   769  func (s *controllerSuite) TestModifyControllerAccessEmptyArgs(c *gc.C) {
   770  	args := params.ModifyControllerAccessRequest{Changes: []params.ModifyControllerAccess{{}}}
   771  
   772  	result, err := s.controller.ModifyControllerAccess(args)
   773  	c.Assert(err, jc.ErrorIsNil)
   774  	expectedErr := `"" controller access not valid`
   775  	c.Assert(result.OneError(), gc.ErrorMatches, expectedErr)
   776  }
   777  
   778  func (s *controllerSuite) TestModifyControllerAccessInvalidAction(c *gc.C) {
   779  	var dance params.ControllerAction = "dance"
   780  	args := params.ModifyControllerAccessRequest{
   781  		Changes: []params.ModifyControllerAccess{{
   782  			UserTag: "user-user@local",
   783  			Action:  dance,
   784  			Access:  string(permission.LoginAccess),
   785  		}}}
   786  
   787  	result, err := s.controller.ModifyControllerAccess(args)
   788  	c.Assert(err, jc.ErrorIsNil)
   789  	expectedErr := `unknown action "dance"`
   790  	c.Assert(result.OneError(), gc.ErrorMatches, expectedErr)
   791  }
   792  
   793  func (s *controllerSuite) TestGetControllerAccess(c *gc.C) {
   794  	user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true})
   795  	user2 := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true})
   796  
   797  	err := s.controllerGrant(c, user.UserTag(), string(permission.SuperuserAccess))
   798  	c.Assert(err, gc.IsNil)
   799  	req := params.Entities{
   800  		Entities: []params.Entity{{Tag: user.Tag().String()}, {Tag: user2.Tag().String()}},
   801  	}
   802  	results, err := s.controller.GetControllerAccess(req)
   803  	c.Assert(err, jc.ErrorIsNil)
   804  	c.Assert(results.Results, gc.DeepEquals, []params.UserAccessResult{{
   805  		Result: &params.UserAccess{
   806  			Access:  "superuser",
   807  			UserTag: user.Tag().String(),
   808  		}}, {
   809  		Result: &params.UserAccess{
   810  			Access:  "login",
   811  			UserTag: user2.Tag().String(),
   812  		}}})
   813  }
   814  
   815  func (s *controllerSuite) TestGetControllerAccessPermissions(c *gc.C) {
   816  	// Set up the user making the call.
   817  	user := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true})
   818  	anAuthoriser := apiservertesting.FakeAuthorizer{
   819  		Tag: user.Tag(),
   820  	}
   821  	endpoint, err := controller.NewControllerAPIv4(
   822  		facadetest.Context{
   823  			State_:     s.State,
   824  			Resources_: s.resources,
   825  			Auth_:      anAuthoriser,
   826  		})
   827  	c.Assert(err, jc.ErrorIsNil)
   828  	args := params.ModifyControllerAccessRequest{
   829  		Changes: []params.ModifyControllerAccess{{
   830  			UserTag: user.Tag().String(),
   831  			Action:  params.GrantControllerAccess,
   832  			Access:  "superuser",
   833  		}}}
   834  	result, err := s.controller.ModifyControllerAccess(args)
   835  	c.Assert(err, jc.ErrorIsNil)
   836  	c.Assert(result.OneError(), jc.ErrorIsNil)
   837  
   838  	// We ask for permissions for a different user as well as ourselves.
   839  	differentUser := s.Factory.MakeUser(c, &factory.UserParams{NoModelUser: true})
   840  	req := params.Entities{
   841  		Entities: []params.Entity{{Tag: user.Tag().String()}, {Tag: differentUser.Tag().String()}},
   842  	}
   843  	results, err := endpoint.GetControllerAccess(req)
   844  	c.Assert(err, jc.ErrorIsNil)
   845  	c.Assert(results.Results, gc.HasLen, 2)
   846  	c.Assert(*results.Results[0].Result, jc.DeepEquals, params.UserAccess{
   847  		Access:  "superuser",
   848  		UserTag: user.Tag().String(),
   849  	})
   850  	c.Assert(*results.Results[1].Error, gc.DeepEquals, params.Error{
   851  		Message: "permission denied", Code: "unauthorized access",
   852  	})
   853  }
   854  
   855  func (s *controllerSuite) TestModelStatusV3(c *gc.C) {
   856  	api, err := controller.NewControllerAPIv3(
   857  		facadetest.Context{
   858  			State_:     s.State,
   859  			StatePool_: s.StatePool,
   860  			Resources_: s.resources,
   861  			Auth_:      s.authorizer,
   862  		})
   863  	c.Assert(err, jc.ErrorIsNil)
   864  
   865  	// Check that we err out immediately if a model errs.
   866  	results, err := api.ModelStatus(params.Entities{[]params.Entity{{
   867  		Tag: "bad-tag",
   868  	}, {
   869  		Tag: s.Model.ModelTag().String(),
   870  	}}})
   871  	c.Assert(err, gc.ErrorMatches, `"bad-tag" is not a valid tag`)
   872  	c.Assert(results, gc.DeepEquals, params.ModelStatusResults{Results: make([]params.ModelStatus, 2)})
   873  
   874  	// Check that we err out if a model errs even if some firsts in collection pass.
   875  	results, err = api.ModelStatus(params.Entities{[]params.Entity{{
   876  		Tag: s.Model.ModelTag().String(),
   877  	}, {
   878  		Tag: "bad-tag",
   879  	}}})
   880  	c.Assert(err, gc.ErrorMatches, `"bad-tag" is not a valid tag`)
   881  	c.Assert(results, gc.DeepEquals, params.ModelStatusResults{Results: make([]params.ModelStatus, 2)})
   882  
   883  	// Check that we return successfully if no errors.
   884  	results, err = api.ModelStatus(params.Entities{[]params.Entity{{
   885  		Tag: s.Model.ModelTag().String(),
   886  	}}})
   887  	c.Assert(err, jc.ErrorIsNil)
   888  	c.Assert(results.Results, gc.HasLen, 1)
   889  }
   890  
   891  func (s *controllerSuite) TestModelStatus(c *gc.C) {
   892  	// Check that we don't err out immediately if a model errs.
   893  	results, err := s.controller.ModelStatus(params.Entities{[]params.Entity{{
   894  		Tag: "bad-tag",
   895  	}, {
   896  		Tag: s.Model.ModelTag().String(),
   897  	}}})
   898  	c.Assert(err, jc.ErrorIsNil)
   899  	c.Assert(results.Results, gc.HasLen, 2)
   900  	c.Assert(results.Results[0].Error, gc.ErrorMatches, `"bad-tag" is not a valid tag`)
   901  
   902  	// Check that we don't err out if a model errs even if some firsts in collection pass.
   903  	results, err = s.controller.ModelStatus(params.Entities{[]params.Entity{{
   904  		Tag: s.Model.ModelTag().String(),
   905  	}, {
   906  		Tag: "bad-tag",
   907  	}}})
   908  	c.Assert(err, jc.ErrorIsNil)
   909  	c.Assert(results.Results, gc.HasLen, 2)
   910  	c.Assert(results.Results[1].Error, gc.ErrorMatches, `"bad-tag" is not a valid tag`)
   911  
   912  	// Check that we return successfully if no errors.
   913  	results, err = s.controller.ModelStatus(params.Entities{[]params.Entity{{
   914  		Tag: s.Model.ModelTag().String(),
   915  	}}})
   916  	c.Assert(err, jc.ErrorIsNil)
   917  	c.Assert(results.Results, gc.HasLen, 1)
   918  }
   919  
   920  func (s *controllerSuite) TestConfigSet(c *gc.C) {
   921  	config, err := s.State.ControllerConfig()
   922  	c.Assert(err, jc.ErrorIsNil)
   923  	// Sanity check.
   924  	c.Assert(config.AuditingEnabled(), gc.Equals, false)
   925  
   926  	err = s.controller.ConfigSet(params.ControllerConfigSet{Config: map[string]interface{}{
   927  		"auditing-enabled": true,
   928  	}})
   929  	c.Assert(err, jc.ErrorIsNil)
   930  
   931  	config, err = s.State.ControllerConfig()
   932  	c.Assert(err, jc.ErrorIsNil)
   933  	c.Assert(config.AuditingEnabled(), gc.Equals, true)
   934  }
   935  
   936  func (s *controllerSuite) TestConfigSetRequiresSuperUser(c *gc.C) {
   937  	user := s.Factory.MakeUser(c, &factory.UserParams{
   938  		Access: permission.ReadAccess,
   939  	})
   940  	anAuthoriser := apiservertesting.FakeAuthorizer{
   941  		Tag: user.Tag(),
   942  	}
   943  	endpoint, err := controller.NewControllerAPIv5(
   944  		facadetest.Context{
   945  			State_:     s.State,
   946  			Resources_: s.resources,
   947  			Auth_:      anAuthoriser,
   948  		})
   949  	c.Assert(err, jc.ErrorIsNil)
   950  
   951  	err = endpoint.ConfigSet(params.ControllerConfigSet{Config: map[string]interface{}{
   952  		"something": 23,
   953  	}})
   954  
   955  	c.Assert(err, gc.ErrorMatches, "permission denied")
   956  }
   957  
   958  func (s *controllerSuite) TestConfigSetPublishesEvent(c *gc.C) {
   959  	done := make(chan struct{})
   960  	var config corecontroller.Config
   961  	s.hub.Subscribe(pscontroller.ConfigChanged, func(topic string, data pscontroller.ConfigChangedMessage, err error) {
   962  		c.Check(err, jc.ErrorIsNil)
   963  		config = data.Config
   964  		close(done)
   965  	})
   966  
   967  	err := s.controller.ConfigSet(params.ControllerConfigSet{Config: map[string]interface{}{
   968  		"features": []string{"foo", "bar"},
   969  	}})
   970  	c.Assert(err, jc.ErrorIsNil)
   971  
   972  	select {
   973  	case <-done:
   974  	case <-time.After(testing.LongWait):
   975  		c.Fatal("no event sent}")
   976  	}
   977  
   978  	c.Assert(config.Features().SortedValues(), jc.DeepEquals, []string{"bar", "foo"})
   979  }
   980  
   981  func (s *controllerSuite) TestMongoVersion(c *gc.C) {
   982  	result, err := s.controller.MongoVersion()
   983  	c.Assert(err, jc.ErrorIsNil)
   984  
   985  	var resErr *params.Error
   986  	c.Assert(result.Error, gc.Equals, resErr)
   987  	// We can't guarantee which version of mongo is running, so let's just
   988  	// attempt to match it to a very basic version (major.minor.patch)
   989  	c.Assert(result.Result, gc.Matches, "^([0-9]{1,}).([0-9]{1,}).([0-9]{1,})$")
   990  }
   991  
   992  func (s *controllerSuite) TestIdentityProviderURL(c *gc.C) {
   993  	// Preserve default controller config as we will be mutating it just
   994  	// for this test
   995  	defer func(orig map[string]interface{}) {
   996  		s.ControllerConfig = orig
   997  	}(s.ControllerConfig)
   998  
   999  	// Our default test configuration does not specify an IdentityURL
  1000  	urlRes, err := s.controller.IdentityProviderURL()
  1001  	c.Assert(err, jc.ErrorIsNil)
  1002  	c.Assert(urlRes.Result, gc.Equals, "")
  1003  
  1004  	// IdentityURL cannot be changed after bootstrap; we need to spin up
  1005  	// another controller with IdentityURL pre-configured
  1006  	s.TearDownTest(c)
  1007  	expURL := "https://api.jujucharms.com/identity"
  1008  	s.ControllerConfig = map[string]interface{}{
  1009  		corecontroller.IdentityURL: expURL,
  1010  	}
  1011  	s.SetUpTest(c)
  1012  
  1013  	urlRes, err = s.controller.IdentityProviderURL()
  1014  	c.Assert(err, jc.ErrorIsNil)
  1015  	c.Assert(urlRes.Result, gc.Equals, expURL)
  1016  }