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