github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/cmd/juju/cmd_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 "io" 8 "io/ioutil" 9 "os" 10 "reflect" 11 12 gc "launchpad.net/gocheck" 13 14 "launchpad.net/juju-core/cmd" 15 "launchpad.net/juju-core/juju/osenv" 16 "launchpad.net/juju-core/juju/testing" 17 "launchpad.net/juju-core/provider/dummy" 18 coretesting "launchpad.net/juju-core/testing" 19 jc "launchpad.net/juju-core/testing/checkers" 20 ) 21 22 type CmdSuite struct { 23 testing.JujuConnSuite 24 home *coretesting.FakeHome 25 } 26 27 var _ = gc.Suite(&CmdSuite{}) 28 29 const envConfig = ` 30 default: 31 peckham 32 environments: 33 peckham: 34 type: dummy 35 state-server: false 36 admin-secret: arble 37 authorized-keys: i-am-a-key 38 default-series: raring 39 walthamstow: 40 type: dummy 41 state-server: false 42 authorized-keys: i-am-a-key 43 brokenenv: 44 type: dummy 45 broken: Bootstrap Destroy 46 state-server: false 47 authorized-keys: i-am-a-key 48 ` 49 50 func (s *CmdSuite) SetUpTest(c *gc.C) { 51 s.JujuConnSuite.SetUpTest(c) 52 s.home = coretesting.MakeFakeHome(c, envConfig, "peckham", "walthamstow", "brokenenv") 53 } 54 55 func (s *CmdSuite) TearDownTest(c *gc.C) { 56 s.home.Restore() 57 s.JujuConnSuite.TearDownTest(c) 58 } 59 60 // testInit checks that a command initialises correctly 61 // with the given set of arguments. 62 func testInit(c *gc.C, com cmd.Command, args []string, errPat string) { 63 err := coretesting.InitCommand(com, args) 64 if errPat != "" { 65 c.Assert(err, gc.ErrorMatches, errPat) 66 } else { 67 c.Assert(err, gc.IsNil) 68 } 69 } 70 71 // assertConnName asserts that the Command is using 72 // the given environment name. 73 // Since every command has a different type, 74 // we use reflection to look at the value of the 75 // Conn field in the value. 76 func assertConnName(c *gc.C, com cmd.Command, name string) { 77 v := reflect.ValueOf(com).Elem().FieldByName("EnvName") 78 c.Assert(v, jc.Satisfies, reflect.Value.IsValid) 79 c.Assert(v.Interface(), gc.Equals, name) 80 } 81 82 // All members of EnvironmentInitTests are tested for the -environment and -e 83 // flags, and that extra arguments will cause parsing to fail. 84 var EnvironmentInitTests = []func() (cmd.Command, []string){ 85 func() (cmd.Command, []string) { return new(BootstrapCommand), nil }, 86 func() (cmd.Command, []string) { 87 return new(DeployCommand), []string{"charm-name", "service-name"} 88 }, 89 func() (cmd.Command, []string) { return new(StatusCommand), nil }, 90 } 91 92 // TestEnvironmentInit tests that all commands which accept 93 // the --environment variable initialise their 94 // environment name correctly. 95 func (*CmdSuite) TestEnvironmentInit(c *gc.C) { 96 for i, cmdFunc := range EnvironmentInitTests { 97 c.Logf("test %d", i) 98 com, args := cmdFunc() 99 testInit(c, com, args, "") 100 assertConnName(c, com, "") 101 102 com, args = cmdFunc() 103 testInit(c, com, append(args, "-e", "walthamstow"), "") 104 assertConnName(c, com, "walthamstow") 105 106 com, args = cmdFunc() 107 testInit(c, com, append(args, "--environment", "walthamstow"), "") 108 assertConnName(c, com, "walthamstow") 109 110 // JUJU_ENV is the final place the environment can be overriden 111 com, args = cmdFunc() 112 oldenv := os.Getenv(osenv.JujuEnvEnvKey) 113 os.Setenv(osenv.JujuEnvEnvKey, "walthamstow") 114 testInit(c, com, args, "") 115 os.Setenv(osenv.JujuEnvEnvKey, oldenv) 116 assertConnName(c, com, "walthamstow") 117 118 com, args = cmdFunc() 119 if _, ok := com.(*StatusCommand); !ok { 120 testInit(c, com, append(args, "hotdog"), "unrecognized args.*") 121 } 122 } 123 } 124 125 func nullContext() *cmd.Context { 126 ctx := cmd.DefaultContext() 127 ctx.Stdin = io.LimitReader(nil, 0) 128 ctx.Stdout = ioutil.Discard 129 ctx.Stderr = ioutil.Discard 130 return ctx 131 } 132 133 func runCommand(ctx *cmd.Context, com cmd.Command, args ...string) (opc chan dummy.Operation, errc chan error) { 134 if ctx == nil { 135 panic("ctx == nil") 136 } 137 errc = make(chan error, 1) 138 opc = make(chan dummy.Operation, 200) 139 dummy.Listen(opc) 140 go func() { 141 // signal that we're done with this ops channel. 142 defer dummy.Listen(nil) 143 144 err := coretesting.InitCommand(com, args) 145 if err != nil { 146 errc <- err 147 return 148 } 149 150 err = com.Run(ctx) 151 errc <- err 152 }() 153 return 154 } 155 156 var deployTests = []struct { 157 args []string 158 com *DeployCommand 159 }{ 160 { 161 []string{"charm-name"}, 162 &DeployCommand{}, 163 }, { 164 []string{"charm-name", "service-name"}, 165 &DeployCommand{ServiceName: "service-name"}, 166 }, { 167 []string{"--repository", "/path/to/another-repo", "charm-name"}, 168 &DeployCommand{RepoPath: "/path/to/another-repo"}, 169 }, { 170 []string{"--upgrade", "charm-name"}, 171 &DeployCommand{BumpRevision: true}, 172 }, { 173 []string{"-u", "charm-name"}, 174 &DeployCommand{BumpRevision: true}, 175 }, { 176 []string{"--num-units", "33", "charm-name"}, 177 &DeployCommand{UnitCommandBase: UnitCommandBase{NumUnits: 33}}, 178 }, { 179 []string{"-n", "104", "charm-name"}, 180 &DeployCommand{UnitCommandBase: UnitCommandBase{NumUnits: 104}}, 181 }, 182 } 183 184 func initExpectations(com *DeployCommand) { 185 if com.CharmName == "" { 186 com.CharmName = "charm-name" 187 } 188 if com.NumUnits == 0 { 189 com.NumUnits = 1 190 } 191 if com.RepoPath == "" { 192 com.RepoPath = "/path/to/repo" 193 } 194 } 195 196 func initDeployCommand(args ...string) (*DeployCommand, error) { 197 com := &DeployCommand{} 198 return com, coretesting.InitCommand(com, args) 199 } 200 201 func (*CmdSuite) TestDeployCommandInit(c *gc.C) { 202 defer os.Setenv(osenv.JujuRepositoryEnvKey, os.Getenv(osenv.JujuRepositoryEnvKey)) 203 os.Setenv(osenv.JujuRepositoryEnvKey, "/path/to/repo") 204 205 for _, t := range deployTests { 206 initExpectations(t.com) 207 com, err := initDeployCommand(t.args...) 208 c.Assert(err, gc.IsNil) 209 c.Assert(com, gc.DeepEquals, t.com) 210 } 211 212 // test relative --config path 213 ctx := coretesting.Context(c) 214 expected := []byte("test: data") 215 path := ctx.AbsPath("testconfig.yaml") 216 file, err := os.Create(path) 217 c.Assert(err, gc.IsNil) 218 _, err = file.Write(expected) 219 c.Assert(err, gc.IsNil) 220 file.Close() 221 222 com, err := initDeployCommand("--config", "testconfig.yaml", "charm-name") 223 c.Assert(err, gc.IsNil) 224 actual, err := com.Config.Read(ctx) 225 c.Assert(err, gc.IsNil) 226 c.Assert(expected, gc.DeepEquals, actual) 227 228 // missing args 229 _, err = initDeployCommand() 230 c.Assert(err, gc.ErrorMatches, "no charm specified") 231 232 // environment tested elsewhere 233 } 234 235 func initAddUnitCommand(args ...string) (*AddUnitCommand, error) { 236 com := &AddUnitCommand{} 237 return com, coretesting.InitCommand(com, args) 238 } 239 240 func (*CmdSuite) TestAddUnitCommandInit(c *gc.C) { 241 // missing args 242 _, err := initAddUnitCommand() 243 c.Assert(err, gc.ErrorMatches, "no service specified") 244 245 // bad unit count 246 _, err = initDeployCommand("charm-name", "--num-units", "0") 247 c.Assert(err, gc.ErrorMatches, "--num-units must be a positive integer") 248 _, err = initDeployCommand("charm-name", "-n", "0") 249 c.Assert(err, gc.ErrorMatches, "--num-units must be a positive integer") 250 251 // environment tested elsewhere 252 } 253 254 func initExposeCommand(args ...string) (*ExposeCommand, error) { 255 com := &ExposeCommand{} 256 return com, coretesting.InitCommand(com, args) 257 } 258 259 func (*CmdSuite) TestExposeCommandInit(c *gc.C) { 260 // missing args 261 _, err := initExposeCommand() 262 c.Assert(err, gc.ErrorMatches, "no service name specified") 263 264 // environment tested elsewhere 265 } 266 267 func initUnexposeCommand(args ...string) (*UnexposeCommand, error) { 268 com := &UnexposeCommand{} 269 return com, coretesting.InitCommand(com, args) 270 } 271 272 func (*CmdSuite) TestUnexposeCommandInit(c *gc.C) { 273 // missing args 274 _, err := initUnexposeCommand() 275 c.Assert(err, gc.ErrorMatches, "no service name specified") 276 277 // environment tested elsewhere 278 } 279 280 func initSSHCommand(args ...string) (*SSHCommand, error) { 281 com := &SSHCommand{} 282 return com, coretesting.InitCommand(com, args) 283 } 284 285 func (*CmdSuite) TestSSHCommandInit(c *gc.C) { 286 // missing args 287 _, err := initSSHCommand() 288 c.Assert(err, gc.ErrorMatches, "no target name specified") 289 } 290 291 func initSCPCommand(args ...string) (*SCPCommand, error) { 292 com := &SCPCommand{} 293 return com, coretesting.InitCommand(com, args) 294 } 295 296 func (*CmdSuite) TestSCPCommandInit(c *gc.C) { 297 // missing args 298 _, err := initSCPCommand() 299 c.Assert(err, gc.ErrorMatches, "at least two arguments required") 300 301 // not enough args 302 _, err = initSCPCommand("mysql/0:foo") 303 c.Assert(err, gc.ErrorMatches, "at least two arguments required") 304 } 305 306 func initGetCommand(args ...string) (*GetCommand, error) { 307 com := &GetCommand{} 308 return com, coretesting.InitCommand(com, args) 309 } 310 311 func (*CmdSuite) TestGetCommandInit(c *gc.C) { 312 // missing args 313 _, err := initGetCommand() 314 c.Assert(err, gc.ErrorMatches, "no service name specified") 315 } 316 317 func initSetCommand(args ...string) (*SetCommand, error) { 318 com := &SetCommand{} 319 return com, coretesting.InitCommand(com, args) 320 } 321 322 func (*CmdSuite) TestSetCommandInit(c *gc.C) { 323 // missing args 324 _, err := initSetCommand() 325 c.Assert(err, gc.ErrorMatches, "no service name specified") 326 // missing service name 327 _, err = initSetCommand("name=cow") 328 c.Assert(err, gc.ErrorMatches, "no service name specified") 329 330 // test --config path 331 expected := []byte("this: is some test data") 332 ctx := coretesting.Context(c) 333 path := ctx.AbsPath("testconfig.yaml") 334 file, err := os.Create(path) 335 c.Assert(err, gc.IsNil) 336 _, err = file.Write(expected) 337 c.Assert(err, gc.IsNil) 338 file.Close() 339 com, err := initSetCommand("--config", "testconfig.yaml", "service") 340 c.Assert(err, gc.IsNil) 341 c.Assert(com.SettingsYAML.Path, gc.Equals, "testconfig.yaml") 342 actual, err := com.SettingsYAML.Read(ctx) 343 c.Assert(err, gc.IsNil) 344 c.Assert(actual, gc.DeepEquals, expected) 345 346 // --config path, but no service 347 com, err = initSetCommand("--config", "testconfig") 348 c.Assert(err, gc.ErrorMatches, "no service name specified") 349 350 // --config and options specified 351 com, err = initSetCommand("service", "--config", "testconfig", "bees=") 352 c.Assert(err, gc.ErrorMatches, "cannot specify --config when using key=value arguments") 353 } 354 355 func initUnsetCommand(args ...string) (*UnsetCommand, error) { 356 com := &UnsetCommand{} 357 return com, coretesting.InitCommand(com, args) 358 } 359 360 func (*CmdSuite) TestUnsetCommandInit(c *gc.C) { 361 // missing args 362 _, err := initUnsetCommand() 363 c.Assert(err, gc.ErrorMatches, "no service name specified") 364 } 365 366 func initDestroyUnitCommand(args ...string) (*DestroyUnitCommand, error) { 367 com := &DestroyUnitCommand{} 368 return com, coretesting.InitCommand(com, args) 369 } 370 371 func (*CmdSuite) TestDestroyUnitCommandInit(c *gc.C) { 372 // missing args 373 _, err := initDestroyUnitCommand() 374 c.Assert(err, gc.ErrorMatches, "no units specified") 375 // not a unit 376 _, err = initDestroyUnitCommand("seven/nine") 377 c.Assert(err, gc.ErrorMatches, `invalid unit name "seven/nine"`) 378 }