github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/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 gc "launchpad.net/gocheck" 15 16 "launchpad.net/juju-core/testing" 17 jc "launchpad.net/juju-core/testing/checkers" 18 "launchpad.net/juju-core/testing/testbase" 19 ) 20 21 type PluginSuite struct { 22 testbase.LoggingSuite 23 oldPath string 24 home *testing.FakeHome 25 } 26 27 var _ = gc.Suite(&PluginSuite{}) 28 29 func (suite *PluginSuite) SetUpTest(c *gc.C) { 30 suite.LoggingSuite.SetUpTest(c) 31 suite.oldPath = os.Getenv("PATH") 32 suite.home = testing.MakeSampleHome(c) 33 os.Setenv("PATH", "/bin:"+testing.HomePath()) 34 } 35 36 func (suite *PluginSuite) TearDownTest(c *gc.C) { 37 suite.home.Restore() 38 os.Setenv("PATH", suite.oldPath) 39 suite.LoggingSuite.TearDownTest(c) 40 } 41 42 func (*PluginSuite) TestFindPlugins(c *gc.C) { 43 plugins := findPlugins() 44 c.Assert(plugins, gc.DeepEquals, []string{}) 45 } 46 47 func (suite *PluginSuite) TestFindPluginsOrder(c *gc.C) { 48 suite.makePlugin("foo", 0744) 49 suite.makePlugin("bar", 0654) 50 suite.makePlugin("baz", 0645) 51 plugins := findPlugins() 52 c.Assert(plugins, gc.DeepEquals, []string{"juju-bar", "juju-baz", "juju-foo"}) 53 } 54 55 func (suite *PluginSuite) TestFindPluginsIgnoreNotExec(c *gc.C) { 56 suite.makePlugin("foo", 0644) 57 suite.makePlugin("bar", 0666) 58 plugins := findPlugins() 59 c.Assert(plugins, gc.DeepEquals, []string{}) 60 } 61 62 func (suite *PluginSuite) TestRunPluginExising(c *gc.C) { 63 suite.makePlugin("foo", 0755) 64 ctx := testing.Context(c) 65 err := RunPlugin(ctx, "foo", []string{"some params"}) 66 c.Assert(err, gc.IsNil) 67 c.Assert(testing.Stdout(ctx), gc.Equals, "foo some params\n") 68 c.Assert(testing.Stderr(ctx), gc.Equals, "") 69 } 70 71 func (suite *PluginSuite) TestRunPluginWithFailing(c *gc.C) { 72 suite.makeFailingPlugin("foo", 2) 73 ctx := testing.Context(c) 74 err := RunPlugin(ctx, "foo", []string{"some params"}) 75 c.Assert(err, gc.ErrorMatches, "exit status 2") 76 c.Assert(testing.Stdout(ctx), gc.Equals, "failing\n") 77 c.Assert(testing.Stderr(ctx), gc.Equals, "") 78 } 79 80 func (suite *PluginSuite) TestGatherDescriptionsInParallel(c *gc.C) { 81 // Make plugins that will deadlock if we don't start them in parallel. 82 // Each plugin depends on another one being started before they will 83 // complete. They make a full loop, so no sequential ordering will ever 84 // succeed. 85 suite.makeFullPlugin(PluginParams{Name: "foo", Creates: "foo", DependsOn: "bar"}) 86 suite.makeFullPlugin(PluginParams{Name: "bar", Creates: "bar", DependsOn: "baz"}) 87 suite.makeFullPlugin(PluginParams{Name: "baz", Creates: "baz", DependsOn: "error"}) 88 suite.makeFullPlugin(PluginParams{Name: "error", ExitStatus: 1, Creates: "error", DependsOn: "foo"}) 89 90 // If the code was wrong, GetPluginDescriptions would deadlock, 91 // so timeout after a short while 92 resultChan := make(chan []PluginDescription) 93 go func() { 94 resultChan <- GetPluginDescriptions() 95 }() 96 // 10 seconds is arbitrary but should always be generously long. Test 97 // actually only takes about 15ms in practice. But 10s allows for system hiccups, etc. 98 waitTime := 10 * time.Second 99 var results []PluginDescription 100 select { 101 case results = <-resultChan: 102 break 103 case <-time.After(waitTime): 104 c.Fatalf("Took too more than %fs to complete.", waitTime.Seconds()) 105 } 106 107 c.Assert(results, gc.HasLen, 4) 108 c.Assert(results[0].name, gc.Equals, "bar") 109 c.Assert(results[0].description, gc.Equals, "bar description") 110 c.Assert(results[1].name, gc.Equals, "baz") 111 c.Assert(results[1].description, gc.Equals, "baz description") 112 c.Assert(results[2].name, gc.Equals, "error") 113 c.Assert(results[2].description, gc.Equals, "error occurred running 'juju-error --description'") 114 c.Assert(results[3].name, gc.Equals, "foo") 115 c.Assert(results[3].description, gc.Equals, "foo description") 116 } 117 118 func (suite *PluginSuite) TestHelpPluginsWithNoPlugins(c *gc.C) { 119 output := badrun(c, 0, "help", "plugins") 120 c.Assert(output, jc.HasPrefix, PluginTopicText) 121 c.Assert(output, jc.HasSuffix, "\n\nNo plugins found.\n") 122 } 123 124 func (suite *PluginSuite) TestHelpPluginsWithPlugins(c *gc.C) { 125 suite.makeFullPlugin(PluginParams{Name: "foo"}) 126 suite.makeFullPlugin(PluginParams{Name: "bar"}) 127 output := badrun(c, 0, "help", "plugins") 128 c.Assert(output, jc.HasPrefix, PluginTopicText) 129 expectedPlugins := ` 130 131 bar bar description 132 foo foo description 133 ` 134 c.Assert(output, jc.HasSuffix, expectedPlugins) 135 } 136 137 func (suite *PluginSuite) TestHelpPluginName(c *gc.C) { 138 suite.makeFullPlugin(PluginParams{Name: "foo"}) 139 output := badrun(c, 0, "help", "foo") 140 expectedHelp := `foo longer help 141 142 something useful 143 ` 144 c.Assert(output, gc.Matches, expectedHelp) 145 } 146 147 func (suite *PluginSuite) TestHelpPluginNameNotAPlugin(c *gc.C) { 148 output := badrun(c, 0, "help", "foo") 149 expectedHelp := "ERROR unknown command or topic for foo\n" 150 c.Assert(output, gc.Matches, expectedHelp) 151 } 152 153 func (suite *PluginSuite) TestHelpAsArg(c *gc.C) { 154 suite.makeFullPlugin(PluginParams{Name: "foo"}) 155 output := badrun(c, 0, "foo", "--help") 156 expectedHelp := `foo longer help 157 158 something useful 159 ` 160 c.Assert(output, gc.Matches, expectedHelp) 161 } 162 163 func (suite *PluginSuite) TestDebugAsArg(c *gc.C) { 164 suite.makeFullPlugin(PluginParams{Name: "foo"}) 165 output := badrun(c, 0, "foo", "--debug") 166 expectedDebug := "some debug\n" 167 c.Assert(output, gc.Matches, expectedDebug) 168 } 169 170 func (suite *PluginSuite) TestJujuEnvVars(c *gc.C) { 171 suite.makeFullPlugin(PluginParams{Name: "foo"}) 172 output := badrun(c, 0, "foo", "-e", "myenv", "-p", "pluginarg") 173 expectedDebug := `foo -e myenv -p pluginarg\n.*env is: myenv\n.*home is: .*\.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 := testing.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 := testing.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 "env is: " $JUJU_ENV 224 echo "home is: " $JUJU_HOME 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 := testing.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 = testing.HomePath(params.Creates) 236 } 237 if params.DependsOn != "" { 238 params.DependsOn = testing.HomePath(params.DependsOn) 239 } 240 t.Execute(content, params) 241 ioutil.WriteFile(filename, content.Bytes(), 0755) 242 }