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