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 }