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