github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/api/watcher/watcher_test.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package watcher_test
     5  
     6  import (
     7  	"time"
     8  
     9  	jc "github.com/juju/testing/checkers"
    10  	"github.com/juju/utils"
    11  	gc "gopkg.in/check.v1"
    12  	"gopkg.in/juju/names.v2"
    13  
    14  	"github.com/juju/juju/api"
    15  	"github.com/juju/juju/api/migrationminion"
    16  	"github.com/juju/juju/api/watcher"
    17  	"github.com/juju/juju/apiserver/params"
    18  	"github.com/juju/juju/core/migration"
    19  	"github.com/juju/juju/juju/testing"
    20  	"github.com/juju/juju/state"
    21  	coretesting "github.com/juju/juju/testing"
    22  	"github.com/juju/juju/testing/factory"
    23  	corewatcher "github.com/juju/juju/watcher"
    24  	"github.com/juju/juju/watcher/watchertest"
    25  	"github.com/juju/juju/worker"
    26  )
    27  
    28  type watcherSuite struct {
    29  	testing.JujuConnSuite
    30  
    31  	stateAPI api.Connection
    32  
    33  	// These are raw State objects. Use them for setup and assertions, but
    34  	// should never be touched by the API calls themselves
    35  	rawMachine *state.Machine
    36  }
    37  
    38  var _ = gc.Suite(&watcherSuite{})
    39  
    40  func (s *watcherSuite) SetUpTest(c *gc.C) {
    41  	s.JujuConnSuite.SetUpTest(c)
    42  	s.stateAPI, s.rawMachine = s.OpenAPIAsNewMachine(c, state.JobManageModel, state.JobHostUnits)
    43  }
    44  
    45  func (s *watcherSuite) TestWatchInitialEventConsumed(c *gc.C) {
    46  	// Machiner.Watch should send the initial event as part of the Watch
    47  	// call (for NotifyWatchers there is no state to be transmitted). So a
    48  	// call to Next() should not have anything to return.
    49  	var results params.NotifyWatchResults
    50  	args := params.Entities{Entities: []params.Entity{{Tag: s.rawMachine.Tag().String()}}}
    51  	err := s.stateAPI.APICall("Machiner", s.stateAPI.BestFacadeVersion("Machiner"), "", "Watch", args, &results)
    52  	c.Assert(err, jc.ErrorIsNil)
    53  	c.Assert(results.Results, gc.HasLen, 1)
    54  	result := results.Results[0]
    55  	c.Assert(result.Error, gc.IsNil)
    56  
    57  	// We expect the Call() to "Next" to block, so run it in a goroutine.
    58  	done := make(chan error)
    59  	go func() {
    60  		ignored := struct{}{}
    61  		done <- s.stateAPI.APICall("NotifyWatcher", s.stateAPI.BestFacadeVersion("NotifyWatcher"), result.NotifyWatcherId, "Next", nil, &ignored)
    62  	}()
    63  
    64  	select {
    65  	case err := <-done:
    66  		c.Errorf("Call(Next) did not block immediately after Watch(): err %v", err)
    67  	case <-time.After(coretesting.ShortWait):
    68  	}
    69  }
    70  
    71  func (s *watcherSuite) TestWatchMachine(c *gc.C) {
    72  	var results params.NotifyWatchResults
    73  	args := params.Entities{Entities: []params.Entity{{Tag: s.rawMachine.Tag().String()}}}
    74  	err := s.stateAPI.APICall("Machiner", s.stateAPI.BestFacadeVersion("Machiner"), "", "Watch", args, &results)
    75  	c.Assert(err, jc.ErrorIsNil)
    76  	c.Assert(results.Results, gc.HasLen, 1)
    77  	result := results.Results[0]
    78  	c.Assert(result.Error, gc.IsNil)
    79  
    80  	w := watcher.NewNotifyWatcher(s.stateAPI, result)
    81  	wc := watchertest.NewNotifyWatcherC(c, w, s.BackingState.StartSync)
    82  	defer wc.AssertStops()
    83  	wc.AssertOneChange()
    84  }
    85  
    86  func (s *watcherSuite) TestNotifyWatcherStopsWithPendingSend(c *gc.C) {
    87  	var results params.NotifyWatchResults
    88  	args := params.Entities{Entities: []params.Entity{{Tag: s.rawMachine.Tag().String()}}}
    89  	err := s.stateAPI.APICall("Machiner", s.stateAPI.BestFacadeVersion("Machiner"), "", "Watch", args, &results)
    90  	c.Assert(err, jc.ErrorIsNil)
    91  	c.Assert(results.Results, gc.HasLen, 1)
    92  	result := results.Results[0]
    93  	c.Assert(result.Error, gc.IsNil)
    94  
    95  	// params.NotifyWatcher conforms to the watcher.NotifyWatcher interface
    96  	w := watcher.NewNotifyWatcher(s.stateAPI, result)
    97  	wc := watchertest.NewNotifyWatcherC(c, w, s.BackingState.StartSync)
    98  	wc.AssertStops()
    99  }
   100  
   101  func (s *watcherSuite) TestWatchUnitsKeepsEvents(c *gc.C) {
   102  	// Create two services, relate them, and add one unit to each - a
   103  	// principal and a subordinate.
   104  	mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql"))
   105  	s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging"))
   106  	eps, err := s.State.InferEndpoints("mysql", "logging")
   107  	c.Assert(err, jc.ErrorIsNil)
   108  	rel, err := s.State.AddRelation(eps...)
   109  	c.Assert(err, jc.ErrorIsNil)
   110  	principal, err := mysql.AddUnit()
   111  	c.Assert(err, jc.ErrorIsNil)
   112  	err = principal.AssignToMachine(s.rawMachine)
   113  	c.Assert(err, jc.ErrorIsNil)
   114  	relUnit, err := rel.Unit(principal)
   115  	c.Assert(err, jc.ErrorIsNil)
   116  	err = relUnit.EnterScope(nil)
   117  	c.Assert(err, jc.ErrorIsNil)
   118  	subordinate, err := s.State.Unit("logging/0")
   119  	c.Assert(err, jc.ErrorIsNil)
   120  
   121  	// Call the Deployer facade's WatchUnits for machine-0.
   122  	var results params.StringsWatchResults
   123  	args := params.Entities{Entities: []params.Entity{{Tag: s.rawMachine.Tag().String()}}}
   124  	err = s.stateAPI.APICall("Deployer", s.stateAPI.BestFacadeVersion("Deployer"), "", "WatchUnits", args, &results)
   125  	c.Assert(err, jc.ErrorIsNil)
   126  	c.Assert(results.Results, gc.HasLen, 1)
   127  	result := results.Results[0]
   128  	c.Assert(result.Error, gc.IsNil)
   129  
   130  	// Start a StringsWatcher and check the initial event.
   131  	w := watcher.NewStringsWatcher(s.stateAPI, result)
   132  	wc := watchertest.NewStringsWatcherC(c, w, s.BackingState.StartSync)
   133  	defer wc.AssertStops()
   134  
   135  	wc.AssertChange("mysql/0", "logging/0")
   136  	wc.AssertNoChange()
   137  
   138  	// Now, without reading any changes advance the lifecycle of both
   139  	// units, inducing an update server-side after each two changes to
   140  	// ensure they're reported as separate events over the API.
   141  	err = subordinate.EnsureDead()
   142  	c.Assert(err, jc.ErrorIsNil)
   143  	s.BackingState.StartSync()
   144  	err = subordinate.Remove()
   145  	c.Assert(err, jc.ErrorIsNil)
   146  	err = principal.EnsureDead()
   147  	c.Assert(err, jc.ErrorIsNil)
   148  	s.BackingState.StartSync()
   149  
   150  	// Expect these changes as 2 separate events, so that
   151  	// nothing gets lost.
   152  	wc.AssertChange("logging/0")
   153  	wc.AssertChange("mysql/0")
   154  	wc.AssertNoChange()
   155  }
   156  
   157  func (s *watcherSuite) TestStringsWatcherStopsWithPendingSend(c *gc.C) {
   158  	// Call the Deployer facade's WatchUnits for machine-0.
   159  	var results params.StringsWatchResults
   160  	args := params.Entities{Entities: []params.Entity{{Tag: s.rawMachine.Tag().String()}}}
   161  	err := s.stateAPI.APICall("Deployer", s.stateAPI.BestFacadeVersion("Deployer"), "", "WatchUnits", args, &results)
   162  	c.Assert(err, jc.ErrorIsNil)
   163  	c.Assert(results.Results, gc.HasLen, 1)
   164  	result := results.Results[0]
   165  	c.Assert(result.Error, gc.IsNil)
   166  
   167  	// Start a StringsWatcher and check the initial event.
   168  	w := watcher.NewStringsWatcher(s.stateAPI, result)
   169  	wc := watchertest.NewStringsWatcherC(c, w, s.BackingState.StartSync)
   170  	defer wc.AssertStops()
   171  
   172  	// Create a service, deploy a unit of it on the machine.
   173  	mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql"))
   174  	principal, err := mysql.AddUnit()
   175  	c.Assert(err, jc.ErrorIsNil)
   176  	err = principal.AssignToMachine(s.rawMachine)
   177  	c.Assert(err, jc.ErrorIsNil)
   178  }
   179  
   180  // TODO(fwereade): 2015-11-18 lp:1517391
   181  func (s *watcherSuite) TestWatchMachineStorage(c *gc.C) {
   182  	f := factory.NewFactory(s.BackingState)
   183  	f.MakeMachine(c, &factory.MachineParams{
   184  		Volumes: []state.MachineVolumeParams{{
   185  			Volume: state.VolumeParams{
   186  				Pool: "environscoped",
   187  				Size: 1024,
   188  			},
   189  		}},
   190  	})
   191  
   192  	var results params.MachineStorageIdsWatchResults
   193  	args := params.Entities{Entities: []params.Entity{{
   194  		Tag: s.State.ModelTag().String(),
   195  	}}}
   196  	err := s.stateAPI.APICall(
   197  		"StorageProvisioner",
   198  		s.stateAPI.BestFacadeVersion("StorageProvisioner"),
   199  		"", "WatchVolumeAttachments", args, &results)
   200  	c.Assert(err, jc.ErrorIsNil)
   201  	c.Assert(results.Results, gc.HasLen, 1)
   202  	result := results.Results[0]
   203  	c.Assert(result.Error, gc.IsNil)
   204  
   205  	w := watcher.NewVolumeAttachmentsWatcher(s.stateAPI, result)
   206  	defer func() {
   207  
   208  		// Check we can stop the watcher...
   209  		w.Kill()
   210  		wait := make(chan error)
   211  		go func() {
   212  			wait <- w.Wait()
   213  		}()
   214  		select {
   215  		case err := <-wait:
   216  			c.Assert(err, jc.ErrorIsNil)
   217  		case <-time.After(coretesting.LongWait):
   218  			c.Fatalf("watcher never stopped")
   219  		}
   220  
   221  		// ...and that its channel hasn't been closed.
   222  		s.BackingState.StartSync()
   223  		select {
   224  		case change, ok := <-w.Changes():
   225  			c.Fatalf("watcher sent unexpected change: (%#v, %v)", change, ok)
   226  		default:
   227  		}
   228  
   229  	}()
   230  
   231  	// Check initial event;
   232  	s.BackingState.StartSync()
   233  	select {
   234  	case changes, ok := <-w.Changes():
   235  		c.Assert(ok, jc.IsTrue)
   236  		c.Assert(changes, jc.SameContents, []corewatcher.MachineStorageId{{
   237  			MachineTag:    "machine-1",
   238  			AttachmentTag: "volume-0",
   239  		}})
   240  	case <-time.After(coretesting.LongWait):
   241  		c.Fatalf("timed out waiting for change")
   242  	}
   243  
   244  	// check no subsequent event.
   245  	s.BackingState.StartSync()
   246  	select {
   247  	case <-w.Changes():
   248  		c.Fatalf("received unexpected change")
   249  	case <-time.After(coretesting.ShortWait):
   250  	}
   251  }
   252  
   253  type migrationSuite struct {
   254  	testing.JujuConnSuite
   255  }
   256  
   257  var _ = gc.Suite(&migrationSuite{})
   258  
   259  func (s *migrationSuite) startSync(c *gc.C, st *state.State) {
   260  	backingSt, err := s.BackingStatePool.Get(st.ModelUUID())
   261  	c.Assert(err, jc.ErrorIsNil)
   262  	backingSt.StartSync()
   263  }
   264  
   265  func (s *migrationSuite) TestMigrationStatusWatcher(c *gc.C) {
   266  	const nonce = "noncey"
   267  
   268  	// Create a model to migrate.
   269  	hostedState := s.Factory.MakeModel(c, &factory.ModelParams{})
   270  	defer hostedState.Close()
   271  	hostedFactory := factory.NewFactory(hostedState)
   272  
   273  	// Create a machine in the hosted model to connect as.
   274  	m, password := hostedFactory.MakeMachineReturningPassword(c, &factory.MachineParams{
   275  		Nonce: nonce,
   276  	})
   277  
   278  	// Connect as the machine to watch for migration status.
   279  	apiInfo := s.APIInfo(c)
   280  	apiInfo.Tag = m.Tag()
   281  	apiInfo.Password = password
   282  	apiInfo.ModelTag = hostedState.ModelTag()
   283  	apiInfo.Nonce = nonce
   284  
   285  	apiConn, err := api.Open(apiInfo, api.DialOpts{})
   286  	c.Assert(err, jc.ErrorIsNil)
   287  	defer apiConn.Close()
   288  
   289  	// Start watching for a migration.
   290  	client := migrationminion.NewClient(apiConn)
   291  	w, err := client.Watch()
   292  	c.Assert(err, jc.ErrorIsNil)
   293  	defer func() {
   294  		c.Assert(worker.Stop(w), jc.ErrorIsNil)
   295  	}()
   296  
   297  	assertNoChange := func() {
   298  		s.startSync(c, hostedState)
   299  		select {
   300  		case _, ok := <-w.Changes():
   301  			c.Fatalf("watcher sent unexpected change: (_, %v)", ok)
   302  		case <-time.After(coretesting.ShortWait):
   303  		}
   304  	}
   305  
   306  	assertChange := func(id string, phase migration.Phase) {
   307  		s.startSync(c, hostedState)
   308  		select {
   309  		case status, ok := <-w.Changes():
   310  			c.Assert(ok, jc.IsTrue)
   311  			c.Check(status.MigrationId, gc.Equals, id)
   312  			c.Check(status.Phase, gc.Equals, phase)
   313  		case <-time.After(coretesting.LongWait):
   314  			c.Fatalf("watcher didn't emit an event")
   315  		}
   316  		assertNoChange()
   317  	}
   318  
   319  	// Initial event with no migration in progress.
   320  	assertChange("", migration.NONE)
   321  
   322  	// Now create a migration, should trigger watcher.
   323  	spec := state.MigrationSpec{
   324  		InitiatedBy: names.NewUserTag("someone"),
   325  		TargetInfo: migration.TargetInfo{
   326  			ControllerTag: names.NewControllerTag(utils.MustNewUUID().String()),
   327  			Addrs:         []string{"1.2.3.4:5"},
   328  			CACert:        "cert",
   329  			AuthTag:       names.NewUserTag("dog"),
   330  			Password:      "sekret",
   331  		},
   332  	}
   333  	mig, err := hostedState.CreateMigration(spec)
   334  	c.Assert(err, jc.ErrorIsNil)
   335  	assertChange(mig.Id(), migration.QUIESCE)
   336  
   337  	// Now abort the migration, this should be reported too.
   338  	c.Assert(mig.SetPhase(migration.ABORT), jc.ErrorIsNil)
   339  	assertChange(mig.Id(), migration.ABORT)
   340  	c.Assert(mig.SetPhase(migration.ABORTDONE), jc.ErrorIsNil)
   341  	assertChange(mig.Id(), migration.ABORTDONE)
   342  
   343  	// Start a new migration, this should also trigger.
   344  	mig2, err := hostedState.CreateMigration(spec)
   345  	c.Assert(err, jc.ErrorIsNil)
   346  	assertChange(mig2.Id(), migration.QUIESCE)
   347  }