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