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