github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/worker/migrationmaster/worker_test.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package migrationmaster_test
     5  
     6  import (
     7  	"reflect"
     8  	"time"
     9  
    10  	"github.com/juju/errors"
    11  	jujutesting "github.com/juju/testing"
    12  	jc "github.com/juju/testing/checkers"
    13  	"github.com/juju/utils"
    14  	"github.com/juju/version"
    15  	gc "gopkg.in/check.v1"
    16  	"gopkg.in/juju/names.v2"
    17  	"gopkg.in/macaroon.v1"
    18  
    19  	"github.com/juju/juju/api"
    20  	"github.com/juju/juju/apiserver/params"
    21  	coremigration "github.com/juju/juju/core/migration"
    22  	"github.com/juju/juju/migration"
    23  	coretesting "github.com/juju/juju/testing"
    24  	"github.com/juju/juju/watcher"
    25  	"github.com/juju/juju/worker"
    26  	"github.com/juju/juju/worker/fortress"
    27  	"github.com/juju/juju/worker/migrationmaster"
    28  	"github.com/juju/juju/worker/workertest"
    29  )
    30  
    31  type Suite struct {
    32  	coretesting.BaseSuite
    33  	clock         *jujutesting.Clock
    34  	stub          *jujutesting.Stub
    35  	connection    *stubConnection
    36  	connectionErr error
    37  	facade        *stubMasterFacade
    38  	config        migrationmaster.Config
    39  }
    40  
    41  var _ = gc.Suite(&Suite{})
    42  
    43  var (
    44  	fakeModelBytes      = []byte("model")
    45  	targetControllerTag = names.NewControllerTag("controller-uuid")
    46  	modelUUID           = "model-uuid"
    47  	modelTag            = names.NewModelTag(modelUUID)
    48  	modelName           = "model-name"
    49  	ownerTag            = names.NewUserTag("owner")
    50  	modelVersion        = version.MustParse("1.2.4")
    51  
    52  	// Define stub calls that commonly appear in tests here to allow reuse.
    53  	apiOpenControllerCall = jujutesting.StubCall{
    54  		"apiOpen",
    55  		[]interface{}{
    56  			&api.Info{
    57  				Addrs:    []string{"1.2.3.4:5"},
    58  				CACert:   "cert",
    59  				Tag:      names.NewUserTag("admin"),
    60  				Password: "secret",
    61  			},
    62  			migration.ControllerDialOpts(),
    63  		},
    64  	}
    65  	apiOpenModelCall = jujutesting.StubCall{
    66  		"apiOpen",
    67  		[]interface{}{
    68  			&api.Info{
    69  				Addrs:    []string{"1.2.3.4:5"},
    70  				CACert:   "cert",
    71  				Tag:      names.NewUserTag("admin"),
    72  				Password: "secret",
    73  				ModelTag: modelTag,
    74  			},
    75  			migration.ControllerDialOpts(),
    76  		},
    77  	}
    78  	importCall = jujutesting.StubCall{
    79  		"MigrationTarget.Import",
    80  		[]interface{}{
    81  			params.SerializedModel{Bytes: fakeModelBytes},
    82  		},
    83  	}
    84  	activateCall = jujutesting.StubCall{
    85  		"MigrationTarget.Activate",
    86  		[]interface{}{
    87  			params.ModelArgs{ModelTag: modelTag.String()},
    88  		},
    89  	}
    90  	apiCloseCall = jujutesting.StubCall{"Connection.Close", nil}
    91  	abortCall    = jujutesting.StubCall{
    92  		"MigrationTarget.Abort",
    93  		[]interface{}{
    94  			params.ModelArgs{ModelTag: modelTag.String()},
    95  		},
    96  	}
    97  	watchStatusLockdownCalls = []jujutesting.StubCall{
    98  		{"facade.Watch", nil},
    99  		{"facade.MigrationStatus", nil},
   100  		{"guard.Lockdown", nil},
   101  	}
   102  	prechecksCalls = []jujutesting.StubCall{
   103  		{"facade.Prechecks", nil},
   104  		{"facade.ModelInfo", nil},
   105  		apiOpenControllerCall,
   106  		{"MigrationTarget.Prechecks", []interface{}{params.MigrationModelInfo{
   107  			UUID:         modelUUID,
   108  			Name:         modelName,
   109  			OwnerTag:     ownerTag.String(),
   110  			AgentVersion: modelVersion,
   111  		}}},
   112  		apiCloseCall,
   113  	}
   114  	abortCalls = []jujutesting.StubCall{
   115  		{"facade.SetPhase", []interface{}{coremigration.ABORT}},
   116  		apiOpenControllerCall,
   117  		abortCall,
   118  		apiCloseCall,
   119  		{"facade.SetPhase", []interface{}{coremigration.ABORTDONE}},
   120  	}
   121  )
   122  
   123  func (s *Suite) SetUpTest(c *gc.C) {
   124  	s.BaseSuite.SetUpTest(c)
   125  
   126  	s.clock = jujutesting.NewClock(time.Now())
   127  	s.stub = new(jujutesting.Stub)
   128  	s.connection = &stubConnection{
   129  		stub:          s.stub,
   130  		controllerTag: targetControllerTag,
   131  	}
   132  	s.connectionErr = nil
   133  
   134  	s.facade = newStubMasterFacade(s.stub, s.clock.Now())
   135  
   136  	// The default worker Config used by most of the tests. Tests may
   137  	// tweak parts of this as needed.
   138  	s.config = migrationmaster.Config{
   139  		ModelUUID:       utils.MustNewUUID().String(),
   140  		Facade:          s.facade,
   141  		Guard:           newStubGuard(s.stub),
   142  		APIOpen:         s.apiOpen,
   143  		UploadBinaries:  nullUploadBinaries,
   144  		CharmDownloader: fakeCharmDownloader,
   145  		ToolsDownloader: fakeToolsDownloader,
   146  		Clock:           s.clock,
   147  	}
   148  }
   149  
   150  func (s *Suite) apiOpen(info *api.Info, dialOpts api.DialOpts) (api.Connection, error) {
   151  	s.stub.AddCall("apiOpen", info, dialOpts)
   152  	if s.connectionErr != nil {
   153  		return nil, s.connectionErr
   154  	}
   155  	return s.connection, nil
   156  }
   157  
   158  func (s *Suite) makeStatus(phase coremigration.Phase) coremigration.MigrationStatus {
   159  	return coremigration.MigrationStatus{
   160  		MigrationId:      "model-uuid:2",
   161  		ModelUUID:        "model-uuid",
   162  		Phase:            phase,
   163  		PhaseChangedTime: s.clock.Now(),
   164  		TargetInfo: coremigration.TargetInfo{
   165  			ControllerTag: targetControllerTag,
   166  			Addrs:         []string{"1.2.3.4:5"},
   167  			CACert:        "cert",
   168  			AuthTag:       names.NewUserTag("admin"),
   169  			Password:      "secret",
   170  		},
   171  	}
   172  }
   173  
   174  func (s *Suite) TestSuccessfulMigration(c *gc.C) {
   175  	s.facade.queueStatus(s.makeStatus(coremigration.QUIESCE))
   176  	s.facade.queueMinionReports(makeMinionReports(coremigration.QUIESCE))
   177  	s.facade.queueMinionReports(makeMinionReports(coremigration.VALIDATION))
   178  	s.facade.queueMinionReports(makeMinionReports(coremigration.SUCCESS))
   179  	s.config.UploadBinaries = makeStubUploadBinaries(s.stub)
   180  
   181  	s.checkWorkerReturns(c, migrationmaster.ErrMigrated)
   182  
   183  	// Observe that the migration was seen, the model exported, an API
   184  	// connection to the target controller was made, the model was
   185  	// imported and then the migration completed.
   186  	s.stub.CheckCalls(c, joinCalls(
   187  		// Wait for migration to start.
   188  		watchStatusLockdownCalls,
   189  
   190  		// QUIESCE
   191  		prechecksCalls,
   192  		[]jujutesting.StubCall{
   193  			{"facade.WatchMinionReports", nil},
   194  			{"facade.MinionReports", nil},
   195  			{"facade.SetPhase", []interface{}{coremigration.IMPORT}},
   196  
   197  			//IMPORT
   198  			{"facade.Export", nil},
   199  			apiOpenControllerCall,
   200  			importCall,
   201  			apiOpenModelCall,
   202  			{"UploadBinaries", []interface{}{
   203  				[]string{"charm0", "charm1"},
   204  				fakeCharmDownloader,
   205  				map[version.Binary]string{
   206  					version.MustParseBinary("2.1.0-trusty-amd64"): "/tools/0",
   207  				},
   208  				fakeToolsDownloader,
   209  			}},
   210  			apiCloseCall, // for target model
   211  			apiCloseCall, // for target controller
   212  			{"facade.SetPhase", []interface{}{coremigration.VALIDATION}},
   213  
   214  			// VALIDATION
   215  			{"facade.WatchMinionReports", nil},
   216  			{"facade.MinionReports", nil},
   217  			apiOpenControllerCall,
   218  			activateCall,
   219  			apiCloseCall,
   220  			{"facade.SetPhase", []interface{}{coremigration.SUCCESS}},
   221  
   222  			// SUCCESS
   223  			{"facade.WatchMinionReports", nil},
   224  			{"facade.MinionReports", nil},
   225  			{"facade.SetPhase", []interface{}{coremigration.LOGTRANSFER}},
   226  
   227  			// LOGTRANSFER
   228  			{"facade.SetPhase", []interface{}{coremigration.REAP}},
   229  
   230  			// REAP
   231  			{"facade.Reap", nil},
   232  			{"facade.SetPhase", []interface{}{coremigration.DONE}},
   233  		}),
   234  	)
   235  }
   236  
   237  func (s *Suite) TestMigrationResume(c *gc.C) {
   238  	// Test that a partially complete migration can be resumed.
   239  	s.facade.queueStatus(s.makeStatus(coremigration.SUCCESS))
   240  	s.facade.queueMinionReports(makeMinionReports(coremigration.SUCCESS))
   241  
   242  	s.checkWorkerReturns(c, migrationmaster.ErrMigrated)
   243  	s.stub.CheckCalls(c, joinCalls(
   244  		watchStatusLockdownCalls,
   245  		[]jujutesting.StubCall{
   246  			{"facade.WatchMinionReports", nil},
   247  			{"facade.MinionReports", nil},
   248  			{"facade.SetPhase", []interface{}{coremigration.LOGTRANSFER}},
   249  			{"facade.SetPhase", []interface{}{coremigration.REAP}},
   250  			{"facade.Reap", nil},
   251  			{"facade.SetPhase", []interface{}{coremigration.DONE}},
   252  		},
   253  	))
   254  }
   255  
   256  func (s *Suite) TestPreviouslyAbortedMigration(c *gc.C) {
   257  	s.facade.queueStatus(s.makeStatus(coremigration.ABORTDONE))
   258  
   259  	worker, err := migrationmaster.New(s.config)
   260  	c.Assert(err, jc.ErrorIsNil)
   261  	defer workertest.CleanKill(c, worker)
   262  
   263  	s.waitForStubCalls(c, []string{
   264  		"facade.Watch",
   265  		"facade.MigrationStatus",
   266  		"guard.Unlock",
   267  	})
   268  }
   269  
   270  func (s *Suite) TestPreviouslyCompletedMigration(c *gc.C) {
   271  	s.facade.queueStatus(s.makeStatus(coremigration.DONE))
   272  	s.checkWorkerReturns(c, migrationmaster.ErrMigrated)
   273  	s.stub.CheckCalls(c, []jujutesting.StubCall{
   274  		{"facade.Watch", nil},
   275  		{"facade.MigrationStatus", nil},
   276  	})
   277  }
   278  
   279  func (s *Suite) TestWatchFailure(c *gc.C) {
   280  	s.facade.watchErr = errors.New("boom")
   281  	s.checkWorkerErr(c, "watching for migration: boom")
   282  }
   283  
   284  func (s *Suite) TestStatusError(c *gc.C) {
   285  	s.facade.queueStatus(s.makeStatus(coremigration.QUIESCE))
   286  	s.facade.statusErr = errors.New("splat")
   287  
   288  	s.checkWorkerErr(c, "retrieving migration status: splat")
   289  	s.stub.CheckCalls(c, []jujutesting.StubCall{
   290  		{"facade.Watch", nil},
   291  		{"facade.MigrationStatus", nil},
   292  	})
   293  }
   294  
   295  func (s *Suite) TestStatusNotFound(c *gc.C) {
   296  	s.facade.statusErr = &params.Error{Code: params.CodeNotFound}
   297  	s.facade.triggerWatcher()
   298  
   299  	worker, err := migrationmaster.New(s.config)
   300  	c.Assert(err, jc.ErrorIsNil)
   301  	defer workertest.CleanKill(c, worker)
   302  
   303  	s.waitForStubCalls(c, []string{
   304  		"facade.Watch",
   305  		"facade.MigrationStatus",
   306  		"guard.Unlock",
   307  	})
   308  }
   309  
   310  func (s *Suite) TestUnlockError(c *gc.C) {
   311  	s.facade.statusErr = &params.Error{Code: params.CodeNotFound}
   312  	s.facade.triggerWatcher()
   313  	guard := newStubGuard(s.stub)
   314  	guard.unlockErr = errors.New("pow")
   315  	s.config.Guard = guard
   316  
   317  	s.checkWorkerErr(c, "pow")
   318  	s.stub.CheckCalls(c, []jujutesting.StubCall{
   319  		{"facade.Watch", nil},
   320  		{"facade.MigrationStatus", nil},
   321  		{"guard.Unlock", nil},
   322  	})
   323  }
   324  
   325  func (s *Suite) TestLockdownError(c *gc.C) {
   326  	s.facade.queueStatus(s.makeStatus(coremigration.QUIESCE))
   327  	guard := newStubGuard(s.stub)
   328  	guard.lockdownErr = errors.New("biff")
   329  	s.config.Guard = guard
   330  
   331  	s.checkWorkerErr(c, "biff")
   332  	s.stub.CheckCalls(c, watchStatusLockdownCalls)
   333  }
   334  
   335  func (s *Suite) TestQUIESCEMinionWaitWatchError(c *gc.C) {
   336  	s.checkMinionWaitWatchError(c, coremigration.QUIESCE)
   337  }
   338  
   339  func (s *Suite) TestQUIESCEMinionWaitGetError(c *gc.C) {
   340  	s.checkMinionWaitGetError(c, coremigration.QUIESCE)
   341  }
   342  
   343  func (s *Suite) TestQUIESCEFailedAgent(c *gc.C) {
   344  	s.facade.queueStatus(s.makeStatus(coremigration.QUIESCE))
   345  	s.facade.queueMinionReports(coremigration.MinionReports{
   346  		MigrationId:    "model-uuid:2",
   347  		Phase:          coremigration.QUIESCE,
   348  		FailedMachines: []string{"42"}, // a machine failed
   349  	})
   350  
   351  	s.checkWorkerReturns(c, migrationmaster.ErrInactive)
   352  	s.stub.CheckCalls(c, joinCalls(
   353  		watchStatusLockdownCalls,
   354  		prechecksCalls,
   355  		[]jujutesting.StubCall{
   356  			{"facade.WatchMinionReports", nil},
   357  			{"facade.MinionReports", nil},
   358  		},
   359  		abortCalls,
   360  	))
   361  }
   362  
   363  func (s *Suite) TestQUIESCEWrongController(c *gc.C) {
   364  	s.facade.queueStatus(s.makeStatus(coremigration.QUIESCE))
   365  	s.connection.controllerTag = names.NewControllerTag("another-controller")
   366  
   367  	s.checkWorkerReturns(c, migrationmaster.ErrInactive)
   368  	s.stub.CheckCalls(c, joinCalls(
   369  		watchStatusLockdownCalls,
   370  		[]jujutesting.StubCall{
   371  			{"facade.Prechecks", nil},
   372  			{"facade.ModelInfo", nil},
   373  			apiOpenControllerCall,
   374  			apiCloseCall,
   375  		},
   376  		abortCalls,
   377  	))
   378  }
   379  
   380  func (s *Suite) TestQUIESCESourceChecksFail(c *gc.C) {
   381  	s.facade.queueStatus(s.makeStatus(coremigration.QUIESCE))
   382  	s.facade.prechecksErr = errors.New("boom")
   383  
   384  	s.checkWorkerReturns(c, migrationmaster.ErrInactive)
   385  	s.stub.CheckCalls(c, joinCalls(
   386  		watchStatusLockdownCalls,
   387  		[]jujutesting.StubCall{{"facade.Prechecks", nil}},
   388  		abortCalls,
   389  	))
   390  }
   391  
   392  func (s *Suite) TestQUIESCEModelInfoFail(c *gc.C) {
   393  	s.facade.queueStatus(s.makeStatus(coremigration.QUIESCE))
   394  	s.facade.modelInfoErr = errors.New("boom")
   395  
   396  	s.checkWorkerReturns(c, migrationmaster.ErrInactive)
   397  	s.stub.CheckCalls(c, joinCalls(
   398  		watchStatusLockdownCalls,
   399  		[]jujutesting.StubCall{
   400  			{"facade.Prechecks", nil},
   401  			{"facade.ModelInfo", nil},
   402  		},
   403  		abortCalls,
   404  	))
   405  }
   406  
   407  func (s *Suite) TestQUIESCETargetChecksFail(c *gc.C) {
   408  	s.facade.queueStatus(s.makeStatus(coremigration.QUIESCE))
   409  	s.connection.prechecksErr = errors.New("boom")
   410  
   411  	s.checkWorkerReturns(c, migrationmaster.ErrInactive)
   412  	s.stub.CheckCalls(c, joinCalls(
   413  		watchStatusLockdownCalls,
   414  		prechecksCalls,
   415  		abortCalls,
   416  	))
   417  }
   418  
   419  func (s *Suite) TestExportFailure(c *gc.C) {
   420  	s.facade.queueStatus(s.makeStatus(coremigration.IMPORT))
   421  	s.facade.exportErr = errors.New("boom")
   422  
   423  	s.checkWorkerReturns(c, migrationmaster.ErrInactive)
   424  	s.stub.CheckCalls(c, joinCalls(
   425  		watchStatusLockdownCalls,
   426  		[]jujutesting.StubCall{
   427  			{"facade.Export", nil},
   428  		},
   429  		abortCalls,
   430  	))
   431  }
   432  
   433  func (s *Suite) TestAPIOpenFailure(c *gc.C) {
   434  	s.facade.queueStatus(s.makeStatus(coremigration.IMPORT))
   435  	s.connectionErr = errors.New("boom")
   436  
   437  	s.checkWorkerReturns(c, migrationmaster.ErrInactive)
   438  	s.stub.CheckCalls(c, joinCalls(
   439  		watchStatusLockdownCalls,
   440  		[]jujutesting.StubCall{
   441  			{"facade.Export", nil},
   442  			apiOpenControllerCall,
   443  			{"facade.SetPhase", []interface{}{coremigration.ABORT}},
   444  			apiOpenControllerCall,
   445  			{"facade.SetPhase", []interface{}{coremigration.ABORTDONE}},
   446  		},
   447  	))
   448  }
   449  
   450  func (s *Suite) TestImportFailure(c *gc.C) {
   451  	s.facade.queueStatus(s.makeStatus(coremigration.IMPORT))
   452  	s.connection.importErr = errors.New("boom")
   453  
   454  	s.checkWorkerReturns(c, migrationmaster.ErrInactive)
   455  	s.stub.CheckCalls(c, joinCalls(
   456  		watchStatusLockdownCalls,
   457  		[]jujutesting.StubCall{
   458  			{"facade.Export", nil},
   459  			apiOpenControllerCall,
   460  			importCall,
   461  			apiCloseCall,
   462  		},
   463  		abortCalls,
   464  	))
   465  }
   466  
   467  func (s *Suite) TestVALIDATIONMinionWaitWatchError(c *gc.C) {
   468  	s.checkMinionWaitWatchError(c, coremigration.VALIDATION)
   469  }
   470  
   471  func (s *Suite) TestVALIDATIONMinionWaitGetError(c *gc.C) {
   472  	s.checkMinionWaitGetError(c, coremigration.VALIDATION)
   473  }
   474  
   475  func (s *Suite) TestVALIDATIONFailedAgent(c *gc.C) {
   476  	s.facade.queueStatus(s.makeStatus(coremigration.VALIDATION))
   477  	s.facade.queueMinionReports(coremigration.MinionReports{
   478  		MigrationId:    "model-uuid:2",
   479  		Phase:          coremigration.VALIDATION,
   480  		FailedMachines: []string{"42"}, // a machine failed
   481  	})
   482  
   483  	s.checkWorkerReturns(c, migrationmaster.ErrInactive)
   484  	s.stub.CheckCalls(c, joinCalls(
   485  		watchStatusLockdownCalls,
   486  		[]jujutesting.StubCall{
   487  			{"facade.WatchMinionReports", nil},
   488  			{"facade.MinionReports", nil},
   489  		},
   490  		abortCalls,
   491  	))
   492  }
   493  
   494  func (s *Suite) TestSUCCESSMinionWaitWatchError(c *gc.C) {
   495  	s.checkMinionWaitWatchError(c, coremigration.SUCCESS)
   496  }
   497  
   498  func (s *Suite) TestSUCCESSMinionWaitGetError(c *gc.C) {
   499  	s.checkMinionWaitGetError(c, coremigration.SUCCESS)
   500  }
   501  
   502  func (s *Suite) TestSUCCESSMinionWaitFailedMachine(c *gc.C) {
   503  	// With the SUCCESS phase the master should wait for all reports,
   504  	// continuing even if some minions report failure.
   505  	s.facade.queueStatus(s.makeStatus(coremigration.SUCCESS))
   506  	s.facade.queueMinionReports(coremigration.MinionReports{
   507  		MigrationId:    "model-uuid:2",
   508  		Phase:          coremigration.SUCCESS,
   509  		FailedMachines: []string{"42"},
   510  	})
   511  
   512  	s.checkWorkerReturns(c, migrationmaster.ErrMigrated)
   513  	s.stub.CheckCalls(c, joinCalls(
   514  		watchStatusLockdownCalls,
   515  		[]jujutesting.StubCall{
   516  			{"facade.WatchMinionReports", nil},
   517  			{"facade.MinionReports", nil},
   518  			{"facade.SetPhase", []interface{}{coremigration.LOGTRANSFER}},
   519  			{"facade.SetPhase", []interface{}{coremigration.REAP}},
   520  			{"facade.Reap", nil},
   521  			{"facade.SetPhase", []interface{}{coremigration.DONE}},
   522  		},
   523  	))
   524  }
   525  
   526  func (s *Suite) TestSUCCESSMinionWaitFailedUnit(c *gc.C) {
   527  	// See note for TestMinionWaitFailedMachine above.
   528  	s.facade.queueStatus(s.makeStatus(coremigration.SUCCESS))
   529  	s.facade.queueMinionReports(coremigration.MinionReports{
   530  		MigrationId: "model-uuid:2",
   531  		Phase:       coremigration.SUCCESS,
   532  		FailedUnits: []string{"foo/2"},
   533  	})
   534  
   535  	s.checkWorkerReturns(c, migrationmaster.ErrMigrated)
   536  	s.stub.CheckCalls(c, joinCalls(
   537  		watchStatusLockdownCalls,
   538  		[]jujutesting.StubCall{
   539  			{"facade.WatchMinionReports", nil},
   540  			{"facade.MinionReports", nil},
   541  			{"facade.SetPhase", []interface{}{coremigration.LOGTRANSFER}},
   542  			{"facade.SetPhase", []interface{}{coremigration.REAP}},
   543  			{"facade.Reap", nil},
   544  			{"facade.SetPhase", []interface{}{coremigration.DONE}},
   545  		},
   546  	))
   547  }
   548  
   549  func (s *Suite) TestSUCCESSMinionWaitTimeout(c *gc.C) {
   550  	// The SUCCESS phase is special in that even if some minions fail
   551  	// to report the migration should continue. There's no turning
   552  	// back from SUCCESS.
   553  	s.facade.queueStatus(s.makeStatus(coremigration.SUCCESS))
   554  
   555  	worker, err := migrationmaster.New(s.config)
   556  	c.Assert(err, jc.ErrorIsNil)
   557  	defer workertest.DirtyKill(c, worker)
   558  
   559  	select {
   560  	case <-s.clock.Alarms():
   561  	case <-time.After(coretesting.LongWait):
   562  		c.Fatal("timed out waiting for clock.After call")
   563  	}
   564  
   565  	// Move time ahead in order to trigger timeout.
   566  	s.clock.Advance(15 * time.Minute)
   567  
   568  	err = workertest.CheckKilled(c, worker)
   569  	c.Assert(err, gc.Equals, migrationmaster.ErrMigrated)
   570  
   571  	s.stub.CheckCalls(c, joinCalls(
   572  		watchStatusLockdownCalls,
   573  		[]jujutesting.StubCall{
   574  			{"facade.WatchMinionReports", nil},
   575  			{"facade.SetPhase", []interface{}{coremigration.LOGTRANSFER}},
   576  			{"facade.SetPhase", []interface{}{coremigration.REAP}},
   577  			{"facade.Reap", nil},
   578  			{"facade.SetPhase", []interface{}{coremigration.DONE}},
   579  		},
   580  	))
   581  }
   582  
   583  func (s *Suite) TestMinionWaitWrongPhase(c *gc.C) {
   584  	s.facade.queueStatus(s.makeStatus(coremigration.SUCCESS))
   585  
   586  	// Have the phase in the minion reports be different from the
   587  	// migration status. This shouldn't happen but the migrationmaster
   588  	// should handle it.
   589  	s.facade.queueMinionReports(makeMinionReports(coremigration.IMPORT))
   590  
   591  	s.checkWorkerErr(c,
   592  		`minion reports phase \(IMPORT\) does not match migration phase \(SUCCESS\)`)
   593  }
   594  
   595  func (s *Suite) TestMinionWaitMigrationIdChanged(c *gc.C) {
   596  	s.facade.queueStatus(s.makeStatus(coremigration.SUCCESS))
   597  
   598  	// Have the migration id in the minion reports be different from
   599  	// the migration status. This shouldn't happen but the
   600  	// migrationmaster should handle it.
   601  	s.facade.queueMinionReports(coremigration.MinionReports{
   602  		MigrationId: "blah",
   603  		Phase:       coremigration.SUCCESS,
   604  	})
   605  
   606  	s.checkWorkerErr(c,
   607  		"unexpected migration id in minion reports, got blah, expected model-uuid:2")
   608  }
   609  
   610  func (s *Suite) TestAPIConnectWithMacaroon(c *gc.C) {
   611  	// Use ABORT because it involves an API connection to the target
   612  	// and is convenient.
   613  	status := s.makeStatus(coremigration.ABORT)
   614  
   615  	// Set up macaroon based auth to the target.
   616  	mac, err := macaroon.New([]byte("secret"), "id", "location")
   617  	c.Assert(err, jc.ErrorIsNil)
   618  	macs := []macaroon.Slice{{mac}}
   619  	status.TargetInfo.Password = ""
   620  	status.TargetInfo.Macaroons = macs
   621  
   622  	s.facade.queueStatus(status)
   623  
   624  	s.checkWorkerReturns(c, migrationmaster.ErrInactive)
   625  	s.stub.CheckCalls(c, joinCalls(
   626  		watchStatusLockdownCalls,
   627  		[]jujutesting.StubCall{
   628  			{
   629  				"apiOpen",
   630  				[]interface{}{
   631  					&api.Info{
   632  						Addrs:     []string{"1.2.3.4:5"},
   633  						CACert:    "cert",
   634  						Tag:       names.NewUserTag("admin"),
   635  						Macaroons: macs, // <---
   636  					},
   637  					migration.ControllerDialOpts(),
   638  				},
   639  			},
   640  			abortCall,
   641  			apiCloseCall,
   642  			{"facade.SetPhase", []interface{}{coremigration.ABORTDONE}},
   643  		},
   644  	))
   645  }
   646  
   647  func (s *Suite) TestExternalControl(c *gc.C) {
   648  	status := s.makeStatus(coremigration.QUIESCE)
   649  	status.ExternalControl = true
   650  	s.facade.queueStatus(status)
   651  
   652  	status.Phase = coremigration.DONE
   653  	s.facade.queueStatus(status)
   654  
   655  	s.checkWorkerReturns(c, migrationmaster.ErrMigrated)
   656  	s.stub.CheckCalls(c, joinCalls(
   657  		// Wait for migration to start.
   658  		watchStatusLockdownCalls,
   659  
   660  		// Wait for migration to end.
   661  		[]jujutesting.StubCall{
   662  			{"facade.Watch", nil},
   663  			{"facade.MigrationStatus", nil},
   664  		},
   665  	))
   666  }
   667  
   668  func (s *Suite) TestExternalControlABORT(c *gc.C) {
   669  	status := s.makeStatus(coremigration.QUIESCE)
   670  	status.ExternalControl = true
   671  	s.facade.queueStatus(status)
   672  
   673  	status.Phase = coremigration.ABORTDONE
   674  	s.facade.queueStatus(status)
   675  
   676  	s.checkWorkerReturns(c, migrationmaster.ErrInactive)
   677  	s.stub.CheckCalls(c, joinCalls(
   678  		// Wait for migration to start.
   679  		watchStatusLockdownCalls,
   680  
   681  		// Wait for migration to end.
   682  		[]jujutesting.StubCall{
   683  			{"facade.Watch", nil},
   684  			{"facade.MigrationStatus", nil},
   685  		},
   686  	))
   687  }
   688  
   689  func (s *Suite) checkWorkerReturns(c *gc.C, expected error) {
   690  	err := s.runWorker(c)
   691  	c.Check(errors.Cause(err), gc.Equals, expected)
   692  }
   693  
   694  func (s *Suite) checkWorkerErr(c *gc.C, expected string) {
   695  	err := s.runWorker(c)
   696  	c.Check(err, gc.ErrorMatches, expected)
   697  }
   698  
   699  func (s *Suite) runWorker(c *gc.C) error {
   700  	w, err := migrationmaster.New(s.config)
   701  	c.Assert(err, jc.ErrorIsNil)
   702  	defer workertest.DirtyKill(c, w)
   703  	return workertest.CheckKilled(c, w)
   704  }
   705  
   706  func (s *Suite) waitForStubCalls(c *gc.C, expectedCallNames []string) {
   707  	var callNames []string
   708  	for a := coretesting.LongAttempt.Start(); a.Next(); {
   709  		callNames = stubCallNames(s.stub)
   710  		if reflect.DeepEqual(callNames, expectedCallNames) {
   711  			return
   712  		}
   713  	}
   714  	c.Fatalf("failed to see expected calls\nobtained: %v\nexpected: %v",
   715  		callNames, expectedCallNames)
   716  }
   717  
   718  func (s *Suite) checkMinionWaitWatchError(c *gc.C, phase coremigration.Phase) {
   719  	s.facade.minionReportsWatchErr = errors.New("boom")
   720  	s.facade.queueStatus(s.makeStatus(phase))
   721  
   722  	s.checkWorkerErr(c, "boom")
   723  }
   724  
   725  func (s *Suite) checkMinionWaitGetError(c *gc.C, phase coremigration.Phase) {
   726  	s.facade.queueStatus(s.makeStatus(phase))
   727  
   728  	s.facade.minionReportsErr = errors.New("boom")
   729  	s.facade.triggerMinionReports()
   730  
   731  	s.checkWorkerErr(c, "boom")
   732  }
   733  
   734  func stubCallNames(stub *jujutesting.Stub) []string {
   735  	var out []string
   736  	for _, call := range stub.Calls() {
   737  		out = append(out, call.FuncName)
   738  	}
   739  	return out
   740  }
   741  
   742  func newStubGuard(stub *jujutesting.Stub) *stubGuard {
   743  	return &stubGuard{stub: stub}
   744  }
   745  
   746  type stubGuard struct {
   747  	stub        *jujutesting.Stub
   748  	unlockErr   error
   749  	lockdownErr error
   750  }
   751  
   752  func (g *stubGuard) Lockdown(fortress.Abort) error {
   753  	g.stub.AddCall("guard.Lockdown")
   754  	return g.lockdownErr
   755  }
   756  
   757  func (g *stubGuard) Unlock() error {
   758  	g.stub.AddCall("guard.Unlock")
   759  	return g.unlockErr
   760  }
   761  
   762  func newStubMasterFacade(stub *jujutesting.Stub, now time.Time) *stubMasterFacade {
   763  	return &stubMasterFacade{
   764  		stub:           stub,
   765  		watcherChanges: make(chan struct{}, 999),
   766  
   767  		// Give minionReportsChanges a larger-than-required buffer to
   768  		// support waits at a number of phases.
   769  		minionReportsChanges: make(chan struct{}, 999),
   770  	}
   771  }
   772  
   773  type stubMasterFacade struct {
   774  	migrationmaster.Facade
   775  
   776  	stub *jujutesting.Stub
   777  
   778  	watcherChanges chan struct{}
   779  	watchErr       error
   780  	status         []coremigration.MigrationStatus
   781  	statusErr      error
   782  
   783  	prechecksErr error
   784  	modelInfoErr error
   785  	exportErr    error
   786  
   787  	minionReportsChanges  chan struct{}
   788  	minionReportsWatchErr error
   789  	minionReports         []coremigration.MinionReports
   790  	minionReportsErr      error
   791  }
   792  
   793  func (f *stubMasterFacade) triggerWatcher() {
   794  	select {
   795  	case f.watcherChanges <- struct{}{}:
   796  	default:
   797  		panic("migration watcher channel unexpectedly closed")
   798  	}
   799  }
   800  
   801  func (f *stubMasterFacade) queueStatus(status coremigration.MigrationStatus) {
   802  	f.status = append(f.status, status)
   803  	f.triggerWatcher()
   804  }
   805  
   806  func (f *stubMasterFacade) triggerMinionReports() {
   807  	select {
   808  	case f.minionReportsChanges <- struct{}{}:
   809  	default:
   810  		panic("minion reports watcher channel unexpectedly closed")
   811  	}
   812  }
   813  
   814  func (f *stubMasterFacade) queueMinionReports(r coremigration.MinionReports) {
   815  	f.minionReports = append(f.minionReports, r)
   816  	f.triggerMinionReports()
   817  }
   818  
   819  func (f *stubMasterFacade) Watch() (watcher.NotifyWatcher, error) {
   820  	f.stub.AddCall("facade.Watch")
   821  	if f.watchErr != nil {
   822  		return nil, f.watchErr
   823  	}
   824  	return newMockWatcher(f.watcherChanges), nil
   825  }
   826  
   827  func (f *stubMasterFacade) MigrationStatus() (coremigration.MigrationStatus, error) {
   828  	f.stub.AddCall("facade.MigrationStatus")
   829  	if f.statusErr != nil {
   830  		return coremigration.MigrationStatus{}, f.statusErr
   831  	}
   832  	if len(f.status) == 0 {
   833  		panic("no status queued to report")
   834  	}
   835  	out := f.status[0]
   836  	f.status = f.status[1:]
   837  	return out, nil
   838  }
   839  
   840  func (f *stubMasterFacade) WatchMinionReports() (watcher.NotifyWatcher, error) {
   841  	f.stub.AddCall("facade.WatchMinionReports")
   842  	if f.minionReportsWatchErr != nil {
   843  		return nil, f.minionReportsWatchErr
   844  	}
   845  	return newMockWatcher(f.minionReportsChanges), nil
   846  }
   847  
   848  func (f *stubMasterFacade) MinionReports() (coremigration.MinionReports, error) {
   849  	f.stub.AddCall("facade.MinionReports")
   850  	if f.minionReportsErr != nil {
   851  		return coremigration.MinionReports{}, f.minionReportsErr
   852  	}
   853  	if len(f.minionReports) == 0 {
   854  		return coremigration.MinionReports{}, errors.NotFoundf("reports")
   855  
   856  	}
   857  	r := f.minionReports[0]
   858  	f.minionReports = f.minionReports[1:]
   859  	return r, nil
   860  }
   861  
   862  func (f *stubMasterFacade) Prechecks() error {
   863  	f.stub.AddCall("facade.Prechecks")
   864  	return f.prechecksErr
   865  }
   866  
   867  func (f *stubMasterFacade) ModelInfo() (coremigration.ModelInfo, error) {
   868  	f.stub.AddCall("facade.ModelInfo")
   869  	if f.modelInfoErr != nil {
   870  		return coremigration.ModelInfo{}, f.modelInfoErr
   871  	}
   872  	return coremigration.ModelInfo{
   873  		UUID:         modelUUID,
   874  		Name:         modelName,
   875  		Owner:        ownerTag,
   876  		AgentVersion: modelVersion,
   877  	}, nil
   878  }
   879  
   880  func (f *stubMasterFacade) Export() (coremigration.SerializedModel, error) {
   881  	f.stub.AddCall("facade.Export")
   882  	if f.exportErr != nil {
   883  		return coremigration.SerializedModel{}, f.exportErr
   884  	}
   885  	return coremigration.SerializedModel{
   886  		Bytes:  fakeModelBytes,
   887  		Charms: []string{"charm0", "charm1"},
   888  		Tools: map[version.Binary]string{
   889  			version.MustParseBinary("2.1.0-trusty-amd64"): "/tools/0",
   890  		},
   891  	}, nil
   892  }
   893  
   894  func (f *stubMasterFacade) SetPhase(phase coremigration.Phase) error {
   895  	f.stub.AddCall("facade.SetPhase", phase)
   896  	return nil
   897  }
   898  
   899  func (f *stubMasterFacade) SetStatusMessage(message string) error {
   900  	return nil
   901  }
   902  
   903  func (f *stubMasterFacade) Reap() error {
   904  	f.stub.AddCall("facade.Reap")
   905  	return nil
   906  }
   907  
   908  func newMockWatcher(changes chan struct{}) *mockWatcher {
   909  	return &mockWatcher{
   910  		Worker:  workertest.NewErrorWorker(nil),
   911  		changes: changes,
   912  	}
   913  }
   914  
   915  type mockWatcher struct {
   916  	worker.Worker
   917  	changes chan struct{}
   918  }
   919  
   920  func (w *mockWatcher) Changes() watcher.NotifyChannel {
   921  	return w.changes
   922  }
   923  
   924  type stubConnection struct {
   925  	api.Connection
   926  	stub          *jujutesting.Stub
   927  	prechecksErr  error
   928  	importErr     error
   929  	controllerTag names.ControllerTag
   930  }
   931  
   932  func (c *stubConnection) BestFacadeVersion(string) int {
   933  	return 1
   934  }
   935  
   936  func (c *stubConnection) APICall(objType string, version int, id, request string, params, response interface{}) error {
   937  	c.stub.AddCall(objType+"."+request, params)
   938  
   939  	if objType == "MigrationTarget" {
   940  		switch request {
   941  		case "Prechecks":
   942  			return c.prechecksErr
   943  		case "Import":
   944  			return c.importErr
   945  		case "Activate":
   946  			return nil
   947  		}
   948  	}
   949  	return errors.New("unexpected API call")
   950  }
   951  
   952  func (c *stubConnection) Client() *api.Client {
   953  	// This is kinda crappy but the *Client doesn't have to be
   954  	// functional...
   955  	return new(api.Client)
   956  }
   957  
   958  func (c *stubConnection) Close() error {
   959  	c.stub.AddCall("Connection.Close")
   960  	return nil
   961  }
   962  
   963  func (c *stubConnection) ControllerTag() names.ControllerTag {
   964  	return c.controllerTag
   965  }
   966  
   967  func makeStubUploadBinaries(stub *jujutesting.Stub) func(migration.UploadBinariesConfig) error {
   968  	return func(config migration.UploadBinariesConfig) error {
   969  		stub.AddCall(
   970  			"UploadBinaries",
   971  			config.Charms,
   972  			config.CharmDownloader,
   973  			config.Tools,
   974  			config.ToolsDownloader,
   975  		)
   976  		return nil
   977  	}
   978  }
   979  
   980  // nullUploadBinaries is a UploadBinaries variant which is intended to
   981  // not get called.
   982  func nullUploadBinaries(migration.UploadBinariesConfig) error {
   983  	panic("should not get called")
   984  }
   985  
   986  var fakeCharmDownloader = struct{ migration.CharmDownloader }{}
   987  
   988  var fakeToolsDownloader = struct{ migration.ToolsDownloader }{}
   989  
   990  func joinCalls(allCalls ...[]jujutesting.StubCall) (out []jujutesting.StubCall) {
   991  	for _, calls := range allCalls {
   992  		out = append(out, calls...)
   993  	}
   994  	return
   995  }
   996  
   997  func makeMinionReports(p coremigration.Phase) coremigration.MinionReports {
   998  	return coremigration.MinionReports{
   999  		MigrationId:  "model-uuid:2",
  1000  		Phase:        p,
  1001  		SuccessCount: 5,
  1002  		UnknownCount: 0,
  1003  	}
  1004  }