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