github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/cmd/juju/plugin_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  	"fmt"
     9  	"io/ioutil"
    10  	"os"
    11  	"text/template"
    12  	"time"
    13  
    14  	"github.com/juju/cmd"
    15  	gitjujutesting "github.com/juju/testing"
    16  	jc "github.com/juju/testing/checkers"
    17  	gc "gopkg.in/check.v1"
    18  
    19  	"github.com/juju/juju/testing"
    20  )
    21  
    22  type PluginSuite struct {
    23  	testing.FakeJujuHomeSuite
    24  	oldPath string
    25  }
    26  
    27  var _ = gc.Suite(&PluginSuite{})
    28  
    29  func (suite *PluginSuite) SetUpTest(c *gc.C) {
    30  	suite.FakeJujuHomeSuite.SetUpTest(c)
    31  	suite.oldPath = os.Getenv("PATH")
    32  	os.Setenv("PATH", "/bin:"+gitjujutesting.HomePath())
    33  }
    34  
    35  func (suite *PluginSuite) TearDownTest(c *gc.C) {
    36  	os.Setenv("PATH", suite.oldPath)
    37  	suite.FakeJujuHomeSuite.TearDownTest(c)
    38  }
    39  
    40  func (*PluginSuite) TestFindPlugins(c *gc.C) {
    41  	plugins := findPlugins()
    42  	c.Assert(plugins, gc.DeepEquals, []string{})
    43  }
    44  
    45  func (suite *PluginSuite) TestFindPluginsOrder(c *gc.C) {
    46  	suite.makePlugin("foo", 0744)
    47  	suite.makePlugin("bar", 0654)
    48  	suite.makePlugin("baz", 0645)
    49  	plugins := findPlugins()
    50  	c.Assert(plugins, gc.DeepEquals, []string{"juju-bar", "juju-baz", "juju-foo"})
    51  }
    52  
    53  func (suite *PluginSuite) TestFindPluginsIgnoreNotExec(c *gc.C) {
    54  	suite.makePlugin("foo", 0644)
    55  	suite.makePlugin("bar", 0666)
    56  	plugins := findPlugins()
    57  	c.Assert(plugins, gc.DeepEquals, []string{})
    58  }
    59  
    60  func (suite *PluginSuite) TestRunPluginExising(c *gc.C) {
    61  	suite.makePlugin("foo", 0755)
    62  	ctx := testing.Context(c)
    63  	err := RunPlugin(ctx, "foo", []string{"some params"})
    64  	c.Assert(err, jc.ErrorIsNil)
    65  	c.Assert(testing.Stdout(ctx), gc.Equals, "foo some params\n")
    66  	c.Assert(testing.Stderr(ctx), gc.Equals, "")
    67  }
    68  
    69  func (suite *PluginSuite) TestRunPluginWithFailing(c *gc.C) {
    70  	suite.makeFailingPlugin("foo", 2)
    71  	ctx := testing.Context(c)
    72  	err := RunPlugin(ctx, "foo", []string{"some params"})
    73  	c.Assert(err, gc.ErrorMatches, "subprocess encountered error code 2")
    74  	c.Assert(err, jc.Satisfies, cmd.IsRcPassthroughError)
    75  	c.Assert(testing.Stdout(ctx), gc.Equals, "failing\n")
    76  	c.Assert(testing.Stderr(ctx), gc.Equals, "")
    77  }
    78  
    79  func (suite *PluginSuite) TestGatherDescriptionsInParallel(c *gc.C) {
    80  	// Make plugins that will deadlock if we don't start them in parallel.
    81  	// Each plugin depends on another one being started before they will
    82  	// complete. They make a full loop, so no sequential ordering will ever
    83  	// succeed.
    84  	suite.makeFullPlugin(PluginParams{Name: "foo", Creates: "foo", DependsOn: "bar"})
    85  	suite.makeFullPlugin(PluginParams{Name: "bar", Creates: "bar", DependsOn: "baz"})
    86  	suite.makeFullPlugin(PluginParams{Name: "baz", Creates: "baz", DependsOn: "error"})
    87  	suite.makeFullPlugin(PluginParams{Name: "error", ExitStatus: 1, Creates: "error", DependsOn: "foo"})
    88  
    89  	// If the code was wrong, GetPluginDescriptions would deadlock,
    90  	// so timeout after a short while
    91  	resultChan := make(chan []PluginDescription)
    92  	go func() {
    93  		resultChan <- GetPluginDescriptions()
    94  	}()
    95  	// 10 seconds is arbitrary but should always be generously long. Test
    96  	// actually only takes about 15ms in practice. But 10s allows for system hiccups, etc.
    97  	waitTime := 10 * time.Second
    98  	var results []PluginDescription
    99  	select {
   100  	case results = <-resultChan:
   101  		break
   102  	case <-time.After(waitTime):
   103  		c.Fatalf("took longer than %fs to complete.", waitTime.Seconds())
   104  	}
   105  
   106  	c.Assert(results, gc.HasLen, 4)
   107  	c.Assert(results[0].name, gc.Equals, "bar")
   108  	c.Assert(results[0].description, gc.Equals, "bar description")
   109  	c.Assert(results[1].name, gc.Equals, "baz")
   110  	c.Assert(results[1].description, gc.Equals, "baz description")
   111  	c.Assert(results[2].name, gc.Equals, "error")
   112  	c.Assert(results[2].description, gc.Equals, "error occurred running 'juju-error --description'")
   113  	c.Assert(results[3].name, gc.Equals, "foo")
   114  	c.Assert(results[3].description, gc.Equals, "foo description")
   115  }
   116  
   117  func (suite *PluginSuite) TestHelpPluginsWithNoPlugins(c *gc.C) {
   118  	output := badrun(c, 0, "help", "plugins")
   119  	c.Assert(output, jc.HasPrefix, PluginTopicText)
   120  	c.Assert(output, jc.HasSuffix, "\n\nNo plugins found.\n")
   121  }
   122  
   123  func (suite *PluginSuite) TestHelpPluginsWithPlugins(c *gc.C) {
   124  	suite.makeFullPlugin(PluginParams{Name: "foo"})
   125  	suite.makeFullPlugin(PluginParams{Name: "bar"})
   126  	output := badrun(c, 0, "help", "plugins")
   127  	c.Assert(output, jc.HasPrefix, PluginTopicText)
   128  	expectedPlugins := `
   129  
   130  bar  bar description
   131  foo  foo description
   132  `
   133  	c.Assert(output, jc.HasSuffix, expectedPlugins)
   134  }
   135  
   136  func (suite *PluginSuite) TestHelpPluginName(c *gc.C) {
   137  	suite.makeFullPlugin(PluginParams{Name: "foo"})
   138  	output := badrun(c, 0, "help", "foo")
   139  	expectedHelp := `foo longer help
   140  
   141  something useful
   142  `
   143  	c.Assert(output, gc.Matches, expectedHelp)
   144  }
   145  
   146  func (suite *PluginSuite) TestHelpPluginNameNotAPlugin(c *gc.C) {
   147  	output := badrun(c, 0, "help", "foo")
   148  	expectedHelp := "ERROR unknown command or topic for foo\n"
   149  	c.Assert(output, gc.Matches, expectedHelp)
   150  }
   151  
   152  func (suite *PluginSuite) TestHelpAsArg(c *gc.C) {
   153  	suite.makeFullPlugin(PluginParams{Name: "foo"})
   154  	output := badrun(c, 0, "foo", "--help")
   155  	expectedHelp := `foo longer help
   156  
   157  something useful
   158  `
   159  	c.Assert(output, gc.Matches, expectedHelp)
   160  }
   161  
   162  func (suite *PluginSuite) TestDebugAsArg(c *gc.C) {
   163  	suite.makeFullPlugin(PluginParams{Name: "foo"})
   164  	output := badrun(c, 0, "foo", "--debug")
   165  	expectedDebug := "some debug\n"
   166  	c.Assert(output, gc.Matches, expectedDebug)
   167  }
   168  
   169  func (suite *PluginSuite) TestJujuEnvVars(c *gc.C) {
   170  	suite.makeFullPlugin(PluginParams{Name: "foo"})
   171  	output := badrun(c, 0, "foo", "-e", "myenv", "-p", "pluginarg")
   172  	expectedDebug := `foo -e myenv -p pluginarg\n.*env is:  myenv\n.*home is: .*\.juju\n`
   173  	c.Assert(output, gc.Matches, expectedDebug)
   174  }
   175  
   176  func (suite *PluginSuite) makePlugin(name string, perm os.FileMode) {
   177  	content := fmt.Sprintf("#!/bin/bash --norc\necho %s $*", name)
   178  	filename := gitjujutesting.HomePath(JujuPluginPrefix + name)
   179  	ioutil.WriteFile(filename, []byte(content), perm)
   180  }
   181  
   182  func (suite *PluginSuite) makeFailingPlugin(name string, exitStatus int) {
   183  	content := fmt.Sprintf("#!/bin/bash --norc\necho failing\nexit %d", exitStatus)
   184  	filename := gitjujutesting.HomePath(JujuPluginPrefix + name)
   185  	ioutil.WriteFile(filename, []byte(content), 0755)
   186  }
   187  
   188  type PluginParams struct {
   189  	Name       string
   190  	ExitStatus int
   191  	Creates    string
   192  	DependsOn  string
   193  }
   194  
   195  const pluginTemplate = `#!/bin/bash --norc
   196  
   197  if [ "$1" = "--description" ]; then
   198    if [ -n "{{.Creates}}" ]; then
   199      touch "{{.Creates}}"
   200    fi
   201    if [ -n "{{.DependsOn}}" ]; then
   202      # Sleep 10ms while waiting to allow other stuff to do work
   203      while [ ! -e "{{.DependsOn}}" ]; do sleep 0.010; done
   204    fi
   205    echo "{{.Name}} description"
   206    exit {{.ExitStatus}}
   207  fi
   208  
   209  if [ "$1" = "--help" ]; then
   210    echo "{{.Name}} longer help"
   211    echo ""
   212    echo "something useful"
   213    exit {{.ExitStatus}}
   214  fi
   215  
   216  if [ "$1" = "--debug" ]; then
   217    echo "some debug"
   218    exit {{.ExitStatus}}
   219  fi
   220  
   221  echo {{.Name}} $*
   222  echo "env is: " $JUJU_ENV
   223  echo "home is: " $JUJU_HOME
   224  exit {{.ExitStatus}}
   225  `
   226  
   227  func (suite *PluginSuite) makeFullPlugin(params PluginParams) {
   228  	// Create a new template and parse the plugin into it.
   229  	t := template.Must(template.New("plugin").Parse(pluginTemplate))
   230  	content := &bytes.Buffer{}
   231  	filename := gitjujutesting.HomePath("juju-" + params.Name)
   232  	// Create the files in the temp dirs, so we don't pollute the working space
   233  	if params.Creates != "" {
   234  		params.Creates = gitjujutesting.HomePath(params.Creates)
   235  	}
   236  	if params.DependsOn != "" {
   237  		params.DependsOn = gitjujutesting.HomePath(params.DependsOn)
   238  	}
   239  	t.Execute(content, params)
   240  	ioutil.WriteFile(filename, content.Bytes(), 0755)
   241  }