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