github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/application_leader_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state_test
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	jc "github.com/juju/testing/checkers"
     9  	"github.com/juju/utils"
    10  	gc "gopkg.in/check.v1"
    11  	"gopkg.in/mgo.v2/txn"
    12  
    13  	"github.com/juju/juju/state"
    14  	"github.com/juju/juju/state/testing"
    15  	"github.com/juju/juju/testing/factory"
    16  )
    17  
    18  type ServiceLeaderSuite struct {
    19  	ConnSuite
    20  	service *state.Application
    21  }
    22  
    23  var _ = gc.Suite(&ServiceLeaderSuite{})
    24  
    25  func (s *ServiceLeaderSuite) SetUpTest(c *gc.C) {
    26  	s.ConnSuite.SetUpTest(c)
    27  	s.service = s.Factory.MakeApplication(c, nil)
    28  }
    29  
    30  func (s *ServiceLeaderSuite) TestReadEmpty(c *gc.C) {
    31  	s.checkSettings(c, map[string]string{})
    32  }
    33  
    34  func (s *ServiceLeaderSuite) TestWrite(c *gc.C) {
    35  	s.writeSettings(c, map[string]string{
    36  		"foo":     "bar",
    37  		"baz.qux": "ping",
    38  		"pong":    "",
    39  		"$unset":  "foo",
    40  	})
    41  
    42  	s.checkSettings(c, map[string]string{
    43  		"foo":     "bar",
    44  		"baz.qux": "ping",
    45  		// pong: "" value is ignored
    46  		"$unset": "foo",
    47  	})
    48  }
    49  
    50  func (s *ServiceLeaderSuite) TestOverwrite(c *gc.C) {
    51  	s.writeSettings(c, map[string]string{
    52  		"one":    "foo",
    53  		"2.0":    "bar",
    54  		"$three": "baz",
    55  		"fo-ur":  "qux",
    56  	})
    57  
    58  	s.writeSettings(c, map[string]string{
    59  		"one":    "",
    60  		"2.0":    "ping",
    61  		"$three": "pong",
    62  		"$unset": "2.0",
    63  	})
    64  
    65  	s.checkSettings(c, map[string]string{
    66  		// one: "" value is cleared
    67  		"2.0":    "ping",
    68  		"$three": "pong",
    69  		"fo-ur":  "qux",
    70  		"$unset": "2.0",
    71  	})
    72  }
    73  
    74  func (s *ServiceLeaderSuite) TestTxnRevnoChange(c *gc.C) {
    75  	defer state.SetBeforeHooks(c, s.State, func() {
    76  		s.writeSettings(c, map[string]string{
    77  			"other":   "values",
    78  			"slipped": "in",
    79  			"before":  "we",
    80  			"managed": "to",
    81  		})
    82  	}).Check()
    83  
    84  	s.writeSettings(c, map[string]string{
    85  		"but":       "we",
    86  		"overwrite": "those",
    87  		"before":    "",
    88  	})
    89  
    90  	s.checkSettings(c, map[string]string{
    91  		"other":     "values",
    92  		"slipped":   "in",
    93  		"but":       "we",
    94  		"managed":   "to",
    95  		"overwrite": "those",
    96  	})
    97  }
    98  
    99  func (s *ServiceLeaderSuite) TestTokenError(c *gc.C) {
   100  	err := s.service.UpdateLeaderSettings(&failToken{}, map[string]string{"blah": "blah"})
   101  	c.Check(err, gc.ErrorMatches, "prerequisites failed: something bad happened")
   102  }
   103  
   104  func (s *ServiceLeaderSuite) TestTokenAssertFailure(c *gc.C) {
   105  	err := s.service.UpdateLeaderSettings(&raceToken{}, map[string]string{"blah": "blah"})
   106  	c.Check(err, gc.ErrorMatches, "prerequisites failed: too late")
   107  }
   108  
   109  func (s *ServiceLeaderSuite) TestReadWriteDying(c *gc.C) {
   110  	s.preventRemove(c)
   111  	s.destroyService(c)
   112  
   113  	s.writeSettings(c, map[string]string{
   114  		"this":  "should",
   115  		"still": "work",
   116  	})
   117  	s.checkSettings(c, map[string]string{
   118  		"this":  "should",
   119  		"still": "work",
   120  	})
   121  }
   122  
   123  func (s *ServiceLeaderSuite) TestReadRemoved(c *gc.C) {
   124  	s.destroyService(c)
   125  
   126  	actual, err := s.service.LeaderSettings()
   127  	c.Check(err, gc.ErrorMatches, "application not found")
   128  	c.Check(err, jc.Satisfies, errors.IsNotFound)
   129  	c.Check(actual, gc.IsNil)
   130  }
   131  
   132  func (s *ServiceLeaderSuite) TestWriteRemoved(c *gc.C) {
   133  	s.destroyService(c)
   134  
   135  	err := s.service.UpdateLeaderSettings(&fakeToken{}, map[string]string{
   136  		"should": "fail",
   137  	})
   138  	c.Check(err, gc.ErrorMatches, "application not found")
   139  	c.Check(err, jc.Satisfies, errors.IsNotFound)
   140  }
   141  
   142  func (s *ServiceLeaderSuite) TestWatchInitialEvent(c *gc.C) {
   143  	w := s.service.WatchLeaderSettings()
   144  	defer testing.AssertStop(c, w)
   145  
   146  	wc := testing.NewNotifyWatcherC(c, s.State, w)
   147  	wc.AssertOneChange()
   148  }
   149  
   150  func (s *ServiceLeaderSuite) TestWatchDetectChange(c *gc.C) {
   151  	w := s.service.WatchLeaderSettings()
   152  	defer testing.AssertStop(c, w)
   153  	wc := testing.NewNotifyWatcherC(c, s.State, w)
   154  	wc.AssertOneChange()
   155  
   156  	err := s.service.UpdateLeaderSettings(&fakeToken{}, map[string]string{
   157  		"something": "changed",
   158  	})
   159  	c.Assert(err, jc.ErrorIsNil)
   160  	wc.AssertOneChange()
   161  }
   162  
   163  func (s *ServiceLeaderSuite) TestWatchIgnoreNullChange(c *gc.C) {
   164  	w := s.service.WatchLeaderSettings()
   165  	defer testing.AssertStop(c, w)
   166  	wc := testing.NewNotifyWatcherC(c, s.State, w)
   167  	wc.AssertOneChange()
   168  	err := s.service.UpdateLeaderSettings(&fakeToken{}, map[string]string{
   169  		"something": "changed",
   170  	})
   171  	c.Assert(err, jc.ErrorIsNil)
   172  	wc.AssertOneChange()
   173  
   174  	err = s.service.UpdateLeaderSettings(&fakeToken{}, map[string]string{
   175  		"something": "changed",
   176  	})
   177  	c.Assert(err, jc.ErrorIsNil)
   178  	wc.AssertNoChange()
   179  }
   180  
   181  func (s *ServiceLeaderSuite) TestWatchCoalesceChanges(c *gc.C) {
   182  	w := s.service.WatchLeaderSettings()
   183  	defer testing.AssertStop(c, w)
   184  	wc := testing.NewNotifyWatcherC(c, s.State, w)
   185  	wc.AssertOneChange()
   186  
   187  	err := s.service.UpdateLeaderSettings(&fakeToken{}, map[string]string{
   188  		"something": "changed",
   189  	})
   190  	c.Assert(err, jc.ErrorIsNil)
   191  	err = s.service.UpdateLeaderSettings(&fakeToken{}, map[string]string{
   192  		"very": "excitingly",
   193  	})
   194  	c.Assert(err, jc.ErrorIsNil)
   195  	wc.AssertOneChange()
   196  }
   197  
   198  func (s *ServiceLeaderSuite) writeSettings(c *gc.C, update map[string]string) {
   199  	err := s.service.UpdateLeaderSettings(&fakeToken{}, update)
   200  	c.Check(err, jc.ErrorIsNil)
   201  }
   202  
   203  func (s *ServiceLeaderSuite) checkSettings(c *gc.C, expect map[string]string) {
   204  	actual, err := s.service.LeaderSettings()
   205  	c.Check(err, jc.ErrorIsNil)
   206  	c.Check(actual, gc.DeepEquals, expect)
   207  }
   208  
   209  func (s *ServiceLeaderSuite) preventRemove(c *gc.C) {
   210  	s.Factory.MakeUnit(c, &factory.UnitParams{Application: s.service})
   211  }
   212  
   213  func (s *ServiceLeaderSuite) destroyService(c *gc.C) {
   214  	killService, err := s.State.Application(s.service.Name())
   215  	c.Assert(err, jc.ErrorIsNil)
   216  	err = killService.Destroy()
   217  	c.Assert(err, jc.ErrorIsNil)
   218  }
   219  
   220  // fakeToken implements leadership.Token.
   221  type fakeToken struct{}
   222  
   223  // Check is part of the leadership.Token interface. It always claims success,
   224  // and never checks or writes the userdata.
   225  func (*fakeToken) Check(interface{}) error {
   226  	return nil
   227  }
   228  
   229  // failToken implements leadership.Token.
   230  type failToken struct{}
   231  
   232  // Check is part of the leadership.Token interface. It always returns an error,
   233  // and never checks or writes the userdata.
   234  func (*failToken) Check(interface{}) error {
   235  	return errors.New("something bad happened")
   236  }
   237  
   238  // raceToken implements leadership.Token.
   239  type raceToken struct {
   240  	checkedOnce bool
   241  }
   242  
   243  // Check is part of the leadership.Token interface. On the first call, it expects
   244  // a *[]txn.Op, into which it will copy a failing assertion; on subsequent calls,
   245  // it just returns an error.
   246  func (t *raceToken) Check(out interface{}) error {
   247  	if t.checkedOnce {
   248  		return errors.New("too late")
   249  	}
   250  	t.checkedOnce = true
   251  	outPtr, ok := out.(*[]txn.Op)
   252  	if !ok {
   253  		return errors.Errorf("SUT passed in bad value: %#v", out)
   254  	}
   255  	wontExist := utils.MustNewUUID()
   256  	*outPtr = []txn.Op{{
   257  		C:      "units", // we have to use a collection defined in the schema
   258  		Id:     wontExist.String(),
   259  		Assert: txn.DocExists,
   260  	}}
   261  	return nil
   262  }