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  `