github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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  	"net/http"
     8  	"net/textproto"
     9  	"net/url"
    10  	"reflect"
    11  	"time"
    12  
    13  	"github.com/juju/clock/testclock"
    14  	"github.com/juju/errors"
    15  	"github.com/juju/loggo"
    16  	jujutesting "github.com/juju/testing"
    17  	jc "github.com/juju/testing/checkers"
    18  	"github.com/juju/utils"
    19  	"github.com/juju/version"
    20  	gc "gopkg.in/check.v1"
    21  	"gopkg.in/juju/names.v2"
    22  	"gopkg.in/juju/worker.v1"
    23  	"gopkg.in/juju/worker.v1/workertest"
    24  	"gopkg.in/macaroon.v2-unstable"
    25  
    26  	"github.com/juju/juju/api"
    27  	"github.com/juju/juju/api/base"
    28  	"github.com/juju/juju/api/common"
    29  	servercommon "github.com/juju/juju/apiserver/common"
    30  	"github.com/juju/juju/apiserver/params"
    31  	coremigration "github.com/juju/juju/core/migration"
    32  	"github.com/juju/juju/core/watcher"
    33  	"github.com/juju/juju/migration"
    34  	"github.com/juju/juju/resource/resourcetesting"
    35  	coretesting "github.com/juju/juju/testing"
    36  	jujuversion "github.com/juju/juju/version"
    37  	"github.com/juju/juju/worker/fortress"
    38  	"github.com/juju/juju/worker/migrationmaster"
    39  )
    40  
    41  type Suite struct {
    42  	coretesting.BaseSuite
    43  	clock         *testclock.Clock
    44  	stub          *jujutesting.Stub
    45  	connection    *stubConnection
    46  	connectionErr error
    47  	facade        *stubMasterFacade
    48  	config        migrationmaster.Config
    49  }
    50  
    51  var _ = gc.Suite(&Suite{})
    52  
    53  var (
    54  	fakeModelBytes      = []byte("model")
    55  	targetControllerTag = names.NewControllerTag("controller-uuid")
    56  	modelUUID           = "model-uuid"
    57  	modelTag            = names.NewModelTag(modelUUID)
    58  	modelName           = "model-name"
    59  	ownerTag            = names.NewUserTag("owner")
    60  	modelVersion        = version.MustParse("1.2.4")
    61  
    62  	// Define stub calls that commonly appear in tests here to allow reuse.
    63  	apiOpenControllerCall = jujutesting.StubCall{
    64  		"apiOpen",
    65  		[]interface{}{
    66  			&api.Info{
    67  				Addrs:    []string{"1.2.3.4:5"},
    68  				CACert:   "cert",
    69  				Tag:      names.NewUserTag("admin"),
    70  				Password: "secret",
    71  			},
    72  			migration.ControllerDialOpts(),
    73  		},
    74  	}
    75  	apiOpenModelCall = jujutesting.StubCall{
    76  		"apiOpen",
    77  		[]interface{}{
    78  			&api.Info{
    79  				Addrs:    []string{"1.2.3.4:5"},
    80  				CACert:   "cert",
    81  				Tag:      names.NewUserTag("admin"),
    82  				Password: "secret",
    83  				ModelTag: modelTag,
    84  			},
    85  			migration.ControllerDialOpts(),
    86  		},
    87  	}
    88  	importCall = jujutesting.StubCall{
    89  		"MigrationTarget.Import",
    90  		[]interface{}{
    91  			params.SerializedModel{Bytes: fakeModelBytes},
    92  		},
    93  	}
    94  	activateCall = jujutesting.StubCall{
    95  		"MigrationTarget.Activate",
    96  		[]interface{}{
    97  			params.ModelArgs{ModelTag: modelTag.String()},
    98  		},
    99  	}
   100  	checkMachinesCall = jujutesting.StubCall{
   101  		"MigrationTarget.CheckMachines",
   102  		[]interface{}{
   103  			params.ModelArgs{ModelTag: modelTag.String()},
   104  		},
   105  	}
   106  	adoptResourcesCall = jujutesting.StubCall{
   107  		"MigrationTarget.AdoptResources",
   108  		[]interface{}{
   109  			params.AdoptResourcesArgs{
   110  				ModelTag:                modelTag.String(),
   111  				SourceControllerVersion: jujuversion.Current,
   112  			},
   113  		},
   114  	}
   115  	latestLogTimeCall = jujutesting.StubCall{
   116  		"MigrationTarget.LatestLogTime",
   117  		[]interface{}{
   118  			params.ModelArgs{ModelTag: modelTag.String()},
   119  		},
   120  	}
   121  	apiCloseCall = jujutesting.StubCall{"Connection.Close", nil}
   122  	abortCall    = jujutesting.StubCall{
   123  		"MigrationTarget.Abort",
   124  		[]interface{}{
   125  			params.ModelArgs{ModelTag: modelTag.String()},
   126  		},
   127  	}
   128  	watchStatusLockdownCalls = []jujutesting.StubCall{
   129  		{"facade.Watch", nil},
   130  		{"facade.MigrationStatus", nil},
   131  		{"guard.Lockdown", nil},
   132  	}
   133  	prechecksCalls = []jujutesting.StubCall{
   134  		{"facade.Prechecks", nil},
   135  		{"facade.ModelInfo", nil},
   136  		apiOpenControllerCall,
   137  		{"MigrationTarget.Prechecks", []interface{}{params.MigrationModelInfo{
   138  			UUID:         modelUUID,
   139  			Name:         modelName,
   140  			OwnerTag:     ownerTag.String(),
   141  			AgentVersion: modelVersion,
   142  		}}},
   143  		apiCloseCall,
   144  	}
   145  	abortCalls = []jujutesting.StubCall{
   146  		{"facade.SetPhase", []interface{}{coremigration.ABORT}},
   147  		apiOpenControllerCall,
   148  		abortCall,
   149  		apiCloseCall,
   150  		{"facade.SetPhase", []interface{}{coremigration.ABORTDONE}},
   151  	}
   152  	openDestLogStreamCall = jujutesting.StubCall{"ConnectControllerStream", []interface{}{
   153  		"/migrate/logtransfer",
   154  		url.Values{"jujuclientversion": {jujuversion.Current.String()}},
   155  		http.Header{
   156  			textproto.CanonicalMIMEHeaderKey(params.MigrationModelHTTPHeader): {modelUUID},
   157  		},
   158  	}}
   159  )
   160  
   161  func (s *Suite) SetUpTest(c *gc.C) {
   162  	s.BaseSuite.SetUpTest(c)
   163  
   164  	s.clock = testclock.NewClock(time.Now())
   165  	s.stub = new(jujutesting.Stub)
   166  	s.connection = &stubConnection{
   167  		stub:          s.stub,
   168  		controllerTag: targetControllerTag,
   169  		logStream:     &mockStream{},
   170  	}
   171  	s.connectionErr = nil
   172  
   173  	s.facade = newStubMasterFacade(s.stub, s.clock.Now())
   174  
   175  	// The default worker Config used by most of the tests. Tests may
   176  	// tweak parts of this as needed.
   177  	s.config = migrationmaster.Config{
   178  		ModelUUID:       utils.MustNewUUID().String(),
   179  		Facade:          s.facade,
   180  		Guard:           newStubGuard(s.stub),
   181  		APIOpen:         s.apiOpen,
   182  		UploadBinaries:  nullUploadBinaries,
   183  		CharmDownloader: fakeCharmDownloader,
   184  		ToolsDownloader: fakeToolsDownloader,
   185  		Clock:           s.clock,
   186  	}
   187  }
   188  
   189  func (s *Suite) apiOpen(info *api.Info, dialOpts api.DialOpts) (api.Connection, error) {
   190  	s.stub.AddCall("apiOpen", info, dialOpts)
   191  	if s.connectionErr != nil {
   192  		return nil, s.connectionErr
   193  	}
   194  	return s.connection, nil
   195  }
   196  
   197  func (s *Suite) makeStatus(phase coremigration.Phase) coremigration.MigrationStatus {
   198  	return coremigration.MigrationStatus{
   199  		MigrationId:      "model-uuid:2",
   200  		ModelUUID:        "model-uuid",
   201  		Phase:            phase,
   202  		PhaseChangedTime: s.clock.Now(),
   203  		TargetInfo: coremigration.TargetInfo{
   204  			ControllerTag: targetControllerTag,
   205  			Addrs:         []string{"1.2.3.4:5"},
   206  			CACert:        "cert",
   207  			AuthTag:       names.NewUserTag("admin"),
   208  			Password:      "secret",
   209  		},
   210  	}
   211  }
   212  
   213  func (s *Suite) TestSuccessfulMigration(c *gc.C) {
   214  	s.facade.exportedResources = []coremigration.SerializedModelResource{{
   215  		ApplicationRevision: resourcetesting.NewResource(c, nil, "blob", "app", "").Resource,
   216  	}}
   217  
   218  	s.facade.queueStatus(s.makeStatus(coremigration.QUIESCE))
   219  	s.facade.queueMinionReports(makeMinionReports(coremigration.QUIESCE))
   220  	s.facade.queueMinionReports(makeMinionReports(coremigration.VALIDATION))
   221  	s.facade.queueMinionReports(makeMinionReports(coremigration.SUCCESS))
   222  	s.config.UploadBinaries = makeStubUploadBinaries(s.stub)
   223  
   224  	s.checkWorkerReturns(c, migrationmaster.ErrMigrated)
   225  
   226  	// Observe that the migration was seen, the model exported, an API
   227  	// connection to the target controller was made, the model was
   228  	// imported and then the migration completed.
   229  	s.stub.CheckCalls(c, joinCalls(
   230  		// Wait for migration to start.
   231  		watchStatusLockdownCalls,
   232  
   233  		// QUIESCE
   234  		prechecksCalls,
   235  		[]jujutesting.StubCall{
   236  			{"facade.WatchMinionReports", nil},
   237  			{"facade.MinionReports", nil},
   238  		},
   239  		prechecksCalls,
   240  		[]jujutesting.StubCall{
   241  			{"facade.SetPhase", []interface{}{coremigration.IMPORT}},
   242  
   243  			//IMPORT
   244  			{"facade.Export", nil},
   245  			apiOpenControllerCall,
   246  			importCall,
   247  			{"UploadBinaries", []interface{}{
   248  				[]string{"charm0", "charm1"},
   249  				fakeCharmDownloader,
   250  				map[version.Binary]string{
   251  					version.MustParseBinary("2.1.0-trusty-amd64"): "/tools/0",
   252  				},
   253  				fakeToolsDownloader,
   254  				s.facade.exportedResources,
   255  				s.facade,
   256  			}},
   257  			apiCloseCall, // for target controller
   258  			{"facade.SetPhase", []interface{}{coremigration.VALIDATION}},
   259  
   260  			// VALIDATION
   261  			{"facade.WatchMinionReports", nil},
   262  			{"facade.MinionReports", nil},
   263  			apiOpenControllerCall,
   264  			checkMachinesCall,
   265  			activateCall,
   266  			apiCloseCall,
   267  			{"facade.SetPhase", []interface{}{coremigration.SUCCESS}},
   268  
   269  			// SUCCESS
   270  			{"facade.WatchMinionReports", nil},
   271  			{"facade.MinionReports", nil},
   272  			apiOpenControllerCall,
   273  			adoptResourcesCall,
   274  			apiCloseCall,
   275  			{"facade.SetPhase", []interface{}{coremigration.LOGTRANSFER}},
   276  
   277  			// LOGTRANSFER
   278  			apiOpenControllerCall,
   279  			latestLogTimeCall,
   280  			{"StreamModelLog", []interface{}{time.Time{}}},
   281  			openDestLogStreamCall,
   282  			{"facade.SetPhase", []interface{}{coremigration.REAP}},
   283  
   284  			// REAP
   285  			{"facade.Reap", nil},
   286  			{"facade.SetPhase", []interface{}{coremigration.DONE}},
   287  		}),
   288  	)
   289  }
   290  
   291  func (s *Suite) TestMigrationResume(c *gc.C) {
   292  	// Test that a partially complete migration can be resumed.
   293  	s.facade.queueStatus(s.makeStatus(coremigration.SUCCESS))
   294  	s.facade.queueMinionReports(makeMinionReports(coremigration.SUCCESS))
   295  
   296  	s.checkWorkerReturns(c, migrationmaster.ErrMigrated)
   297  	s.stub.CheckCalls(c, joinCalls(
   298  		watchStatusLockdownCalls,
   299  		[]jujutesting.StubCall{
   300  			{"facade.WatchMinionReports", nil},
   301  			{"facade.MinionReports", nil},
   302  			apiOpenControllerCall,
   303  			adoptResourcesCall,
   304  			apiCloseCall,
   305  			{"facade.SetPhase", []interface{}{coremigration.LOGTRANSFER}},
   306  			apiOpenControllerCall,
   307  			latestLogTimeCall,
   308  			{"StreamModelLog", []interface{}{time.Time{}}},
   309  			openDestLogStreamCall,
   310  			{"facade.SetPhase", []interface{}{coremigration.REAP}},
   311  			{"facade.Reap", nil},
   312  			{"facade.SetPhase", []interface{}{coremigration.DONE}},
   313  		},
   314  	))
   315  }
   316  
   317  func (s *Suite) TestPreviouslyAbortedMigration(c *gc.C) {
   318  	s.facade.queueStatus(s.makeStatus(coremigration.ABORTDONE))
   319  
   320  	worker, err := migrationmaster.New(s.config)
   321  	c.Assert(err, jc.ErrorIsNil)
   322  	defer workertest.CleanKill(c, worker)
   323  
   324  	s.waitForStubCalls(c, []string{
   325  		"facade.Watch",
   326  		"facade.MigrationStatus",
   327  		"guard.Unlock",
   328  	})
   329  }
   330  
   331  func (s *Suite) TestPreviouslyCompletedMigration(c *gc.C) {
   332  	s.facade.queueStatus(s.makeStatus(coremigration.DONE))
   333  	s.checkWorkerReturns(c, migrationmaster.ErrMigrated)
   334  	s.stub.CheckCalls(c, []jujutesting.StubCall{
   335  		{"facade.Watch", nil},
   336  		{"facade.MigrationStatus", nil},
   337  	})
   338  }
   339  
   340  func (s *Suite) TestWatchFailure(c *gc.C) {
   341  	s.facade.watchErr = errors.New("boom")
   342  	s.checkWorkerErr(c, "watching for migration: boom")
   343  }
   344  
   345  func (s *Suite) TestStatusError(c *gc.C) {
   346  	s.facade.queueStatus(s.makeStatus(coremigration.QUIESCE))
   347  	s.facade.statusErr = errors.New("splat")
   348  
   349  	s.checkWorkerErr(c, "retrieving migration status: splat")
   350  	s.stub.CheckCalls(c, []jujutesting.StubCall{
   351  		{"facade.Watch", nil},
   352  		{"facade.MigrationStatus", nil},
   353  	})
   354  }
   355  
   356  func (s *Suite) TestStatusNotFound(c *gc.C) {
   357  	s.facade.statusErr = &params.Error{Code: params.CodeNotFound}
   358  	s.facade.triggerWatcher()
   359  
   360  	worker, err := migrationmaster.New(s.config)
   361  	c.Assert(err, jc.ErrorIsNil)
   362  	defer workertest.CleanKill(c, worker)
   363  
   364  	s.waitForStubCalls(c, []string{
   365  		"facade.Watch",
   366  		"facade.MigrationStatus",
   367  		"guard.Unlock",
   368  	})
   369  }
   370  
   371  func (s *Suite) TestUnlockError(c *gc.C) {
   372  	s.facade.statusErr = &params.Error{Code: params.CodeNotFound}
   373  	s.facade.triggerWatcher()
   374  	guard := newStubGuard(s.stub)
   375  	guard.unlockErr = errors.New("pow")
   376  	s.config.Guard = guard
   377  
   378  	s.checkWorkerErr(c, "pow")
   379  	s.stub.CheckCalls(c, []jujutesting.StubCall{
   380  		{"facade.Watch", nil},
   381  		{"facade.MigrationStatus", nil},
   382  		{"guard.Unlock", nil},
   383  	})
   384  }
   385  
   386  func (s *Suite) TestLockdownError(c *gc.C) {
   387  	s.facade.queueStatus(s.makeStatus(coremigration.QUIESCE))
   388  	guard := newStubGuard(s.stub)
   389  	guard.lockdownErr = errors.New("biff")
   390  	s.config.Guard = guard
   391  
   392  	s.checkWorkerErr(c, "biff")
   393  	s.stub.CheckCalls(c, watchStatusLockdownCalls)
   394  }
   395  
   396  func (s *Suite) TestQUIESCEMinionWaitWatchError(c *gc.C) {
   397  	s.checkMinionWaitWatchError(c, coremigration.QUIESCE)
   398  }
   399  
   400  func (s *Suite) TestQUIESCEMinionWaitGetError(c *gc.C) {
   401  	s.checkMinionWaitGetError(c, coremigration.QUIESCE)
   402  }
   403  
   404  func (s *Suite) TestQUIESCEFailedAgent(c *gc.C) {
   405  	s.facade.queueStatus(s.makeStatus(coremigration.QUIESCE))
   406  	s.facade.queueMinionReports(coremigration.MinionReports{
   407  		MigrationId:    "model-uuid:2",
   408  		Phase:          coremigration.QUIESCE,
   409  		FailedMachines: []string{"42"}, // a machine failed
   410  	})
   411  
   412  	s.checkWorkerReturns(c, migrationmaster.ErrInactive)
   413  	s.stub.CheckCalls(c, joinCalls(
   414  		watchStatusLockdownCalls,
   415  		prechecksCalls,
   416  		[]jujutesting.StubCall{
   417  			{"facade.WatchMinionReports", nil},
   418  			{"facade.MinionReports", nil},
   419  		},
   420  		abortCalls,
   421  	))
   422  }
   423  
   424  func (s *Suite) TestQUIESCEWrongController(c *gc.C) {
   425  	s.facade.queueStatus(s.makeStatus(coremigration.QUIESCE))
   426  	s.connection.controllerTag = names.NewControllerTag("another-controller")
   427  
   428  	s.checkWorkerReturns(c, migrationmaster.ErrInactive)
   429  	s.stub.CheckCalls(c, joinCalls(
   430  		watchStatusLockdownCalls,
   431  		[]jujutesting.StubCall{
   432  			{"facade.Prechecks", nil},
   433  			{"facade.ModelInfo", nil},
   434  			apiOpenControllerCall,
   435  			apiCloseCall,
   436  		},
   437  		abortCalls,
   438  	))
   439  }
   440  
   441  func (s *Suite) TestQUIESCESourceChecksFail(c *gc.C) {
   442  	s.facade.queueStatus(s.makeStatus(coremigration.QUIESCE))
   443  	s.facade.prechecksErr = errors.New("boom")
   444  
   445  	s.checkWorkerReturns(c, migrationmaster.ErrInactive)
   446  	s.stub.CheckCalls(c, joinCalls(
   447  		watchStatusLockdownCalls,
   448  		[]jujutesting.StubCall{{"facade.Prechecks", nil}},
   449  		abortCalls,
   450  	))
   451  }
   452  
   453  func (s *Suite) TestQUIESCEModelInfoFail(c *gc.C) {
   454  	s.facade.queueStatus(s.makeStatus(coremigration.QUIESCE))
   455  	s.facade.modelInfoErr = errors.New("boom")
   456  
   457  	s.checkWorkerReturns(c, migrationmaster.ErrInactive)
   458  	s.stub.CheckCalls(c, joinCalls(
   459  		watchStatusLockdownCalls,
   460  		[]jujutesting.StubCall{
   461  			{"facade.Prechecks", nil},
   462  			{"facade.ModelInfo", nil},
   463  		},
   464  		abortCalls,
   465  	))
   466  }
   467  
   468  func (s *Suite) TestQUIESCETargetChecksFail(c *gc.C) {
   469  	s.facade.queueStatus(s.makeStatus(coremigration.QUIESCE))
   470  	s.connection.prechecksErr = errors.New("boom")
   471  
   472  	s.checkWorkerReturns(c, migrationmaster.ErrInactive)
   473  	s.stub.CheckCalls(c, joinCalls(
   474  		watchStatusLockdownCalls,
   475  		prechecksCalls,
   476  		abortCalls,
   477  	))
   478  }
   479  
   480  func (s *Suite) TestExportFailure(c *gc.C) {
   481  	s.facade.queueStatus(s.makeStatus(coremigration.IMPORT))
   482  	s.facade.exportErr = errors.New("boom")
   483  
   484  	s.checkWorkerReturns(c, migrationmaster.ErrInactive)
   485  	s.stub.CheckCalls(c, joinCalls(
   486  		watchStatusLockdownCalls,
   487  		[]jujutesting.StubCall{
   488  			{"facade.Export", nil},
   489  		},
   490  		abortCalls,
   491  	))
   492  }
   493  
   494  func (s *Suite) TestAPIOpenFailure(c *gc.C) {
   495  	s.facade.queueStatus(s.makeStatus(coremigration.IMPORT))
   496  	s.connectionErr = errors.New("boom")
   497  
   498  	s.checkWorkerReturns(c, migrationmaster.ErrInactive)
   499  	s.stub.CheckCalls(c, joinCalls(
   500  		watchStatusLockdownCalls,
   501  		[]jujutesting.StubCall{
   502  			{"facade.Export", nil},
   503  			apiOpenControllerCall,
   504  			{"facade.SetPhase", []interface{}{coremigration.ABORT}},
   505  			apiOpenControllerCall,
   506  			{"facade.SetPhase", []interface{}{coremigration.ABORTDONE}},
   507  		},
   508  	))
   509  }
   510  
   511  func (s *Suite) TestImportFailure(c *gc.C) {
   512  	s.facade.queueStatus(s.makeStatus(coremigration.IMPORT))
   513  	s.connection.importErr = errors.New("boom")
   514  
   515  	s.checkWorkerReturns(c, migrationmaster.ErrInactive)
   516  	s.stub.CheckCalls(c, joinCalls(
   517  		watchStatusLockdownCalls,
   518  		[]jujutesting.StubCall{
   519  			{"facade.Export", nil},
   520  			apiOpenControllerCall,
   521  			importCall,
   522  			apiCloseCall,
   523  		},
   524  		abortCalls,
   525  	))
   526  }
   527  
   528  func (s *Suite) TestVALIDATIONMinionWaitWatchError(c *gc.C) {
   529  	s.checkMinionWaitWatchError(c, coremigration.VALIDATION)
   530  }
   531  
   532  func (s *Suite) TestVALIDATIONMinionWaitGetError(c *gc.C) {
   533  	s.checkMinionWaitGetError(c, coremigration.VALIDATION)
   534  }
   535  
   536  func (s *Suite) TestVALIDATIONFailedAgent(c *gc.C) {
   537  	s.facade.queueStatus(s.makeStatus(coremigration.VALIDATION))
   538  	s.facade.queueMinionReports(coremigration.MinionReports{
   539  		MigrationId:    "model-uuid:2",
   540  		Phase:          coremigration.VALIDATION,
   541  		FailedMachines: []string{"42"}, // a machine failed
   542  	})
   543  
   544  	s.checkWorkerReturns(c, migrationmaster.ErrInactive)
   545  	s.stub.CheckCalls(c, joinCalls(
   546  		watchStatusLockdownCalls,
   547  		[]jujutesting.StubCall{
   548  			{"facade.WatchMinionReports", nil},
   549  			{"facade.MinionReports", nil},
   550  		},
   551  		abortCalls,
   552  	))
   553  }
   554  
   555  func (s *Suite) TestVALIDATIONCheckMachinesOneError(c *gc.C) {
   556  	s.facade.queueStatus(s.makeStatus(coremigration.VALIDATION))
   557  	s.facade.queueMinionReports(makeMinionReports(coremigration.VALIDATION))
   558  
   559  	s.connection.machineErrs = []string{"been so strange"}
   560  	s.checkWorkerReturns(c, migrationmaster.ErrInactive)
   561  	s.stub.CheckCalls(c, joinCalls(
   562  		watchStatusLockdownCalls,
   563  		[]jujutesting.StubCall{
   564  			{"facade.WatchMinionReports", nil},
   565  			{"facade.MinionReports", nil},
   566  			apiOpenControllerCall,
   567  			checkMachinesCall,
   568  			apiCloseCall,
   569  		},
   570  		abortCalls,
   571  	))
   572  	lastMessages := s.facade.statuses[len(s.facade.statuses)-2:]
   573  	c.Assert(lastMessages, gc.DeepEquals, []string{
   574  		"machine sanity check failed, 1 error found",
   575  		"aborted, removing model from target controller: machine sanity check failed, 1 error found",
   576  	})
   577  }
   578  
   579  func (s *Suite) TestVALIDATIONCheckMachinesSeveralErrors(c *gc.C) {
   580  	s.facade.queueStatus(s.makeStatus(coremigration.VALIDATION))
   581  	s.facade.queueMinionReports(makeMinionReports(coremigration.VALIDATION))
   582  	s.connection.machineErrs = []string{"been so strange", "lit up"}
   583  	s.checkWorkerReturns(c, migrationmaster.ErrInactive)
   584  	s.stub.CheckCalls(c, joinCalls(
   585  		watchStatusLockdownCalls,
   586  		[]jujutesting.StubCall{
   587  			{"facade.WatchMinionReports", nil},
   588  			{"facade.MinionReports", nil},
   589  			apiOpenControllerCall,
   590  			checkMachinesCall,
   591  			apiCloseCall,
   592  		},
   593  		abortCalls,
   594  	))
   595  	lastMessages := s.facade.statuses[len(s.facade.statuses)-2:]
   596  	c.Assert(lastMessages, gc.DeepEquals, []string{
   597  		"machine sanity check failed, 2 errors found",
   598  		"aborted, removing model from target controller: machine sanity check failed, 2 errors found",
   599  	})
   600  }
   601  
   602  func (s *Suite) TestVALIDATIONCheckMachinesOtherError(c *gc.C) {
   603  	s.facade.queueStatus(s.makeStatus(coremigration.VALIDATION))
   604  	s.facade.queueMinionReports(makeMinionReports(coremigration.VALIDATION))
   605  	s.connection.checkMachineErr = errors.Errorf("something went bang")
   606  
   607  	s.checkWorkerReturns(c, s.connection.checkMachineErr)
   608  	s.stub.CheckCalls(c, joinCalls(
   609  		watchStatusLockdownCalls,
   610  		[]jujutesting.StubCall{
   611  			{"facade.WatchMinionReports", nil},
   612  			{"facade.MinionReports", nil},
   613  			apiOpenControllerCall,
   614  			checkMachinesCall,
   615  			apiCloseCall,
   616  		},
   617  	))
   618  }
   619  
   620  func (s *Suite) TestSUCCESSMinionWaitWatchError(c *gc.C) {
   621  	s.checkMinionWaitWatchError(c, coremigration.SUCCESS)
   622  }
   623  
   624  func (s *Suite) TestSUCCESSMinionWaitGetError(c *gc.C) {
   625  	s.checkMinionWaitGetError(c, coremigration.SUCCESS)
   626  }
   627  
   628  func (s *Suite) TestSUCCESSMinionWaitFailedMachine(c *gc.C) {
   629  	// With the SUCCESS phase the master should wait for all reports,
   630  	// continuing even if some minions report failure.
   631  	s.facade.queueStatus(s.makeStatus(coremigration.SUCCESS))
   632  	s.facade.queueMinionReports(coremigration.MinionReports{
   633  		MigrationId:    "model-uuid:2",
   634  		Phase:          coremigration.SUCCESS,
   635  		FailedMachines: []string{"42"},
   636  	})
   637  
   638  	s.checkWorkerReturns(c, migrationmaster.ErrMigrated)
   639  	s.stub.CheckCalls(c, joinCalls(
   640  		watchStatusLockdownCalls,
   641  		[]jujutesting.StubCall{
   642  			{"facade.WatchMinionReports", nil},
   643  			{"facade.MinionReports", nil},
   644  			apiOpenControllerCall,
   645  			adoptResourcesCall,
   646  			apiCloseCall,
   647  			{"facade.SetPhase", []interface{}{coremigration.LOGTRANSFER}},
   648  			apiOpenControllerCall,
   649  			latestLogTimeCall,
   650  			{"StreamModelLog", []interface{}{time.Time{}}},
   651  			openDestLogStreamCall,
   652  			{"facade.SetPhase", []interface{}{coremigration.REAP}},
   653  			{"facade.Reap", nil},
   654  			{"facade.SetPhase", []interface{}{coremigration.DONE}},
   655  		},
   656  	))
   657  }
   658  
   659  func (s *Suite) TestSUCCESSMinionWaitFailedUnit(c *gc.C) {
   660  	// See note for TestMinionWaitFailedMachine above.
   661  	s.facade.queueStatus(s.makeStatus(coremigration.SUCCESS))
   662  	s.facade.queueMinionReports(coremigration.MinionReports{
   663  		MigrationId:        "model-uuid:2",
   664  		Phase:              coremigration.SUCCESS,
   665  		FailedUnits:        []string{"foo/2"},
   666  		FailedApplications: []string{"bar"},
   667  	})
   668  
   669  	s.checkWorkerReturns(c, migrationmaster.ErrMigrated)
   670  	s.stub.CheckCalls(c, joinCalls(
   671  		watchStatusLockdownCalls,
   672  		[]jujutesting.StubCall{
   673  			{"facade.WatchMinionReports", nil},
   674  			{"facade.MinionReports", nil},
   675  			apiOpenControllerCall,
   676  			adoptResourcesCall,
   677  			apiCloseCall,
   678  			{"facade.SetPhase", []interface{}{coremigration.LOGTRANSFER}},
   679  			apiOpenControllerCall,
   680  			latestLogTimeCall,
   681  			{"StreamModelLog", []interface{}{time.Time{}}},
   682  			openDestLogStreamCall,
   683  			{"facade.SetPhase", []interface{}{coremigration.REAP}},
   684  			{"facade.Reap", nil},
   685  			{"facade.SetPhase", []interface{}{coremigration.DONE}},
   686  		},
   687  	))
   688  }
   689  
   690  func (s *Suite) TestSUCCESSMinionWaitTimeout(c *gc.C) {
   691  	// The SUCCESS phase is special in that even if some minions fail
   692  	// to report the migration should continue. There's no turning
   693  	// back from SUCCESS.
   694  	s.facade.queueStatus(s.makeStatus(coremigration.SUCCESS))
   695  
   696  	worker, err := migrationmaster.New(s.config)
   697  	c.Assert(err, jc.ErrorIsNil)
   698  	defer workertest.DirtyKill(c, worker)
   699  
   700  	select {
   701  	case <-s.clock.Alarms():
   702  	case <-time.After(coretesting.LongWait):
   703  		c.Fatal("timed out waiting for clock.After call")
   704  	}
   705  
   706  	// Move time ahead in order to trigger timeout.
   707  	s.clock.Advance(15 * time.Minute)
   708  
   709  	err = workertest.CheckKilled(c, worker)
   710  	c.Assert(err, gc.Equals, migrationmaster.ErrMigrated)
   711  
   712  	s.stub.CheckCalls(c, joinCalls(
   713  		watchStatusLockdownCalls,
   714  		[]jujutesting.StubCall{
   715  			{"facade.WatchMinionReports", nil},
   716  			apiOpenControllerCall,
   717  			adoptResourcesCall,
   718  			apiCloseCall,
   719  			{"facade.SetPhase", []interface{}{coremigration.LOGTRANSFER}},
   720  			apiOpenControllerCall,
   721  			latestLogTimeCall,
   722  			{"StreamModelLog", []interface{}{time.Time{}}},
   723  			openDestLogStreamCall,
   724  			{"facade.SetPhase", []interface{}{coremigration.REAP}},
   725  			{"facade.Reap", nil},
   726  			{"facade.SetPhase", []interface{}{coremigration.DONE}},
   727  		},
   728  	))
   729  }
   730  
   731  func (s *Suite) TestMinionWaitWrongPhase(c *gc.C) {
   732  	s.facade.queueStatus(s.makeStatus(coremigration.SUCCESS))
   733  
   734  	// Have the phase in the minion reports be different from the
   735  	// migration status. This shouldn't happen but the migrationmaster
   736  	// should handle it.
   737  	s.facade.queueMinionReports(makeMinionReports(coremigration.IMPORT))
   738  
   739  	s.checkWorkerErr(c,
   740  		`minion reports phase \(IMPORT\) does not match migration phase \(SUCCESS\)`)
   741  }
   742  
   743  func (s *Suite) TestMinionWaitMigrationIdChanged(c *gc.C) {
   744  	s.facade.queueStatus(s.makeStatus(coremigration.SUCCESS))
   745  
   746  	// Have the migration id in the minion reports be different from
   747  	// the migration status. This shouldn't happen but the
   748  	// migrationmaster should handle it.
   749  	s.facade.queueMinionReports(coremigration.MinionReports{
   750  		MigrationId: "blah",
   751  		Phase:       coremigration.SUCCESS,
   752  	})
   753  
   754  	s.checkWorkerErr(c,
   755  		"unexpected migration id in minion reports, got blah, expected model-uuid:2")
   756  }
   757  
   758  func (s *Suite) TestAPIConnectWithMacaroon(c *gc.C) {
   759  	// Use ABORT because it involves an API connection to the target
   760  	// and is convenient.
   761  	status := s.makeStatus(coremigration.ABORT)
   762  
   763  	// Set up macaroon based auth to the target.
   764  	mac, err := macaroon.New([]byte("secret"), []byte("id"), "location")
   765  	c.Assert(err, jc.ErrorIsNil)
   766  	macs := []macaroon.Slice{{mac}}
   767  	status.TargetInfo.Password = ""
   768  	status.TargetInfo.Macaroons = macs
   769  
   770  	s.facade.queueStatus(status)
   771  
   772  	s.checkWorkerReturns(c, migrationmaster.ErrInactive)
   773  	s.stub.CheckCalls(c, joinCalls(
   774  		watchStatusLockdownCalls,
   775  		[]jujutesting.StubCall{
   776  			{
   777  				"apiOpen",
   778  				[]interface{}{
   779  					&api.Info{
   780  						Addrs:     []string{"1.2.3.4:5"},
   781  						CACert:    "cert",
   782  						Tag:       names.NewUserTag("admin"),
   783  						Macaroons: macs, // <---
   784  					},
   785  					migration.ControllerDialOpts(),
   786  				},
   787  			},
   788  			abortCall,
   789  			apiCloseCall,
   790  			{"facade.SetPhase", []interface{}{coremigration.ABORTDONE}},
   791  		},
   792  	))
   793  }
   794  
   795  func (s *Suite) TestLogTransferErrorOpeningTargetAPI(c *gc.C) {
   796  	s.facade.queueStatus(s.makeStatus(coremigration.LOGTRANSFER))
   797  	s.connectionErr = errors.New("people of earth")
   798  
   799  	s.checkWorkerReturns(c, s.connectionErr)
   800  	s.stub.CheckCalls(c, joinCalls(
   801  		watchStatusLockdownCalls,
   802  		[]jujutesting.StubCall{
   803  			apiOpenControllerCall,
   804  		},
   805  	))
   806  }
   807  
   808  func (s *Suite) TestLogTransferErrorGettingStartTime(c *gc.C) {
   809  	s.facade.queueStatus(s.makeStatus(coremigration.LOGTRANSFER))
   810  	s.connection.latestLogErr = errors.New("tender vittles")
   811  
   812  	s.checkWorkerReturns(c, s.connection.latestLogErr)
   813  	s.stub.CheckCalls(c, joinCalls(
   814  		watchStatusLockdownCalls,
   815  		[]jujutesting.StubCall{
   816  			apiOpenControllerCall,
   817  			latestLogTimeCall,
   818  		},
   819  	))
   820  }
   821  
   822  func (s *Suite) TestLogTransferErrorOpeningLogSource(c *gc.C) {
   823  	s.facade.queueStatus(s.makeStatus(coremigration.LOGTRANSFER))
   824  	s.facade.streamErr = errors.New("chicken bones")
   825  
   826  	s.checkWorkerReturns(c, s.facade.streamErr)
   827  	s.stub.CheckCalls(c, joinCalls(
   828  		watchStatusLockdownCalls,
   829  		[]jujutesting.StubCall{
   830  			apiOpenControllerCall,
   831  			latestLogTimeCall,
   832  			{"StreamModelLog", []interface{}{time.Time{}}},
   833  		},
   834  	))
   835  }
   836  
   837  func (s *Suite) TestLogTransferErrorOpeningLogDest(c *gc.C) {
   838  	s.facade.queueStatus(s.makeStatus(coremigration.LOGTRANSFER))
   839  	s.connection.streamErr = errors.New("tule lake shuffle")
   840  
   841  	s.checkWorkerReturns(c, s.connection.streamErr)
   842  	s.stub.CheckCalls(c, joinCalls(
   843  		watchStatusLockdownCalls,
   844  		[]jujutesting.StubCall{
   845  			apiOpenControllerCall,
   846  			latestLogTimeCall,
   847  			{"StreamModelLog", []interface{}{time.Time{}}},
   848  			openDestLogStreamCall,
   849  		},
   850  	))
   851  }
   852  
   853  func (s *Suite) TestLogTransferErrorWriting(c *gc.C) {
   854  	s.facade.queueStatus(s.makeStatus(coremigration.LOGTRANSFER))
   855  	s.facade.logMessages = func(d chan<- common.LogMessage) {
   856  		safeSend(c, d, common.LogMessage{Message: "the go team"})
   857  	}
   858  	s.connection.logStream.writeErr = errors.New("bottle rocket")
   859  	s.checkWorkerReturns(c, s.connection.logStream.writeErr)
   860  	s.stub.CheckCalls(c, joinCalls(
   861  		watchStatusLockdownCalls,
   862  		[]jujutesting.StubCall{
   863  			apiOpenControllerCall,
   864  			latestLogTimeCall,
   865  			{"StreamModelLog", []interface{}{time.Time{}}},
   866  			openDestLogStreamCall,
   867  		},
   868  	))
   869  	c.Assert(s.connection.logStream.closeCount, gc.Equals, 1)
   870  }
   871  
   872  func (s *Suite) TestLogTransferSendsRecords(c *gc.C) {
   873  	t1, err := time.Parse("2006-01-02 15:04", "2016-11-28 16:11")
   874  	c.Assert(err, jc.ErrorIsNil)
   875  	s.facade.queueStatus(s.makeStatus(coremigration.LOGTRANSFER))
   876  	messages := []common.LogMessage{
   877  		{Message: "the go team"},
   878  		{Message: "joan as police woman"},
   879  		{
   880  			Entity:    "the mules",
   881  			Timestamp: t1,
   882  			Severity:  "warning",
   883  			Module:    "this one",
   884  			Location:  "nearby",
   885  			Message:   "ham shank",
   886  		},
   887  	}
   888  	s.facade.logMessages = func(d chan<- common.LogMessage) {
   889  		for _, message := range messages {
   890  			safeSend(c, d, message)
   891  		}
   892  	}
   893  
   894  	s.checkWorkerReturns(c, migrationmaster.ErrMigrated)
   895  	s.stub.CheckCalls(c, joinCalls(
   896  		watchStatusLockdownCalls,
   897  		[]jujutesting.StubCall{
   898  			apiOpenControllerCall,
   899  			latestLogTimeCall,
   900  			{"StreamModelLog", []interface{}{time.Time{}}},
   901  			openDestLogStreamCall,
   902  			{"facade.SetPhase", []interface{}{coremigration.REAP}},
   903  			{"facade.Reap", nil},
   904  			{"facade.SetPhase", []interface{}{coremigration.DONE}},
   905  		},
   906  	))
   907  	c.Assert(s.connection.logStream.written, gc.DeepEquals, []params.LogRecord{
   908  		{Message: "the go team"},
   909  		{Message: "joan as police woman"},
   910  		{
   911  			Time:     t1,
   912  			Module:   "this one",
   913  			Location: "nearby",
   914  			Level:    "warning",
   915  			Message:  "ham shank",
   916  			Entity:   "the mules",
   917  		},
   918  	})
   919  	c.Assert(s.connection.logStream.closeCount, gc.Equals, 1)
   920  }
   921  
   922  func (s *Suite) TestLogTransferReportsProgress(c *gc.C) {
   923  	s.facade.queueStatus(s.makeStatus(coremigration.LOGTRANSFER))
   924  	messages := []common.LogMessage{
   925  		{Message: "captain beefheart"},
   926  		{Message: "super furry animals"},
   927  		{Message: "ezra furman"},
   928  		{Message: "these new puritans"},
   929  	}
   930  	s.facade.logMessages = func(d chan<- common.LogMessage) {
   931  		for _, message := range messages {
   932  			safeSend(c, d, message)
   933  			s.clock.WaitAdvance(20*time.Second, coretesting.LongWait, 1)
   934  		}
   935  	}
   936  
   937  	var logWriter loggo.TestWriter
   938  	c.Assert(loggo.RegisterWriter("migrationmaster-tests", &logWriter), jc.ErrorIsNil)
   939  	defer func() {
   940  		loggo.RemoveWriter("migrationmaster-tests")
   941  		logWriter.Clear()
   942  	}()
   943  
   944  	s.checkWorkerReturns(c, migrationmaster.ErrMigrated)
   945  
   946  	c.Assert(logWriter.Log()[:3], jc.LogMatches, []string{
   947  		"successful, transferring logs to target controller \\(0 sent\\)",
   948  		// This is a bit of a punt, but without accepting a range
   949  		// we sometimes see this test failing on loaded test machines.
   950  		"successful, transferring logs to target controller \\([23] sent\\)",
   951  		"successful, transferr(ing|ed) logs to target controller \\([234] sent\\)",
   952  	})
   953  }
   954  
   955  func (s *Suite) TestLogTransfer_ChecksLatestTime(c *gc.C) {
   956  	s.facade.queueStatus(s.makeStatus(coremigration.LOGTRANSFER))
   957  	t := time.Date(2016, 12, 2, 10, 39, 10, 20, time.UTC)
   958  	s.connection.latestLogTime = t
   959  
   960  	s.checkWorkerReturns(c, migrationmaster.ErrMigrated)
   961  	s.stub.CheckCalls(c, joinCalls(
   962  		watchStatusLockdownCalls,
   963  		[]jujutesting.StubCall{
   964  			apiOpenControllerCall,
   965  			latestLogTimeCall,
   966  			{"StreamModelLog", []interface{}{t}},
   967  			openDestLogStreamCall,
   968  			{"facade.SetPhase", []interface{}{coremigration.REAP}},
   969  			{"facade.Reap", nil},
   970  			{"facade.SetPhase", []interface{}{coremigration.DONE}},
   971  		},
   972  	))
   973  }
   974  
   975  func safeSend(c *gc.C, d chan<- common.LogMessage, message common.LogMessage) {
   976  	select {
   977  	case d <- message:
   978  	case <-time.After(coretesting.ShortWait):
   979  		c.Fatalf("timed out sending log message")
   980  	}
   981  }
   982  
   983  func (s *Suite) checkWorkerReturns(c *gc.C, expected error) {
   984  	err := s.runWorker(c)
   985  	c.Check(errors.Cause(err), gc.Equals, expected)
   986  }
   987  
   988  func (s *Suite) checkWorkerErr(c *gc.C, expected string) {
   989  	err := s.runWorker(c)
   990  	c.Check(err, gc.ErrorMatches, expected)
   991  }
   992  
   993  func (s *Suite) runWorker(c *gc.C) error {
   994  	w, err := migrationmaster.New(s.config)
   995  	c.Assert(err, jc.ErrorIsNil)
   996  	defer workertest.DirtyKill(c, w)
   997  	return workertest.CheckKilled(c, w)
   998  }
   999  
  1000  func (s *Suite) waitForStubCalls(c *gc.C, expectedCallNames []string) {
  1001  	var callNames []string
  1002  	for a := coretesting.LongAttempt.Start(); a.Next(); {
  1003  		callNames = stubCallNames(s.stub)
  1004  		if reflect.DeepEqual(callNames, expectedCallNames) {
  1005  			return
  1006  		}
  1007  	}
  1008  	c.Fatalf("failed to see expected calls\nobtained: %v\nexpected: %v",
  1009  		callNames, expectedCallNames)
  1010  }
  1011  
  1012  func (s *Suite) checkMinionWaitWatchError(c *gc.C, phase coremigration.Phase) {
  1013  	s.facade.minionReportsWatchErr = errors.New("boom")
  1014  	s.facade.queueStatus(s.makeStatus(phase))
  1015  
  1016  	s.checkWorkerErr(c, "boom")
  1017  }
  1018  
  1019  func (s *Suite) checkMinionWaitGetError(c *gc.C, phase coremigration.Phase) {
  1020  	s.facade.queueStatus(s.makeStatus(phase))
  1021  
  1022  	s.facade.minionReportsErr = errors.New("boom")
  1023  	s.facade.triggerMinionReports()
  1024  
  1025  	s.checkWorkerErr(c, "boom")
  1026  }
  1027  
  1028  func stubCallNames(stub *jujutesting.Stub) []string {
  1029  	var out []string
  1030  	for _, call := range stub.Calls() {
  1031  		out = append(out, call.FuncName)
  1032  	}
  1033  	return out
  1034  }
  1035  
  1036  func newStubGuard(stub *jujutesting.Stub) *stubGuard {
  1037  	return &stubGuard{stub: stub}
  1038  }
  1039  
  1040  type stubGuard struct {
  1041  	stub        *jujutesting.Stub
  1042  	unlockErr   error
  1043  	lockdownErr error
  1044  }
  1045  
  1046  func (g *stubGuard) Lockdown(fortress.Abort) error {
  1047  	g.stub.AddCall("guard.Lockdown")
  1048  	return g.lockdownErr
  1049  }
  1050  
  1051  func (g *stubGuard) Unlock() error {
  1052  	g.stub.AddCall("guard.Unlock")
  1053  	return g.unlockErr
  1054  }
  1055  
  1056  func newStubMasterFacade(stub *jujutesting.Stub, now time.Time) *stubMasterFacade {
  1057  	return &stubMasterFacade{
  1058  		stub:           stub,
  1059  		watcherChanges: make(chan struct{}, 999),
  1060  
  1061  		// Give minionReportsChanges a larger-than-required buffer to
  1062  		// support waits at a number of phases.
  1063  		minionReportsChanges: make(chan struct{}, 999),
  1064  	}
  1065  }
  1066  
  1067  type stubMasterFacade struct {
  1068  	migrationmaster.Facade
  1069  
  1070  	stub *jujutesting.Stub
  1071  
  1072  	watcherChanges chan struct{}
  1073  	watchErr       error
  1074  	status         []coremigration.MigrationStatus
  1075  	statusErr      error
  1076  
  1077  	prechecksErr error
  1078  	modelInfoErr error
  1079  	exportErr    error
  1080  
  1081  	logMessages func(chan<- common.LogMessage)
  1082  	streamErr   error
  1083  
  1084  	minionReportsChanges  chan struct{}
  1085  	minionReportsWatchErr error
  1086  	minionReports         []coremigration.MinionReports
  1087  	minionReportsErr      error
  1088  
  1089  	exportedResources []coremigration.SerializedModelResource
  1090  
  1091  	statuses []string
  1092  }
  1093  
  1094  func (f *stubMasterFacade) triggerWatcher() {
  1095  	select {
  1096  	case f.watcherChanges <- struct{}{}:
  1097  	default:
  1098  		panic("migration watcher channel unexpectedly closed")
  1099  	}
  1100  }
  1101  
  1102  func (f *stubMasterFacade) queueStatus(status coremigration.MigrationStatus) {
  1103  	f.status = append(f.status, status)
  1104  	f.triggerWatcher()
  1105  }
  1106  
  1107  func (f *stubMasterFacade) triggerMinionReports() {
  1108  	select {
  1109  	case f.minionReportsChanges <- struct{}{}:
  1110  	default:
  1111  		panic("minion reports watcher channel unexpectedly closed")
  1112  	}
  1113  }
  1114  
  1115  func (f *stubMasterFacade) queueMinionReports(r coremigration.MinionReports) {
  1116  	f.minionReports = append(f.minionReports, r)
  1117  	f.triggerMinionReports()
  1118  }
  1119  
  1120  func (f *stubMasterFacade) Watch() (watcher.NotifyWatcher, error) {
  1121  	f.stub.AddCall("facade.Watch")
  1122  	if f.watchErr != nil {
  1123  		return nil, f.watchErr
  1124  	}
  1125  	return newMockWatcher(f.watcherChanges), nil
  1126  }
  1127  
  1128  func (f *stubMasterFacade) MigrationStatus() (coremigration.MigrationStatus, error) {
  1129  	f.stub.AddCall("facade.MigrationStatus")
  1130  	if f.statusErr != nil {
  1131  		return coremigration.MigrationStatus{}, f.statusErr
  1132  	}
  1133  	if len(f.status) == 0 {
  1134  		panic("no status queued to report")
  1135  	}
  1136  	out := f.status[0]
  1137  	f.status = f.status[1:]
  1138  	return out, nil
  1139  }
  1140  
  1141  func (f *stubMasterFacade) WatchMinionReports() (watcher.NotifyWatcher, error) {
  1142  	f.stub.AddCall("facade.WatchMinionReports")
  1143  	if f.minionReportsWatchErr != nil {
  1144  		return nil, f.minionReportsWatchErr
  1145  	}
  1146  	return newMockWatcher(f.minionReportsChanges), nil
  1147  }
  1148  
  1149  func (f *stubMasterFacade) MinionReports() (coremigration.MinionReports, error) {
  1150  	f.stub.AddCall("facade.MinionReports")
  1151  	if f.minionReportsErr != nil {
  1152  		return coremigration.MinionReports{}, f.minionReportsErr
  1153  	}
  1154  	if len(f.minionReports) == 0 {
  1155  		return coremigration.MinionReports{}, errors.NotFoundf("reports")
  1156  
  1157  	}
  1158  	r := f.minionReports[0]
  1159  	f.minionReports = f.minionReports[1:]
  1160  	return r, nil
  1161  }
  1162  
  1163  func (f *stubMasterFacade) Prechecks() error {
  1164  	f.stub.AddCall("facade.Prechecks")
  1165  	return f.prechecksErr
  1166  }
  1167  
  1168  func (f *stubMasterFacade) ModelInfo() (coremigration.ModelInfo, error) {
  1169  	f.stub.AddCall("facade.ModelInfo")
  1170  	if f.modelInfoErr != nil {
  1171  		return coremigration.ModelInfo{}, f.modelInfoErr
  1172  	}
  1173  	return coremigration.ModelInfo{
  1174  		UUID:         modelUUID,
  1175  		Name:         modelName,
  1176  		Owner:        ownerTag,
  1177  		AgentVersion: modelVersion,
  1178  	}, nil
  1179  }
  1180  
  1181  func (f *stubMasterFacade) Export() (coremigration.SerializedModel, error) {
  1182  	f.stub.AddCall("facade.Export")
  1183  	if f.exportErr != nil {
  1184  		return coremigration.SerializedModel{}, f.exportErr
  1185  	}
  1186  	return coremigration.SerializedModel{
  1187  		Bytes:  fakeModelBytes,
  1188  		Charms: []string{"charm0", "charm1"},
  1189  		Tools: map[version.Binary]string{
  1190  			version.MustParseBinary("2.1.0-trusty-amd64"): "/tools/0",
  1191  		},
  1192  		Resources: f.exportedResources,
  1193  	}, nil
  1194  }
  1195  
  1196  func (f *stubMasterFacade) SetPhase(phase coremigration.Phase) error {
  1197  	f.stub.AddCall("facade.SetPhase", phase)
  1198  	return nil
  1199  }
  1200  
  1201  func (f *stubMasterFacade) SetStatusMessage(message string) error {
  1202  	f.statuses = append(f.statuses, message)
  1203  	return nil
  1204  }
  1205  
  1206  func (f *stubMasterFacade) Reap() error {
  1207  	f.stub.AddCall("facade.Reap")
  1208  	return nil
  1209  }
  1210  
  1211  func (f *stubMasterFacade) StreamModelLog(start time.Time) (<-chan common.LogMessage, error) {
  1212  	f.stub.AddCall("StreamModelLog", start)
  1213  	if f.streamErr != nil {
  1214  		return nil, f.streamErr
  1215  	}
  1216  	result := make(chan common.LogMessage)
  1217  	messageFunc := f.logMessages
  1218  	if messageFunc == nil {
  1219  		messageFunc = func(chan<- common.LogMessage) {}
  1220  	}
  1221  	go func() {
  1222  		defer close(result)
  1223  		messageFunc(result)
  1224  	}()
  1225  	return result, nil
  1226  }
  1227  
  1228  func newMockWatcher(changes chan struct{}) *mockWatcher {
  1229  	return &mockWatcher{
  1230  		Worker:  workertest.NewErrorWorker(nil),
  1231  		changes: changes,
  1232  	}
  1233  }
  1234  
  1235  type mockWatcher struct {
  1236  	worker.Worker
  1237  	changes chan struct{}
  1238  }
  1239  
  1240  func (w *mockWatcher) Changes() watcher.NotifyChannel {
  1241  	return w.changes
  1242  }
  1243  
  1244  type stubConnection struct {
  1245  	api.Connection
  1246  	stub          *jujutesting.Stub
  1247  	prechecksErr  error
  1248  	importErr     error
  1249  	controllerTag names.ControllerTag
  1250  
  1251  	streamErr error
  1252  	logStream *mockStream
  1253  
  1254  	latestLogErr  error
  1255  	latestLogTime time.Time
  1256  
  1257  	machineErrs     []string
  1258  	checkMachineErr error
  1259  }
  1260  
  1261  func (c *stubConnection) BestFacadeVersion(string) int {
  1262  	return 1
  1263  }
  1264  
  1265  func (c *stubConnection) APICall(objType string, version int, id, request string, args, response interface{}) error {
  1266  	c.stub.AddCall(objType+"."+request, args)
  1267  
  1268  	if objType == "MigrationTarget" {
  1269  		switch request {
  1270  		case "Prechecks":
  1271  			return c.prechecksErr
  1272  		case "Import":
  1273  			return c.importErr
  1274  		case "Activate", "AdoptResources":
  1275  			return nil
  1276  		case "LatestLogTime":
  1277  			responseTime := response.(*time.Time)
  1278  			// This is needed because even if a zero time comes back
  1279  			// from the API it will have a timezone attached.
  1280  			*responseTime = c.latestLogTime.In(time.UTC)
  1281  			return c.latestLogErr
  1282  		case "CheckMachines":
  1283  			results := response.(*params.ErrorResults)
  1284  			for _, msg := range c.machineErrs {
  1285  				results.Results = append(results.Results, params.ErrorResult{
  1286  					Error: servercommon.ServerError(errors.Errorf(msg)),
  1287  				})
  1288  			}
  1289  			return c.checkMachineErr
  1290  		}
  1291  	}
  1292  	return errors.New("unexpected API call")
  1293  }
  1294  
  1295  func (c *stubConnection) Client() *api.Client {
  1296  	// This is kinda crappy but the *Client doesn't have to be
  1297  	// functional...
  1298  	return new(api.Client)
  1299  }
  1300  
  1301  func (c *stubConnection) Close() error {
  1302  	c.stub.AddCall("Connection.Close")
  1303  	return nil
  1304  }
  1305  
  1306  func (c *stubConnection) ControllerTag() names.ControllerTag {
  1307  	return c.controllerTag
  1308  }
  1309  
  1310  func (c *stubConnection) ConnectControllerStream(path string, attrs url.Values, headers http.Header) (base.Stream, error) {
  1311  	c.stub.AddCall("ConnectControllerStream", path, attrs, headers)
  1312  	if c.streamErr != nil {
  1313  		return nil, c.streamErr
  1314  	}
  1315  	return c.logStream, nil
  1316  }
  1317  
  1318  func makeStubUploadBinaries(stub *jujutesting.Stub) func(migration.UploadBinariesConfig) error {
  1319  	return func(config migration.UploadBinariesConfig) error {
  1320  		stub.AddCall(
  1321  			"UploadBinaries",
  1322  			config.Charms,
  1323  			config.CharmDownloader,
  1324  			config.Tools,
  1325  			config.ToolsDownloader,
  1326  			config.Resources,
  1327  			config.ResourceDownloader,
  1328  		)
  1329  		return nil
  1330  	}
  1331  }
  1332  
  1333  // nullUploadBinaries is a UploadBinaries variant which is intended to
  1334  // not get called.
  1335  func nullUploadBinaries(migration.UploadBinariesConfig) error {
  1336  	panic("should not get called")
  1337  }
  1338  
  1339  var fakeCharmDownloader = struct{ migration.CharmDownloader }{}
  1340  
  1341  var fakeToolsDownloader = struct{ migration.ToolsDownloader }{}
  1342  
  1343  func joinCalls(allCalls ...[]jujutesting.StubCall) (out []jujutesting.StubCall) {
  1344  	for _, calls := range allCalls {
  1345  		out = append(out, calls...)
  1346  	}
  1347  	return
  1348  }
  1349  
  1350  func makeMinionReports(p coremigration.Phase) coremigration.MinionReports {
  1351  	return coremigration.MinionReports{
  1352  		MigrationId:  "model-uuid:2",
  1353  		Phase:        p,
  1354  		SuccessCount: 5,
  1355  		UnknownCount: 0,
  1356  	}
  1357  }
  1358  
  1359  type mockStream struct {
  1360  	base.Stream
  1361  	c          *gc.C
  1362  	written    []params.LogRecord
  1363  	writeErr   error
  1364  	closeCount int
  1365  }
  1366  
  1367  func (s *mockStream) WriteJSON(v interface{}) error {
  1368  	if s.writeErr != nil {
  1369  		return s.writeErr
  1370  	}
  1371  	rec, ok := v.(params.LogRecord)
  1372  	if !ok {
  1373  		s.c.Errorf("unexpected value written to stream: %v", v)
  1374  		return nil
  1375  	}
  1376  	s.written = append(s.written, rec)
  1377  	return nil
  1378  }
  1379  
  1380  func (s *mockStream) Close() error {
  1381  	s.closeCount++
  1382  	return nil
  1383  }