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  }