github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/state/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 "io/ioutil" 9 "path/filepath" 10 "time" 11 12 gc "launchpad.net/gocheck" 13 14 "launchpad.net/juju-core/instance" 15 "launchpad.net/juju-core/state" 16 "launchpad.net/juju-core/state/api/params" 17 "launchpad.net/juju-core/state/apiserver/client" 18 "launchpad.net/juju-core/testing" 19 jc "launchpad.net/juju-core/testing/checkers" 20 "launchpad.net/juju-core/utils/exec" 21 "launchpad.net/juju-core/utils/ssh" 22 ) 23 24 type runSuite struct { 25 baseSuite 26 } 27 28 var _ = gc.Suite(&runSuite{}) 29 30 func (s *runSuite) addMachine(c *gc.C) *state.Machine { 31 machine, err := s.State.AddMachine("quantal", state.JobHostUnits) 32 c.Assert(err, gc.IsNil) 33 return machine 34 } 35 36 func (s *runSuite) addMachineWithAddress(c *gc.C, address string) *state.Machine { 37 machine := s.addMachine(c) 38 machine.SetAddresses([]instance.Address{instance.NewAddress(address)}) 39 return machine 40 } 41 42 func (s *runSuite) TestRemoteParamsForMachinePopulates(c *gc.C) { 43 machine := s.addMachine(c) 44 result := client.RemoteParamsForMachine(machine, "command", time.Minute) 45 c.Assert(result.Command, gc.Equals, "command") 46 c.Assert(result.Timeout, gc.Equals, time.Minute) 47 c.Assert(result.MachineId, gc.Equals, machine.Id()) 48 // Now an empty host isn't particularly useful, but the machine doesn't 49 // have an address to use. 50 c.Assert(machine.Addresses(), gc.HasLen, 0) 51 c.Assert(result.Host, gc.Equals, "") 52 } 53 54 func (s *runSuite) TestRemoteParamsForMachinePopulatesWithAddress(c *gc.C) { 55 machine := s.addMachineWithAddress(c, "10.3.2.1") 56 57 result := client.RemoteParamsForMachine(machine, "command", time.Minute) 58 c.Assert(result.Command, gc.Equals, "command") 59 c.Assert(result.Timeout, gc.Equals, time.Minute) 60 c.Assert(result.MachineId, gc.Equals, machine.Id()) 61 c.Assert(result.Host, gc.Equals, "ubuntu@10.3.2.1") 62 } 63 64 func (s *runSuite) addUnit(c *gc.C, service *state.Service) *state.Unit { 65 unit, err := service.AddUnit() 66 c.Assert(err, gc.IsNil) 67 err = unit.AssignToNewMachine() 68 c.Assert(err, gc.IsNil) 69 mId, err := unit.AssignedMachineId() 70 c.Assert(err, gc.IsNil) 71 machine, err := s.State.Machine(mId) 72 c.Assert(err, gc.IsNil) 73 machine.SetAddresses([]instance.Address{instance.NewAddress("10.3.2.1")}) 74 return unit 75 } 76 77 func (s *runSuite) TestGetAllUnitNames(c *gc.C) { 78 charm := s.AddTestingCharm(c, "dummy") 79 magic, err := s.State.AddService("magic", "user-admin", charm) 80 s.addUnit(c, magic) 81 s.addUnit(c, magic) 82 83 notAssigned, err := s.State.AddService("not-assigned", "user-admin", charm) 84 c.Assert(err, gc.IsNil) 85 _, err = notAssigned.AddUnit() 86 c.Assert(err, gc.IsNil) 87 88 _, err = s.State.AddService("no-units", "user-admin", charm) 89 c.Assert(err, gc.IsNil) 90 91 for i, test := range []struct { 92 message string 93 expected []string 94 units []string 95 services []string 96 error string 97 }{{ 98 message: "no units, expected nil slice", 99 }, { 100 message: "asking for a unit that isn't there", 101 units: []string{"foo/0"}, 102 error: `unit "foo/0" not found`, 103 }, { 104 message: "asking for a service that isn't there", 105 services: []string{"foo"}, 106 error: `service "foo" not found`, 107 }, { 108 message: "service with no units is not really an error", 109 services: []string{"no-units"}, 110 }, { 111 message: "A service with units not assigned is an error", 112 services: []string{"not-assigned"}, 113 error: `unit "not-assigned/0" is not assigned to a machine`, 114 }, { 115 message: "A service with units", 116 services: []string{"magic"}, 117 expected: []string{"magic/0", "magic/1"}, 118 }, { 119 message: "Asking for just a unit", 120 units: []string{"magic/0"}, 121 expected: []string{"magic/0"}, 122 }, { 123 message: "Asking for a unit, and the service", 124 services: []string{"magic"}, 125 units: []string{"magic/0"}, 126 expected: []string{"magic/0", "magic/1"}, 127 }} { 128 c.Logf("%v: %s", i, test.message) 129 result, err := client.GetAllUnitNames(s.State, test.units, test.services) 130 if test.error == "" { 131 c.Check(err, gc.IsNil) 132 var units []string 133 for _, unit := range result { 134 units = append(units, unit.Name()) 135 } 136 c.Check(units, jc.SameContents, test.expected) 137 } else { 138 c.Check(err, gc.ErrorMatches, test.error) 139 } 140 } 141 } 142 143 func (s *runSuite) mockSSH(c *gc.C, cmd string) { 144 testbin := c.MkDir() 145 fakessh := filepath.Join(testbin, "ssh") 146 s.PatchEnvPathPrepend(testbin) 147 err := ioutil.WriteFile(fakessh, []byte(cmd), 0755) 148 c.Assert(err, gc.IsNil) 149 } 150 151 func (s *runSuite) TestParallelExecuteErrorsOnBlankHost(c *gc.C) { 152 s.mockSSH(c, echoInputShowArgs) 153 154 params := []*client.RemoteExec{ 155 &client.RemoteExec{ 156 ExecParams: ssh.ExecParams{ 157 Command: "foo", 158 Timeout: testing.LongWait, 159 }, 160 }, 161 } 162 163 runResults := client.ParallelExecute("/some/dir", params) 164 c.Assert(runResults.Results, gc.HasLen, 1) 165 result := runResults.Results[0] 166 c.Assert(result.Error, gc.Equals, "missing host address") 167 } 168 169 func (s *runSuite) TestParallelExecuteAddsIdentity(c *gc.C) { 170 s.mockSSH(c, echoInputShowArgs) 171 172 params := []*client.RemoteExec{ 173 &client.RemoteExec{ 174 ExecParams: ssh.ExecParams{ 175 Host: "localhost", 176 Command: "foo", 177 Timeout: testing.LongWait, 178 }, 179 }, 180 } 181 182 runResults := client.ParallelExecute("/some/dir", params) 183 c.Assert(runResults.Results, gc.HasLen, 1) 184 result := runResults.Results[0] 185 c.Assert(result.Error, gc.Equals, "") 186 c.Assert(string(result.Stderr), jc.Contains, "-i /some/dir/system-identity") 187 } 188 189 func (s *runSuite) TestParallelExecuteCopiesAcrossMachineAndUnit(c *gc.C) { 190 s.mockSSH(c, echoInputShowArgs) 191 192 params := []*client.RemoteExec{ 193 &client.RemoteExec{ 194 ExecParams: ssh.ExecParams{ 195 Host: "localhost", 196 Command: "foo", 197 Timeout: testing.LongWait, 198 }, 199 MachineId: "machine-id", 200 UnitId: "unit-id", 201 }, 202 } 203 204 runResults := client.ParallelExecute("/some/dir", params) 205 c.Assert(runResults.Results, gc.HasLen, 1) 206 result := runResults.Results[0] 207 c.Assert(result.Error, gc.Equals, "") 208 c.Assert(result.MachineId, gc.Equals, "machine-id") 209 c.Assert(result.UnitId, gc.Equals, "unit-id") 210 } 211 212 func (s *runSuite) TestRunOnAllMachines(c *gc.C) { 213 // Make three machines. 214 s.addMachineWithAddress(c, "10.3.2.1") 215 s.addMachineWithAddress(c, "10.3.2.2") 216 s.addMachineWithAddress(c, "10.3.2.3") 217 218 s.mockSSH(c, echoInput) 219 220 // hmm... this seems to be going through the api client, and from there 221 // through to the apiserver implementation. Not ideal, but it is how the 222 // other client tests are written. 223 client := s.APIState.Client() 224 results, err := client.RunOnAllMachines("hostname", testing.LongWait) 225 c.Assert(err, gc.IsNil) 226 c.Assert(results, gc.HasLen, 3) 227 var expectedResults []params.RunResult 228 for i := 0; i < 3; i++ { 229 expectedResults = append(expectedResults, 230 params.RunResult{ 231 ExecResponse: exec.ExecResponse{Stdout: []byte("juju-run --no-context 'hostname'\n")}, 232 MachineId: fmt.Sprint(i), 233 }) 234 } 235 236 c.Assert(results, jc.DeepEquals, expectedResults) 237 } 238 239 func (s *runSuite) TestRunMachineAndService(c *gc.C) { 240 // Make three machines. 241 s.addMachineWithAddress(c, "10.3.2.1") 242 243 charm := s.AddTestingCharm(c, "dummy") 244 magic, err := s.State.AddService("magic", "user-admin", charm) 245 s.addUnit(c, magic) 246 s.addUnit(c, magic) 247 248 s.mockSSH(c, echoInput) 249 250 // hmm... this seems to be going through the api client, and from there 251 // through to the apiserver implementation. Not ideal, but it is how the 252 // other client tests are written. 253 client := s.APIState.Client() 254 results, err := client.Run( 255 params.RunParams{ 256 Commands: "hostname", 257 Timeout: testing.LongWait, 258 Machines: []string{"0"}, 259 Services: []string{"magic"}, 260 }) 261 c.Assert(err, gc.IsNil) 262 c.Assert(results, gc.HasLen, 3) 263 expectedResults := []params.RunResult{ 264 params.RunResult{ 265 ExecResponse: exec.ExecResponse{Stdout: []byte("juju-run --no-context 'hostname'\n")}, 266 MachineId: "0", 267 }, 268 params.RunResult{ 269 ExecResponse: exec.ExecResponse{Stdout: []byte("juju-run magic/0 'hostname'\n")}, 270 MachineId: "1", 271 UnitId: "magic/0", 272 }, 273 params.RunResult{ 274 ExecResponse: exec.ExecResponse{Stdout: []byte("juju-run magic/1 'hostname'\n")}, 275 MachineId: "2", 276 UnitId: "magic/1", 277 }, 278 } 279 280 c.Assert(results, jc.DeepEquals, expectedResults) 281 } 282 283 var echoInputShowArgs = `#!/bin/bash 284 # Write the args to stderr 285 echo "$*" >&2 286 # And echo stdin to stdout 287 while read line 288 do echo $line 289 done <&0 290 ` 291 292 var echoInput = `#!/bin/bash 293 # And echo stdin to stdout 294 while read line 295 do echo $line 296 done <&0 297 `