github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/cmd/jujud/unit_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package main 5 6 import ( 7 "reflect" 8 "time" 9 10 "github.com/juju/names" 11 gc "launchpad.net/gocheck" 12 13 "github.com/juju/juju/agent" 14 "github.com/juju/juju/cmd" 15 envtesting "github.com/juju/juju/environs/testing" 16 "github.com/juju/juju/instance" 17 jujutesting "github.com/juju/juju/juju/testing" 18 "github.com/juju/juju/state" 19 "github.com/juju/juju/state/api/params" 20 apirsyslog "github.com/juju/juju/state/api/rsyslog" 21 coretesting "github.com/juju/juju/testing" 22 "github.com/juju/juju/tools" 23 "github.com/juju/juju/version" 24 "github.com/juju/juju/worker" 25 "github.com/juju/juju/worker/rsyslog" 26 "github.com/juju/juju/worker/upgrader" 27 ) 28 29 type UnitSuite struct { 30 coretesting.GitSuite 31 agentSuite 32 } 33 34 var _ = gc.Suite(&UnitSuite{}) 35 36 func (s *UnitSuite) SetUpTest(c *gc.C) { 37 s.GitSuite.SetUpTest(c) 38 s.agentSuite.SetUpTest(c) 39 } 40 41 func (s *UnitSuite) TearDownTest(c *gc.C) { 42 s.agentSuite.TearDownTest(c) 43 s.GitSuite.TearDownTest(c) 44 } 45 46 const initialUnitPassword = "unit-password-1234567890" 47 48 // primeAgent creates a unit, and sets up the unit agent's directory. 49 // It returns the assigned machine, new unit and the agent's configuration. 50 func (s *UnitSuite) primeAgent(c *gc.C) (*state.Machine, *state.Unit, agent.Config, *tools.Tools) { 51 jujutesting.AddStateServerMachine(c, s.State) 52 svc := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 53 unit, err := svc.AddUnit() 54 c.Assert(err, gc.IsNil) 55 err = unit.SetPassword(initialUnitPassword) 56 c.Assert(err, gc.IsNil) 57 // Assign the unit to a machine. 58 err = unit.AssignToNewMachine() 59 c.Assert(err, gc.IsNil) 60 id, err := unit.AssignedMachineId() 61 c.Assert(err, gc.IsNil) 62 machine, err := s.State.Machine(id) 63 c.Assert(err, gc.IsNil) 64 conf, tools := s.agentSuite.primeAgent(c, unit.Tag(), initialUnitPassword, version.Current) 65 return machine, unit, conf, tools 66 } 67 68 func (s *UnitSuite) newAgent(c *gc.C, unit *state.Unit) *UnitAgent { 69 a := &UnitAgent{} 70 s.initAgent(c, a, "--unit-name", unit.Name()) 71 err := a.ReadConfig(unit.Tag()) 72 c.Assert(err, gc.IsNil) 73 return a 74 } 75 76 func (s *UnitSuite) TestParseSuccess(c *gc.C) { 77 create := func() (cmd.Command, *AgentConf) { 78 a := &UnitAgent{} 79 return a, &a.AgentConf 80 } 81 uc := CheckAgentCommand(c, create, []string{"--unit-name", "w0rd-pre55/1"}) 82 c.Assert(uc.(*UnitAgent).UnitName, gc.Equals, "w0rd-pre55/1") 83 } 84 85 func (s *UnitSuite) TestParseMissing(c *gc.C) { 86 uc := &UnitAgent{} 87 err := ParseAgentCommand(uc, []string{}) 88 c.Assert(err, gc.ErrorMatches, "--unit-name option must be set") 89 } 90 91 func (s *UnitSuite) TestParseNonsense(c *gc.C) { 92 for _, args := range [][]string{ 93 {"--unit-name", "wordpress"}, 94 {"--unit-name", "wordpress/seventeen"}, 95 {"--unit-name", "wordpress/-32"}, 96 {"--unit-name", "wordpress/wild/9"}, 97 {"--unit-name", "20/20"}, 98 } { 99 err := ParseAgentCommand(&UnitAgent{}, args) 100 c.Assert(err, gc.ErrorMatches, `--unit-name option expects "<service>/<n>" argument`) 101 } 102 } 103 104 func (s *UnitSuite) TestParseUnknown(c *gc.C) { 105 uc := &UnitAgent{} 106 err := ParseAgentCommand(uc, []string{"--unit-name", "wordpress/1", "thundering typhoons"}) 107 c.Assert(err, gc.ErrorMatches, `unrecognized args: \["thundering typhoons"\]`) 108 } 109 110 func waitForUnitStarted(stateConn *state.State, unit *state.Unit, c *gc.C) { 111 timeout := time.After(5 * time.Second) 112 113 for { 114 select { 115 case <-timeout: 116 c.Fatalf("no activity detected") 117 case <-time.After(coretesting.ShortWait): 118 err := unit.Refresh() 119 c.Assert(err, gc.IsNil) 120 st, info, data, err := unit.Status() 121 c.Assert(err, gc.IsNil) 122 switch st { 123 case params.StatusPending, params.StatusInstalled: 124 c.Logf("waiting...") 125 continue 126 case params.StatusStarted: 127 c.Logf("started!") 128 return 129 case params.StatusDown: 130 stateConn.StartSync() 131 c.Logf("unit is still down") 132 default: 133 c.Fatalf("unexpected status %s %s %v", st, info, data) 134 } 135 } 136 } 137 } 138 139 func (s *UnitSuite) TestRunStop(c *gc.C) { 140 _, unit, _, _ := s.primeAgent(c) 141 a := s.newAgent(c, unit) 142 go func() { c.Check(a.Run(nil), gc.IsNil) }() 143 defer func() { c.Check(a.Stop(), gc.IsNil) }() 144 waitForUnitStarted(s.State, unit, c) 145 } 146 147 func (s *UnitSuite) TestUpgrade(c *gc.C) { 148 machine, unit, _, currentTools := s.primeAgent(c) 149 agent := s.newAgent(c, unit) 150 newVers := version.Current 151 newVers.Patch++ 152 envtesting.AssertUploadFakeToolsVersions(c, s.Conn.Environ.Storage(), newVers) 153 154 // Set the machine agent version to trigger an upgrade. 155 err := machine.SetAgentVersion(newVers) 156 c.Assert(err, gc.IsNil) 157 err = runWithTimeout(agent) 158 envtesting.CheckUpgraderReadyError(c, err, &upgrader.UpgradeReadyError{ 159 AgentName: unit.Tag(), 160 OldTools: currentTools.Version, 161 NewTools: newVers, 162 DataDir: s.DataDir(), 163 }) 164 } 165 166 func (s *UnitSuite) TestUpgradeFailsWithoutTools(c *gc.C) { 167 machine, unit, _, _ := s.primeAgent(c) 168 agent := s.newAgent(c, unit) 169 newVers := version.Current 170 newVers.Patch++ 171 err := machine.SetAgentVersion(newVers) 172 c.Assert(err, gc.IsNil) 173 err = runWithTimeout(agent) 174 c.Assert(err, gc.ErrorMatches, "timed out waiting for agent to finish.*") 175 } 176 177 func (s *UnitSuite) TestWithDeadUnit(c *gc.C) { 178 _, unit, _, _ := s.primeAgent(c) 179 err := unit.EnsureDead() 180 c.Assert(err, gc.IsNil) 181 a := s.newAgent(c, unit) 182 err = runWithTimeout(a) 183 c.Assert(err, gc.IsNil) 184 185 // try again when the unit has been removed. 186 err = unit.Remove() 187 c.Assert(err, gc.IsNil) 188 a = s.newAgent(c, unit) 189 err = runWithTimeout(a) 190 c.Assert(err, gc.IsNil) 191 } 192 193 func (s *UnitSuite) TestOpenAPIState(c *gc.C) { 194 _, unit, _, _ := s.primeAgent(c) 195 s.testOpenAPIState(c, unit, s.newAgent(c, unit), initialUnitPassword) 196 } 197 198 func (s *UnitSuite) TestOpenAPIStateWithBadCredsTerminates(c *gc.C) { 199 conf, _ := s.agentSuite.primeAgent(c, "unit-missing-0", "no-password", version.Current) 200 _, _, err := openAPIState(conf, nil) 201 c.Assert(err, gc.Equals, worker.ErrTerminateAgent) 202 } 203 204 type fakeUnitAgent struct { 205 unitName string 206 } 207 208 func (f *fakeUnitAgent) Tag() string { 209 return names.UnitTag(f.unitName) 210 } 211 212 func (f *fakeUnitAgent) ChangeConfig(func(agent.ConfigSetter)) error { 213 panic("fakeUnitAgent.ChangeConfig called unexpectedly") 214 } 215 216 func (s *UnitSuite) TestOpenAPIStateWithDeadEntityTerminates(c *gc.C) { 217 _, unit, conf, _ := s.primeAgent(c) 218 err := unit.EnsureDead() 219 c.Assert(err, gc.IsNil) 220 _, _, err = openAPIState(conf, &fakeUnitAgent{"wordpress/0"}) 221 c.Assert(err, gc.Equals, worker.ErrTerminateAgent) 222 } 223 224 func (s *UnitSuite) TestOpenStateFails(c *gc.C) { 225 // Start a unit agent and make sure it doesn't set a mongo password 226 // we can use to connect to state with. 227 _, unit, conf, _ := s.primeAgent(c) 228 a := s.newAgent(c, unit) 229 go func() { c.Check(a.Run(nil), gc.IsNil) }() 230 defer func() { c.Check(a.Stop(), gc.IsNil) }() 231 waitForUnitStarted(s.State, unit, c) 232 233 s.assertCannotOpenState(c, conf.Tag(), conf.DataDir()) 234 } 235 236 func (s *UnitSuite) TestRsyslogConfigWorker(c *gc.C) { 237 created := make(chan rsyslog.RsyslogMode, 1) 238 s.PatchValue(&newRsyslogConfigWorker, func(_ *apirsyslog.State, _ agent.Config, mode rsyslog.RsyslogMode) (worker.Worker, error) { 239 created <- mode 240 return newDummyWorker(), nil 241 }) 242 243 _, unit, _, _ := s.primeAgent(c) 244 a := s.newAgent(c, unit) 245 go func() { c.Check(a.Run(nil), gc.IsNil) }() 246 defer func() { c.Check(a.Stop(), gc.IsNil) }() 247 248 select { 249 case <-time.After(coretesting.LongWait): 250 c.Fatalf("timeout while waiting for rsyslog worker to be created") 251 case mode := <-created: 252 c.Assert(mode, gc.Equals, rsyslog.RsyslogModeForwarding) 253 } 254 } 255 256 func (s *UnitSuite) TestUnitAgentRunsAPIAddressUpdaterWorker(c *gc.C) { 257 _, unit, _, _ := s.primeAgent(c) 258 a := s.newAgent(c, unit) 259 go func() { c.Check(a.Run(nil), gc.IsNil) }() 260 defer func() { c.Check(a.Stop(), gc.IsNil) }() 261 262 // Update the API addresses. 263 updatedServers := [][]instance.HostPort{instance.AddressesWithPort( 264 instance.NewAddresses("localhost"), 1234, 265 )} 266 err := s.BackingState.SetAPIHostPorts(updatedServers) 267 c.Assert(err, gc.IsNil) 268 269 // Wait for config to be updated. 270 s.BackingState.StartSync() 271 for attempt := coretesting.LongAttempt.Start(); attempt.Next(); { 272 addrs, err := a.CurrentConfig().APIAddresses() 273 c.Assert(err, gc.IsNil) 274 if reflect.DeepEqual(addrs, []string{"localhost:1234"}) { 275 return 276 } 277 } 278 c.Fatalf("timeout while waiting for agent config to change") 279 }