github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/migrationminion/worker_test.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package migrationminion_test
     5  
     6  import (
     7  	"reflect"
     8  	"sync"
     9  	"time"
    10  
    11  	"github.com/juju/errors"
    12  	jujutesting "github.com/juju/testing"
    13  	jc "github.com/juju/testing/checkers"
    14  	gc "gopkg.in/check.v1"
    15  	"gopkg.in/juju/names.v2"
    16  	"gopkg.in/juju/worker.v1"
    17  	"gopkg.in/juju/worker.v1/workertest"
    18  
    19  	"github.com/juju/juju/agent"
    20  	"github.com/juju/juju/api"
    21  	"github.com/juju/juju/api/base"
    22  	"github.com/juju/juju/core/migration"
    23  	"github.com/juju/juju/core/watcher"
    24  	"github.com/juju/juju/network"
    25  	coretesting "github.com/juju/juju/testing"
    26  	"github.com/juju/juju/worker/fortress"
    27  	"github.com/juju/juju/worker/migrationminion"
    28  )
    29  
    30  var (
    31  	modelTag      = names.NewModelTag("model-uuid")
    32  	addrs         = []string{"1.1.1.1:1111", "2.2.2.2:2222"}
    33  	agentTag      = names.NewMachineTag("42")
    34  	agentPassword = "sekret"
    35  	caCert        = "cert"
    36  )
    37  
    38  type Suite struct {
    39  	coretesting.BaseSuite
    40  	config migrationminion.Config
    41  	stub   *jujutesting.Stub
    42  	client *stubMinionClient
    43  	guard  *stubGuard
    44  	agent  *stubAgent
    45  }
    46  
    47  var _ = gc.Suite(&Suite{})
    48  
    49  func (s *Suite) SetUpTest(c *gc.C) {
    50  	s.BaseSuite.SetUpTest(c)
    51  	s.stub = new(jujutesting.Stub)
    52  	s.client = newStubMinionClient(s.stub)
    53  	s.guard = newStubGuard(s.stub)
    54  	s.agent = newStubAgent()
    55  	s.config = migrationminion.Config{
    56  		Facade:  s.client,
    57  		Guard:   s.guard,
    58  		Agent:   s.agent,
    59  		APIOpen: s.apiOpen,
    60  		ValidateMigration: func(base.APICaller) error {
    61  			s.stub.AddCall("ValidateMigration")
    62  			return nil
    63  		},
    64  	}
    65  }
    66  
    67  func (s *Suite) apiOpen(info *api.Info, dialOpts api.DialOpts) (api.Connection, error) {
    68  	s.stub.AddCall("API open", info)
    69  	return &stubConnection{stub: s.stub}, nil
    70  }
    71  
    72  func (s *Suite) TestStartAndStop(c *gc.C) {
    73  	w, err := migrationminion.New(s.config)
    74  	c.Assert(err, jc.ErrorIsNil)
    75  	workertest.CleanKill(c, w)
    76  	s.stub.CheckCallNames(c, "Watch")
    77  }
    78  
    79  func (s *Suite) TestWatchFailure(c *gc.C) {
    80  	s.client.watchErr = errors.New("boom")
    81  	w, err := migrationminion.New(s.config)
    82  	c.Assert(err, jc.ErrorIsNil)
    83  	err = workertest.CheckKilled(c, w)
    84  	c.Check(err, gc.ErrorMatches, "setting up watcher: boom")
    85  }
    86  
    87  func (s *Suite) TestClosedWatcherChannel(c *gc.C) {
    88  	close(s.client.watcher.changes)
    89  	w, err := migrationminion.New(s.config)
    90  	c.Assert(err, jc.ErrorIsNil)
    91  	err = workertest.CheckKilled(c, w)
    92  	c.Check(err, gc.ErrorMatches, "watcher channel closed")
    93  }
    94  
    95  func (s *Suite) TestUnlockError(c *gc.C) {
    96  	s.client.watcher.changes <- watcher.MigrationStatus{
    97  		Phase: migration.NONE,
    98  	}
    99  	s.guard.unlockErr = errors.New("squish")
   100  	w, err := migrationminion.New(s.config)
   101  	c.Assert(err, jc.ErrorIsNil)
   102  
   103  	err = workertest.CheckKilled(c, w)
   104  	c.Check(err, gc.ErrorMatches, "squish")
   105  	s.stub.CheckCallNames(c, "Watch", "Unlock")
   106  }
   107  
   108  func (s *Suite) TestLockdownError(c *gc.C) {
   109  	s.client.watcher.changes <- watcher.MigrationStatus{
   110  		Phase: migration.QUIESCE,
   111  	}
   112  	s.guard.lockdownErr = errors.New("squash")
   113  	w, err := migrationminion.New(s.config)
   114  	c.Assert(err, jc.ErrorIsNil)
   115  
   116  	err = workertest.CheckKilled(c, w)
   117  	c.Check(err, gc.ErrorMatches, "squash")
   118  	s.stub.CheckCallNames(c, "Watch", "Lockdown")
   119  }
   120  
   121  func (s *Suite) TestNonRunningPhases(c *gc.C) {
   122  	phases := []migration.Phase{
   123  		migration.UNKNOWN,
   124  		migration.NONE,
   125  		migration.LOGTRANSFER,
   126  		migration.REAP,
   127  		migration.REAPFAILED,
   128  		migration.DONE,
   129  		migration.ABORT,
   130  		migration.ABORTDONE,
   131  	}
   132  	for _, phase := range phases {
   133  		s.checkNonRunningPhase(c, phase)
   134  	}
   135  }
   136  
   137  func (s *Suite) checkNonRunningPhase(c *gc.C, phase migration.Phase) {
   138  	c.Logf("checking %s", phase)
   139  	s.stub.ResetCalls()
   140  	s.client.watcher.changes <- watcher.MigrationStatus{Phase: phase}
   141  	w, err := migrationminion.New(s.config)
   142  	c.Assert(err, jc.ErrorIsNil)
   143  	workertest.CheckAlive(c, w)
   144  	workertest.CleanKill(c, w)
   145  	s.stub.CheckCallNames(c, "Watch", "Unlock")
   146  }
   147  
   148  func (s *Suite) TestQUIESCE(c *gc.C) {
   149  	s.client.watcher.changes <- watcher.MigrationStatus{
   150  		MigrationId: "id",
   151  		Phase:       migration.QUIESCE,
   152  	}
   153  	w, err := migrationminion.New(s.config)
   154  	c.Assert(err, jc.ErrorIsNil)
   155  	defer workertest.CleanKill(c, w)
   156  
   157  	s.waitForStubCalls(c, []string{
   158  		"Watch",
   159  		"Lockdown",
   160  		"Report",
   161  	})
   162  	s.stub.CheckCall(c, 2, "Report", "id", migration.QUIESCE, true)
   163  }
   164  
   165  func (s *Suite) TestVALIDATION(c *gc.C) {
   166  	s.client.watcher.changes <- watcher.MigrationStatus{
   167  		MigrationId:    "id",
   168  		Phase:          migration.VALIDATION,
   169  		TargetAPIAddrs: addrs,
   170  		TargetCACert:   caCert,
   171  	}
   172  	w, err := migrationminion.New(s.config)
   173  	c.Assert(err, jc.ErrorIsNil)
   174  	defer workertest.CleanKill(c, w)
   175  
   176  	s.waitForStubCalls(c, []string{
   177  		"Watch",
   178  		"Lockdown",
   179  		"API open",
   180  		"ValidateMigration",
   181  		"API close",
   182  		"Report",
   183  	})
   184  	s.stub.CheckCall(c, 2, "API open", &api.Info{
   185  		ModelTag: modelTag,
   186  		Tag:      agentTag,
   187  		Password: agentPassword,
   188  		Addrs:    addrs,
   189  		CACert:   caCert,
   190  	})
   191  	s.stub.CheckCall(c, 5, "Report", "id", migration.VALIDATION, true)
   192  }
   193  
   194  func (s *Suite) TestVALIDATIONCantConnect(c *gc.C) {
   195  	s.client.watcher.changes <- watcher.MigrationStatus{
   196  		MigrationId: "id",
   197  		Phase:       migration.VALIDATION,
   198  	}
   199  	s.config.APIOpen = func(*api.Info, api.DialOpts) (api.Connection, error) {
   200  		s.stub.AddCall("API open")
   201  		return nil, errors.New("boom")
   202  	}
   203  	w, err := migrationminion.New(s.config)
   204  	c.Assert(err, jc.ErrorIsNil)
   205  	defer workertest.CleanKill(c, w)
   206  
   207  	s.waitForStubCalls(c, []string{
   208  		"Watch",
   209  		"Lockdown",
   210  		"API open",
   211  		"Report",
   212  	})
   213  	s.stub.CheckCall(c, 3, "Report", "id", migration.VALIDATION, false)
   214  }
   215  
   216  func (s *Suite) TestVALIDATIONFail(c *gc.C) {
   217  	s.client.watcher.changes <- watcher.MigrationStatus{
   218  		MigrationId: "id",
   219  		Phase:       migration.VALIDATION,
   220  	}
   221  	s.config.ValidateMigration = func(base.APICaller) error {
   222  		s.stub.AddCall("ValidateMigration")
   223  		return errors.New("boom")
   224  	}
   225  	w, err := migrationminion.New(s.config)
   226  	c.Assert(err, jc.ErrorIsNil)
   227  	defer workertest.CleanKill(c, w)
   228  
   229  	s.waitForStubCalls(c, []string{
   230  		"Watch",
   231  		"Lockdown",
   232  		"API open",
   233  		"ValidateMigration",
   234  		"API close",
   235  		"Report",
   236  	})
   237  	s.stub.CheckCall(c, 5, "Report", "id", migration.VALIDATION, false)
   238  }
   239  
   240  func (s *Suite) TestSUCCESS(c *gc.C) {
   241  	s.client.watcher.changes <- watcher.MigrationStatus{
   242  		MigrationId:    "id",
   243  		Phase:          migration.SUCCESS,
   244  		TargetAPIAddrs: addrs,
   245  		TargetCACert:   caCert,
   246  	}
   247  	w, err := migrationminion.New(s.config)
   248  	c.Assert(err, jc.ErrorIsNil)
   249  
   250  	select {
   251  	case <-s.agent.configChanged:
   252  	case <-time.After(coretesting.LongWait):
   253  		c.Fatal("timed out")
   254  	}
   255  	workertest.CleanKill(c, w)
   256  	c.Assert(s.agent.conf.addrs, gc.DeepEquals, addrs)
   257  	c.Assert(s.agent.conf.caCert, gc.DeepEquals, caCert)
   258  	s.stub.CheckCallNames(c, "Watch", "Lockdown", "Report")
   259  	s.stub.CheckCall(c, 2, "Report", "id", migration.SUCCESS, true)
   260  }
   261  
   262  func (s *Suite) waitForStubCalls(c *gc.C, expectedCallNames []string) {
   263  	var callNames []string
   264  	for a := coretesting.LongAttempt.Start(); a.Next(); {
   265  		callNames = stubCallNames(s.stub)
   266  		if reflect.DeepEqual(callNames, expectedCallNames) {
   267  			return
   268  		}
   269  	}
   270  	c.Fatalf("failed to see expected calls. saw: %v", callNames)
   271  }
   272  
   273  // Make this a feature of stub
   274  func stubCallNames(stub *jujutesting.Stub) []string {
   275  	var out []string
   276  	for _, call := range stub.Calls() {
   277  		out = append(out, call.FuncName)
   278  	}
   279  	return out
   280  }
   281  
   282  func newStubGuard(stub *jujutesting.Stub) *stubGuard {
   283  	return &stubGuard{stub: stub}
   284  }
   285  
   286  type stubGuard struct {
   287  	stub        *jujutesting.Stub
   288  	unlockErr   error
   289  	lockdownErr error
   290  }
   291  
   292  func (g *stubGuard) Lockdown(fortress.Abort) error {
   293  	g.stub.AddCall("Lockdown")
   294  	return g.lockdownErr
   295  }
   296  
   297  func (g *stubGuard) Unlock() error {
   298  	g.stub.AddCall("Unlock")
   299  	return g.unlockErr
   300  }
   301  
   302  func newStubMinionClient(stub *jujutesting.Stub) *stubMinionClient {
   303  	return &stubMinionClient{
   304  		stub:    stub,
   305  		watcher: newStubWatcher(),
   306  	}
   307  }
   308  
   309  type stubMinionClient struct {
   310  	stub     *jujutesting.Stub
   311  	watcher  *stubWatcher
   312  	watchErr error
   313  }
   314  
   315  func (c *stubMinionClient) Watch() (watcher.MigrationStatusWatcher, error) {
   316  	c.stub.MethodCall(c, "Watch")
   317  	if c.watchErr != nil {
   318  		return nil, c.watchErr
   319  	}
   320  	return c.watcher, nil
   321  }
   322  
   323  func (c *stubMinionClient) Report(id string, phase migration.Phase, success bool) error {
   324  	c.stub.MethodCall(c, "Report", id, phase, success)
   325  	return nil
   326  }
   327  
   328  func newStubWatcher() *stubWatcher {
   329  	return &stubWatcher{
   330  		Worker:  workertest.NewErrorWorker(nil),
   331  		changes: make(chan watcher.MigrationStatus, 1),
   332  	}
   333  }
   334  
   335  type stubWatcher struct {
   336  	worker.Worker
   337  	changes chan watcher.MigrationStatus
   338  }
   339  
   340  func (w *stubWatcher) Changes() <-chan watcher.MigrationStatus {
   341  	return w.changes
   342  }
   343  
   344  func newStubAgent() *stubAgent {
   345  	return &stubAgent{
   346  		configChanged: make(chan bool),
   347  	}
   348  }
   349  
   350  type stubAgent struct {
   351  	agent.Agent
   352  	configChanged chan bool
   353  	conf          stubAgentConfig
   354  }
   355  
   356  func (ma *stubAgent) CurrentConfig() agent.Config {
   357  	return &ma.conf
   358  }
   359  
   360  func (ma *stubAgent) ChangeConfig(f agent.ConfigMutator) error {
   361  	defer close(ma.configChanged)
   362  	return f(&ma.conf)
   363  }
   364  
   365  type stubAgentConfig struct {
   366  	agent.ConfigSetter
   367  
   368  	mu     sync.Mutex
   369  	addrs  []string
   370  	caCert string
   371  }
   372  
   373  func (mc *stubAgentConfig) SetAPIHostPorts(servers [][]network.HostPort) {
   374  	mc.mu.Lock()
   375  	defer mc.mu.Unlock()
   376  	mc.addrs = nil
   377  	for _, hps := range servers {
   378  		for _, hp := range hps {
   379  			mc.addrs = append(mc.addrs, hp.NetAddr())
   380  		}
   381  	}
   382  }
   383  
   384  func (mc *stubAgentConfig) SetCACert(cert string) {
   385  	mc.mu.Lock()
   386  	defer mc.mu.Unlock()
   387  	mc.caCert = cert
   388  }
   389  
   390  func (mc *stubAgentConfig) APIInfo() (*api.Info, bool) {
   391  	mc.mu.Lock()
   392  	defer mc.mu.Unlock()
   393  	return &api.Info{
   394  		Addrs:    mc.addrs,
   395  		CACert:   mc.caCert,
   396  		ModelTag: modelTag,
   397  		Tag:      agentTag,
   398  		Password: agentPassword,
   399  	}, true
   400  }
   401  
   402  type stubConnection struct {
   403  	api.Connection
   404  	stub *jujutesting.Stub
   405  }
   406  
   407  func (c *stubConnection) Close() error {
   408  	c.stub.AddCall("API close")
   409  	return nil
   410  }