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  }