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