github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/caasoperator/caasoperator_test.go (about) 1 // Copyright 2017 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package caasoperator_test 5 6 import ( 7 "io/ioutil" 8 "net/url" 9 "os" 10 "path/filepath" 11 "time" 12 13 "github.com/juju/clock/testclock" 14 "github.com/juju/errors" 15 "github.com/juju/os/series" 16 "github.com/juju/testing" 17 jc "github.com/juju/testing/checkers" 18 "github.com/juju/utils" 19 "github.com/juju/utils/arch" 20 "github.com/juju/version" 21 gc "gopkg.in/check.v1" 22 "gopkg.in/juju/names.v2" 23 "gopkg.in/juju/worker.v1" 24 "gopkg.in/juju/worker.v1/workertest" 25 26 agenttools "github.com/juju/juju/agent/tools" 27 apiuniter "github.com/juju/juju/api/uniter" 28 "github.com/juju/juju/core/leadership" 29 "github.com/juju/juju/core/life" 30 "github.com/juju/juju/core/status" 31 "github.com/juju/juju/core/watcher" 32 "github.com/juju/juju/core/watcher/watchertest" 33 "github.com/juju/juju/downloader" 34 "github.com/juju/juju/testcharms" 35 coretesting "github.com/juju/juju/testing" 36 jujuversion "github.com/juju/juju/version" 37 "github.com/juju/juju/worker/caasoperator" 38 "github.com/juju/juju/worker/uniter" 39 runnertesting "github.com/juju/juju/worker/uniter/runner/testing" 40 ) 41 42 type WorkerSuite struct { 43 testing.IsolationSuite 44 45 clock *testclock.Clock 46 config caasoperator.Config 47 unitsChanges chan []string 48 appChanges chan struct{} 49 appWatched chan struct{} 50 unitRemoved chan struct{} 51 client fakeClient 52 charmDownloader fakeDownloader 53 charmSHA256 string 54 uniterParams *uniter.UniterParams 55 leadershipTrackerFunc func(unitTag names.UnitTag) leadership.Tracker 56 uniterFacadeFunc func(unitTag names.UnitTag) *apiuniter.State 57 } 58 59 var _ = gc.Suite(&WorkerSuite{}) 60 61 func (s *WorkerSuite) SetUpTest(c *gc.C) { 62 s.IsolationSuite.SetUpTest(c) 63 64 // Create a charm archive, and compute its SHA256 hash 65 // for comparison in the tests. 66 fakeDownloadDir := c.MkDir() 67 s.charmDownloader = fakeDownloader{ 68 path: testcharms.Repo.CharmArchivePath( 69 fakeDownloadDir, 70 "../kubernetes/gitlab", 71 ), 72 } 73 charmSHA256, _, err := utils.ReadFileSHA256(s.charmDownloader.path) 74 c.Assert(err, jc.ErrorIsNil) 75 s.charmSHA256 = charmSHA256 76 77 s.clock = testclock.NewClock(time.Time{}) 78 s.appWatched = make(chan struct{}, 1) 79 s.unitRemoved = make(chan struct{}, 1) 80 s.client = fakeClient{ 81 applicationWatched: s.appWatched, 82 unitRemoved: s.unitRemoved, 83 life: life.Alive, 84 } 85 s.unitsChanges = make(chan []string) 86 s.appChanges = make(chan struct{}) 87 s.client.unitsWatcher = watchertest.NewMockStringsWatcher(s.unitsChanges) 88 s.client.watcher = watchertest.NewMockNotifyWatcher(s.appChanges) 89 s.charmDownloader.ResetCalls() 90 s.uniterParams = &uniter.UniterParams{} 91 s.leadershipTrackerFunc = func(unitTag names.UnitTag) leadership.Tracker { 92 return &runnertesting.FakeTracker{} 93 } 94 s.uniterFacadeFunc = func(unitTag names.UnitTag) *apiuniter.State { 95 return &apiuniter.State{} 96 } 97 s.config = caasoperator.Config{ 98 Application: "gitlab", 99 CharmGetter: &s.client, 100 Clock: s.clock, 101 PodSpecSetter: &s.client, 102 DataDir: c.MkDir(), 103 Downloader: &s.charmDownloader, 104 StatusSetter: &s.client, 105 ApplicationWatcher: &s.client, 106 UnitGetter: &s.client, 107 UnitRemover: &s.client, 108 VersionSetter: &s.client, 109 UniterParams: s.uniterParams, 110 LeadershipTrackerFunc: s.leadershipTrackerFunc, 111 UniterFacadeFunc: s.uniterFacadeFunc, 112 StartUniterFunc: func(runner *worker.Runner, params *uniter.UniterParams) error { return nil }, 113 } 114 115 agentBinaryDir := agenttools.ToolsDir(s.config.DataDir, "application-gitlab") 116 err = os.MkdirAll(agentBinaryDir, 0755) 117 c.Assert(err, jc.ErrorIsNil) 118 err = ioutil.WriteFile(filepath.Join(s.config.DataDir, "tools", "jujud"), []byte("jujud"), 0755) 119 c.Assert(err, jc.ErrorIsNil) 120 } 121 122 func (s *WorkerSuite) TestValidateConfig(c *gc.C) { 123 s.testValidateConfig(c, func(config *caasoperator.Config) { 124 config.Application = "" 125 }, `application name "" not valid`) 126 127 s.testValidateConfig(c, func(config *caasoperator.Config) { 128 config.ApplicationWatcher = nil 129 }, `missing ApplicationWatcher not valid`) 130 131 s.testValidateConfig(c, func(config *caasoperator.Config) { 132 config.UnitGetter = nil 133 }, `missing UnitGetter not valid`) 134 135 s.testValidateConfig(c, func(config *caasoperator.Config) { 136 config.UnitRemover = nil 137 }, `missing UnitRemover not valid`) 138 139 s.testValidateConfig(c, func(config *caasoperator.Config) { 140 config.LeadershipTrackerFunc = nil 141 }, `missing LeadershipTrackerFunc not valid`) 142 143 s.testValidateConfig(c, func(config *caasoperator.Config) { 144 config.UniterFacadeFunc = nil 145 }, `missing UniterFacadeFunc not valid`) 146 147 s.testValidateConfig(c, func(config *caasoperator.Config) { 148 config.UniterParams = nil 149 }, `missing UniterParams not valid`) 150 151 s.testValidateConfig(c, func(config *caasoperator.Config) { 152 config.CharmGetter = nil 153 }, `missing CharmGetter not valid`) 154 155 s.testValidateConfig(c, func(config *caasoperator.Config) { 156 config.Clock = nil 157 }, `missing Clock not valid`) 158 159 s.testValidateConfig(c, func(config *caasoperator.Config) { 160 config.PodSpecSetter = nil 161 }, `missing PodSpecSetter not valid`) 162 163 s.testValidateConfig(c, func(config *caasoperator.Config) { 164 config.DataDir = "" 165 }, `missing DataDir not valid`) 166 167 s.testValidateConfig(c, func(config *caasoperator.Config) { 168 config.Downloader = nil 169 }, `missing Downloader not valid`) 170 171 s.testValidateConfig(c, func(config *caasoperator.Config) { 172 config.StatusSetter = nil 173 }, `missing StatusSetter not valid`) 174 175 s.testValidateConfig(c, func(config *caasoperator.Config) { 176 config.VersionSetter = nil 177 }, `missing VersionSetter not valid`) 178 179 } 180 181 func (s *WorkerSuite) testValidateConfig(c *gc.C, f func(*caasoperator.Config), expect string) { 182 config := s.config 183 f(&config) 184 w, err := caasoperator.NewWorker(config) 185 if err == nil { 186 workertest.DirtyKill(c, w) 187 } 188 c.Check(err, gc.ErrorMatches, expect) 189 } 190 191 func (s *WorkerSuite) TestStartStop(c *gc.C) { 192 w, err := caasoperator.NewWorker(s.config) 193 c.Assert(err, jc.ErrorIsNil) 194 workertest.CheckAlive(c, w) 195 workertest.CleanKill(c, w) 196 } 197 198 func (s *WorkerSuite) TestWorkerDownloadsCharm(c *gc.C) { 199 uniterStarted := make(chan struct{}) 200 s.config.StartUniterFunc = func(runner *worker.Runner, params *uniter.UniterParams) error { 201 c.Assert(params.UnitTag.Id(), gc.Equals, "gitlab/0") 202 close(uniterStarted) 203 return nil 204 } 205 206 w, err := caasoperator.NewWorker(s.config) 207 c.Assert(err, jc.ErrorIsNil) 208 defer workertest.CleanKill(c, w) 209 210 select { 211 case s.appChanges <- struct{}{}: 212 case <-time.After(coretesting.LongWait): 213 c.Fatal("timed out sending application change") 214 } 215 select { 216 case s.unitsChanges <- []string{"gitlab/0"}: 217 case <-time.After(coretesting.LongWait): 218 c.Fatal("timed out sending unit change") 219 } 220 select { 221 case <-s.appWatched: 222 case <-time.After(coretesting.LongWait): 223 c.Fatal("timed out waiting for application to be watched") 224 } 225 select { 226 case <-uniterStarted: 227 case <-time.After(coretesting.LongWait): 228 c.Fatalf("timeout while waiting for uniter to start") 229 } 230 231 s.client.CheckCallNames(c, "Charm", "SetStatus", "SetVersion", "WatchUnits", "SetStatus", "Watch", "Charm", "Life") 232 s.client.CheckCall(c, 0, "Charm", "gitlab") 233 s.client.CheckCall(c, 2, "SetVersion", "gitlab", version.Binary{ 234 Number: jujuversion.Current, 235 Series: series.MustHostSeries(), 236 Arch: arch.HostArch(), 237 }) 238 s.client.CheckCall(c, 3, "WatchUnits", "gitlab") 239 s.client.CheckCall(c, 5, "Watch", "gitlab") 240 241 s.charmDownloader.CheckCallNames(c, "Download") 242 downloadArgs := s.charmDownloader.Calls()[0].Args 243 c.Assert(downloadArgs, gc.HasLen, 1) 244 c.Assert(downloadArgs[0], gc.FitsTypeOf, downloader.Request{}) 245 downloadRequest := downloadArgs[0].(downloader.Request) 246 c.Assert(downloadRequest.Abort, gc.NotNil) 247 c.Assert(downloadRequest.Verify, gc.NotNil) 248 249 // fakeClient.Charm returns the SHA256 sum of fakeCharmContent. 250 fakeCharmPath := filepath.Join(c.MkDir(), "fake.charm") 251 err = ioutil.WriteFile(fakeCharmPath, fakeCharmContent, 0644) 252 c.Assert(err, jc.ErrorIsNil) 253 f, err := os.Open(fakeCharmPath) 254 c.Assert(err, jc.ErrorIsNil) 255 defer f.Close() 256 err = downloadRequest.Verify(f) 257 c.Assert(err, jc.ErrorIsNil) 258 259 downloadRequest.Abort = nil 260 downloadRequest.Verify = nil 261 agentDir := filepath.Join(s.config.DataDir, "agents", "application-gitlab") 262 c.Assert(downloadRequest, jc.DeepEquals, downloader.Request{ 263 URL: &url.URL{Scheme: "cs", Opaque: "gitlab-1"}, 264 TargetDir: filepath.Join(agentDir, "state", "bundles", "downloads"), 265 }) 266 267 // The download directory should have been removed. 268 _, err = os.Stat(downloadRequest.TargetDir) 269 c.Assert(err, jc.Satisfies, os.IsNotExist) 270 271 // The charm archive should have been unpacked into <data-dir>/charm. 272 charmDir := filepath.Join(agentDir, "charm") 273 _, err = os.Stat(filepath.Join(charmDir, "metadata.yaml")) 274 c.Assert(err, jc.ErrorIsNil) 275 276 } 277 278 func (s *WorkerSuite) assertUniterStarted(c *gc.C) (worker.Worker, watcher.NotifyChannel) { 279 applicationChannel := make(chan watcher.NotifyChannel) 280 s.config.StartUniterFunc = func(runner *worker.Runner, params *uniter.UniterParams) error { 281 c.Assert(params.UnitTag.Id(), gc.Equals, "gitlab/0") 282 select { 283 case applicationChannel <- params.ApplicationChannel: 284 case <-time.After(coretesting.LongWait): 285 c.Fatalf("timeout sending application channel") 286 } 287 return nil 288 } 289 290 w, err := caasoperator.NewWorker(s.config) 291 c.Assert(err, jc.ErrorIsNil) 292 293 select { 294 case s.appChanges <- struct{}{}: 295 case <-time.After(coretesting.LongWait): 296 c.Fatal("timed out sending application change") 297 } 298 select { 299 case s.unitsChanges <- []string{"gitlab/0"}: 300 case <-time.After(coretesting.LongWait): 301 c.Fatal("timed out sending unit change") 302 } 303 304 select { 305 case channel := <-applicationChannel: 306 return w, channel 307 case <-time.After(coretesting.LongWait): 308 c.Fatalf("timeout while waiting for uniter to start") 309 } 310 panic("not reachable") 311 } 312 313 func (s *WorkerSuite) TestUpgradeCharm(c *gc.C) { 314 w, applicationChannel := s.assertUniterStarted(c) 315 defer workertest.CleanKill(c, w) 316 317 select { 318 case <-applicationChannel: 319 c.Fatal("unexpected application change") 320 case <-time.After(coretesting.ShortWait): 321 } 322 323 select { 324 case s.appChanges <- struct{}{}: 325 case <-time.After(coretesting.LongWait): 326 c.Fatal("timed out sending application change") 327 } 328 329 select { 330 case <-applicationChannel: 331 case <-time.After(coretesting.LongWait): 332 c.Fatal("timed out waiting for application change") 333 } 334 } 335 336 func (s *WorkerSuite) TestWorkerSetsStatus(c *gc.C) { 337 w, err := caasoperator.NewWorker(s.config) 338 c.Assert(err, jc.ErrorIsNil) 339 workertest.CleanKill(c, w) 340 341 s.client.CheckCallNames(c, "Charm", "SetStatus", "SetVersion", "WatchUnits", "SetStatus", "Watch") 342 s.client.CheckCall(c, 1, "SetStatus", "gitlab", status.Maintenance, "downloading charm (cs:gitlab-1)", map[string]interface{}(nil)) 343 } 344 345 func (s *WorkerSuite) TestWatcherFailureStopsWorker(c *gc.C) { 346 w, err := caasoperator.NewWorker(s.config) 347 c.Assert(err, jc.ErrorIsNil) 348 defer workertest.DirtyKill(c, w) 349 350 s.client.unitsWatcher.KillErr(errors.New("splat")) 351 err = workertest.CheckKilled(c, w) 352 c.Assert(err, gc.ErrorMatches, "splat") 353 } 354 355 func (s *WorkerSuite) TestRemovedUnit(c *gc.C) { 356 w, _ := s.assertUniterStarted(c) 357 defer workertest.CleanKill(c, w) 358 359 s.client.ResetCalls() 360 s.client.life = life.Dead 361 select { 362 case s.unitsChanges <- []string{"gitlab/0"}: 363 case <-time.After(coretesting.LongWait): 364 c.Fatal("timed out sending unit change") 365 } 366 select { 367 case <-s.unitRemoved: 368 case <-time.After(coretesting.LongWait): 369 c.Fatal("timed out waiting for unit to be removed") 370 } 371 s.client.CheckCallNames(c, "Life", "RemoveUnit") 372 s.client.CheckCall(c, 0, "Life", "gitlab/0") 373 s.client.CheckCall(c, 1, "RemoveUnit", "gitlab/0") 374 }