launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/cmd/jujud/main_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 "errors" 8 "flag" 9 "fmt" 10 "io/ioutil" 11 "os" 12 "os/exec" 13 "path/filepath" 14 "strings" 15 stdtesting "testing" 16 17 "launchpad.net/gnuflag" 18 gc "launchpad.net/gocheck" 19 20 "launchpad.net/juju-core/cmd" 21 "launchpad.net/juju-core/environs" 22 "launchpad.net/juju-core/testing" 23 "launchpad.net/juju-core/worker/uniter/jujuc" 24 ) 25 26 var caCertFile string 27 28 func mktemp(prefix string, content string) string { 29 f, err := ioutil.TempFile("", prefix) 30 if err != nil { 31 panic(err) 32 } 33 _, err = f.WriteString(content) 34 if err != nil { 35 panic(err) 36 } 37 f.Close() 38 return f.Name() 39 } 40 41 func TestPackage(t *stdtesting.T) { 42 // Change the path to "juju-run", so that the 43 // tests don't try to write to /usr/local/bin. 44 jujuRun = mktemp("juju-run", "") 45 defer os.Remove(jujuRun) 46 47 // Create a CA certificate available for all tests. 48 caCertFile = mktemp("juju-test-cert", testing.CACert) 49 defer os.Remove(caCertFile) 50 51 testing.MgoTestPackage(t) 52 } 53 54 type MainSuite struct{} 55 56 var _ = gc.Suite(&MainSuite{}) 57 58 var flagRunMain = flag.Bool("run-main", false, "Run the application's main function for recursive testing") 59 60 // Reentrancy point for testing (something as close as possible to) the jujud 61 // tool itself. 62 func TestRunMain(t *stdtesting.T) { 63 if *flagRunMain { 64 Main(flag.Args()) 65 } 66 } 67 68 func checkMessage(c *gc.C, msg string, cmd ...string) { 69 args := append([]string{"-test.run", "TestRunMain", "-run-main", "--", "jujud"}, cmd...) 70 c.Logf("check %#v", args) 71 ps := exec.Command(os.Args[0], args...) 72 output, err := ps.CombinedOutput() 73 c.Logf(string(output)) 74 c.Assert(err, gc.ErrorMatches, "exit status 2") 75 lines := strings.Split(string(output), "\n") 76 c.Assert(lines[len(lines)-2], gc.Equals, "error: "+msg) 77 } 78 79 func (s *MainSuite) TestParseErrors(c *gc.C) { 80 // Check all the obvious parse errors 81 checkMessage(c, "unrecognized command: jujud cavitate", "cavitate") 82 msgf := "flag provided but not defined: --cheese" 83 checkMessage(c, msgf, "--cheese", "cavitate") 84 85 cmds := []string{"bootstrap-state", "unit", "machine"} 86 for _, cmd := range cmds { 87 checkMessage(c, msgf, cmd, "--cheese") 88 } 89 90 msga := `unrecognized args: ["toastie"]` 91 checkMessage(c, msga, 92 "bootstrap-state", 93 "--env-config", b64yaml{"blah": "blah"}.encode(), 94 "toastie") 95 checkMessage(c, msga, "unit", 96 "--unit-name", "un/0", 97 "toastie") 98 checkMessage(c, msga, "machine", 99 "--machine-id", "42", 100 "toastie") 101 } 102 103 var expectedProviders = []string{ 104 "ec2", 105 "maas", 106 "openstack", 107 } 108 109 func (s *MainSuite) TestProvidersAreRegistered(c *gc.C) { 110 // check that all the expected providers are registered 111 for _, name := range expectedProviders { 112 _, err := environs.Provider(name) 113 c.Assert(err, gc.IsNil) 114 } 115 } 116 117 type RemoteCommand struct { 118 cmd.CommandBase 119 msg string 120 } 121 122 var expectUsage = `usage: remote [options] 123 purpose: test jujuc 124 125 options: 126 --error (= "") 127 if set, fail 128 129 here is some documentation 130 ` 131 132 func (c *RemoteCommand) Info() *cmd.Info { 133 return &cmd.Info{ 134 Name: "remote", 135 Purpose: "test jujuc", 136 Doc: "here is some documentation", 137 } 138 } 139 140 func (c *RemoteCommand) SetFlags(f *gnuflag.FlagSet) { 141 f.StringVar(&c.msg, "error", "", "if set, fail") 142 } 143 144 func (c *RemoteCommand) Init(args []string) error { 145 return cmd.CheckEmpty(args) 146 } 147 148 func (c *RemoteCommand) Run(ctx *cmd.Context) error { 149 if c.msg != "" { 150 return errors.New(c.msg) 151 } 152 fmt.Fprintf(ctx.Stdout, "success!\n") 153 return nil 154 } 155 156 func run(c *gc.C, sockPath string, contextId string, exit int, cmd ...string) string { 157 args := append([]string{"-test.run", "TestRunMain", "-run-main", "--"}, cmd...) 158 c.Logf("check %v %#v", os.Args[0], args) 159 ps := exec.Command(os.Args[0], args...) 160 ps.Dir = c.MkDir() 161 ps.Env = []string{ 162 fmt.Sprintf("JUJU_AGENT_SOCKET=%s", sockPath), 163 fmt.Sprintf("JUJU_CONTEXT_ID=%s", contextId), 164 // Code that imports launchpad.net/juju-core/testing needs to 165 // be able to find that module at runtime (via build.Import), 166 // so we have to preserve that env variable. 167 os.ExpandEnv("GOPATH=${GOPATH}"), 168 } 169 output, err := ps.CombinedOutput() 170 if exit == 0 { 171 c.Assert(err, gc.IsNil) 172 } else { 173 c.Assert(err, gc.ErrorMatches, fmt.Sprintf("exit status %d", exit)) 174 } 175 return string(output) 176 } 177 178 type JujuCMainSuite struct { 179 sockPath string 180 server *jujuc.Server 181 } 182 183 var _ = gc.Suite(&JujuCMainSuite{}) 184 185 func (s *JujuCMainSuite) SetUpSuite(c *gc.C) { 186 factory := func(contextId, cmdName string) (cmd.Command, error) { 187 if contextId != "bill" { 188 return nil, fmt.Errorf("bad context: %s", contextId) 189 } 190 if cmdName != "remote" { 191 return nil, fmt.Errorf("bad command: %s", cmdName) 192 } 193 return &RemoteCommand{}, nil 194 } 195 s.sockPath = filepath.Join(c.MkDir(), "test.sock") 196 srv, err := jujuc.NewServer(factory, s.sockPath) 197 c.Assert(err, gc.IsNil) 198 s.server = srv 199 go func() { 200 if err := s.server.Run(); err != nil { 201 c.Fatalf("server died: %s", err) 202 } 203 }() 204 } 205 206 func (s *JujuCMainSuite) TearDownSuite(c *gc.C) { 207 s.server.Close() 208 } 209 210 var argsTests = []struct { 211 args []string 212 code int 213 output string 214 }{ 215 {[]string{"jujuc", "whatever"}, 2, jujudDoc + "error: jujuc should not be called directly\n"}, 216 {[]string{"remote"}, 0, "success!\n"}, 217 {[]string{"/path/to/remote"}, 0, "success!\n"}, 218 {[]string{"remote", "--help"}, 0, expectUsage}, 219 {[]string{"unknown"}, 1, "error: bad request: bad command: unknown\n"}, 220 {[]string{"remote", "--error", "borken"}, 1, "error: borken\n"}, 221 {[]string{"remote", "--unknown"}, 2, "error: flag provided but not defined: --unknown\n"}, 222 {[]string{"remote", "unwanted"}, 2, `error: unrecognized args: ["unwanted"]` + "\n"}, 223 } 224 225 func (s *JujuCMainSuite) TestArgs(c *gc.C) { 226 for _, t := range argsTests { 227 fmt.Println(t.args) 228 output := run(c, s.sockPath, "bill", t.code, t.args...) 229 c.Assert(output, gc.Equals, t.output) 230 } 231 } 232 233 func (s *JujuCMainSuite) TestNoClientId(c *gc.C) { 234 output := run(c, s.sockPath, "", 1, "remote") 235 c.Assert(output, gc.Equals, "error: JUJU_CONTEXT_ID not set\n") 236 } 237 238 func (s *JujuCMainSuite) TestBadClientId(c *gc.C) { 239 output := run(c, s.sockPath, "ben", 1, "remote") 240 c.Assert(output, gc.Equals, "error: bad request: bad context: ben\n") 241 } 242 243 func (s *JujuCMainSuite) TestNoSockPath(c *gc.C) { 244 output := run(c, "", "bill", 1, "remote") 245 c.Assert(output, gc.Equals, "error: JUJU_AGENT_SOCKET not set\n") 246 } 247 248 func (s *JujuCMainSuite) TestBadSockPath(c *gc.C) { 249 badSock := filepath.Join(c.MkDir(), "bad.sock") 250 output := run(c, badSock, "bill", 1, "remote") 251 err := fmt.Sprintf("error: dial unix %s: .*\n", badSock) 252 c.Assert(output, gc.Matches, err) 253 }