github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/cmd/jujud/agent/unit_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package agent 5 6 import ( 7 "encoding/json" 8 "io/ioutil" 9 "os" 10 "path/filepath" 11 "reflect" 12 "time" 13 14 "github.com/juju/cmd" 15 "github.com/juju/cmd/cmdtesting" 16 jc "github.com/juju/testing/checkers" 17 "github.com/juju/utils/arch" 18 "github.com/juju/utils/series" 19 "github.com/juju/utils/voyeur" 20 "github.com/juju/version" 21 gc "gopkg.in/check.v1" 22 "gopkg.in/natefinch/lumberjack.v2" 23 24 "github.com/juju/juju/agent" 25 agenttools "github.com/juju/juju/agent/tools" 26 "github.com/juju/juju/cmd/jujud/agent/agenttest" 27 envtesting "github.com/juju/juju/environs/testing" 28 "github.com/juju/juju/network" 29 "github.com/juju/juju/state" 30 "github.com/juju/juju/status" 31 coretesting "github.com/juju/juju/testing" 32 "github.com/juju/juju/testing/factory" 33 "github.com/juju/juju/tools" 34 jujuversion "github.com/juju/juju/version" 35 "github.com/juju/juju/worker/upgrader" 36 ) 37 38 type UnitSuite struct { 39 coretesting.GitSuite 40 agenttest.AgentSuite 41 } 42 43 var _ = gc.Suite(&UnitSuite{}) 44 45 func (s *UnitSuite) SetUpSuite(c *gc.C) { 46 s.GitSuite.SetUpSuite(c) 47 s.AgentSuite.SetUpSuite(c) 48 } 49 50 func (s *UnitSuite) TearDownSuite(c *gc.C) { 51 s.AgentSuite.TearDownSuite(c) 52 s.GitSuite.TearDownSuite(c) 53 } 54 55 func (s *UnitSuite) SetUpTest(c *gc.C) { 56 s.GitSuite.SetUpTest(c) 57 s.AgentSuite.SetUpTest(c) 58 } 59 60 func (s *UnitSuite) TearDownTest(c *gc.C) { 61 s.AgentSuite.TearDownTest(c) 62 s.GitSuite.TearDownTest(c) 63 } 64 65 // primeAgent creates a unit, and sets up the unit agent's directory. 66 // It returns the assigned machine, new unit and the agent's configuration. 67 func (s *UnitSuite) primeAgent(c *gc.C) (*state.Machine, *state.Unit, agent.Config, *tools.Tools) { 68 machine := s.Factory.MakeMachine(c, nil) 69 app := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 70 unit := s.Factory.MakeUnit(c, &factory.UnitParams{ 71 Application: app, 72 Machine: machine, 73 Password: initialUnitPassword, 74 }) 75 conf, tools := s.PrimeAgent(c, unit.Tag(), initialUnitPassword) 76 return machine, unit, conf, tools 77 } 78 79 func (s *UnitSuite) newAgent(c *gc.C, unit *state.Unit) *UnitAgent { 80 a := NewUnitAgent(nil, nil) 81 s.InitAgent(c, a, "--unit-name", unit.Name(), "--log-to-stderr=true") 82 err := a.ReadConfig(unit.Tag().String()) 83 c.Assert(err, jc.ErrorIsNil) 84 return a 85 } 86 87 func (s *UnitSuite) TestParseSuccess(c *gc.C) { 88 a := NewUnitAgent(nil, nil) 89 err := coretesting.InitCommand(a, []string{ 90 "--data-dir", "jd", 91 "--unit-name", "w0rd-pre55/1", 92 "--log-to-stderr", 93 }) 94 95 c.Assert(err, gc.IsNil) 96 c.Check(a.AgentConf.DataDir(), gc.Equals, "jd") 97 c.Check(a.UnitName, gc.Equals, "w0rd-pre55/1") 98 } 99 100 func (s *UnitSuite) TestParseMissing(c *gc.C) { 101 uc := NewUnitAgent(nil, nil) 102 err := coretesting.InitCommand(uc, []string{ 103 "--data-dir", "jc", 104 }) 105 106 c.Assert(err, gc.ErrorMatches, "--unit-name option must be set") 107 } 108 109 func (s *UnitSuite) TestParseNonsense(c *gc.C) { 110 for _, args := range [][]string{ 111 {"--unit-name", "wordpress"}, 112 {"--unit-name", "wordpress/seventeen"}, 113 {"--unit-name", "wordpress/-32"}, 114 {"--unit-name", "wordpress/wild/9"}, 115 {"--unit-name", "20/20"}, 116 } { 117 err := coretesting.InitCommand(NewUnitAgent(nil, nil), append(args, "--data-dir", "jc")) 118 c.Check(err, gc.ErrorMatches, `--unit-name option expects "<service>/<n>" argument`) 119 } 120 } 121 122 func (s *UnitSuite) TestParseUnknown(c *gc.C) { 123 err := coretesting.InitCommand(NewUnitAgent(nil, nil), []string{ 124 "--unit-name", "wordpress/1", 125 "thundering typhoons", 126 }) 127 c.Check(err, gc.ErrorMatches, `unrecognized args: \["thundering typhoons"\]`) 128 } 129 130 func waitForUnitActive(stateConn *state.State, unit *state.Unit, c *gc.C) { 131 timeout := time.After(coretesting.LongWait) 132 133 for { 134 select { 135 case <-timeout: 136 c.Fatalf("no activity detected") 137 case <-time.After(coretesting.ShortWait): 138 err := unit.Refresh() 139 c.Assert(err, jc.ErrorIsNil) 140 statusInfo, err := unit.Status() 141 c.Assert(err, jc.ErrorIsNil) 142 switch statusInfo.Status { 143 case status.Maintenance, status.Waiting, status.Blocked: 144 c.Logf("waiting...") 145 continue 146 case status.Active: 147 c.Logf("active!") 148 return 149 case status.Unknown: 150 // Active units may have a status of unknown if they have 151 // started but not run status-set. 152 c.Logf("unknown but active!") 153 return 154 default: 155 c.Fatalf("unexpected status %s %s %v", statusInfo.Status, statusInfo.Message, statusInfo.Data) 156 } 157 statusInfo, err = unit.AgentStatus() 158 c.Assert(err, jc.ErrorIsNil) 159 switch statusInfo.Status { 160 case status.Allocating, status.Executing, status.Rebooting, status.Idle: 161 c.Logf("waiting...") 162 continue 163 case status.Error: 164 stateConn.StartSync() 165 c.Logf("unit is still down") 166 default: 167 c.Fatalf("unexpected status %s %s %v", statusInfo.Status, statusInfo.Message, statusInfo.Data) 168 } 169 } 170 } 171 } 172 173 func (s *UnitSuite) TestRunStop(c *gc.C) { 174 _, unit, _, _ := s.primeAgent(c) 175 a := s.newAgent(c, unit) 176 go func() { c.Check(a.Run(nil), gc.IsNil) }() 177 defer func() { c.Check(a.Stop(), gc.IsNil) }() 178 waitForUnitActive(s.State, unit, c) 179 } 180 181 func (s *UnitSuite) TestUpgrade(c *gc.C) { 182 machine, unit, _, currentTools := s.primeAgent(c) 183 agent := s.newAgent(c, unit) 184 newVers := version.Binary{ 185 Number: jujuversion.Current, 186 Arch: arch.HostArch(), 187 Series: series.HostSeries(), 188 } 189 newVers.Patch++ 190 envtesting.AssertUploadFakeToolsVersions( 191 c, s.DefaultToolsStorage, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(), newVers) 192 193 // The machine agent downloads the tools; fake this by 194 // creating downloaded-tools.txt in data-dir/tools/<version>. 195 toolsDir := agenttools.SharedToolsDir(s.DataDir(), newVers) 196 err := os.MkdirAll(toolsDir, 0755) 197 c.Assert(err, jc.ErrorIsNil) 198 toolsPath := filepath.Join(toolsDir, "downloaded-tools.txt") 199 testTools := tools.Tools{Version: newVers, URL: "http://testing.invalid/tools"} 200 data, err := json.Marshal(testTools) 201 c.Assert(err, jc.ErrorIsNil) 202 err = ioutil.WriteFile(toolsPath, data, 0644) 203 c.Assert(err, jc.ErrorIsNil) 204 205 // Set the machine agent version to trigger an upgrade. 206 err = machine.SetAgentVersion(newVers) 207 c.Assert(err, jc.ErrorIsNil) 208 err = runWithTimeout(agent) 209 envtesting.CheckUpgraderReadyError(c, err, &upgrader.UpgradeReadyError{ 210 AgentName: unit.Tag().String(), 211 OldTools: currentTools.Version, 212 NewTools: newVers, 213 DataDir: s.DataDir(), 214 }) 215 } 216 217 func (s *UnitSuite) TestUpgradeFailsWithoutTools(c *gc.C) { 218 machine, unit, _, _ := s.primeAgent(c) 219 agent := s.newAgent(c, unit) 220 newVers := version.Binary{ 221 Number: jujuversion.Current, 222 Arch: arch.HostArch(), 223 Series: series.HostSeries(), 224 } 225 newVers.Patch++ 226 err := machine.SetAgentVersion(newVers) 227 c.Assert(err, jc.ErrorIsNil) 228 err = runWithTimeout(agent) 229 c.Assert(err, gc.ErrorMatches, "timed out waiting for agent to finish.*") 230 } 231 232 func (s *UnitSuite) TestWithDeadUnit(c *gc.C) { 233 _, unit, _, _ := s.primeAgent(c) 234 err := unit.EnsureDead() 235 c.Assert(err, jc.ErrorIsNil) 236 a := s.newAgent(c, unit) 237 err = runWithTimeout(a) 238 c.Assert(err, jc.ErrorIsNil) 239 240 // try again when the unit has been removed. 241 err = unit.Remove() 242 c.Assert(err, jc.ErrorIsNil) 243 a = s.newAgent(c, unit) 244 err = runWithTimeout(a) 245 c.Assert(err, jc.ErrorIsNil) 246 } 247 248 func (s *UnitSuite) TestOpenStateFails(c *gc.C) { 249 // Start a unit agent and make sure it doesn't set a mongo password 250 // we can use to connect to state with. 251 _, unit, conf, _ := s.primeAgent(c) 252 a := s.newAgent(c, unit) 253 go func() { c.Check(a.Run(nil), gc.IsNil) }() 254 defer func() { c.Check(a.Stop(), gc.IsNil) }() 255 waitForUnitActive(s.State, unit, c) 256 257 s.AssertCannotOpenState(c, conf.Tag(), conf.DataDir()) 258 } 259 260 func (s *UnitSuite) TestAgentSetsToolsVersion(c *gc.C) { 261 _, unit, _, _ := s.primeAgent(c) 262 vers := version.Binary{ 263 Number: jujuversion.Current, 264 Arch: arch.HostArch(), 265 Series: series.HostSeries(), 266 } 267 vers.Minor++ 268 err := unit.SetAgentVersion(vers) 269 c.Assert(err, jc.ErrorIsNil) 270 271 a := s.newAgent(c, unit) 272 go func() { c.Check(a.Run(nil), gc.IsNil) }() 273 defer func() { c.Check(a.Stop(), gc.IsNil) }() 274 275 timeout := time.After(coretesting.LongWait) 276 for done := false; !done; { 277 select { 278 case <-timeout: 279 c.Fatalf("timeout while waiting for agent version to be set") 280 case <-time.After(coretesting.ShortWait): 281 err := unit.Refresh() 282 c.Assert(err, jc.ErrorIsNil) 283 agentTools, err := unit.AgentTools() 284 c.Assert(err, jc.ErrorIsNil) 285 if agentTools.Version.Minor != jujuversion.Current.Minor { 286 continue 287 } 288 current := version.Binary{ 289 Number: jujuversion.Current, 290 Arch: arch.HostArch(), 291 Series: series.HostSeries(), 292 } 293 c.Assert(agentTools.Version, gc.DeepEquals, current) 294 done = true 295 } 296 } 297 } 298 299 func (s *UnitSuite) TestUnitAgentRunsAPIAddressUpdaterWorker(c *gc.C) { 300 _, unit, _, _ := s.primeAgent(c) 301 a := s.newAgent(c, unit) 302 go func() { c.Check(a.Run(nil), gc.IsNil) }() 303 defer func() { c.Check(a.Stop(), gc.IsNil) }() 304 305 // Update the API addresses. 306 updatedServers := [][]network.HostPort{ 307 network.NewHostPorts(1234, "localhost"), 308 } 309 err := s.BackingState.SetAPIHostPorts(updatedServers) 310 c.Assert(err, jc.ErrorIsNil) 311 312 // Wait for config to be updated. 313 s.BackingState.StartSync() 314 for attempt := coretesting.LongAttempt.Start(); attempt.Next(); { 315 addrs, err := a.CurrentConfig().APIAddresses() 316 c.Assert(err, jc.ErrorIsNil) 317 if reflect.DeepEqual(addrs, []string{"localhost:1234"}) { 318 return 319 } 320 } 321 c.Fatalf("timeout while waiting for agent config to change") 322 } 323 324 func (s *UnitSuite) TestUseLumberjack(c *gc.C) { 325 ctx, err := cmd.DefaultContext() 326 c.Assert(err, gc.IsNil) 327 328 a := UnitAgent{ 329 AgentConf: FakeAgentConfig{}, 330 ctx: ctx, 331 UnitName: "mysql/25", 332 } 333 334 err = a.Init(nil) 335 c.Assert(err, gc.IsNil) 336 337 l, ok := ctx.Stderr.(*lumberjack.Logger) 338 c.Assert(ok, jc.IsTrue) 339 c.Check(l.MaxAge, gc.Equals, 0) 340 c.Check(l.MaxBackups, gc.Equals, 2) 341 c.Check(l.Filename, gc.Equals, filepath.FromSlash("/var/log/juju/machine-42.log")) 342 c.Check(l.MaxSize, gc.Equals, 300) 343 } 344 345 func (s *UnitSuite) TestDontUseLumberjack(c *gc.C) { 346 ctx, err := cmd.DefaultContext() 347 c.Assert(err, gc.IsNil) 348 349 a := UnitAgent{ 350 AgentConf: FakeAgentConfig{}, 351 ctx: ctx, 352 UnitName: "mysql/25", 353 354 // this is what would get set by the CLI flags to tell us not to log to 355 // the file. 356 logToStdErr: true, 357 } 358 359 err = a.Init(nil) 360 c.Assert(err, gc.IsNil) 361 362 _, ok := ctx.Stderr.(*lumberjack.Logger) 363 c.Assert(ok, jc.IsFalse) 364 } 365 366 func (s *UnitSuite) TestChangeConfig(c *gc.C) { 367 config := FakeAgentConfig{} 368 configChanged := voyeur.NewValue(true) 369 a := UnitAgent{ 370 AgentConf: config, 371 configChangedVal: configChanged, 372 } 373 374 var mutateCalled bool 375 mutate := func(config agent.ConfigSetter) error { 376 mutateCalled = true 377 return nil 378 } 379 380 configChangedCh := make(chan bool) 381 watcher := configChanged.Watch() 382 watcher.Next() // consume initial event 383 go func() { 384 configChangedCh <- watcher.Next() 385 }() 386 387 err := a.ChangeConfig(mutate) 388 c.Assert(err, jc.ErrorIsNil) 389 390 c.Check(mutateCalled, jc.IsTrue) 391 select { 392 case result := <-configChangedCh: 393 c.Check(result, jc.IsTrue) 394 case <-time.After(coretesting.LongWait): 395 c.Fatal("timed out waiting for config changed signal") 396 } 397 } 398 399 func (s *UnitSuite) TestWorkers(c *gc.C) { 400 tracker := NewEngineTracker() 401 instrumented := TrackUnits(c, tracker, unitManifolds) 402 s.PatchValue(&unitManifolds, instrumented) 403 404 _, unit, _, _ := s.primeAgent(c) 405 ctx := cmdtesting.Context(c) 406 a := NewUnitAgent(ctx, nil) 407 s.InitAgent(c, a, "--unit-name", unit.Name()) 408 409 go func() { c.Check(a.Run(nil), gc.IsNil) }() 410 defer func() { c.Check(a.Stop(), gc.IsNil) }() 411 412 matcher := NewWorkerMatcher(c, tracker, a.Tag().String(), 413 append(alwaysUnitWorkers, notMigratingUnitWorkers...)) 414 WaitMatch(c, matcher.Check, coretesting.LongWait, s.BackingState.StartSync) 415 }