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