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