github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/controller/migrationtarget/migrationtarget_test.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package migrationtarget_test
     5  
     6  import (
     7  	"io/ioutil"
     8  	"time"
     9  
    10  	"github.com/juju/description"
    11  	"github.com/juju/errors"
    12  	"github.com/juju/loggo"
    13  	"github.com/juju/testing"
    14  	jc "github.com/juju/testing/checkers"
    15  	"github.com/juju/utils"
    16  	"github.com/juju/version"
    17  	gc "gopkg.in/check.v1"
    18  	"gopkg.in/juju/names.v2"
    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/controller/migrationtarget"
    24  	"github.com/juju/juju/apiserver/params"
    25  	apiservertesting "github.com/juju/juju/apiserver/testing"
    26  	"github.com/juju/juju/caas"
    27  	"github.com/juju/juju/core/instance"
    28  	"github.com/juju/juju/core/leadership"
    29  	"github.com/juju/juju/core/lease"
    30  	"github.com/juju/juju/environs"
    31  	"github.com/juju/juju/environs/context"
    32  	"github.com/juju/juju/environs/instances"
    33  	"github.com/juju/juju/provider/dummy"
    34  	"github.com/juju/juju/state"
    35  	"github.com/juju/juju/state/stateenvirons"
    36  	statetesting "github.com/juju/juju/state/testing"
    37  	jujutesting "github.com/juju/juju/testing"
    38  	"github.com/juju/juju/testing/factory"
    39  )
    40  
    41  type Suite struct {
    42  	statetesting.StateSuite
    43  	resources  *common.Resources
    44  	authorizer *apiservertesting.FakeAuthorizer
    45  
    46  	facadeContext facadetest.Context
    47  	callContext   context.ProviderCallContext
    48  }
    49  
    50  var _ = gc.Suite(&Suite{})
    51  
    52  func (s *Suite) SetUpTest(c *gc.C) {
    53  	// Set up InitialConfig with a dummy provider configuration. This
    54  	// is required to allow model import test to work.
    55  	s.InitialConfig = jujutesting.CustomModelConfig(c, dummy.SampleConfig())
    56  
    57  	// The call up to StateSuite's SetUpTest uses s.InitialConfig so
    58  	// it has to happen here.
    59  	s.StateSuite.SetUpTest(c)
    60  
    61  	s.resources = common.NewResources()
    62  	s.AddCleanup(func(*gc.C) { s.resources.StopAll() })
    63  
    64  	s.authorizer = &apiservertesting.FakeAuthorizer{
    65  		Tag:      s.Owner,
    66  		AdminTag: s.Owner,
    67  	}
    68  	s.callContext = context.NewCloudCallContext()
    69  	s.facadeContext = facadetest.Context{
    70  		State_:     s.State,
    71  		StatePool_: s.StatePool,
    72  		Resources_: s.resources,
    73  		Auth_:      s.authorizer,
    74  	}
    75  }
    76  
    77  func (s *Suite) TestFacadeRegistered(c *gc.C) {
    78  	factory, err := apiserver.AllFacades().GetFactory("MigrationTarget", 1)
    79  	c.Assert(err, jc.ErrorIsNil)
    80  
    81  	api, err := factory(&facadetest.Context{
    82  		State_:     s.State,
    83  		Resources_: s.resources,
    84  		Auth_:      s.authorizer,
    85  	})
    86  	c.Assert(err, jc.ErrorIsNil)
    87  	c.Assert(api, gc.FitsTypeOf, new(migrationtarget.API))
    88  }
    89  
    90  func (s *Suite) TestNotUser(c *gc.C) {
    91  	s.authorizer.Tag = names.NewMachineTag("0")
    92  	_, err := s.newAPI(nil, nil)
    93  	c.Assert(errors.Cause(err), gc.Equals, common.ErrPerm)
    94  }
    95  
    96  func (s *Suite) TestNotControllerAdmin(c *gc.C) {
    97  	s.authorizer.Tag = names.NewUserTag("jrandomuser")
    98  	_, err := s.newAPI(nil, nil)
    99  	c.Assert(errors.Cause(err), gc.Equals, common.ErrPerm)
   100  }
   101  
   102  func (s *Suite) importModel(c *gc.C, api *migrationtarget.API) names.ModelTag {
   103  	uuid, bytes := s.makeExportedModel(c)
   104  	err := api.Import(params.SerializedModel{Bytes: bytes})
   105  	c.Assert(err, jc.ErrorIsNil)
   106  	return names.NewModelTag(uuid)
   107  }
   108  
   109  func (s *Suite) TestPrechecks(c *gc.C) {
   110  	api := s.mustNewAPI(c)
   111  	args := params.MigrationModelInfo{
   112  		UUID:                   "uuid",
   113  		Name:                   "some-model",
   114  		OwnerTag:               names.NewUserTag("someone").String(),
   115  		AgentVersion:           s.controllerVersion(c),
   116  		ControllerAgentVersion: s.controllerVersion(c),
   117  	}
   118  	err := api.Prechecks(args)
   119  	c.Assert(err, jc.ErrorIsNil)
   120  }
   121  
   122  func (s *Suite) TestCACert(c *gc.C) {
   123  	api := s.mustNewAPI(c)
   124  	r, err := api.CACert()
   125  	c.Assert(err, jc.ErrorIsNil)
   126  	c.Assert(string(r.Result), gc.Equals, jujutesting.CACert)
   127  }
   128  
   129  func (s *Suite) TestPrechecksFail(c *gc.C) {
   130  	controllerVersion := s.controllerVersion(c)
   131  
   132  	// Set the model version ahead of the controller.
   133  	modelVersion := controllerVersion
   134  	modelVersion.Minor++
   135  
   136  	api := s.mustNewAPI(c)
   137  	args := params.MigrationModelInfo{
   138  		AgentVersion: modelVersion,
   139  	}
   140  	err := api.Prechecks(args)
   141  	c.Assert(err, gc.NotNil)
   142  }
   143  
   144  func (s *Suite) TestImport(c *gc.C) {
   145  	api := s.mustNewAPI(c)
   146  	tag := s.importModel(c, api)
   147  	// Check the model was imported.
   148  	model, ph, err := s.StatePool.GetModel(tag.Id())
   149  	c.Assert(err, jc.ErrorIsNil)
   150  	defer ph.Release()
   151  	c.Assert(model.Name(), gc.Equals, "some-model")
   152  	c.Assert(model.MigrationMode(), gc.Equals, state.MigrationModeImporting)
   153  }
   154  
   155  func (s *Suite) TestImportLeadership(c *gc.C) {
   156  	application := s.Factory.MakeApplication(c, &factory.ApplicationParams{
   157  		Charm: s.Factory.MakeCharm(c, &factory.CharmParams{
   158  			Name: "wordpress",
   159  		}),
   160  	})
   161  	for i := 0; i < 3; i++ {
   162  		s.Factory.MakeUnit(c, &factory.UnitParams{Application: application})
   163  	}
   164  	target := s.State.LeaseNotifyTarget(
   165  		ioutil.Discard,
   166  		loggo.GetLogger("migrationtarget_test"),
   167  	)
   168  	target.Claimed(
   169  		lease.Key{"application-leadership", s.State.ModelUUID(), "wordpress"},
   170  		"wordpress/2",
   171  	)
   172  
   173  	var claimer fakeClaimer
   174  	s.facadeContext.LeadershipClaimer_ = &claimer
   175  	api := s.mustNewAPI(c)
   176  	s.importModel(c, api)
   177  
   178  	c.Assert(claimer.stub.Calls(), gc.HasLen, 1)
   179  	claimer.stub.CheckCall(c, 0, "ClaimLeadership", "wordpress", "wordpress/2", time.Minute)
   180  }
   181  
   182  func (s *Suite) TestAbort(c *gc.C) {
   183  	api := s.mustNewAPI(c)
   184  	tag := s.importModel(c, api)
   185  
   186  	err := api.Abort(params.ModelArgs{ModelTag: tag.String()})
   187  	c.Assert(err, jc.ErrorIsNil)
   188  
   189  	// The model should no longer exist.
   190  	exists, err := s.State.ModelExists(tag.Id())
   191  	c.Assert(err, jc.ErrorIsNil)
   192  	c.Check(exists, jc.IsFalse)
   193  }
   194  
   195  func (s *Suite) TestAbortNotATag(c *gc.C) {
   196  	api := s.mustNewAPI(c)
   197  	err := api.Abort(params.ModelArgs{ModelTag: "not-a-tag"})
   198  	c.Assert(err, gc.ErrorMatches, `"not-a-tag" is not a valid tag`)
   199  }
   200  
   201  func (s *Suite) TestAbortMissingModel(c *gc.C) {
   202  	api := s.mustNewAPI(c)
   203  	newUUID := utils.MustNewUUID().String()
   204  	err := api.Abort(params.ModelArgs{ModelTag: names.NewModelTag(newUUID).String()})
   205  	c.Assert(err, gc.ErrorMatches, `model "`+newUUID+`" not found`)
   206  }
   207  
   208  func (s *Suite) TestAbortNotImportingModel(c *gc.C) {
   209  	st := s.Factory.MakeModel(c, nil)
   210  	defer st.Close()
   211  	model, err := st.Model()
   212  	c.Assert(err, jc.ErrorIsNil)
   213  
   214  	api := s.mustNewAPI(c)
   215  	err = api.Abort(params.ModelArgs{ModelTag: model.ModelTag().String()})
   216  	c.Assert(err, gc.ErrorMatches, `migration mode for the model is not importing`)
   217  }
   218  
   219  func (s *Suite) TestActivate(c *gc.C) {
   220  	api := s.mustNewAPI(c)
   221  	tag := s.importModel(c, api)
   222  
   223  	err := api.Activate(params.ModelArgs{ModelTag: tag.String()})
   224  	c.Assert(err, jc.ErrorIsNil)
   225  
   226  	model, ph, err := s.StatePool.GetModel(tag.Id())
   227  	c.Assert(err, jc.ErrorIsNil)
   228  	defer ph.Release()
   229  	c.Assert(model.MigrationMode(), gc.Equals, state.MigrationModeNone)
   230  }
   231  
   232  func (s *Suite) TestActivateNotATag(c *gc.C) {
   233  	api := s.mustNewAPI(c)
   234  	err := api.Activate(params.ModelArgs{ModelTag: "not-a-tag"})
   235  	c.Assert(err, gc.ErrorMatches, `"not-a-tag" is not a valid tag`)
   236  }
   237  
   238  func (s *Suite) TestActivateMissingModel(c *gc.C) {
   239  	api := s.mustNewAPI(c)
   240  	newUUID := utils.MustNewUUID().String()
   241  	err := api.Activate(params.ModelArgs{ModelTag: names.NewModelTag(newUUID).String()})
   242  	c.Assert(err, gc.ErrorMatches, `model "`+newUUID+`" not found`)
   243  }
   244  
   245  func (s *Suite) TestActivateNotImportingModel(c *gc.C) {
   246  	st := s.Factory.MakeModel(c, nil)
   247  	defer st.Close()
   248  	model, err := st.Model()
   249  	c.Assert(err, jc.ErrorIsNil)
   250  
   251  	api := s.mustNewAPI(c)
   252  	err = api.Activate(params.ModelArgs{ModelTag: model.ModelTag().String()})
   253  	c.Assert(err, gc.ErrorMatches, `migration mode for the model is not importing`)
   254  }
   255  
   256  func (s *Suite) TestLatestLogTime(c *gc.C) {
   257  	st := s.Factory.MakeModel(c, nil)
   258  	defer st.Close()
   259  	model, err := st.Model()
   260  	c.Assert(err, jc.ErrorIsNil)
   261  
   262  	t := time.Date(2016, 11, 30, 18, 14, 0, 100, time.UTC)
   263  	tracker := state.NewLastSentLogTracker(st, model.UUID(), "migration-logtransfer")
   264  	defer tracker.Close()
   265  	err = tracker.Set(0, t.UnixNano())
   266  	c.Assert(err, jc.ErrorIsNil)
   267  
   268  	api := s.mustNewAPI(c)
   269  	latest, err := api.LatestLogTime(params.ModelArgs{ModelTag: model.ModelTag().String()})
   270  	c.Assert(err, jc.ErrorIsNil)
   271  	c.Assert(latest, gc.Equals, t)
   272  }
   273  
   274  func (s *Suite) TestLatestLogTimeNeverSet(c *gc.C) {
   275  	st := s.Factory.MakeModel(c, nil)
   276  	defer st.Close()
   277  	model, err := st.Model()
   278  	c.Assert(err, jc.ErrorIsNil)
   279  
   280  	api := s.mustNewAPI(c)
   281  	latest, err := api.LatestLogTime(params.ModelArgs{ModelTag: model.ModelTag().String()})
   282  	c.Assert(err, jc.ErrorIsNil)
   283  	c.Assert(latest, gc.Equals, time.Time{})
   284  }
   285  
   286  func (s *Suite) TestAdoptIAASResources(c *gc.C) {
   287  	st := s.Factory.MakeModel(c, nil)
   288  	defer st.Close()
   289  
   290  	env := mockEnv{Stub: &testing.Stub{}}
   291  	api, err := s.newAPI(func(modelSt *state.State) (environs.Environ, error) {
   292  		c.Assert(modelSt.ModelUUID(), gc.Equals, st.ModelUUID())
   293  		return &env, nil
   294  	}, func(modelSt *state.State) (caas.Broker, error) {
   295  		return nil, errors.New("should not be called")
   296  	})
   297  	c.Assert(err, jc.ErrorIsNil)
   298  
   299  	m, err := st.Model()
   300  	c.Assert(err, jc.ErrorIsNil)
   301  
   302  	err = api.AdoptResources(params.AdoptResourcesArgs{
   303  		ModelTag:                m.ModelTag().String(),
   304  		SourceControllerVersion: version.MustParse("3.2.1"),
   305  	})
   306  	c.Assert(err, jc.ErrorIsNil)
   307  
   308  	c.Assert(env.Stub.Calls(), gc.HasLen, 1)
   309  	env.Stub.CheckCall(c, 0, "AdoptResources", s.callContext, st.ControllerUUID(), version.MustParse("3.2.1"))
   310  }
   311  
   312  func (s *Suite) TestAdoptCAASResources(c *gc.C) {
   313  	st := s.Factory.MakeCAASModel(c, nil)
   314  	defer st.Close()
   315  
   316  	broker := mockBroker{Stub: &testing.Stub{}}
   317  	api, err := s.newAPI(func(modelSt *state.State) (environs.Environ, error) {
   318  		return nil, errors.New("should not be called")
   319  	}, func(modelSt *state.State) (caas.Broker, error) {
   320  		c.Assert(modelSt.ModelUUID(), gc.Equals, st.ModelUUID())
   321  		return &broker, nil
   322  	})
   323  	c.Assert(err, jc.ErrorIsNil)
   324  
   325  	m, err := st.Model()
   326  	c.Assert(err, jc.ErrorIsNil)
   327  
   328  	err = api.AdoptResources(params.AdoptResourcesArgs{
   329  		ModelTag:                m.ModelTag().String(),
   330  		SourceControllerVersion: version.MustParse("3.2.1"),
   331  	})
   332  	c.Assert(err, jc.ErrorIsNil)
   333  
   334  	c.Assert(broker.Stub.Calls(), gc.HasLen, 1)
   335  	broker.Stub.CheckCall(c, 0, "AdoptResources", s.callContext, st.ControllerUUID(), version.MustParse("3.2.1"))
   336  }
   337  
   338  func (s *Suite) TestCheckMachinesSuccess(c *gc.C) {
   339  	st := s.Factory.MakeModel(c, nil)
   340  	defer st.Close()
   341  
   342  	fact := factory.NewFactory(st, s.StatePool)
   343  	fact.MakeMachine(c, &factory.MachineParams{
   344  		InstanceId: "eriatarka",
   345  	})
   346  	m := fact.MakeMachine(c, &factory.MachineParams{
   347  		InstanceId: "volta",
   348  	})
   349  	c.Assert(m.Id(), gc.Equals, "1")
   350  
   351  	mockEnv := mockEnv{
   352  		Stub: &testing.Stub{},
   353  		instances: []*mockInstance{
   354  			{id: "volta"},
   355  			{id: "eriatarka"},
   356  		},
   357  	}
   358  	api := s.mustNewAPIWithModel(c, &mockEnv, &mockBroker{})
   359  	model, err := st.Model()
   360  	c.Assert(err, jc.ErrorIsNil)
   361  	results, err := api.CheckMachines(
   362  		params.ModelArgs{ModelTag: model.ModelTag().String()})
   363  	c.Assert(err, jc.ErrorIsNil)
   364  	c.Assert(results, gc.DeepEquals, params.ErrorResults{})
   365  }
   366  
   367  func (s *Suite) TestCheckMachinesHandlesContainers(c *gc.C) {
   368  	st := s.Factory.MakeModel(c, nil)
   369  	defer st.Close()
   370  
   371  	fact := factory.NewFactory(st, s.StatePool)
   372  	m := fact.MakeMachine(c, &factory.MachineParams{
   373  		InstanceId: "birds",
   374  	})
   375  	fact.MakeMachineNested(c, m.Id(), nil)
   376  
   377  	mockEnv := mockEnv{
   378  		Stub:      &testing.Stub{},
   379  		instances: []*mockInstance{{id: "birds"}},
   380  	}
   381  	api := s.mustNewAPIWithModel(c, &mockEnv, &mockBroker{})
   382  	model, err := st.Model()
   383  	c.Assert(err, jc.ErrorIsNil)
   384  	results, err := api.CheckMachines(
   385  		params.ModelArgs{ModelTag: model.ModelTag().String()})
   386  	c.Assert(err, jc.ErrorIsNil)
   387  	c.Assert(results, gc.DeepEquals, params.ErrorResults{})
   388  }
   389  
   390  func (s *Suite) TestCheckMachinesHandlesManual(c *gc.C) {
   391  	st := s.Factory.MakeModel(c, nil)
   392  	defer st.Close()
   393  
   394  	fact := factory.NewFactory(st, s.StatePool)
   395  	fact.MakeMachine(c, &factory.MachineParams{
   396  		InstanceId: "birds",
   397  	})
   398  	fact.MakeMachine(c, &factory.MachineParams{
   399  		Nonce: "manual:flibbertigibbert",
   400  	})
   401  
   402  	mockEnv := mockEnv{
   403  		Stub:      &testing.Stub{},
   404  		instances: []*mockInstance{{id: "birds"}},
   405  	}
   406  	api := s.mustNewAPIWithModel(c, &mockEnv, &mockBroker{})
   407  
   408  	model, err := st.Model()
   409  	c.Assert(err, jc.ErrorIsNil)
   410  	results, err := api.CheckMachines(
   411  		params.ModelArgs{ModelTag: model.ModelTag().String()})
   412  	c.Assert(err, jc.ErrorIsNil)
   413  	c.Assert(results, gc.DeepEquals, params.ErrorResults{})
   414  }
   415  
   416  func (s *Suite) newAPI(environFunc stateenvirons.NewEnvironFunc, brokerFunc stateenvirons.NewCAASBrokerFunc) (*migrationtarget.API, error) {
   417  	api, err := migrationtarget.NewAPI(&s.facadeContext, environFunc, brokerFunc, s.callContext)
   418  	return api, err
   419  }
   420  
   421  func (s *Suite) mustNewAPI(c *gc.C) *migrationtarget.API {
   422  	api, err := s.newAPI(nil, nil)
   423  	c.Assert(err, jc.ErrorIsNil)
   424  	return api
   425  }
   426  
   427  func (s *Suite) mustNewAPIWithModel(c *gc.C, env environs.Environ, broker caas.Broker) *migrationtarget.API {
   428  	api, err := s.newAPI(func(*state.State) (environs.Environ, error) {
   429  		return env, nil
   430  	}, func(*state.State) (caas.Broker, error) {
   431  		return broker, nil
   432  	})
   433  	c.Assert(err, jc.ErrorIsNil)
   434  	return api
   435  }
   436  
   437  func (s *Suite) makeExportedModel(c *gc.C) (string, []byte) {
   438  	model, err := s.State.Export()
   439  	c.Assert(err, jc.ErrorIsNil)
   440  
   441  	newUUID := utils.MustNewUUID().String()
   442  	model.UpdateConfig(map[string]interface{}{
   443  		"name": "some-model",
   444  		"uuid": newUUID,
   445  	})
   446  
   447  	bytes, err := description.Serialize(model)
   448  	c.Assert(err, jc.ErrorIsNil)
   449  	return newUUID, bytes
   450  }
   451  
   452  func (s *Suite) controllerVersion(c *gc.C) version.Number {
   453  	cfg, err := s.Model.ModelConfig()
   454  	c.Assert(err, jc.ErrorIsNil)
   455  	vers, ok := cfg.AgentVersion()
   456  	c.Assert(ok, jc.IsTrue)
   457  	return vers
   458  }
   459  
   460  type mockEnv struct {
   461  	environs.Environ
   462  	*testing.Stub
   463  
   464  	instances []*mockInstance
   465  }
   466  
   467  func (e *mockEnv) AdoptResources(ctx context.ProviderCallContext, controllerUUID string, sourceVersion version.Number) error {
   468  	e.MethodCall(e, "AdoptResources", ctx, controllerUUID, sourceVersion)
   469  	return e.NextErr()
   470  }
   471  
   472  func (e *mockEnv) AllInstances(ctx context.ProviderCallContext) ([]instances.Instance, error) {
   473  	e.MethodCall(e, "AllInstances", ctx)
   474  	results := make([]instances.Instance, len(e.instances))
   475  	for i, instance := range e.instances {
   476  		results[i] = instance
   477  	}
   478  	return results, e.NextErr()
   479  }
   480  
   481  type mockBroker struct {
   482  	caas.Broker
   483  	*testing.Stub
   484  }
   485  
   486  func (e *mockBroker) AdoptResources(ctx context.ProviderCallContext, controllerUUID string, sourceVersion version.Number) error {
   487  	e.MethodCall(e, "AdoptResources", ctx, controllerUUID, sourceVersion)
   488  	return e.NextErr()
   489  }
   490  
   491  type mockInstance struct {
   492  	instances.Instance
   493  	id string
   494  }
   495  
   496  func (i *mockInstance) Id() instance.Id {
   497  	return instance.Id(i.id)
   498  }
   499  
   500  type fakeClaimer struct {
   501  	leadership.Claimer
   502  	stub testing.Stub
   503  }
   504  
   505  func (c *fakeClaimer) ClaimLeadership(application, unit string, duration time.Duration) error {
   506  	c.stub.AddCall("ClaimLeadership", application, unit, duration)
   507  	return c.stub.NextErr()
   508  }