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