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 }