github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/cmd/jujud/run_test.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package main
     5  
     6  import (
     7  	"fmt"
     8  	"os"
     9  	"path/filepath"
    10  	"time"
    11  
    12  	"github.com/juju/loggo"
    13  	jc "github.com/juju/testing/checkers"
    14  	"github.com/juju/utils/exec"
    15  	"github.com/juju/utils/fslock"
    16  	gc "launchpad.net/gocheck"
    17  
    18  	"github.com/juju/juju/cmd"
    19  	"github.com/juju/juju/testing"
    20  	"github.com/juju/juju/worker/uniter"
    21  )
    22  
    23  type RunTestSuite struct {
    24  	testing.BaseSuite
    25  }
    26  
    27  var _ = gc.Suite(&RunTestSuite{})
    28  
    29  func (*RunTestSuite) TestWrongArgs(c *gc.C) {
    30  	for i, test := range []struct {
    31  		title        string
    32  		args         []string
    33  		errMatch     string
    34  		unit         string
    35  		commands     string
    36  		avoidContext bool
    37  	}{{
    38  		title:    "no args",
    39  		errMatch: "missing unit-name",
    40  	}, {
    41  		title:    "one arg",
    42  		args:     []string{"foo"},
    43  		errMatch: "missing commands",
    44  	}, {
    45  		title:    "more than two arg",
    46  		args:     []string{"foo", "bar", "baz"},
    47  		errMatch: `unrecognized args: \["baz"\]`,
    48  	}, {
    49  		title:    "unit and command assignment",
    50  		args:     []string{"unit-name", "command"},
    51  		unit:     "unit-name",
    52  		commands: "command",
    53  	}, {
    54  		title:    "unit id converted to tag",
    55  		args:     []string{"foo/1", "command"},
    56  		unit:     "unit-foo-1",
    57  		commands: "command",
    58  	}, {
    59  		title:        "execute not in a context",
    60  		args:         []string{"--no-context", "command"},
    61  		commands:     "command",
    62  		avoidContext: true,
    63  	},
    64  	} {
    65  		c.Logf("\n%d: %s", i, test.title)
    66  		runCommand := &RunCommand{}
    67  		err := testing.InitCommand(runCommand, test.args)
    68  		if test.errMatch == "" {
    69  			c.Assert(err, gc.IsNil)
    70  			c.Assert(runCommand.unit, gc.Equals, test.unit)
    71  			c.Assert(runCommand.commands, gc.Equals, test.commands)
    72  			c.Assert(runCommand.noContext, gc.Equals, test.avoidContext)
    73  		} else {
    74  			c.Assert(err, gc.ErrorMatches, test.errMatch)
    75  		}
    76  	}
    77  }
    78  
    79  func (s *RunTestSuite) TestInsideContext(c *gc.C) {
    80  	s.PatchEnvironment("JUJU_CONTEXT_ID", "fake-id")
    81  	runCommand := &RunCommand{}
    82  	err := runCommand.Init([]string{"foo", "bar"})
    83  	c.Assert(err, gc.ErrorMatches, "juju-run cannot be called from within a hook.*")
    84  }
    85  
    86  func (s *RunTestSuite) TestMissingAgent(c *gc.C) {
    87  	s.PatchValue(&AgentDir, c.MkDir())
    88  
    89  	_, err := testing.RunCommand(c, &RunCommand{}, "foo", "bar")
    90  	c.Assert(err, gc.ErrorMatches, `unit "foo" not found on this machine`)
    91  }
    92  
    93  func waitForResult(running <-chan *cmd.Context) (*cmd.Context, error) {
    94  	select {
    95  	case result := <-running:
    96  		return result, nil
    97  	case <-time.After(testing.ShortWait):
    98  		return nil, fmt.Errorf("timeout")
    99  	}
   100  }
   101  
   102  func startRunAsync(c *gc.C, params []string) <-chan *cmd.Context {
   103  	resultChannel := make(chan *cmd.Context)
   104  	go func() {
   105  		ctx, err := testing.RunCommand(c, &RunCommand{}, params...)
   106  		c.Assert(err, jc.Satisfies, cmd.IsRcPassthroughError)
   107  		c.Assert(err, gc.ErrorMatches, "subprocess encountered error code 0")
   108  		resultChannel <- ctx
   109  		close(resultChannel)
   110  	}()
   111  	return resultChannel
   112  }
   113  
   114  func (s *RunTestSuite) TestNoContext(c *gc.C) {
   115  	s.PatchValue(&LockDir, c.MkDir())
   116  	s.PatchValue(&AgentDir, c.MkDir())
   117  
   118  	ctx, err := testing.RunCommand(c, &RunCommand{}, "--no-context", "echo done")
   119  	c.Assert(err, jc.Satisfies, cmd.IsRcPassthroughError)
   120  	c.Assert(err, gc.ErrorMatches, "subprocess encountered error code 0")
   121  	c.Assert(testing.Stdout(ctx), gc.Equals, "done\n")
   122  }
   123  
   124  func (s *RunTestSuite) TestNoContextAsync(c *gc.C) {
   125  	s.PatchValue(&LockDir, c.MkDir())
   126  	s.PatchValue(&AgentDir, c.MkDir())
   127  
   128  	channel := startRunAsync(c, []string{"--no-context", "echo done"})
   129  	ctx, err := waitForResult(channel)
   130  	c.Assert(err, gc.IsNil)
   131  	c.Assert(testing.Stdout(ctx), gc.Equals, "done\n")
   132  }
   133  
   134  func (s *RunTestSuite) TestNoContextWithLock(c *gc.C) {
   135  	s.PatchValue(&LockDir, c.MkDir())
   136  	s.PatchValue(&AgentDir, c.MkDir())
   137  	s.PatchValue(&fslock.LockWaitDelay, 10*time.Millisecond)
   138  
   139  	lock, err := getLock()
   140  	c.Assert(err, gc.IsNil)
   141  	lock.Lock("juju-run test")
   142  
   143  	channel := startRunAsync(c, []string{"--no-context", "echo done"})
   144  	ctx, err := waitForResult(channel)
   145  	c.Assert(err, gc.ErrorMatches, "timeout")
   146  
   147  	lock.Unlock()
   148  
   149  	ctx, err = waitForResult(channel)
   150  	c.Assert(err, gc.IsNil)
   151  	c.Assert(testing.Stdout(ctx), gc.Equals, "done\n")
   152  }
   153  
   154  func (s *RunTestSuite) TestMissingSocket(c *gc.C) {
   155  	s.PatchValue(&AgentDir, c.MkDir())
   156  	testAgentDir := filepath.Join(AgentDir, "foo")
   157  	err := os.Mkdir(testAgentDir, 0755)
   158  	c.Assert(err, gc.IsNil)
   159  
   160  	_, err = testing.RunCommand(c, &RunCommand{}, "foo", "bar")
   161  	c.Assert(err, gc.ErrorMatches, `dial unix .*/run.socket: no such file or directory`)
   162  }
   163  
   164  func (s *RunTestSuite) TestRunning(c *gc.C) {
   165  	loggo.GetLogger("worker.uniter").SetLogLevel(loggo.TRACE)
   166  	s.runListenerForAgent(c, "foo")
   167  
   168  	ctx, err := testing.RunCommand(c, &RunCommand{}, "foo", "bar")
   169  	c.Check(cmd.IsRcPassthroughError(err), jc.IsTrue)
   170  	c.Assert(err, gc.ErrorMatches, "subprocess encountered error code 42")
   171  	c.Assert(testing.Stdout(ctx), gc.Equals, "bar stdout")
   172  	c.Assert(testing.Stderr(ctx), gc.Equals, "bar stderr")
   173  }
   174  
   175  func (s *RunTestSuite) runListenerForAgent(c *gc.C, agent string) {
   176  	s.PatchValue(&AgentDir, c.MkDir())
   177  
   178  	testAgentDir := filepath.Join(AgentDir, agent)
   179  	err := os.Mkdir(testAgentDir, 0755)
   180  	c.Assert(err, gc.IsNil)
   181  
   182  	socketPath := filepath.Join(testAgentDir, uniter.RunListenerFile)
   183  	listener, err := uniter.NewRunListener(&mockRunner{c}, socketPath)
   184  	c.Assert(err, gc.IsNil)
   185  	c.Assert(listener, gc.NotNil)
   186  	s.AddCleanup(func(*gc.C) {
   187  		listener.Close()
   188  	})
   189  }
   190  
   191  type mockRunner struct {
   192  	c *gc.C
   193  }
   194  
   195  var _ uniter.CommandRunner = (*mockRunner)(nil)
   196  
   197  func (r *mockRunner) RunCommands(commands string) (results *exec.ExecResponse, err error) {
   198  	r.c.Log("mock runner: " + commands)
   199  	return &exec.ExecResponse{
   200  		Code:   42,
   201  		Stdout: []byte(commands + " stdout"),
   202  		Stderr: []byte(commands + " stderr"),
   203  	}, nil
   204  }