github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/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  	gc "gopkg.in/check.v1"
    10  
    11  	"github.com/juju/juju/state"
    12  	"github.com/juju/juju/state/testing"
    13  	"github.com/juju/juju/testing/factory"
    14  )
    15  
    16  type ApplicationLeaderSuite struct {
    17  	ConnSuite
    18  	application *state.Application
    19  }
    20  
    21  var _ = gc.Suite(&ApplicationLeaderSuite{})
    22  
    23  func (s *ApplicationLeaderSuite) SetUpTest(c *gc.C) {
    24  	s.ConnSuite.SetUpTest(c)
    25  	s.application = s.Factory.MakeApplication(c, nil)
    26  	// Before we get into the tests, ensure that all the creation events have flowed through the system.
    27  	s.WaitForModelWatchersIdle(c, s.Model.UUID())
    28  }
    29  
    30  func (s *ApplicationLeaderSuite) TestReadEmpty(c *gc.C) {
    31  	s.checkSettings(c, map[string]string{})
    32  }
    33  
    34  func (s *ApplicationLeaderSuite) 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 *ApplicationLeaderSuite) 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 *ApplicationLeaderSuite) 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 *ApplicationLeaderSuite) TestTokenError(c *gc.C) {
   100  	err := s.application.UpdateLeaderSettings(&failToken{}, map[string]string{"blah": "blah"})
   101  	c.Check(err, gc.ErrorMatches, `application "mysql": checking leadership continuity: something bad happened`)
   102  }
   103  
   104  func (s *ApplicationLeaderSuite) TestReadWriteDying(c *gc.C) {
   105  	s.preventRemove(c)
   106  	s.destroyApplication(c)
   107  
   108  	s.writeSettings(c, map[string]string{
   109  		"this":  "should",
   110  		"still": "work",
   111  	})
   112  	s.checkSettings(c, map[string]string{
   113  		"this":  "should",
   114  		"still": "work",
   115  	})
   116  }
   117  
   118  func (s *ApplicationLeaderSuite) TestReadRemoved(c *gc.C) {
   119  	s.destroyApplication(c)
   120  
   121  	actual, err := s.application.LeaderSettings()
   122  	c.Check(err, gc.ErrorMatches, `application "mysql" not found`)
   123  	c.Check(err, jc.Satisfies, errors.IsNotFound)
   124  	c.Check(actual, gc.IsNil)
   125  }
   126  
   127  func (s *ApplicationLeaderSuite) TestWriteRemoved(c *gc.C) {
   128  	s.destroyApplication(c)
   129  
   130  	err := s.application.UpdateLeaderSettings(&fakeToken{}, map[string]string{
   131  		"should": "fail",
   132  	})
   133  	c.Check(err, gc.ErrorMatches, `application "mysql" not found`)
   134  	c.Check(err, jc.Satisfies, errors.IsNotFound)
   135  }
   136  
   137  func (s *ApplicationLeaderSuite) TestWatchInitialEvent(c *gc.C) {
   138  	w := s.application.WatchLeaderSettings()
   139  	defer testing.AssertStop(c, w)
   140  
   141  	wc := testing.NewNotifyWatcherC(c, w)
   142  	wc.AssertOneChange()
   143  }
   144  
   145  func (s *ApplicationLeaderSuite) TestWatchDetectChange(c *gc.C) {
   146  	w := s.application.WatchLeaderSettings()
   147  	defer testing.AssertStop(c, w)
   148  	wc := testing.NewNotifyWatcherC(c, w)
   149  	wc.AssertOneChange()
   150  
   151  	err := s.application.UpdateLeaderSettings(&fakeToken{}, map[string]string{
   152  		"something": "changed",
   153  	})
   154  	c.Assert(err, jc.ErrorIsNil)
   155  	wc.AssertOneChange()
   156  }
   157  
   158  func (s *ApplicationLeaderSuite) TestWatchIgnoreNullChange(c *gc.C) {
   159  	w := s.application.WatchLeaderSettings()
   160  	defer testing.AssertStop(c, w)
   161  	wc := testing.NewNotifyWatcherC(c, w)
   162  	wc.AssertOneChange()
   163  	err := s.application.UpdateLeaderSettings(&fakeToken{}, map[string]string{
   164  		"something": "changed",
   165  	})
   166  	c.Assert(err, jc.ErrorIsNil)
   167  	wc.AssertOneChange()
   168  
   169  	err = s.application.UpdateLeaderSettings(&fakeToken{}, map[string]string{
   170  		"something": "changed",
   171  	})
   172  	c.Assert(err, jc.ErrorIsNil)
   173  	wc.AssertNoChange()
   174  }
   175  
   176  func (s *ApplicationLeaderSuite) TestWatchCoalesceChanges(c *gc.C) {
   177  	w := s.application.WatchLeaderSettings()
   178  	defer testing.AssertStop(c, w)
   179  	wc := testing.NewNotifyWatcherC(c, w)
   180  	wc.AssertOneChange()
   181  
   182  	err := s.application.UpdateLeaderSettings(&fakeToken{}, map[string]string{
   183  		"something": "changed",
   184  	})
   185  	c.Assert(err, jc.ErrorIsNil)
   186  	// TODO(quiescence): these two changes should be one event.
   187  	wc.AssertOneChange()
   188  	err = s.application.UpdateLeaderSettings(&fakeToken{}, map[string]string{
   189  		"very": "excitingly",
   190  	})
   191  	c.Assert(err, jc.ErrorIsNil)
   192  	wc.AssertOneChange()
   193  }
   194  
   195  func (s *ApplicationLeaderSuite) writeSettings(c *gc.C, update map[string]string) {
   196  	err := s.application.UpdateLeaderSettings(&fakeToken{}, update)
   197  	c.Check(err, jc.ErrorIsNil)
   198  }
   199  
   200  func (s *ApplicationLeaderSuite) checkSettings(c *gc.C, expect map[string]string) {
   201  	actual, err := s.application.LeaderSettings()
   202  	c.Check(err, jc.ErrorIsNil)
   203  	c.Check(actual, gc.DeepEquals, expect)
   204  }
   205  
   206  func (s *ApplicationLeaderSuite) preventRemove(c *gc.C) {
   207  	s.Factory.MakeUnit(c, &factory.UnitParams{Application: s.application})
   208  }
   209  
   210  func (s *ApplicationLeaderSuite) destroyApplication(c *gc.C) {
   211  	killApplication, err := s.State.Application(s.application.Name())
   212  	c.Assert(err, jc.ErrorIsNil)
   213  	err = killApplication.Destroy()
   214  	c.Assert(err, jc.ErrorIsNil)
   215  }
   216  
   217  // fakeToken implements leadership.Token.
   218  type fakeToken struct {
   219  	err error
   220  }
   221  
   222  // Check is part of the leadership.Token interface. It returns its
   223  // contained error (which defaults to nil), and never checks or writes
   224  // the userdata.
   225  func (t *fakeToken) Check() error {
   226  	return t.err
   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  func (*failToken) Check() error {
   234  	return errors.New("something bad happened")
   235  }