github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/core/machinelock/machinelock_test.go (about)

     1  // Copyright 2018 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package machinelock_test
     5  
     6  import (
     7  	"io/ioutil"
     8  	"path/filepath"
     9  	"time"
    10  
    11  	"github.com/juju/errors"
    12  	"github.com/juju/loggo"
    13  	"github.com/juju/mutex"
    14  	"github.com/juju/testing"
    15  	jc "github.com/juju/testing/checkers"
    16  	gc "gopkg.in/check.v1"
    17  
    18  	"github.com/juju/juju/core/machinelock"
    19  	jujutesting "github.com/juju/juju/testing"
    20  )
    21  
    22  type Lock interface {
    23  	Acquire(machinelock.Spec) (func(), error)
    24  	Report(...machinelock.ReportOption) (string, error)
    25  }
    26  
    27  type lockSuite struct {
    28  	testing.IsolationSuite
    29  	logfile string
    30  	clock   *fakeClock
    31  	lock    Lock
    32  
    33  	notify       chan struct{}
    34  	allowAcquire chan struct{}
    35  	release      chan struct{}
    36  }
    37  
    38  var _ = gc.Suite(&lockSuite{})
    39  
    40  func (s *lockSuite) SetUpTest(c *gc.C) {
    41  	s.IsolationSuite.SetUpTest(c)
    42  	s.clock = &fakeClock{time.Date(2018, 7, 10, 12, 0, 0, 0, time.UTC)}
    43  
    44  	s.logfile = filepath.Join(c.MkDir(), "logfile")
    45  
    46  	s.notify = make(chan struct{})
    47  	s.allowAcquire = make(chan struct{})
    48  	s.release = make(chan struct{})
    49  
    50  	lock, err := machinelock.NewTestLock(machinelock.Config{
    51  		AgentName:   "test",
    52  		Clock:       s.clock,
    53  		Logger:      loggo.GetLogger("test"),
    54  		LogFilename: s.logfile,
    55  	}, s.acquireLock)
    56  	c.Assert(err, jc.ErrorIsNil)
    57  	s.lock = lock
    58  
    59  	s.AddCleanup(func(c *gc.C) {
    60  		// release all the pending goroutines
    61  		close(s.allowAcquire)
    62  	})
    63  }
    64  
    65  func (s *lockSuite) TestEmptyOutput(c *gc.C) {
    66  	output, err := s.lock.Report()
    67  	c.Assert(err, jc.ErrorIsNil)
    68  	c.Assert(output, gc.Equals, `
    69  test:
    70    holder: none
    71  `[1:])
    72  
    73  	output, err = s.lock.Report(machinelock.ShowDetailsYAML)
    74  	c.Assert(err, jc.ErrorIsNil)
    75  	c.Assert(output, gc.Equals, `
    76  test:
    77    holder: null
    78  `[1:])
    79  }
    80  
    81  func (s *lockSuite) TestWaitingOutput(c *gc.C) {
    82  
    83  	s.addWaiting(c, "worker1", "being busy")
    84  	s.clock.Advance(time.Minute)
    85  	s.addWaiting(c, "worker", "")
    86  	s.clock.Advance(time.Minute)
    87  
    88  	output, err := s.lock.Report()
    89  	c.Assert(err, jc.ErrorIsNil)
    90  	c.Assert(output, gc.Equals, `
    91  test:
    92    holder: none
    93    waiting:
    94    - worker1 (being busy), waiting 2m0s
    95    - worker, waiting 1m0s
    96  `[1:])
    97  
    98  	output, err = s.lock.Report(machinelock.ShowDetailsYAML)
    99  	c.Assert(err, jc.ErrorIsNil)
   100  	c.Assert(output, gc.Equals, `
   101  test:
   102    holder: null
   103    waiting:
   104    - worker: worker1
   105      comment: being busy
   106      requested: 2018-07-10 12:00:00 +0000 UTC
   107      wait-time: 2m0s
   108    - worker: worker
   109      requested: 2018-07-10 12:01:00 +0000 UTC
   110      wait-time: 1m0s
   111  `[1:])
   112  }
   113  
   114  func (s *lockSuite) TestHoldingOutput(c *gc.C) {
   115  	s.addAcquired(c, "worker1", "being busy", 0)
   116  	s.clock.Advance(time.Minute * 2)
   117  
   118  	output, err := s.lock.Report()
   119  	c.Assert(err, jc.ErrorIsNil)
   120  	c.Assert(output, gc.Equals, `
   121  test:
   122    holder: worker1 (being busy), holding 2m0s
   123  `[1:])
   124  
   125  	output, err = s.lock.Report(machinelock.ShowDetailsYAML)
   126  	c.Assert(err, jc.ErrorIsNil)
   127  	c.Assert(output, gc.Equals, `
   128  test:
   129    holder:
   130      worker: worker1
   131      comment: being busy
   132      requested: 2018-07-10 12:00:00 +0000 UTC
   133      acquired: 2018-07-10 12:00:00 +0000 UTC
   134      hold-time: 2m0s
   135  `[1:])
   136  
   137  }
   138  
   139  func (s *lockSuite) TestHistoryOutput(c *gc.C) {
   140  	short := 5 * time.Second
   141  	long := 2*time.Minute + short
   142  	s.addHistory(c, "uniter", "config-changed", "2018-07-21 15:36:01", time.Second, long)
   143  	s.addHistory(c, "uniter", "update-status", "2018-07-21 15:37:05", time.Second, short)
   144  	s.addHistory(c, "uniter", "update-status", "2018-07-21 15:42:11", time.Second, short)
   145  	s.addHistory(c, "uniter", "update-status", "2018-07-21 15:47:13", time.Second, short)
   146  
   147  	output, err := s.lock.Report()
   148  	c.Assert(err, jc.ErrorIsNil)
   149  	c.Assert(output, gc.Equals, `
   150  test:
   151    holder: none
   152  `[1:])
   153  
   154  	output, err = s.lock.Report(machinelock.ShowHistory)
   155  	c.Assert(err, jc.ErrorIsNil)
   156  	c.Assert(output, gc.Equals, `
   157  test:
   158    holder: none
   159    history:
   160    - 2018-07-21 15:47:13 uniter (update-status), waited 1s, held 5s
   161    - 2018-07-21 15:42:11 uniter (update-status), waited 1s, held 5s
   162    - 2018-07-21 15:37:05 uniter (update-status), waited 1s, held 5s
   163    - 2018-07-21 15:36:01 uniter (config-changed), waited 1s, held 2m5s
   164  `[1:])
   165  
   166  	output, err = s.lock.Report(machinelock.ShowHistory, machinelock.ShowDetailsYAML)
   167  	c.Assert(err, jc.ErrorIsNil)
   168  	c.Assert(output, gc.Equals, `
   169  test:
   170    holder: null
   171    history:
   172    - worker: uniter
   173      comment: update-status
   174      requested: 2018-07-21 15:47:07 +0000 UTC
   175      acquired: 2018-07-21 15:47:08 +0000 UTC
   176      released: 2018-07-21 15:47:13 +0000 UTC
   177      wait-time: 1s
   178      hold-time: 5s
   179    - worker: uniter
   180      comment: update-status
   181      requested: 2018-07-21 15:42:05 +0000 UTC
   182      acquired: 2018-07-21 15:42:06 +0000 UTC
   183      released: 2018-07-21 15:42:11 +0000 UTC
   184      wait-time: 1s
   185      hold-time: 5s
   186    - worker: uniter
   187      comment: update-status
   188      requested: 2018-07-21 15:36:59 +0000 UTC
   189      acquired: 2018-07-21 15:37:00 +0000 UTC
   190      released: 2018-07-21 15:37:05 +0000 UTC
   191      wait-time: 1s
   192      hold-time: 5s
   193    - worker: uniter
   194      comment: config-changed
   195      requested: 2018-07-21 15:33:55 +0000 UTC
   196      acquired: 2018-07-21 15:33:56 +0000 UTC
   197      released: 2018-07-21 15:36:01 +0000 UTC
   198      wait-time: 1s
   199      hold-time: 2m5s
   200  `[1:])
   201  }
   202  
   203  func (s *lockSuite) TestLogfileOutput(c *gc.C) {
   204  	short := 5 * time.Second
   205  	long := 2*time.Minute + short
   206  	s.addHistory(c, "uniter", "config-changed", "2018-07-21 15:36:01", time.Second, long)
   207  	s.addHistory(c, "uniter", "update-status", "2018-07-21 15:37:05", time.Second, short)
   208  	s.addHistory(c, "uniter", "update-status", "2018-07-21 15:42:11", time.Second, short)
   209  	s.addHistory(c, "uniter", "update-status", "2018-07-21 15:47:13", time.Second, short)
   210  
   211  	content, err := ioutil.ReadFile(s.logfile)
   212  	c.Assert(err, jc.ErrorIsNil)
   213  
   214  	c.Assert(string(content), gc.Equals, `
   215  2018-07-10 12:00:00 === agent test started ===
   216  2018-07-21 15:36:01 test: uniter (config-changed), waited 1s, held 2m5s
   217  2018-07-21 15:37:05 test: uniter (update-status), waited 1s, held 5s
   218  2018-07-21 15:42:11 test: uniter (update-status), waited 1s, held 5s
   219  2018-07-21 15:47:13 test: uniter (update-status), waited 1s, held 5s
   220  `[1:])
   221  }
   222  
   223  func (s *lockSuite) addWaiting(c *gc.C, worker, comment string) {
   224  	go func() {
   225  		_, err := s.lock.Acquire(machinelock.Spec{
   226  			Cancel:  make(chan struct{}),
   227  			Worker:  worker,
   228  			Comment: comment,
   229  		})
   230  		c.Check(err, jc.ErrorIsNil)
   231  	}()
   232  
   233  	select {
   234  	case <-s.notify:
   235  	case <-time.After(jujutesting.LongWait):
   236  		c.Fatal("lock acquire didn't happen")
   237  	}
   238  }
   239  
   240  func (s *lockSuite) addAcquired(c *gc.C, worker, comment string, wait time.Duration) func() {
   241  	releaser := make(chan func())
   242  	go func() {
   243  		r, err := s.lock.Acquire(machinelock.Spec{
   244  			Cancel:  make(chan struct{}),
   245  			Worker:  worker,
   246  			Comment: comment,
   247  		})
   248  		c.Check(err, jc.ErrorIsNil)
   249  		releaser <- r
   250  	}()
   251  
   252  	select {
   253  	case <-s.notify:
   254  	case <-time.After(jujutesting.LongWait):
   255  		c.Fatal("lock acquire didn't happen")
   256  	}
   257  	s.clock.Advance(wait)
   258  	select {
   259  	case s.allowAcquire <- struct{}{}:
   260  	case <-time.After(jujutesting.LongWait):
   261  		c.Fatal("lock acquire didn't advance")
   262  	}
   263  	select {
   264  	case r := <-releaser:
   265  		return r
   266  	case <-time.After(jujutesting.LongWait):
   267  		c.Fatal("no releaser returned")
   268  	}
   269  	panic("unreachable")
   270  }
   271  
   272  // This method needs the released time to be after the current suite clock time.
   273  func (s *lockSuite) addHistory(c *gc.C, worker, comment string, released string, waited, held time.Duration) {
   274  	releasedTime, err := time.Parse("2006-01-02 15:04:05", released)
   275  	c.Assert(err, jc.ErrorIsNil)
   276  	// First, advance the lock to the request time.
   277  	diff := releasedTime.Sub(s.clock.Now())
   278  	diff -= waited + held
   279  	s.clock.Advance(diff)
   280  	releaser := s.addAcquired(c, worker, comment, waited)
   281  	s.clock.Advance(held)
   282  	releaser()
   283  }
   284  
   285  func (s *lockSuite) acquireLock(spec mutex.Spec) (mutex.Releaser, error) {
   286  	s.notify <- struct{}{}
   287  	select {
   288  	case <-s.allowAcquire:
   289  	case <-spec.Cancel:
   290  		return nil, errors.New("cancelled")
   291  	}
   292  	return noOpReleaser{}, nil
   293  }
   294  
   295  type noOpReleaser struct{}
   296  
   297  func (noOpReleaser) Release() {}
   298  
   299  type fakeClock struct {
   300  	now time.Time
   301  }
   302  
   303  func (f *fakeClock) Now() time.Time {
   304  	return f.now
   305  }
   306  
   307  func (f *fakeClock) Advance(d time.Duration) {
   308  	f.now = f.now.Add(d)
   309  }
   310  
   311  // This function is necessary for the interface that the mutex package
   312  // requires for the clock, but this isn't used in this test's suite as
   313  // we are mocking out the acquire function.
   314  func (f *fakeClock) After(time.Duration) <-chan time.Time {
   315  	return nil
   316  }