github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/apiserver/client/run_test.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package client_test 5 6 import ( 7 "fmt" 8 "time" 9 10 gitjujutesting "github.com/juju/testing" 11 jc "github.com/juju/testing/checkers" 12 "github.com/juju/utils/exec" 13 gc "gopkg.in/check.v1" 14 15 "github.com/juju/juju/apiserver/client" 16 "github.com/juju/juju/apiserver/params" 17 "github.com/juju/juju/network" 18 "github.com/juju/juju/state" 19 "github.com/juju/juju/testing" 20 "github.com/juju/juju/utils/ssh" 21 ) 22 23 type runSuite struct { 24 baseSuite 25 } 26 27 var _ = gc.Suite(&runSuite{}) 28 29 func (s *runSuite) addMachine(c *gc.C) *state.Machine { 30 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 31 c.Assert(err, jc.ErrorIsNil) 32 return machine 33 } 34 35 func (s *runSuite) addMachineWithAddress(c *gc.C, address string) *state.Machine { 36 machine := s.addMachine(c) 37 machine.SetProviderAddresses(network.NewAddress(address)) 38 return machine 39 } 40 41 func (s *runSuite) TestRemoteParamsForMachinePopulates(c *gc.C) { 42 machine := s.addMachine(c) 43 result := client.RemoteParamsForMachine(machine, "command", time.Minute) 44 c.Assert(result.Command, gc.Equals, "command") 45 c.Assert(result.Timeout, gc.Equals, time.Minute) 46 c.Assert(result.MachineId, gc.Equals, machine.Id()) 47 // Now an empty host isn't particularly useful, but the machine doesn't 48 // have an address to use. 49 c.Assert(machine.Addresses(), gc.HasLen, 0) 50 c.Assert(result.Host, gc.Equals, "") 51 } 52 53 func (s *runSuite) TestRemoteParamsForMachinePopulatesWithAddress(c *gc.C) { 54 machine := s.addMachineWithAddress(c, "10.3.2.1") 55 56 result := client.RemoteParamsForMachine(machine, "command", time.Minute) 57 c.Assert(result.Command, gc.Equals, "command") 58 c.Assert(result.Timeout, gc.Equals, time.Minute) 59 c.Assert(result.MachineId, gc.Equals, machine.Id()) 60 c.Assert(result.Host, gc.Equals, "ubuntu@10.3.2.1") 61 } 62 63 func (s *runSuite) addUnit(c *gc.C, service *state.Service) *state.Unit { 64 unit, err := service.AddUnit() 65 c.Assert(err, jc.ErrorIsNil) 66 err = unit.AssignToNewMachine() 67 c.Assert(err, jc.ErrorIsNil) 68 mId, err := unit.AssignedMachineId() 69 c.Assert(err, jc.ErrorIsNil) 70 machine, err := s.State.Machine(mId) 71 c.Assert(err, jc.ErrorIsNil) 72 machine.SetProviderAddresses(network.NewAddress("10.3.2.1")) 73 return unit 74 } 75 76 func (s *runSuite) TestGetAllUnitNames(c *gc.C) { 77 charm := s.AddTestingCharm(c, "dummy") 78 owner := s.AdminUserTag(c) 79 magic, err := s.State.AddService("magic", owner.String(), charm, nil, nil) 80 s.addUnit(c, magic) 81 s.addUnit(c, magic) 82 83 notAssigned, err := s.State.AddService("not-assigned", owner.String(), charm, nil, nil) 84 c.Assert(err, jc.ErrorIsNil) 85 _, err = notAssigned.AddUnit() 86 c.Assert(err, jc.ErrorIsNil) 87 88 _, err = s.State.AddService("no-units", owner.String(), charm, nil, nil) 89 c.Assert(err, jc.ErrorIsNil) 90 91 wordpress, err := s.State.AddService("wordpress", owner.String(), s.AddTestingCharm(c, "wordpress"), nil, nil) 92 c.Assert(err, jc.ErrorIsNil) 93 wordpress0 := s.addUnit(c, wordpress) 94 _, err = s.State.AddService("logging", owner.String(), s.AddTestingCharm(c, "logging"), nil, nil) 95 c.Assert(err, jc.ErrorIsNil) 96 97 eps, err := s.State.InferEndpoints("logging", "wordpress") 98 c.Assert(err, jc.ErrorIsNil) 99 rel, err := s.State.AddRelation(eps...) 100 c.Assert(err, jc.ErrorIsNil) 101 ru, err := rel.Unit(wordpress0) 102 c.Assert(err, jc.ErrorIsNil) 103 err = ru.EnterScope(nil) 104 c.Assert(err, jc.ErrorIsNil) 105 106 for i, test := range []struct { 107 message string 108 expected []string 109 units []string 110 services []string 111 error string 112 }{{ 113 message: "no units, expected nil slice", 114 }, { 115 message: "asking for a unit that isn't there", 116 units: []string{"foo/0"}, 117 error: `unit "foo/0" not found`, 118 }, { 119 message: "asking for a service that isn't there", 120 services: []string{"foo"}, 121 error: `service "foo" not found`, 122 }, { 123 message: "service with no units is not really an error", 124 services: []string{"no-units"}, 125 }, { 126 message: "A service with units not assigned is an error", 127 services: []string{"not-assigned"}, 128 error: `unit "not-assigned/0" is not assigned to a machine`, 129 }, { 130 message: "A service with units", 131 services: []string{"magic"}, 132 expected: []string{"magic/0", "magic/1"}, 133 }, { 134 message: "Asking for just a unit", 135 units: []string{"magic/0"}, 136 expected: []string{"magic/0"}, 137 }, { 138 message: "Asking for just a subordinate unit", 139 units: []string{"logging/0"}, 140 expected: []string{"logging/0"}, 141 }, { 142 message: "Asking for a unit, and the service", 143 services: []string{"magic"}, 144 units: []string{"magic/0"}, 145 expected: []string{"magic/0", "magic/1"}, 146 }} { 147 c.Logf("%v: %s", i, test.message) 148 result, err := client.GetAllUnitNames(s.State, test.units, test.services) 149 if test.error == "" { 150 c.Check(err, jc.ErrorIsNil) 151 var units []string 152 for _, unit := range result { 153 units = append(units, unit.Name()) 154 } 155 c.Check(units, jc.SameContents, test.expected) 156 } else { 157 c.Check(err, gc.ErrorMatches, test.error) 158 } 159 } 160 } 161 162 func (s *runSuite) mockSSH(c *gc.C, cmd string) { 163 gitjujutesting.PatchExecutable(c, s, "ssh", cmd) 164 gitjujutesting.PatchExecutable(c, s, "scp", cmd) 165 client, _ := ssh.NewOpenSSHClient() 166 s.PatchValue(&ssh.DefaultClient, client) 167 } 168 169 func (s *runSuite) TestParallelExecuteErrorsOnBlankHost(c *gc.C) { 170 s.mockSSH(c, echoInputShowArgs) 171 172 params := []*client.RemoteExec{ 173 { 174 ExecParams: ssh.ExecParams{ 175 Command: "foo", 176 Timeout: testing.LongWait, 177 }, 178 }, 179 } 180 181 runResults := client.ParallelExecute("/some/dir", params) 182 c.Assert(runResults.Results, gc.HasLen, 1) 183 result := runResults.Results[0] 184 c.Assert(result.Error, gc.Equals, "missing host address") 185 } 186 187 func (s *runSuite) TestParallelExecuteAddsIdentity(c *gc.C) { 188 s.mockSSH(c, echoInputShowArgs) 189 190 params := []*client.RemoteExec{ 191 { 192 ExecParams: ssh.ExecParams{ 193 Host: "localhost", 194 Command: "foo", 195 Timeout: testing.LongWait, 196 }, 197 }, 198 } 199 200 runResults := client.ParallelExecute("/some/dir", params) 201 c.Assert(runResults.Results, gc.HasLen, 1) 202 result := runResults.Results[0] 203 c.Assert(result.Error, gc.Equals, "") 204 c.Assert(string(result.Stderr), jc.Contains, "system-identity") 205 } 206 207 func (s *runSuite) TestParallelExecuteCopiesAcrossMachineAndUnit(c *gc.C) { 208 s.mockSSH(c, echoInputShowArgs) 209 210 params := []*client.RemoteExec{ 211 { 212 ExecParams: ssh.ExecParams{ 213 Host: "localhost", 214 Command: "foo", 215 Timeout: testing.LongWait, 216 }, 217 MachineId: "machine-id", 218 UnitId: "unit-id", 219 }, 220 } 221 222 runResults := client.ParallelExecute("/some/dir", params) 223 c.Assert(runResults.Results, gc.HasLen, 1) 224 result := runResults.Results[0] 225 c.Assert(result.Error, gc.Equals, "") 226 c.Assert(result.MachineId, gc.Equals, "machine-id") 227 c.Assert(result.UnitId, gc.Equals, "unit-id") 228 } 229 230 func (s *runSuite) TestRunOnAllMachines(c *gc.C) { 231 // Make three machines. 232 s.addMachineWithAddress(c, "10.3.2.1") 233 s.addMachineWithAddress(c, "10.3.2.2") 234 s.addMachineWithAddress(c, "10.3.2.3") 235 236 s.mockSSH(c, echoInput) 237 238 // hmm... this seems to be going through the api client, and from there 239 // through to the apiserver implementation. Not ideal, but it is how the 240 // other client tests are written. 241 client := s.APIState.Client() 242 results, err := client.RunOnAllMachines("hostname", testing.LongWait) 243 c.Assert(err, jc.ErrorIsNil) 244 c.Assert(results, gc.HasLen, 3) 245 246 var expectedResults []params.RunResult 247 for i := 0; i < 3; i++ { 248 expectedResults = append(expectedResults, 249 params.RunResult{ 250 ExecResponse: exec.ExecResponse{Stdout: []byte(expectedCommand[0])}, 251 252 MachineId: fmt.Sprint(i), 253 }) 254 } 255 256 c.Assert(results, jc.DeepEquals, expectedResults) 257 } 258 259 func (s *runSuite) TestBlockRunOnAllMachines(c *gc.C) { 260 // Make three machines. 261 s.addMachineWithAddress(c, "10.3.2.1") 262 s.addMachineWithAddress(c, "10.3.2.2") 263 s.addMachineWithAddress(c, "10.3.2.3") 264 265 s.mockSSH(c, echoInput) 266 267 // block all changes 268 s.BlockAllChanges(c, "TestBlockRunOnAllMachines") 269 _, err := s.APIState.Client().RunOnAllMachines("hostname", testing.LongWait) 270 s.AssertBlocked(c, err, "TestBlockRunOnAllMachines") 271 } 272 273 func (s *runSuite) TestRunMachineAndService(c *gc.C) { 274 // Make three machines. 275 s.addMachineWithAddress(c, "10.3.2.1") 276 277 charm := s.AddTestingCharm(c, "dummy") 278 owner := s.Factory.MakeUser(c, nil).Tag() 279 magic, err := s.State.AddService("magic", owner.String(), charm, nil, nil) 280 c.Assert(err, jc.ErrorIsNil) 281 s.addUnit(c, magic) 282 s.addUnit(c, magic) 283 284 s.mockSSH(c, echoInput) 285 286 // hmm... this seems to be going through the api client, and from there 287 // through to the apiserver implementation. Not ideal, but it is how the 288 // other client tests are written. 289 client := s.APIState.Client() 290 results, err := client.Run( 291 params.RunParams{ 292 Commands: "hostname", 293 Timeout: testing.LongWait, 294 Machines: []string{"0"}, 295 Services: []string{"magic"}, 296 }) 297 c.Assert(err, jc.ErrorIsNil) 298 c.Assert(results, gc.HasLen, 3) 299 300 expectedResults := []params.RunResult{ 301 { 302 ExecResponse: exec.ExecResponse{Stdout: []byte(expectedCommand[0])}, 303 MachineId: "0", 304 }, 305 { 306 ExecResponse: exec.ExecResponse{Stdout: []byte(expectedCommand[1])}, 307 MachineId: "1", 308 UnitId: "magic/0", 309 }, 310 { 311 ExecResponse: exec.ExecResponse{Stdout: []byte(expectedCommand[2])}, 312 MachineId: "2", 313 UnitId: "magic/1", 314 }, 315 } 316 317 c.Assert(results, jc.DeepEquals, expectedResults) 318 } 319 320 func (s *runSuite) TestBlockRunMachineAndService(c *gc.C) { 321 // Make three machines. 322 s.addMachineWithAddress(c, "10.3.2.1") 323 324 charm := s.AddTestingCharm(c, "dummy") 325 owner := s.Factory.MakeUser(c, nil).Tag() 326 magic, err := s.State.AddService("magic", owner.String(), charm, nil, nil) 327 c.Assert(err, jc.ErrorIsNil) 328 s.addUnit(c, magic) 329 s.addUnit(c, magic) 330 331 s.mockSSH(c, echoInput) 332 333 // hmm... this seems to be going through the api client, and from there 334 // through to the apiserver implementation. Not ideal, but it is how the 335 // other client tests are written. 336 client := s.APIState.Client() 337 338 // block all changes 339 s.BlockAllChanges(c, "TestBlockRunMachineAndService") 340 _, err = client.Run( 341 params.RunParams{ 342 Commands: "hostname", 343 Timeout: testing.LongWait, 344 Machines: []string{"0"}, 345 Services: []string{"magic"}, 346 }) 347 s.AssertBlocked(c, err, "TestBlockRunMachineAndService") 348 }