github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/worker/uniter/runner/jujuc/server_test.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Copyright 2014 Cloudbase Solutions SRL
     3  // Licensed under the AGPLv3, see LICENCE file for details.
     4  
     5  package jujuc_test
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"io/ioutil"
    11  	"net/rpc"
    12  	"os"
    13  	"path/filepath"
    14  	"sync"
    15  	"time"
    16  
    17  	"github.com/juju/cmd"
    18  	jc "github.com/juju/testing/checkers"
    19  	"github.com/juju/utils/exec"
    20  	"github.com/juju/utils/featureflag"
    21  	gc "gopkg.in/check.v1"
    22  	"launchpad.net/gnuflag"
    23  
    24  	"github.com/juju/juju/juju/osenv"
    25  	"github.com/juju/juju/testing"
    26  	"github.com/juju/juju/worker/uniter/runner/jujuc"
    27  )
    28  
    29  type RpcCommand struct {
    30  	cmd.CommandBase
    31  	Value string
    32  	Slow  bool
    33  }
    34  
    35  func (c *RpcCommand) Info() *cmd.Info {
    36  	return &cmd.Info{
    37  		Name:    "remote",
    38  		Purpose: "act at a distance",
    39  		Doc:     "blah doc",
    40  	}
    41  }
    42  
    43  func (c *RpcCommand) SetFlags(f *gnuflag.FlagSet) {
    44  	f.StringVar(&c.Value, "value", "", "doc")
    45  	f.BoolVar(&c.Slow, "slow", false, "doc")
    46  }
    47  
    48  func (c *RpcCommand) Init(args []string) error {
    49  	return cmd.CheckEmpty(args)
    50  }
    51  
    52  func (c *RpcCommand) Run(ctx *cmd.Context) error {
    53  	if c.Value == "error" {
    54  		return errors.New("blam")
    55  	}
    56  	if c.Slow {
    57  		time.Sleep(testing.ShortWait)
    58  		return nil
    59  	}
    60  	ctx.Stdout.Write([]byte("eye of newt\n"))
    61  	ctx.Stderr.Write([]byte("toe of frog\n"))
    62  	return ioutil.WriteFile(ctx.AbsPath("local"), []byte(c.Value), 0644)
    63  }
    64  
    65  func factory(contextId, cmdName string) (cmd.Command, error) {
    66  	if contextId != "validCtx" {
    67  		return nil, fmt.Errorf("unknown context %q", contextId)
    68  	}
    69  	if cmdName != "remote" {
    70  		return nil, fmt.Errorf("unknown command %q", cmdName)
    71  	}
    72  	return &RpcCommand{}, nil
    73  }
    74  
    75  type ServerSuite struct {
    76  	testing.BaseSuite
    77  	server   *jujuc.Server
    78  	sockPath string
    79  	err      chan error
    80  }
    81  
    82  var _ = gc.Suite(&ServerSuite{})
    83  
    84  func (s *ServerSuite) SetUpTest(c *gc.C) {
    85  	s.BaseSuite.SetUpTest(c)
    86  	s.sockPath = filepath.Join(c.MkDir(), "test.sock")
    87  	srv, err := jujuc.NewServer(factory, s.sockPath)
    88  	c.Assert(err, jc.ErrorIsNil)
    89  	c.Assert(srv, gc.NotNil)
    90  	s.server = srv
    91  	s.err = make(chan error)
    92  	go func() { s.err <- s.server.Run() }()
    93  }
    94  
    95  func (s *ServerSuite) TearDownTest(c *gc.C) {
    96  	s.server.Close()
    97  	c.Assert(<-s.err, gc.IsNil)
    98  	_, err := os.Open(s.sockPath)
    99  	c.Assert(err, jc.Satisfies, os.IsNotExist)
   100  	s.BaseSuite.TearDownTest(c)
   101  }
   102  
   103  func (s *ServerSuite) Call(c *gc.C, req jujuc.Request) (resp exec.ExecResponse, err error) {
   104  	client, err := rpc.Dial("unix", s.sockPath)
   105  	c.Assert(err, jc.ErrorIsNil)
   106  	defer client.Close()
   107  	err = client.Call("Jujuc.Main", req, &resp)
   108  	return resp, err
   109  }
   110  
   111  func (s *ServerSuite) TestHappyPath(c *gc.C) {
   112  	dir := c.MkDir()
   113  	resp, err := s.Call(c, jujuc.Request{
   114  		"validCtx", dir, "remote", []string{"--value", "something"},
   115  	})
   116  	c.Assert(err, jc.ErrorIsNil)
   117  	c.Assert(resp.Code, gc.Equals, 0)
   118  	c.Assert(string(resp.Stdout), gc.Equals, "eye of newt\n")
   119  	c.Assert(string(resp.Stderr), gc.Equals, "toe of frog\n")
   120  	content, err := ioutil.ReadFile(filepath.Join(dir, "local"))
   121  	c.Assert(err, jc.ErrorIsNil)
   122  	c.Assert(string(content), gc.Equals, "something")
   123  }
   124  
   125  func (s *ServerSuite) TestLocks(c *gc.C) {
   126  	var wg sync.WaitGroup
   127  	t0 := time.Now()
   128  	for i := 0; i < 4; i++ {
   129  		wg.Add(1)
   130  		go func() {
   131  			dir := c.MkDir()
   132  			resp, err := s.Call(c, jujuc.Request{
   133  				"validCtx", dir, "remote", []string{"--slow"},
   134  			})
   135  			c.Assert(err, jc.ErrorIsNil)
   136  			c.Assert(resp.Code, gc.Equals, 0)
   137  			wg.Done()
   138  		}()
   139  	}
   140  	wg.Wait()
   141  	t1 := time.Now()
   142  	c.Assert(t0.Add(4*testing.ShortWait).Before(t1), jc.IsTrue)
   143  }
   144  
   145  func (s *ServerSuite) TestBadCommandName(c *gc.C) {
   146  	dir := c.MkDir()
   147  	_, err := s.Call(c, jujuc.Request{"validCtx", dir, "", nil})
   148  	c.Assert(err, gc.ErrorMatches, "bad request: command not specified")
   149  	_, err = s.Call(c, jujuc.Request{"validCtx", dir, "witchcraft", nil})
   150  	c.Assert(err, gc.ErrorMatches, `bad request: unknown command "witchcraft"`)
   151  }
   152  
   153  func (s *ServerSuite) TestBadDir(c *gc.C) {
   154  	for _, req := range []jujuc.Request{
   155  		{"validCtx", "", "anything", nil},
   156  		{"validCtx", "foo/bar", "anything", nil},
   157  	} {
   158  		_, err := s.Call(c, req)
   159  		c.Assert(err, gc.ErrorMatches, "bad request: Dir is not absolute")
   160  	}
   161  }
   162  
   163  func (s *ServerSuite) TestBadContextId(c *gc.C) {
   164  	_, err := s.Call(c, jujuc.Request{"whatever", c.MkDir(), "remote", nil})
   165  	c.Assert(err, gc.ErrorMatches, `bad request: unknown context "whatever"`)
   166  }
   167  
   168  func (s *ServerSuite) AssertBadCommand(c *gc.C, args []string, code int) exec.ExecResponse {
   169  	resp, err := s.Call(c, jujuc.Request{"validCtx", c.MkDir(), args[0], args[1:]})
   170  	c.Assert(err, jc.ErrorIsNil)
   171  	c.Assert(resp.Code, gc.Equals, code)
   172  	return resp
   173  }
   174  
   175  func (s *ServerSuite) TestParseError(c *gc.C) {
   176  	resp := s.AssertBadCommand(c, []string{"remote", "--cheese"}, 2)
   177  	c.Assert(string(resp.Stdout), gc.Equals, "")
   178  	c.Assert(string(resp.Stderr), gc.Equals, "error: flag provided but not defined: --cheese\n")
   179  }
   180  
   181  func (s *ServerSuite) TestBrokenCommand(c *gc.C) {
   182  	resp := s.AssertBadCommand(c, []string{"remote", "--value", "error"}, 1)
   183  	c.Assert(string(resp.Stdout), gc.Equals, "")
   184  	c.Assert(string(resp.Stderr), gc.Equals, "error: blam\n")
   185  }
   186  
   187  type NewCommandSuite struct {
   188  	ContextSuite
   189  }
   190  
   191  var _ = gc.Suite(&NewCommandSuite{})
   192  
   193  var newCommandTests = []struct {
   194  	name string
   195  	err  string
   196  }{
   197  	{"close-port", ""},
   198  	{"config-get", ""},
   199  	{"juju-log", ""},
   200  	{"open-port", ""},
   201  	{"opened-ports", ""},
   202  	{"relation-get", ""},
   203  	{"relation-ids", ""},
   204  	{"relation-list", ""},
   205  	{"relation-set", ""},
   206  	{"unit-get", ""},
   207  	{"storage-get", ""},
   208  	// The error message contains .exe on Windows
   209  	{"random", "unknown command: random(.exe)?"},
   210  }
   211  
   212  func (s *NewCommandSuite) TestNewCommand(c *gc.C) {
   213  	s.PatchEnvironment(osenv.JujuFeatureFlagEnvKey, "storage")
   214  	featureflag.SetFlagsFromEnvironment(osenv.JujuFeatureFlagEnvKey)
   215  	ctx := s.GetHookContext(c, 0, "")
   216  	for _, t := range newCommandTests {
   217  		com, err := jujuc.NewCommand(ctx, cmdString(t.name))
   218  		if t.err == "" {
   219  			// At this level, just check basic sanity; commands are tested in
   220  			// more detail elsewhere.
   221  			c.Assert(err, jc.ErrorIsNil)
   222  			c.Assert(com.Info().Name, gc.Equals, t.name)
   223  		} else {
   224  			c.Assert(com, gc.IsNil)
   225  			c.Assert(err, gc.ErrorMatches, t.err)
   226  		}
   227  	}
   228  }