github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/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" 9 "io/ioutil" 10 "os" 11 "path/filepath" 12 "reflect" 13 "time" 14 15 "github.com/juju/cmd" 16 "github.com/juju/errors" 17 "github.com/juju/names" 18 jc "github.com/juju/testing/checkers" 19 gc "gopkg.in/check.v1" 20 "gopkg.in/natefinch/lumberjack.v2" 21 22 "github.com/juju/juju/agent" 23 agenttools "github.com/juju/juju/agent/tools" 24 apirsyslog "github.com/juju/juju/api/rsyslog" 25 agenttesting "github.com/juju/juju/cmd/jujud/agent/testing" 26 cmdutil "github.com/juju/juju/cmd/jujud/util" 27 envtesting "github.com/juju/juju/environs/testing" 28 jujutesting "github.com/juju/juju/juju/testing" 29 "github.com/juju/juju/network" 30 "github.com/juju/juju/state" 31 coretesting "github.com/juju/juju/testing" 32 "github.com/juju/juju/tools" 33 "github.com/juju/juju/version" 34 "github.com/juju/juju/worker" 35 "github.com/juju/juju/worker/rsyslog" 36 "github.com/juju/juju/worker/upgrader" 37 ) 38 39 type UnitSuite struct { 40 coretesting.GitSuite 41 agenttesting.AgentSuite 42 } 43 44 var _ = gc.Suite(&UnitSuite{}) 45 46 func (s *UnitSuite) SetUpSuite(c *gc.C) { 47 s.GitSuite.SetUpSuite(c) 48 s.AgentSuite.SetUpSuite(c) 49 } 50 51 func (s *UnitSuite) TearDownSuite(c *gc.C) { 52 s.AgentSuite.TearDownSuite(c) 53 s.GitSuite.TearDownSuite(c) 54 } 55 56 func (s *UnitSuite) SetUpTest(c *gc.C) { 57 s.GitSuite.SetUpTest(c) 58 s.AgentSuite.SetUpTest(c) 59 } 60 61 func (s *UnitSuite) TearDownTest(c *gc.C) { 62 s.AgentSuite.TearDownTest(c) 63 s.GitSuite.TearDownTest(c) 64 } 65 66 const initialUnitPassword = "unit-password-1234567890" 67 68 // primeAgent creates a unit, and sets up the unit agent's directory. 69 // It returns the assigned machine, new unit and the agent's configuration. 70 func (s *UnitSuite) primeAgent(c *gc.C) (*state.Machine, *state.Unit, agent.Config, *tools.Tools) { 71 jujutesting.AddStateServerMachine(c, s.State) 72 svc := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 73 unit, err := svc.AddUnit() 74 c.Assert(err, jc.ErrorIsNil) 75 err = unit.SetPassword(initialUnitPassword) 76 c.Assert(err, jc.ErrorIsNil) 77 // Assign the unit to a machine. 78 err = unit.AssignToNewMachine() 79 c.Assert(err, jc.ErrorIsNil) 80 id, err := unit.AssignedMachineId() 81 c.Assert(err, jc.ErrorIsNil) 82 machine, err := s.State.Machine(id) 83 c.Assert(err, jc.ErrorIsNil) 84 inst, md := jujutesting.AssertStartInstance(c, s.Environ, id) 85 err = machine.SetProvisioned(inst.Id(), agent.BootstrapNonce, md) 86 c.Assert(err, jc.ErrorIsNil) 87 conf, tools := s.PrimeAgent(c, unit.Tag(), initialUnitPassword, version.Current) 88 return machine, unit, conf, tools 89 } 90 91 func (s *UnitSuite) newAgent(c *gc.C, unit *state.Unit) *UnitAgent { 92 a := NewUnitAgent(nil) 93 s.InitAgent(c, a, "--unit-name", unit.Name(), "--log-to-stderr=true") 94 err := a.ReadConfig(unit.Tag().String()) 95 c.Assert(err, jc.ErrorIsNil) 96 return a 97 } 98 99 func (s *UnitSuite) TestParseSuccess(c *gc.C) { 100 a := NewUnitAgent(nil) 101 err := coretesting.InitCommand(a, []string{ 102 "--data-dir", "jd", 103 "--unit-name", "w0rd-pre55/1", 104 "--log-to-stderr", 105 }) 106 107 c.Assert(err, gc.IsNil) 108 c.Check(a.AgentConf.DataDir(), gc.Equals, "jd") 109 c.Check(a.UnitName, gc.Equals, "w0rd-pre55/1") 110 } 111 112 func (s *UnitSuite) TestParseMissing(c *gc.C) { 113 uc := NewUnitAgent(nil) 114 err := coretesting.InitCommand(uc, []string{ 115 "--data-dir", "jc", 116 }) 117 118 c.Assert(err, gc.ErrorMatches, "--unit-name option must be set") 119 } 120 121 func (s *UnitSuite) TestParseNonsense(c *gc.C) { 122 for _, args := range [][]string{ 123 {"--unit-name", "wordpress"}, 124 {"--unit-name", "wordpress/seventeen"}, 125 {"--unit-name", "wordpress/-32"}, 126 {"--unit-name", "wordpress/wild/9"}, 127 {"--unit-name", "20/20"}, 128 } { 129 err := coretesting.InitCommand(NewUnitAgent(nil), append(args, "--data-dir", "jc")) 130 c.Check(err, gc.ErrorMatches, `--unit-name option expects "<service>/<n>" argument`) 131 } 132 } 133 134 func (s *UnitSuite) TestParseUnknown(c *gc.C) { 135 err := coretesting.InitCommand(NewUnitAgent(nil), []string{ 136 "--unit-name", "wordpress/1", 137 "thundering typhoons", 138 }) 139 c.Check(err, gc.ErrorMatches, `unrecognized args: \["thundering typhoons"\]`) 140 } 141 142 func waitForUnitActive(stateConn *state.State, unit *state.Unit, c *gc.C) { 143 timeout := time.After(5 * time.Second) 144 145 for { 146 select { 147 case <-timeout: 148 c.Fatalf("no activity detected") 149 case <-time.After(coretesting.ShortWait): 150 err := unit.Refresh() 151 c.Assert(err, jc.ErrorIsNil) 152 statusInfo, err := unit.Status() 153 c.Assert(err, jc.ErrorIsNil) 154 switch statusInfo.Status { 155 case state.StatusMaintenance, state.StatusWaiting, state.StatusBlocked: 156 c.Logf("waiting...") 157 continue 158 case state.StatusActive: 159 c.Logf("active!") 160 return 161 case state.StatusUnknown: 162 // Active units may have a status of unknown if they have 163 // started but not run status-set. 164 c.Logf("unknown but active!") 165 return 166 default: 167 c.Fatalf("unexpected status %s %s %v", statusInfo.Status, statusInfo.Message, statusInfo.Data) 168 } 169 statusInfo, err = unit.AgentStatus() 170 c.Assert(err, jc.ErrorIsNil) 171 switch statusInfo.Status { 172 case state.StatusAllocating, state.StatusExecuting, state.StatusRebooting, state.StatusIdle: 173 c.Logf("waiting...") 174 continue 175 case state.StatusError: 176 stateConn.StartSync() 177 c.Logf("unit is still down") 178 default: 179 c.Fatalf("unexpected status %s %s %v", statusInfo.Status, statusInfo.Message, statusInfo.Data) 180 } 181 } 182 } 183 } 184 185 func (s *UnitSuite) TestRunStop(c *gc.C) { 186 _, unit, _, _ := s.primeAgent(c) 187 a := s.newAgent(c, unit) 188 go func() { c.Check(a.Run(nil), gc.IsNil) }() 189 defer func() { c.Check(a.Stop(), gc.IsNil) }() 190 waitForUnitActive(s.State, unit, c) 191 } 192 193 func (s *UnitSuite) TestUpgrade(c *gc.C) { 194 machine, unit, _, currentTools := s.primeAgent(c) 195 agent := s.newAgent(c, unit) 196 newVers := version.Current 197 newVers.Patch++ 198 envtesting.AssertUploadFakeToolsVersions( 199 c, s.DefaultToolsStorage, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(), newVers) 200 201 // The machine agent downloads the tools; fake this by 202 // creating downloaded-tools.txt in data-dir/tools/<version>. 203 toolsDir := agenttools.SharedToolsDir(s.DataDir(), newVers) 204 err := os.MkdirAll(toolsDir, 0755) 205 c.Assert(err, jc.ErrorIsNil) 206 toolsPath := filepath.Join(toolsDir, "downloaded-tools.txt") 207 testTools := tools.Tools{Version: newVers, URL: "http://testing.invalid/tools"} 208 data, err := json.Marshal(testTools) 209 c.Assert(err, jc.ErrorIsNil) 210 err = ioutil.WriteFile(toolsPath, data, 0644) 211 c.Assert(err, jc.ErrorIsNil) 212 213 // Set the machine agent version to trigger an upgrade. 214 err = machine.SetAgentVersion(newVers) 215 c.Assert(err, jc.ErrorIsNil) 216 err = runWithTimeout(agent) 217 envtesting.CheckUpgraderReadyError(c, err, &upgrader.UpgradeReadyError{ 218 AgentName: unit.Tag().String(), 219 OldTools: currentTools.Version, 220 NewTools: newVers, 221 DataDir: s.DataDir(), 222 }) 223 } 224 225 func (s *UnitSuite) TestUpgradeFailsWithoutTools(c *gc.C) { 226 machine, unit, _, _ := s.primeAgent(c) 227 agent := s.newAgent(c, unit) 228 newVers := version.Current 229 newVers.Patch++ 230 err := machine.SetAgentVersion(newVers) 231 c.Assert(err, jc.ErrorIsNil) 232 err = runWithTimeout(agent) 233 c.Assert(err, gc.ErrorMatches, "timed out waiting for agent to finish.*") 234 } 235 236 func (s *UnitSuite) TestWithDeadUnit(c *gc.C) { 237 _, unit, _, _ := s.primeAgent(c) 238 err := unit.EnsureDead() 239 c.Assert(err, jc.ErrorIsNil) 240 a := s.newAgent(c, unit) 241 err = runWithTimeout(a) 242 c.Assert(err, jc.ErrorIsNil) 243 244 // try again when the unit has been removed. 245 err = unit.Remove() 246 c.Assert(err, jc.ErrorIsNil) 247 a = s.newAgent(c, unit) 248 err = runWithTimeout(a) 249 c.Assert(err, jc.ErrorIsNil) 250 } 251 252 func (s *UnitSuite) TestOpenAPIState(c *gc.C) { 253 _, unit, _, _ := s.primeAgent(c) 254 s.RunTestOpenAPIState(c, unit, s.newAgent(c, unit), initialUnitPassword) 255 } 256 257 func (s *UnitSuite) RunTestOpenAPIState(c *gc.C, ent state.AgentEntity, agentCmd Agent, initialPassword string) { 258 conf, err := agent.ReadConfig(agent.ConfigPath(s.DataDir(), ent.Tag())) 259 c.Assert(err, jc.ErrorIsNil) 260 261 conf.SetPassword("") 262 err = conf.Write() 263 c.Assert(err, jc.ErrorIsNil) 264 265 // Check that it starts initially and changes the password 266 assertOpen := func(conf agent.Config) { 267 st, gotEnt, err := OpenAPIState(conf, agentCmd) 268 c.Assert(err, jc.ErrorIsNil) 269 c.Assert(st, gc.NotNil) 270 st.Close() 271 c.Assert(gotEnt.Tag(), gc.Equals, ent.Tag().String()) 272 } 273 assertOpen(conf) 274 275 // Check that the initial password is no longer valid. 276 err = ent.Refresh() 277 c.Assert(err, jc.ErrorIsNil) 278 c.Assert(ent.PasswordValid(initialPassword), jc.IsFalse) 279 280 // Read the configuration and check that we can connect with it. 281 conf, err = agent.ReadConfig(agent.ConfigPath(conf.DataDir(), conf.Tag())) 282 //conf = refreshConfig(c, conf) 283 c.Assert(err, gc.IsNil) 284 // Check we can open the API with the new configuration. 285 assertOpen(conf) 286 } 287 288 func (s *UnitSuite) TestOpenAPIStateWithBadCredsTerminates(c *gc.C) { 289 conf, _ := s.PrimeAgent(c, names.NewUnitTag("missing/0"), "no-password", version.Current) 290 _, _, err := OpenAPIState(conf, nil) 291 c.Assert(err, gc.Equals, worker.ErrTerminateAgent) 292 } 293 294 type fakeUnitAgent struct { 295 unitName string 296 } 297 298 func (f *fakeUnitAgent) Tag() names.Tag { 299 return names.NewUnitTag(f.unitName) 300 } 301 302 func (f *fakeUnitAgent) ChangeConfig(agent.ConfigMutator) error { 303 panic("fakeUnitAgent.ChangeConfig called unexpectedly") 304 } 305 306 func (s *UnitSuite) TestOpenAPIStateWithDeadEntityTerminates(c *gc.C) { 307 _, unit, conf, _ := s.primeAgent(c) 308 err := unit.EnsureDead() 309 c.Assert(err, jc.ErrorIsNil) 310 _, _, err = OpenAPIState(conf, &fakeUnitAgent{"wordpress/0"}) 311 c.Assert(err, gc.Equals, worker.ErrTerminateAgent) 312 } 313 314 func (s *UnitSuite) TestOpenStateFails(c *gc.C) { 315 // Start a unit agent and make sure it doesn't set a mongo password 316 // we can use to connect to state with. 317 _, unit, conf, _ := s.primeAgent(c) 318 a := s.newAgent(c, unit) 319 go func() { c.Check(a.Run(nil), gc.IsNil) }() 320 defer func() { c.Check(a.Stop(), gc.IsNil) }() 321 waitForUnitActive(s.State, unit, c) 322 323 s.AssertCannotOpenState(c, conf.Tag(), conf.DataDir()) 324 } 325 326 func (s *UnitSuite) TestRsyslogConfigWorker(c *gc.C) { 327 created := make(chan rsyslog.RsyslogMode, 1) 328 s.PatchValue(&cmdutil.NewRsyslogConfigWorker, func(_ *apirsyslog.State, _ agent.Config, mode rsyslog.RsyslogMode) (worker.Worker, error) { 329 created <- mode 330 return newDummyWorker(), nil 331 }) 332 333 _, unit, _, _ := s.primeAgent(c) 334 a := s.newAgent(c, unit) 335 go func() { c.Check(a.Run(nil), gc.IsNil) }() 336 defer func() { c.Check(a.Stop(), gc.IsNil) }() 337 338 select { 339 case <-time.After(coretesting.LongWait): 340 c.Fatalf("timeout while waiting for rsyslog worker to be created") 341 case mode := <-created: 342 c.Assert(mode, gc.Equals, rsyslog.RsyslogModeForwarding) 343 } 344 } 345 346 func (s *UnitSuite) TestAgentSetsToolsVersion(c *gc.C) { 347 _, unit, _, _ := s.primeAgent(c) 348 vers := version.Current 349 vers.Minor = version.Current.Minor + 1 350 err := unit.SetAgentVersion(vers) 351 c.Assert(err, jc.ErrorIsNil) 352 353 a := s.newAgent(c, unit) 354 go func() { c.Check(a.Run(nil), gc.IsNil) }() 355 defer func() { c.Check(a.Stop(), gc.IsNil) }() 356 357 timeout := time.After(coretesting.LongWait) 358 for done := false; !done; { 359 select { 360 case <-timeout: 361 c.Fatalf("timeout while waiting for agent version to be set") 362 case <-time.After(coretesting.ShortWait): 363 err := unit.Refresh() 364 c.Assert(err, jc.ErrorIsNil) 365 agentTools, err := unit.AgentTools() 366 c.Assert(err, jc.ErrorIsNil) 367 if agentTools.Version.Minor != version.Current.Minor { 368 continue 369 } 370 c.Assert(agentTools.Version, gc.DeepEquals, version.Current) 371 done = true 372 } 373 } 374 } 375 376 func (s *UnitSuite) TestUnitAgentRunsAPIAddressUpdaterWorker(c *gc.C) { 377 _, unit, _, _ := s.primeAgent(c) 378 a := s.newAgent(c, unit) 379 go func() { c.Check(a.Run(nil), gc.IsNil) }() 380 defer func() { c.Check(a.Stop(), gc.IsNil) }() 381 382 // Update the API addresses. 383 updatedServers := [][]network.HostPort{ 384 network.NewHostPorts(1234, "localhost"), 385 } 386 err := s.BackingState.SetAPIHostPorts(updatedServers) 387 c.Assert(err, jc.ErrorIsNil) 388 389 // Wait for config to be updated. 390 s.BackingState.StartSync() 391 for attempt := coretesting.LongAttempt.Start(); attempt.Next(); { 392 addrs, err := a.CurrentConfig().APIAddresses() 393 c.Assert(err, jc.ErrorIsNil) 394 if reflect.DeepEqual(addrs, []string{"localhost:1234"}) { 395 return 396 } 397 } 398 c.Fatalf("timeout while waiting for agent config to change") 399 } 400 401 func (s *UnitSuite) TestUnitAgentAPIWorkerErrorClosesAPI(c *gc.C) { 402 _, unit, _, _ := s.primeAgent(c) 403 a := s.newAgent(c, unit) 404 a.apiStateUpgrader = &unitAgentUpgrader{} 405 406 closedAPI := make(chan io.Closer, 1) 407 s.AgentSuite.PatchValue(&reportClosedUnitAPI, func(st io.Closer) { 408 select { 409 case closedAPI <- st: 410 close(closedAPI) 411 default: 412 } 413 }) 414 415 worker, err := a.APIWorkers() 416 417 select { 418 case closed := <-closedAPI: 419 c.Assert(closed, gc.NotNil) 420 case <-time.After(coretesting.LongWait): 421 c.Fatalf("API not opened") 422 } 423 424 c.Assert(worker, gc.IsNil) 425 c.Assert(err, gc.ErrorMatches, "cannot set unit agent version: test failure") 426 } 427 428 type unitAgentUpgrader struct{} 429 430 func (u *unitAgentUpgrader) SetVersion(s string, v version.Binary) error { 431 return errors.New("test failure") 432 } 433 434 func (s *UnitSuite) TestUseLumberjack(c *gc.C) { 435 ctx, err := cmd.DefaultContext() 436 c.Assert(err, gc.IsNil) 437 438 a := UnitAgent{ 439 AgentConf: FakeAgentConfig{}, 440 ctx: ctx, 441 UnitName: "mysql/25", 442 } 443 444 err = a.Init(nil) 445 c.Assert(err, gc.IsNil) 446 447 l, ok := ctx.Stderr.(*lumberjack.Logger) 448 c.Assert(ok, jc.IsTrue) 449 c.Check(l.MaxAge, gc.Equals, 0) 450 c.Check(l.MaxBackups, gc.Equals, 2) 451 c.Check(l.Filename, gc.Equals, filepath.FromSlash("/var/log/juju/machine-42.log")) 452 c.Check(l.MaxSize, gc.Equals, 300) 453 } 454 455 func (s *UnitSuite) TestDontUseLumberjack(c *gc.C) { 456 ctx, err := cmd.DefaultContext() 457 c.Assert(err, gc.IsNil) 458 459 a := UnitAgent{ 460 AgentConf: FakeAgentConfig{}, 461 ctx: ctx, 462 UnitName: "mysql/25", 463 464 // this is what would get set by the CLI flags to tell us not to log to 465 // the file. 466 logToStdErr: true, 467 } 468 469 err = a.Init(nil) 470 c.Assert(err, gc.IsNil) 471 472 _, ok := ctx.Stderr.(*lumberjack.Logger) 473 c.Assert(ok, jc.IsFalse) 474 }