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  }