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 }