launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/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 gc "launchpad.net/gocheck" 10 "os" 11 "path/filepath" 12 "time" 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 newPath := testbin + ":" + os.Getenv("PATH") 147 s.PatchEnvironment("PATH", newPath) 148 err := ioutil.WriteFile(fakessh, []byte(cmd), 0755) 149 c.Assert(err, gc.IsNil) 150 } 151 152 func (s *runSuite) TestParallelExecuteErrorsOnBlankHost(c *gc.C) { 153 s.mockSSH(c, echoInputShowArgs) 154 155 params := []*client.RemoteExec{ 156 &client.RemoteExec{ 157 ExecParams: ssh.ExecParams{ 158 Command: "foo", 159 Timeout: testing.LongWait, 160 }, 161 }, 162 } 163 164 runResults := client.ParallelExecute("/some/dir", params) 165 c.Assert(runResults.Results, gc.HasLen, 1) 166 result := runResults.Results[0] 167 c.Assert(result.Error, gc.Equals, "missing host address") 168 } 169 170 func (s *runSuite) TestParallelExecuteAddsIdentity(c *gc.C) { 171 s.mockSSH(c, echoInputShowArgs) 172 173 params := []*client.RemoteExec{ 174 &client.RemoteExec{ 175 ExecParams: ssh.ExecParams{ 176 Host: "localhost", 177 Command: "foo", 178 Timeout: testing.LongWait, 179 }, 180 }, 181 } 182 183 runResults := client.ParallelExecute("/some/dir", params) 184 c.Assert(runResults.Results, gc.HasLen, 1) 185 result := runResults.Results[0] 186 c.Assert(result.Error, gc.Equals, "") 187 c.Assert(string(result.Stderr), jc.Contains, "-i /some/dir/system-identity") 188 } 189 190 func (s *runSuite) TestParallelExecuteCopiesAcrossMachineAndUnit(c *gc.C) { 191 s.mockSSH(c, echoInputShowArgs) 192 193 params := []*client.RemoteExec{ 194 &client.RemoteExec{ 195 ExecParams: ssh.ExecParams{ 196 Host: "localhost", 197 Command: "foo", 198 Timeout: testing.LongWait, 199 }, 200 MachineId: "machine-id", 201 UnitId: "unit-id", 202 }, 203 } 204 205 runResults := client.ParallelExecute("/some/dir", params) 206 c.Assert(runResults.Results, gc.HasLen, 1) 207 result := runResults.Results[0] 208 c.Assert(result.Error, gc.Equals, "") 209 c.Assert(result.MachineId, gc.Equals, "machine-id") 210 c.Assert(result.UnitId, gc.Equals, "unit-id") 211 } 212 213 func (s *runSuite) TestRunOnAllMachines(c *gc.C) { 214 // Make three machines. 215 s.addMachineWithAddress(c, "10.3.2.1") 216 s.addMachineWithAddress(c, "10.3.2.2") 217 s.addMachineWithAddress(c, "10.3.2.3") 218 219 s.mockSSH(c, echoInput) 220 221 // hmm... this seems to be going through the api client, and from there 222 // through to the apiserver implementation. Not ideal, but it is how the 223 // other client tests are written. 224 client := s.APIState.Client() 225 results, err := client.RunOnAllMachines("hostname", testing.LongWait) 226 c.Assert(err, gc.IsNil) 227 c.Assert(results, gc.HasLen, 3) 228 var expectedResults []params.RunResult 229 for i := 0; i < 3; i++ { 230 expectedResults = append(expectedResults, 231 params.RunResult{ 232 ExecResponse: exec.ExecResponse{Stdout: []byte("juju-run --no-context 'hostname'\n")}, 233 MachineId: fmt.Sprint(i), 234 }) 235 } 236 237 c.Assert(results, jc.DeepEquals, expectedResults) 238 } 239 240 func (s *runSuite) TestRunMachineAndService(c *gc.C) { 241 // Make three machines. 242 s.addMachineWithAddress(c, "10.3.2.1") 243 244 charm := s.AddTestingCharm(c, "dummy") 245 magic, err := s.State.AddService("magic", "user-admin", charm) 246 s.addUnit(c, magic) 247 s.addUnit(c, magic) 248 249 s.mockSSH(c, echoInput) 250 251 // hmm... this seems to be going through the api client, and from there 252 // through to the apiserver implementation. Not ideal, but it is how the 253 // other client tests are written. 254 client := s.APIState.Client() 255 results, err := client.Run( 256 params.RunParams{ 257 Commands: "hostname", 258 Timeout: testing.LongWait, 259 Machines: []string{"0"}, 260 Services: []string{"magic"}, 261 }) 262 c.Assert(err, gc.IsNil) 263 c.Assert(results, gc.HasLen, 3) 264 expectedResults := []params.RunResult{ 265 params.RunResult{ 266 ExecResponse: exec.ExecResponse{Stdout: []byte("juju-run --no-context 'hostname'\n")}, 267 MachineId: "0", 268 }, 269 params.RunResult{ 270 ExecResponse: exec.ExecResponse{Stdout: []byte("juju-run magic/0 'hostname'\n")}, 271 MachineId: "1", 272 UnitId: "magic/0", 273 }, 274 params.RunResult{ 275 ExecResponse: exec.ExecResponse{Stdout: []byte("juju-run magic/1 'hostname'\n")}, 276 MachineId: "2", 277 UnitId: "magic/1", 278 }, 279 } 280 281 c.Assert(results, jc.DeepEquals, expectedResults) 282 } 283 284 var echoInputShowArgs = `#!/bin/bash 285 # Write the args to stderr 286 echo "$*" >&2 287 # And echo stdin to stdout 288 while read line 289 do echo $line 290 done <&0 291 ` 292 293 var echoInput = `#!/bin/bash 294 # And echo stdin to stdout 295 while read line 296 do echo $line 297 done <&0 298 `